aboutsummaryrefslogtreecommitdiffstats
path: root/erts/emulator
diff options
context:
space:
mode:
Diffstat (limited to 'erts/emulator')
-rw-r--r--erts/emulator/Makefile.in84
-rw-r--r--erts/emulator/beam/arith_instrs.tab23
-rw-r--r--erts/emulator/beam/atom.c2
-rw-r--r--erts/emulator/beam/atom.h2
-rw-r--r--erts/emulator/beam/atom.names57
-rw-r--r--erts/emulator/beam/beam_bif_load.c195
-rw-r--r--erts/emulator/beam/beam_bp.c136
-rw-r--r--erts/emulator/beam/beam_debug.c145
-rw-r--r--erts/emulator/beam/beam_emu.c234
-rw-r--r--erts/emulator/beam/beam_load.c228
-rw-r--r--erts/emulator/beam/beam_load.h5
-rw-r--r--erts/emulator/beam/beam_ranges.c28
-rw-r--r--erts/emulator/beam/bif.c1902
-rw-r--r--erts/emulator/beam/bif.h75
-rw-r--r--erts/emulator/beam/bif.tab46
-rw-r--r--erts/emulator/beam/bif_instrs.tab8
-rw-r--r--erts/emulator/beam/big.c15
-rw-r--r--erts/emulator/beam/big.h15
-rw-r--r--erts/emulator/beam/binary.c96
-rw-r--r--erts/emulator/beam/break.c277
-rw-r--r--erts/emulator/beam/bs_instrs.tab47
-rw-r--r--erts/emulator/beam/code_ix.h4
-rw-r--r--erts/emulator/beam/copy.c9
-rw-r--r--erts/emulator/beam/dist.c2636
-rw-r--r--erts/emulator/beam/dist.h209
-rw-r--r--erts/emulator/beam/erl_afit_alloc.c2
-rw-r--r--erts/emulator/beam/erl_alloc.c380
-rw-r--r--erts/emulator/beam/erl_alloc.h12
-rw-r--r--erts/emulator/beam/erl_alloc.types24
-rw-r--r--erts/emulator/beam/erl_alloc_util.c2272
-rw-r--r--erts/emulator/beam/erl_alloc_util.h190
-rw-r--r--erts/emulator/beam/erl_ao_firstfit_alloc.c293
-rw-r--r--erts/emulator/beam/erl_ao_firstfit_alloc.h16
-rw-r--r--erts/emulator/beam/erl_arith.c14
-rw-r--r--erts/emulator/beam/erl_bestfit_alloc.c2
-rw-r--r--erts/emulator/beam/erl_bif_binary.c232
-rw-r--r--erts/emulator/beam/erl_bif_chksum.c6
-rw-r--r--erts/emulator/beam/erl_bif_ddll.c15
-rw-r--r--erts/emulator/beam/erl_bif_info.c2188
-rw-r--r--erts/emulator/beam/erl_bif_os.c168
-rw-r--r--erts/emulator/beam/erl_bif_port.c229
-rw-r--r--erts/emulator/beam/erl_bif_re.c18
-rw-r--r--erts/emulator/beam/erl_bif_trace.c26
-rw-r--r--erts/emulator/beam/erl_bif_unique.h4
-rw-r--r--erts/emulator/beam/erl_binary.h7
-rw-r--r--erts/emulator/beam/erl_bits.h2
-rw-r--r--erts/emulator/beam/erl_cpu_topology.c2
-rw-r--r--erts/emulator/beam/erl_db.c37
-rw-r--r--erts/emulator/beam/erl_db.h1
-rw-r--r--erts/emulator/beam/erl_db_hash.c62
-rw-r--r--erts/emulator/beam/erl_db_util.c151
-rw-r--r--erts/emulator/beam/erl_debug.c36
-rw-r--r--erts/emulator/beam/erl_drv_thread.c8
-rw-r--r--erts/emulator/beam/erl_gc.c493
-rw-r--r--erts/emulator/beam/erl_gc.h31
-rw-r--r--erts/emulator/beam/erl_goodfit_alloc.c14
-rw-r--r--erts/emulator/beam/erl_hl_timer.c40
-rw-r--r--erts/emulator/beam/erl_init.c191
-rw-r--r--erts/emulator/beam/erl_instrument.c1257
-rw-r--r--erts/emulator/beam/erl_instrument.h42
-rw-r--r--erts/emulator/beam/erl_io_queue.c66
-rw-r--r--erts/emulator/beam/erl_io_queue.h6
-rw-r--r--erts/emulator/beam/erl_lock_check.c805
-rw-r--r--erts/emulator/beam/erl_lock_check.h2
-rw-r--r--erts/emulator/beam/erl_map.c504
-rw-r--r--erts/emulator/beam/erl_map.h1
-rw-r--r--erts/emulator/beam/erl_message.c449
-rw-r--r--erts/emulator/beam/erl_message.h318
-rw-r--r--erts/emulator/beam/erl_monitor_link.c1341
-rw-r--r--erts/emulator/beam/erl_monitor_link.h2326
-rw-r--r--erts/emulator/beam/erl_monitors.c1073
-rw-r--r--erts/emulator/beam/erl_monitors.h187
-rw-r--r--erts/emulator/beam/erl_msacc.c4
-rw-r--r--erts/emulator/beam/erl_msacc.h36
-rw-r--r--erts/emulator/beam/erl_mtrace.c22
-rw-r--r--erts/emulator/beam/erl_nfunc_sched.h4
-rw-r--r--erts/emulator/beam/erl_nif.c936
-rw-r--r--erts/emulator/beam/erl_nif.h19
-rw-r--r--erts/emulator/beam/erl_nif_api_funcs.h21
-rw-r--r--erts/emulator/beam/erl_node_container_utils.h2
-rw-r--r--erts/emulator/beam/erl_node_tables.c699
-rw-r--r--erts/emulator/beam/erl_node_tables.h77
-rw-r--r--erts/emulator/beam/erl_port.h87
-rw-r--r--erts/emulator/beam/erl_port_task.c25
-rw-r--r--erts/emulator/beam/erl_posix_str.c6
-rw-r--r--erts/emulator/beam/erl_printf_term.c7
-rw-r--r--erts/emulator/beam/erl_proc_sig_queue.c4274
-rw-r--r--erts/emulator/beam/erl_proc_sig_queue.h872
-rw-r--r--erts/emulator/beam/erl_process.c2460
-rw-r--r--erts/emulator/beam/erl_process.h309
-rw-r--r--erts/emulator/beam/erl_process_dict.c229
-rw-r--r--erts/emulator/beam/erl_process_dict.h2
-rw-r--r--erts/emulator/beam/erl_process_dump.c642
-rw-r--r--erts/emulator/beam/erl_process_lock.c98
-rw-r--r--erts/emulator/beam/erl_process_lock.h56
-rw-r--r--erts/emulator/beam/erl_ptab.h6
-rw-r--r--erts/emulator/beam/erl_rbtree.h181
-rw-r--r--erts/emulator/beam/erl_sched_spec_pre_alloc.c13
-rw-r--r--erts/emulator/beam/erl_term.h16
-rw-r--r--erts/emulator/beam/erl_thr_progress.h2
-rw-r--r--erts/emulator/beam/erl_time.h11
-rw-r--r--erts/emulator/beam/erl_time_sup.c97
-rw-r--r--erts/emulator/beam/erl_trace.c36
-rw-r--r--erts/emulator/beam/erl_unicode.c267
-rw-r--r--erts/emulator/beam/erl_unicode.h5
-rw-r--r--erts/emulator/beam/erl_utils.h2
-rw-r--r--erts/emulator/beam/erl_vm.h21
-rw-r--r--erts/emulator/beam/erlang_dtrace.d81
-rw-r--r--erts/emulator/beam/export.c8
-rw-r--r--erts/emulator/beam/export.h2
-rw-r--r--erts/emulator/beam/external.c593
-rw-r--r--erts/emulator/beam/external.h53
-rw-r--r--erts/emulator/beam/global.h44
-rw-r--r--erts/emulator/beam/hash.c4
-rw-r--r--erts/emulator/beam/index.c2
-rw-r--r--erts/emulator/beam/instrs.tab76
-rw-r--r--erts/emulator/beam/io.c1018
-rw-r--r--erts/emulator/beam/lttng-wrapper.h4
-rw-r--r--erts/emulator/beam/macros.tab22
-rw-r--r--erts/emulator/beam/map_instrs.tab8
-rw-r--r--erts/emulator/beam/module.c1
-rw-r--r--erts/emulator/beam/module.h2
-rw-r--r--erts/emulator/beam/msg_instrs.tab106
-rw-r--r--erts/emulator/beam/ops.tab307
-rw-r--r--erts/emulator/beam/packet_parser.c8
-rw-r--r--erts/emulator/beam/sys.h222
-rw-r--r--erts/emulator/beam/trace_instrs.tab4
-rw-r--r--erts/emulator/beam/utils.c39
-rw-r--r--erts/emulator/drivers/common/efile_drv.c4240
-rw-r--r--erts/emulator/drivers/common/erl_efile.h176
-rw-r--r--erts/emulator/drivers/common/gzio.c712
-rw-r--r--erts/emulator/drivers/common/gzio.h8
-rw-r--r--erts/emulator/drivers/common/inet_drv.c476
-rw-r--r--erts/emulator/drivers/unix/unix_efile.c1102
-rw-r--r--erts/emulator/drivers/win32/win_efile.c2058
-rw-r--r--erts/emulator/hipe/hipe_amd64.c117
-rw-r--r--erts/emulator/hipe/hipe_amd64_bifs.m41
-rw-r--r--erts/emulator/hipe/hipe_bif0.c13
-rw-r--r--erts/emulator/hipe/hipe_bif0.tab3
-rw-r--r--erts/emulator/hipe/hipe_bif1.c23
-rw-r--r--erts/emulator/hipe/hipe_bif2.c5
-rw-r--r--erts/emulator/hipe/hipe_bif2.tab1
-rw-r--r--erts/emulator/hipe/hipe_bif_list.m48
-rw-r--r--erts/emulator/hipe/hipe_debug.c32
-rw-r--r--erts/emulator/hipe/hipe_gc.c16
-rw-r--r--erts/emulator/hipe/hipe_instrs.tab6
-rw-r--r--erts/emulator/hipe/hipe_mkliterals.c26
-rw-r--r--erts/emulator/hipe/hipe_mode_switch.c21
-rw-r--r--erts/emulator/hipe/hipe_native_bif.c134
-rw-r--r--erts/emulator/hipe/hipe_native_bif.h15
-rw-r--r--erts/emulator/hipe/hipe_primops.h4
-rw-r--r--erts/emulator/hipe/hipe_risc_stack.c8
-rw-r--r--erts/emulator/hipe/hipe_x86_stack.c8
-rw-r--r--erts/emulator/internal_doc/CarrierMigration.md138
-rw-r--r--erts/emulator/internal_doc/GarbageCollection.md187
-rw-r--r--erts/emulator/internal_doc/beam_makeops.md1846
-rw-r--r--erts/emulator/internal_doc/figures/.gitignore1
-rw-r--r--erts/emulator/internal_doc/figures/Makefile20
-rw-r--r--erts/emulator/internal_doc/figures/gc-heap-scan1.diabin0 -> 2671 bytes
-rw-r--r--erts/emulator/internal_doc/figures/gc-heap-scan1.pngbin0 -> 63026 bytes
-rw-r--r--erts/emulator/internal_doc/figures/gc-heap-stop.diabin0 -> 2612 bytes
-rw-r--r--erts/emulator/internal_doc/figures/gc-heap-stop.pngbin0 -> 62596 bytes
-rw-r--r--erts/emulator/internal_doc/figures/gc-rootset-scan.diabin0 -> 2482 bytes
-rw-r--r--erts/emulator/internal_doc/figures/gc-rootset-scan.pngbin0 -> 59022 bytes
-rw-r--r--erts/emulator/internal_doc/figures/gc-start.diabin0 -> 1812 bytes
-rw-r--r--erts/emulator/internal_doc/figures/gc-start.pngbin0 -> 36619 bytes
-rw-r--r--erts/emulator/internal_doc/figures/gc-watermark-2.diabin0 -> 2571 bytes
-rw-r--r--erts/emulator/internal_doc/figures/gc-watermark-2.pngbin0 -> 56645 bytes
-rw-r--r--erts/emulator/internal_doc/figures/gc-watermark.diabin0 -> 1953 bytes
-rw-r--r--erts/emulator/internal_doc/figures/gc-watermark.pngbin0 -> 48480 bytes
-rw-r--r--erts/emulator/nifs/common/prim_buffer_nif.c512
-rw-r--r--erts/emulator/nifs/common/prim_file_nif.c1237
-rw-r--r--erts/emulator/nifs/common/prim_file_nif.h240
-rw-r--r--erts/emulator/nifs/common/zlib_nif.c197
-rw-r--r--erts/emulator/nifs/unix/unix_prim_file.c944
-rw-r--r--erts/emulator/nifs/win32/win_prim_file.c1428
-rw-r--r--erts/emulator/pcre/README.pcre_update.md2
-rw-r--r--erts/emulator/sys/common/erl_check_io.c4
-rw-r--r--erts/emulator/sys/common/erl_mmap.c103
-rw-r--r--erts/emulator/sys/common/erl_mmap.h16
-rw-r--r--erts/emulator/sys/common/erl_mseg.c10
-rw-r--r--erts/emulator/sys/common/erl_mseg.h2
-rw-r--r--erts/emulator/sys/common/erl_osenv.c396
-rw-r--r--erts/emulator/sys/common/erl_osenv.h121
-rw-r--r--erts/emulator/sys/common/erl_poll.c18
-rw-r--r--erts/emulator/sys/common/erl_sys_common_misc.c195
-rw-r--r--erts/emulator/sys/unix/erl_child_setup.c17
-rw-r--r--erts/emulator/sys/unix/erl_unix_sys.h2
-rw-r--r--erts/emulator/sys/unix/sys.c127
-rw-r--r--erts/emulator/sys/unix/sys_drivers.c186
-rw-r--r--erts/emulator/sys/unix/sys_env.c133
-rw-r--r--erts/emulator/sys/unix/sys_uds.c36
-rw-r--r--erts/emulator/sys/unix/sys_uds.h14
-rw-r--r--erts/emulator/sys/win32/erl_win32_sys_ddll.c13
-rw-r--r--erts/emulator/sys/win32/sys.c87
-rw-r--r--erts/emulator/sys/win32/sys_env.c531
-rw-r--r--erts/emulator/test/Makefile1
-rw-r--r--erts/emulator/test/alloc_SUITE.erl151
-rw-r--r--erts/emulator/test/alloc_SUITE_data/allocator_test.h5
-rw-r--r--erts/emulator/test/alloc_SUITE_data/migration.c112
-rw-r--r--erts/emulator/test/beam_SUITE.erl25
-rw-r--r--erts/emulator/test/beam_literals_SUITE.erl55
-rw-r--r--erts/emulator/test/bif_SUITE.erl251
-rw-r--r--erts/emulator/test/binary_SUITE.erl135
-rw-r--r--erts/emulator/test/call_trace_SUITE.erl4
-rw-r--r--erts/emulator/test/code_SUITE.erl115
-rw-r--r--erts/emulator/test/code_SUITE_data/erl_544.erl35
-rw-r--r--erts/emulator/test/ddll_SUITE.erl2
-rw-r--r--erts/emulator/test/decode_packet_SUITE.erl12
-rw-r--r--erts/emulator/test/dirty_bif_SUITE.erl87
-rw-r--r--erts/emulator/test/dirty_nif_SUITE.erl46
-rw-r--r--erts/emulator/test/dirty_nif_SUITE_data/dirty_nif_SUITE.c35
-rw-r--r--erts/emulator/test/distribution_SUITE.erl305
-rw-r--r--erts/emulator/test/driver_SUITE.erl74
-rw-r--r--erts/emulator/test/driver_SUITE_data/Makefile.src3
-rw-r--r--erts/emulator/test/driver_SUITE_data/chkio_drv.c17
-rw-r--r--erts/emulator/test/driver_SUITE_data/env_drv.c108
-rw-r--r--erts/emulator/test/dump_SUITE.erl125
-rw-r--r--erts/emulator/test/efile_SUITE.erl152
-rw-r--r--erts/emulator/test/emulator_smoke.spec1
-rw-r--r--erts/emulator/test/erl_link_SUITE.erl279
-rw-r--r--erts/emulator/test/exception_SUITE.erl85
-rw-r--r--erts/emulator/test/guard_SUITE.erl5
-rw-r--r--erts/emulator/test/iovec_SUITE.erl85
-rw-r--r--erts/emulator/test/lcnt_SUITE.erl45
-rw-r--r--erts/emulator/test/map_SUITE.erl98
-rw-r--r--erts/emulator/test/match_spec_SUITE.erl48
-rw-r--r--erts/emulator/test/module_info_SUITE.erl18
-rw-r--r--erts/emulator/test/monitor_SUITE.erl10
-rw-r--r--erts/emulator/test/nif_SUITE.erl68
-rw-r--r--erts/emulator/test/nif_SUITE_data/nif_SUITE.c81
-rw-r--r--erts/emulator/test/node_container_SUITE.erl2
-rw-r--r--erts/emulator/test/num_bif_SUITE.erl109
-rw-r--r--erts/emulator/test/port_SUITE.erl24
-rw-r--r--erts/emulator/test/port_trace_SUITE.erl14
-rw-r--r--erts/emulator/test/process_SUITE.erl213
-rw-r--r--erts/emulator/test/receive_SUITE.erl62
-rw-r--r--erts/emulator/test/scheduler_SUITE.erl6
-rw-r--r--erts/emulator/test/signal_SUITE.erl412
-rw-r--r--erts/emulator/test/smoke_test_SUITE.erl14
-rw-r--r--erts/emulator/test/system_info_SUITE.erl6
-rw-r--r--erts/emulator/test/trace_SUITE.erl124
-rw-r--r--erts/emulator/test/tracer_SUITE.erl24
-rw-r--r--erts/emulator/test/z_SUITE.erl8
-rwxr-xr-xerts/emulator/utils/beam_emu_vars122
-rwxr-xr-xerts/emulator/utils/beam_makeops844
-rwxr-xr-xerts/emulator/utils/make_driver_tab38
247 files changed, 34734 insertions, 24111 deletions
diff --git a/erts/emulator/Makefile.in b/erts/emulator/Makefile.in
index 3069c5cb34..5dfa60ee74 100644
--- a/erts/emulator/Makefile.in
+++ b/erts/emulator/Makefile.in
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1996-2017. All Rights Reserved.
+# Copyright Ericsson AB 1996-2018. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -74,9 +74,8 @@ endif
endif
ifeq ($(PROFILE_COMPILER), gcc)
-PROFILE_CORRECTION=@PROFILE_CORRECTION@
PROFILE_GENERATE=-fprofile-generate
-PROFILE_USE=-fprofile-use $(PROFILE_CORRECTION)
+PROFILE_USE=-fprofile-use -fprofile-correction
PROFILE_USE_DEPS=$(OBJDIR)/%_pu.gcda
endif
ifeq ($(PROFILE_COMPILER), clang)
@@ -240,16 +239,7 @@ ARCH=@ARCH@
ultrasparcCFLAGS=-Wa,-xarch=v8plusa
ARCHCFLAGS=$($(ARCH)CFLAGS)
-ifdef HIPE_ENABLED
-ifeq ($(OPSYS),linux)
-ppcBEAMLDFLAGS=-Wl,-m,elf32ppc
-ppc64BEAMLDFLAGS=-Wl,-m,elf64ppc,-T,hipe/elf64ppc.x
-endif
-ifeq ($(OPSYS),darwin)
-amd64BEAMLDFLAGS=-pagezero_size 0x10000000
-endif
-HIPEBEAMLDFLAGS=$($(ARCH)BEAMLDFLAGS)
-endif
+HIPEBEAMLDFLAGS=@HIPEBEAMLDFLAGS@
ERTS_BUILD_FALLBACK_POLL=@ERTS_BUILD_FALLBACK_POLL@
@@ -499,7 +489,6 @@ ifeq ($(TARGET),win32)
RELEASE_INCLUDES += sys/$(ERLANG_OSTYPE)/erl_win_dyn_driver.h
endif
-
.PHONY: release_spec
ifdef VOID_EMULATOR
release_spec:
@@ -578,6 +567,7 @@ $(TTF_DIR)/beam_tr_funcs.h \
$(TTF_DIR)/OPCODES-GENERATED: $(OPCODE_TABLES) utils/beam_makeops
$(gen_verbose)LANG=C $(PERL) utils/beam_makeops \
-wordsize @EXTERNAL_WORD_SIZE@ \
+ -code-model @CODE_MODEL@ \
-outdir $(TTF_DIR) \
-DUSE_VM_PROBES=$(if $(USE_VM_PROBES),1,0) \
-DNO_FPE_SIGNALS=$(if $filter(unreliable,$(FPE)),1,0) \
@@ -644,6 +634,7 @@ GENERATE += $(TTF_DIR)/driver_tab.c
PRELOAD_BEAM = $(ERL_TOP)/erts/preloaded/ebin/otp_ring0.beam \
$(ERL_TOP)/erts/preloaded/ebin/erts_code_purger.beam \
$(ERL_TOP)/erts/preloaded/ebin/init.beam \
+ $(ERL_TOP)/erts/preloaded/ebin/prim_buffer.beam \
$(ERL_TOP)/erts/preloaded/ebin/prim_eval.beam \
$(ERL_TOP)/erts/preloaded/ebin/prim_inet.beam \
$(ERL_TOP)/erts/preloaded/ebin/prim_file.beam \
@@ -654,7 +645,7 @@ PRELOAD_BEAM = $(ERL_TOP)/erts/preloaded/ebin/otp_ring0.beam \
$(ERL_TOP)/erts/preloaded/ebin/erts_internal.beam \
$(ERL_TOP)/erts/preloaded/ebin/erl_tracer.beam \
$(ERL_TOP)/erts/preloaded/ebin/erts_literal_area_collector.beam \
- $(ERL_TOP)/erts/preloaded/ebin/erts_dirty_process_code_checker.beam
+ $(ERL_TOP)/erts/preloaded/ebin/erts_dirty_process_signal_handler.beam
ifeq ($(TARGET),win32)
# On windows the preloaded objects are in a resource object.
@@ -711,16 +702,27 @@ $(OBJDIR)/beams.$(RES_EXT): $(TARGET)/beams.rc
endif
+# We disable the implicit rule of .S -> .o so that the verbose asm
+# generate is not used for compiling erts. This is only a problem on
+# old solaris make
+%.o : %.S
+
# Usually the same as the default rule, but certain platforms (e.g. win32) mix
# different compilers
$(OBJDIR)/beam_emu.o: beam/beam_emu.c
$(V_EMU_CC) $(subst -O2, $(GEN_OPT_FLGS), $(CFLAGS)) $(INCLUDES) -c $< -o $@
+$(OBJDIR)/beam_emu.S: beam/beam_emu.c
+ $(V_EMU_CC) -S -fverbose-asm $(subst -O2, $(GEN_OPT_FLGS), $(CFLAGS)) $(INCLUDES) -c $< -o $@
+
$(OBJDIR)/%_pg.o: beam/%.c
$(V_CC) $(PROFILE_GENERATE) $(subst -O2, $(GEN_OPT_FLGS), $(CFLAGS)) $(INCLUDES) -c $< -o $@
$(OBJDIR)/%_pu.o: beam/%.c $(PROFILE_USE_DEPS)
$(V_CC) $(PROFILE_USE) $(subst -O2, $(GEN_OPT_FLGS), $(CFLAGS)) $(INCLUDES) -c $< -o $@
+$(OBJDIR)/%_pu.S: beam/%.c $(PROFILE_USE_DEPS)
+ $(V_CC) -S -fverbose-asm $(PROFILE_USE) $(subst -O2, $(GEN_OPT_FLGS), $(CFLAGS)) $(INCLUDES) -c $< -o $@
+
$(OBJDIR)/PROFILE: $(BINDIR)/$(PROFILE_EXECUTABLE)
$(V_at)echo " PROFILE ${PROFILE_EXECUTABLE}"
$(V_at)rm -f $(OBJDIR)/erl*.profraw
@@ -732,6 +734,8 @@ $(OBJDIR)/PROFILE: $(BINDIR)/$(PROFILE_EXECUTABLE)
-noshell -s estone_SUITE pgo -s init stop >> $(OBJDIR)/PROFILE_LOG
$(V_at)touch $@
+.SECONDARY: $(patsubst %.o,%_pu.gcda,$(PROFILE_OBJS))
+
$(OBJDIR)/%_pu.gcda: $(OBJDIR)/PROFILE
$(V_at)mv $(OBJDIR)/$*_pg.gcda $@
$(V_at)touch $@
@@ -782,6 +786,9 @@ $(OBJDIR)/%.o: drivers/$(ERLANG_OSTYPE)/%.c
$(OBJDIR)/%.o: nifs/common/%.c
$(V_CC) $(CFLAGS) -DLIBSCTP=$(LIBSCTP) $(INCLUDES) -Inifs/common -Inifs/$(ERLANG_OSTYPE) -c $< -o $@
+$(OBJDIR)/%.o: nifs/$(ERLANG_OSTYPE)/%.c
+ $(V_CC) $(CFLAGS) $(INCLUDES) -Inifs/common -Inifs/$(ERLANG_OSTYPE) -I../etc/$(ERLANG_OSTYPE) -c $< -o $@
+
# ----------------------------------------------------------------------
# Specials
#
@@ -812,14 +819,7 @@ $(ERL_TOP)/lib/%.beam:
INIT_OBJS = $(OBJDIR)/erl_main.o $(PRELOAD_OBJ)
-# -fprofile-correction is needed in order to use PGO on erl_process
-# as multiple threads execute in that file.
-ifeq ($(PROFILE_CORRECTION),)
-PROFILE_OBJS = $(OBJDIR)/beam_emu.o
-RUN_OBJS = $(OBJDIR)/erl_process.o
-else
PROFILE_OBJS = $(OBJDIR)/beam_emu.o $(OBJDIR)/erl_process.o
-endif
EMU_OBJS = \
$(OBJDIR)/beam_opcodes.o \
@@ -832,7 +832,7 @@ RUN_OBJS += \
$(OBJDIR)/erl_alloc.o $(OBJDIR)/erl_mtrace.o \
$(OBJDIR)/erl_alloc_util.o $(OBJDIR)/erl_goodfit_alloc.o \
$(OBJDIR)/erl_bestfit_alloc.o $(OBJDIR)/erl_afit_alloc.o \
- $(OBJDIR)/erl_instrument.o $(OBJDIR)/erl_init.o \
+ $(OBJDIR)/erl_init.o \
$(OBJDIR)/erl_atom_table.o $(OBJDIR)/erl_bif_table.o \
$(OBJDIR)/erl_bif_ddll.o $(OBJDIR)/erl_bif_guard.o \
$(OBJDIR)/erl_bif_info.o $(OBJDIR)/erl_bif_op.o \
@@ -844,7 +844,7 @@ RUN_OBJS += \
$(OBJDIR)/utils.o $(OBJDIR)/bif.o \
$(OBJDIR)/io.o $(OBJDIR)/erl_printf_term.o\
$(OBJDIR)/erl_debug.o $(OBJDIR)/erl_md5.o \
- $(OBJDIR)/erl_message.o \
+ $(OBJDIR)/erl_message.o $(OBJDIR)/erl_proc_sig_queue.o \
$(OBJDIR)/erl_process_dict.o $(OBJDIR)/erl_process_lock.o \
$(OBJDIR)/erl_port_task.o $(OBJDIR)/erl_arith.o \
$(OBJDIR)/time.o $(OBJDIR)/erl_time_sup.o \
@@ -858,11 +858,11 @@ RUN_OBJS += \
$(OBJDIR)/register.o $(OBJDIR)/break.o \
$(OBJDIR)/erl_async.o $(OBJDIR)/erl_lock_check.o \
$(OBJDIR)/erl_gc.o $(OBJDIR)/erl_lock_count.o \
- $(OBJDIR)/erl_posix_str.o \
+ $(OBJDIR)/erl_posix_str.o \
$(OBJDIR)/erl_bits.o $(OBJDIR)/erl_math.o \
$(OBJDIR)/erl_fun.o $(OBJDIR)/erl_bif_port.o \
$(OBJDIR)/erl_term.o $(OBJDIR)/erl_node_tables.o \
- $(OBJDIR)/erl_monitors.o $(OBJDIR)/erl_process_dump.o \
+ $(OBJDIR)/erl_monitor_link.o $(OBJDIR)/erl_process_dump.o \
$(OBJDIR)/erl_hl_timer.o $(OBJDIR)/erl_cpu_topology.o \
$(OBJDIR)/erl_drv_thread.o $(OBJDIR)/erl_bif_chksum.o \
$(OBJDIR)/erl_bif_re.o $(OBJDIR)/erl_unicode.o \
@@ -877,17 +877,17 @@ RUN_OBJS += \
LTTNG_OBJS = $(OBJDIR)/erlang_lttng.o
NIF_OBJS = \
$(OBJDIR)/erl_tracer_nif.o \
+ $(OBJDIR)/prim_buffer_nif.o \
+ $(OBJDIR)/prim_file_nif.o \
$(OBJDIR)/zlib_nif.o
ifeq ($(TARGET),win32)
DRV_OBJS = \
$(OBJDIR)/registry_drv.o \
- $(OBJDIR)/efile_drv.o \
$(OBJDIR)/inet_drv.o \
$(OBJDIR)/ram_file_drv.o \
$(OBJDIR)/ttsl_drv.o
OS_OBJS = \
- $(OBJDIR)/win_efile.o \
$(OBJDIR)/win_con.o \
$(OBJDIR)/dll_sys.o \
$(OBJDIR)/driver_tab.o \
@@ -896,22 +896,23 @@ OS_OBJS = \
$(OBJDIR)/sys_time.o \
$(OBJDIR)/sys_interrupt.o \
$(OBJDIR)/sys_env.o \
- $(OBJDIR)/dosmap.o
+ $(OBJDIR)/dosmap.o \
+ $(OBJDIR)/win_prim_file.o
else
OS_OBJS = \
$(OBJDIR)/sys.o \
$(OBJDIR)/sys_drivers.o \
+ $(OBJDIR)/sys_env.o \
$(OBJDIR)/sys_uds.o \
$(OBJDIR)/driver_tab.o \
- $(OBJDIR)/unix_efile.o \
+ $(OBJDIR)/elib_memmove.o \
$(OBJDIR)/gzio.o \
- $(OBJDIR)/elib_memmove.o
+ $(OBJDIR)/unix_prim_file.o
OS_OBJS += $(OBJDIR)/sys_float.o \
$(OBJDIR)/sys_time.o
DRV_OBJS = \
- $(OBJDIR)/efile_drv.o \
$(OBJDIR)/inet_drv.o \
$(OBJDIR)/ram_file_drv.o \
$(OBJDIR)/ttsl_drv.o
@@ -944,7 +945,8 @@ endif
OS_OBJS += $(OBJDIR)/erl_poll.o \
$(OBJDIR)/erl_check_io.o \
$(OBJDIR)/erl_mseg.o \
- $(OBJDIR)/erl_mmap.o \
+ $(OBJDIR)/erl_mmap.o \
+ $(OBJDIR)/erl_osenv.o \
$(OBJDIR)/erl_$(ERLANG_OSTYPE)_sys_ddll.o \
$(OBJDIR)/erl_mtrace_sys_wrap.o \
$(OBJDIR)/erl_sys_common_misc.o \
@@ -1139,6 +1141,7 @@ BEAM_SRC=$(wildcard beam/*.c)
DRV_COMMON_SRC=$(wildcard drivers/common/*.c)
DRV_OSTYPE_SRC=$(wildcard drivers/$(ERLANG_OSTYPE)/*.c)
NIF_COMMON_SRC=$(wildcard nifs/common/*.c)
+NIF_OSTYPE_SRC=$(wildcard nifs/$(ERLANG_OSTYPE)/*.c)
ALL_SYS_SRC=$(wildcard sys/$(ERLANG_OSTYPE)/*.c) $(wildcard sys/common/*.c)
# We use $(shell ls) here instead of wildcard as $(wildcard ) resolved at
# loadtime of the makefile and at that time these files are not generated yet.
@@ -1151,7 +1154,10 @@ ifeq ($(TARGET),win32)
#DEP_CC=$(EMU_CC)
DEP_CC=$(CC)
-DEP_FLAGS=-MM $(subst -O2,,$(CFLAGS)) $(INCLUDES) -I../etc/win32 -Idrivers/common -Idrivers/$(ERLANG_OSTYPE)
+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)
# VC++ used for compiling. If __GNUC__ is defined we will include
# other headers then when compiling which will result in faulty
@@ -1171,10 +1177,16 @@ MG_FLAG=-MG
endif
DEP_CC=$(CC)
-DEP_FLAGS=-MM $(MG_FLAG) $(CFLAGS) $(INCLUDES) -Inifs/common -Idrivers/common -Idrivers/$(ERLANG_OSTYPE)
+DEP_FLAGS=-MM $(MG_FLAG) $(CFLAGS) $(INCLUDES) \
+ -Idrivers/common -Idrivers/$(ERLANG_OSTYPE) \
+ -Inifs/common -Inifs/$(ERLANG_OSTYPE)
SYS_SRC=$(ALL_SYS_SRC)
endif
+.PHONY: check_emu_registers
+check_emu_registers: $(OBJDIR)/beam_emu$(PROFILE_MARKER).S
+ utils/beam_emu_vars -vars 'c_p E HTOP FCALLS I reg' $^
+
.PHONY: $(TARGET)/gen_git_version.mk
$(TARGET)/gen_git_version.mk:
# We touch beam/erl_bif.info.c if we regenerated the git version to force a
@@ -1198,6 +1210,8 @@ $(TTF_DIR)/depend.mk: $(TTF_DIR)/GENERATED $(PRELOAD_SRC)
| $(SED_DEPEND) >> $(TTF_DIR)/depend.mk
$(V_at)$(DEP_CC) $(DEP_FLAGS) $(NIF_COMMON_SRC) \
| $(SED_DEPEND) >> $(TTF_DIR)/depend.mk
+ $(V_at)$(DEP_CC) $(DEP_FLAGS) -I../etc/$(ERLANG_OSTYPE) $(NIF_OSTYPE_SRC) \
+ | $(SED_DEPEND) >> $(TTF_DIR)/depend.mk
$(V_at)$(DEP_CC) $(DEP_FLAGS) $(SYS_SRC) \
| $(SED_DEPEND) >> $(TTF_DIR)/depend.mk
$(V_at)$(DEP_CC) $(DEP_FLAGS) $(TARGET_SRC) \
diff --git a/erts/emulator/beam/arith_instrs.tab b/erts/emulator/beam/arith_instrs.tab
index 7c9cd47e28..b828e86788 100644
--- a/erts/emulator/beam/arith_instrs.tab
+++ b/erts/emulator/beam/arith_instrs.tab
@@ -51,8 +51,7 @@ plus.fetch(Op1, Op2) {
plus.execute(Fail, Live, Dst) {
if (ERTS_LIKELY(is_both_small(PlusOp1, PlusOp2))) {
Sint i = signed_val(PlusOp1) + signed_val(PlusOp2);
- ASSERT(MY_IS_SSMALL(i) == IS_SSMALL(i));
- if (ERTS_LIKELY(MY_IS_SSMALL(i))) {
+ if (ERTS_LIKELY(IS_SSMALL(i))) {
$Dst = make_small(i);
$NEXT0();
}
@@ -74,8 +73,7 @@ minus.fetch(Op1, Op2) {
minus.execute(Fail, Live, Dst) {
if (ERTS_LIKELY(is_both_small(MinusOp1, MinusOp2))) {
Sint i = signed_val(MinusOp1) - signed_val(MinusOp2);
- ASSERT(MY_IS_SSMALL(i) == IS_SSMALL(i));
- if (ERTS_LIKELY(MY_IS_SSMALL(i))) {
+ if (ERTS_LIKELY(IS_SSMALL(i))) {
$Dst = make_small(i);
$NEXT0();
}
@@ -87,9 +85,6 @@ i_increment := increment.fetch.execute;
increment.head() {
Eterm increment_reg_val;
- Eterm increment_val;
- Uint live;
- Eterm result;
}
increment.fetch(Src) {
@@ -97,11 +92,13 @@ increment.fetch(Src) {
}
increment.execute(IncrementVal, Live, Dst) {
- increment_val = $IncrementVal;
+ Eterm increment_val = $IncrementVal;
+ Uint live;
+ Eterm result;
+
if (ERTS_LIKELY(is_small(increment_reg_val))) {
Sint i = signed_val(increment_reg_val) + increment_val;
- ASSERT(MY_IS_SSMALL(i) == IS_SSMALL(i));
- if (ERTS_LIKELY(MY_IS_SSMALL(i))) {
+ if (ERTS_LIKELY(IS_SSMALL(i))) {
$Dst = make_small(i);
$NEXT0();
}
@@ -142,7 +139,7 @@ i_int_div(Fail, Live, 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);
- if (ERTS_LIKELY(MY_IS_SSMALL(ires))) {
+ if (ERTS_LIKELY(IS_SSMALL(ires))) {
$Dst = make_small(ires);
$NEXT0();
}
@@ -369,10 +366,10 @@ shift.execute(Fail, Live, Dst) {
reg[0] = Op1;
reg[1] = Op2;
SWAPOUT;
- if (I[0] == (BeamInstr) OpCode(i_bsl_ssjtd)) {
+ if (IsOpCode(I[0], i_bsl_ssjtd)) {
I = handle_error(c_p, I, reg, &bif_export[BIF_bsl_2]->info.mfa);
} else {
- ASSERT(I[0] == (BeamInstr) OpCode(i_bsr_ssjtd));
+ ASSERT(IsOpCode(I[0], i_bsr_ssjtd));
I = handle_error(c_p, I, reg, &bif_export[BIF_bsr_2]->info.mfa);
}
goto post_error_handling;
diff --git a/erts/emulator/beam/atom.c b/erts/emulator/beam/atom.c
index bbe1cb3e11..e5b7616a0d 100644
--- a/erts/emulator/beam/atom.c
+++ b/erts/emulator/beam/atom.c
@@ -452,7 +452,7 @@ init_atom_table(void)
/* Ordinary atoms */
for (i = 0; erl_atom_names[i] != 0; i++) {
int ix;
- a.len = strlen(erl_atom_names[i]);
+ a.len = sys_strlen(erl_atom_names[i]);
a.latin1_chars = a.len;
a.name = (byte*)erl_atom_names[i];
a.slot.index = i;
diff --git a/erts/emulator/beam/atom.h b/erts/emulator/beam/atom.h
index be998a46bd..385120a8d9 100644
--- a/erts/emulator/beam/atom.h
+++ b/erts/emulator/beam/atom.h
@@ -36,7 +36,7 @@
/* Internal atom cache needs MAX_ATOM_TABLE_SIZE to be less than an
unsigned 32 bit integer. See external.c(erts_encode_ext_dist_header_setup)
for more details. */
-#define MAX_ATOM_TABLE_SIZE ((MAX_ATOM_INDEX + 1 < (UWORD_CONSTANT(1) << 32)) ? MAX_ATOM_INDEX + 1 : (UWORD_CONSTANT(1) << 32))
+#define MAX_ATOM_TABLE_SIZE ((MAX_ATOM_INDEX + 1 < (UWORD_CONSTANT(1) << 32)) ? MAX_ATOM_INDEX + 1 : ((UWORD_CONSTANT(1) << 31) - 1)) /* Here we use maximum signed interger value to avoid integer overflow */
#else
#define MAX_ATOM_TABLE_SIZE (MAX_ATOM_INDEX + 1)
#endif
diff --git a/erts/emulator/beam/atom.names b/erts/emulator/beam/atom.names
index fc55b687d4..cceca66850 100644
--- a/erts/emulator/beam/atom.names
+++ b/erts/emulator/beam/atom.names
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1996-2017. All Rights Reserved.
+# Copyright Ericsson AB 1996-2018. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -48,7 +48,7 @@ atom Empty=''
#
# Used in the Beam emulator loop. (Smaller literals usually means tighter code.)
#
-atom fun infinity timeout normal call return
+atom infinity timeout normal call return
atom throw error exit
atom undefined
@@ -69,11 +69,9 @@ atom DOWN='DOWN'
atom UP='UP'
atom EXIT='EXIT'
atom abort
-atom aborted
atom abs_path
atom absoluteURI
atom ac
-atom accessor
atom active
atom active_tasks
atom active_tasks_all
@@ -88,7 +86,6 @@ atom allocated_areas
atom allocator
atom allocator_sizes
atom alloc_util_allocators
-atom allow_gc
atom allow_passive_connect
atom already_loaded
atom amd64
@@ -108,6 +105,8 @@ atom asynchronous
atom atom
atom atom_used
atom attributes
+atom auto_connect
+atom await_exit
atom await_microstate_accounting_modifications
atom await_port_send_result
atom await_proc_exit
@@ -121,9 +120,7 @@ atom bag
atom band
atom big
atom bif_return_trap
-atom bif_timer_server
atom binary
-atom binary_bin_to_list_trap
atom binary_copy_trap
atom binary_find_trap
atom binary_longest_prefix_trap
@@ -178,7 +175,6 @@ atom convert_time_unit
atom connect
atom connected
atom connection_closed
-atom cons
atom const
atom context_switches
atom control
@@ -198,9 +194,6 @@ atom debug_flags
atom decimals
atom default
atom delay_trap
-atom dexit
-atom depth
-atom dgroup_leader
atom dictionary
atom dirty_bif_exception
atom dirty_bif_result
@@ -221,18 +214,14 @@ atom dist_ctrl_put_data
atom dist_data
atom Div='/'
atom div
-atom dlink
atom dmonitor_node
-atom dmonitor_p
atom DollarDollar='$$'
atom DollarUnderscore='$_'
atom dollar_endonly
atom dotall
atom driver
atom driver_options
-atom dsend
atom dsend_continue_trap
-atom dunlink
atom duplicate_bag
atom duplicated
atom dupnames
@@ -250,15 +239,14 @@ atom Eqeq='=='
atom erl_tracer
atom erlang
atom erl_signal_server
-atom ERROR='ERROR'
atom error_handler
atom error_logger
atom erts_code_purger
atom erts_debug
+atom erts_dflags
atom erts_internal
atom ets
atom ETS_TRANSFER='ETS-TRANSFER'
-atom event
atom exact_reductions
atom exception_from
atom exception_trace
@@ -285,27 +273,20 @@ atom force
atom format_cpu_topology
atom free
atom fullsweep_after
-atom fullsweep_if_old_binaries
-atom fun
-atom function
atom functions
atom function_clause
atom garbage_collecting
atom garbage_collection
atom garbage_collection_info
-atom gc_end
atom gc_major_end
atom gc_major_start
atom gc_max_heap_size
atom gc_minor_end
atom gc_minor_start
-atom gc_start
atom Ge='>='
atom generational
-atom get_data
atom get_seq_token
atom get_tcw
-atom getenv
atom gather_gc_info_result
atom gather_io_bytes
atom gather_microstate_accounting_result
@@ -347,11 +328,11 @@ atom initial_call
atom input
atom internal
atom internal_error
-atom internal_status
atom instruction_counts
atom invalid
atom is_constant
atom is_seq_trace
+atom iterator
atom io
atom keypos
atom kill
@@ -392,14 +373,12 @@ atom match_spec_result
atom max
atom maximum
atom max_heap_size
-atom max_tables max_processes
atom mbuf_size
atom md5
atom memory
atom memory_internal
atom memory_types
atom message
-atom message_binary
atom message_queue_data
atom message_queue_len
atom messages
@@ -446,21 +425,18 @@ atom new_processes
atom new_ports
atom new_uniq
atom newline
-atom next
atom no
atom nomatch
atom none
atom no_auto_capture
atom noconnect
atom noconnection
-atom nocookie
atom node
atom node_type
atom nodedown
atom nodedown_reason
atom nodeup
atom noeol
-atom nofile
atom noproc
atom normal
atom nosuspend
@@ -482,7 +458,6 @@ atom notempty_atstart
atom notify
atom notsup
atom nouse_stdio
-atom objects
atom off_heap
atom offset
atom ok
@@ -552,7 +527,6 @@ atom re_run_trap
atom read_concurrency
atom ready_input
atom ready_output
-atom ready_async
atom reason
atom receive
atom recent_size
@@ -580,7 +554,8 @@ atom running_procs
atom runtime
atom safe
atom save_calls
-atom scheduler
+atom sbct
+atom scheduler
atom scheduler_id
atom scheduler_wall_time
atom scheduler_wall_time_all
@@ -598,7 +573,6 @@ atom sequential_trace_token
atom serial
atom set
atom set_cpu_topology
-atom set_data
atom set_on_first_link
atom set_on_first_spawn
atom set_on_link
@@ -606,8 +580,6 @@ atom set_on_spawn
atom set_seq_token
atom set_tcw
atom set_tcw_fake
-atom separate
-atom shared
atom sighup
atom sigterm
atom sigusr1
@@ -623,7 +595,6 @@ atom sigtstp
atom sigquit
atom silent
atom size
-atom sl_alloc
atom spawn_executable
atom spawn_driver
atom spawned
@@ -631,7 +602,6 @@ atom ssl_tls
atom stack_size
atom start
atom status
-atom static
atom stderr_to_stdout
atom stop
atom stream
@@ -641,13 +611,11 @@ atom sunrm
atom suspend
atom suspended
atom suspending
-atom sys_misc
atom system
-atom system_error
+atom system_flag_scheduler_wall_time
atom system_limit
atom system_version
atom system_architecture
-atom SYSTEM='SYSTEM'
atom table
atom term_to_binary_trap
atom this
@@ -665,7 +633,7 @@ atom total_heap_size
atom total_run_queue_lengths
atom total_run_queue_lengths_all
atom tpkt
-atom trace trace_ts traced
+atom trace traced
atom trace_control_word
atom trace_status
atom tracer
@@ -674,7 +642,6 @@ atom trim
atom trim_all
atom try_clause
atom true
-atom tuple
atom type
atom ucompile
atom ucp
@@ -691,14 +658,11 @@ atom unblock_normal
atom uniq
atom unless_suspending
atom unloaded
-atom unloading
atom unloaded_only
atom unload_cancelled
atom value
-atom values
atom version
atom visible
-atom wait
atom waiting
atom wall_clock
atom warning
@@ -709,3 +673,4 @@ atom xor
atom x86
atom yes
atom yield
+atom nifs
diff --git a/erts/emulator/beam/beam_bif_load.c b/erts/emulator/beam/beam_bif_load.c
index 7f8dc42aca..5c76aafae7 100644
--- a/erts/emulator/beam/beam_bif_load.c
+++ b/erts/emulator/beam/beam_bif_load.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1999-2017. All Rights Reserved.
+ * Copyright Ericsson AB 1999-2018. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -37,6 +37,7 @@
#include "erl_bits.h"
#include "erl_thr_progress.h"
#include "erl_nfunc_sched.h"
+#include "erl_proc_sig_queue.h"
#ifdef HIPE
# include "hipe_bif0.h"
# define IF_HIPE(X) (X)
@@ -65,7 +66,6 @@ static struct {
Process *erts_code_purger = NULL;
-Process *erts_dirty_process_code_checker;
erts_atomic_t erts_copy_literal_area__;
#define ERTS_SET_COPY_LITERAL_AREA(LA) \
erts_atomic_set_nob(&erts_copy_literal_area__, \
@@ -259,7 +259,7 @@ struct m {
Binary* code;
Eterm module;
Module* modp;
- Uint exception;
+ Eterm exception;
};
static Eterm staging_epilogue(Process* c_p, int, Eterm res, int, struct m*, int, int);
@@ -278,7 +278,7 @@ exception_list(Process* p, Eterm tag, struct m* mp, Sint exceptions)
Eterm res = NIL;
while (exceptions > 0) {
- if (mp->exception) {
+ if (is_value(mp->exception)) {
res = CONS(hp, mp->module, res);
hp += 2;
exceptions--;
@@ -379,9 +379,9 @@ finish_loading_1(BIF_ALIST_1)
exceptions = 0;
for (i = 0; i < n; i++) {
- p[i].exception = 0;
+ p[i].exception = THE_NON_VALUE;
if (p[i].modp->seen) {
- p[i].exception = 1;
+ p[i].exception = am_duplicated;
exceptions++;
}
p[i].modp->seen = 1;
@@ -415,9 +415,9 @@ finish_loading_1(BIF_ALIST_1)
exceptions = 0;
for (i = 0; i < n; i++) {
- p[i].exception = 0;
+ p[i].exception = THE_NON_VALUE;
if (p[i].modp->curr.code_hdr && p[i].modp->old.code_hdr) {
- p[i].exception = 1;
+ p[i].exception = am_not_purged;
exceptions++;
}
}
@@ -438,7 +438,7 @@ finish_loading_1(BIF_ALIST_1)
retval = erts_finish_loading(p[i].code, BIF_P, 0, &mod);
ASSERT(retval == NIL || retval == am_on_load);
if (retval == am_on_load) {
- p[i].exception = 1;
+ p[i].exception = am_on_load;
exceptions++;
}
}
@@ -469,7 +469,8 @@ staging_epilogue(Process* c_p, int commit, Eterm res, int is_blocking,
erts_commit_staging_code_ix();
for (i=0; i < nmods; i++) {
- if (mods[i].modp->curr.code_hdr) {
+ if (mods[i].modp->curr.code_hdr
+ && mods[i].exception != am_on_load) {
set_default_trace_pattern(mods[i].module);
}
#ifdef HIPE
@@ -606,7 +607,9 @@ BIF_RETTYPE erts_internal_check_dirty_process_code_2(BIF_ALIST_2)
int reds = 0;
Eterm res;
- if (BIF_P != erts_dirty_process_code_checker)
+ if (BIF_P != erts_dirty_process_signal_handler
+ && BIF_P != erts_dirty_process_signal_handler_high
+ && BIF_P != erts_dirty_process_signal_handler_max)
BIF_ERROR(BIF_P, EXC_NOTSUP);
if (is_not_internal_pid(BIF_ARG_1))
@@ -686,6 +689,7 @@ BIF_RETTYPE delete_module_1(BIF_ALIST_1)
Eterm retval;
mod.module = BIF_ARG_1;
mod.modp = modp;
+ mod.exception = THE_NON_VALUE;
retval = staging_epilogue(BIF_P, success, res, is_blocking, &mod, 1, 0);
return retval;
}
@@ -766,36 +770,45 @@ BIF_RETTYPE finish_after_on_load_2(BIF_ALIST_2)
ErtsCodeIndex code_ix;
Module* modp;
+ if (BIF_ARG_2 != am_false && BIF_ARG_2 != am_true) {
+ BIF_ERROR(BIF_P, BADARG);
+ }
+
if (!erts_try_seize_code_write_permission(BIF_P)) {
ERTS_BIF_YIELD2(bif_export[BIF_finish_after_on_load_2],
BIF_P, BIF_ARG_1, BIF_ARG_2);
}
- /* ToDo: Use code_ix staging instead of thread blocking */
-
- erts_proc_unlock(BIF_P, ERTS_PROC_LOCK_MAIN);
- erts_thr_progress_block();
-
code_ix = erts_active_code_ix();
modp = erts_get_module(BIF_ARG_1, code_ix);
- if (!modp || !modp->on_load || !modp->on_load->code_hdr) {
- error:
- erts_thr_progress_unblock();
- erts_proc_lock(BIF_P, ERTS_PROC_LOCK_MAIN);
+ if (!modp || !modp->on_load || !modp->on_load->code_hdr
+ || !modp->on_load->code_hdr->on_load_function_ptr) {
+
erts_release_code_write_permission();
BIF_ERROR(BIF_P, BADARG);
}
- if (modp->on_load->code_hdr->on_load_function_ptr == NULL) {
- goto error;
- }
- if (BIF_ARG_2 != am_false && BIF_ARG_2 != am_true) {
- goto error;
- }
if (BIF_ARG_2 == am_true) {
+ struct m mods[1];
+ int is_blocking = 0;
int i, num_exps;
+ erts_start_staging_code_ix(0);
+ code_ix = erts_staging_code_ix();
+ modp = erts_get_module(BIF_ARG_1, code_ix);
+
+ ASSERT(modp && modp->on_load && modp->on_load->code_hdr
+ && modp->on_load->code_hdr->on_load_function_ptr);
+
+ if (erts_is_default_trace_enabled()
+ || IF_HIPE(hipe_need_blocking(modp))) {
+
+ erts_proc_unlock(BIF_P, ERTS_PROC_LOCK_MAIN);
+ erts_thr_progress_block();
+ is_blocking = 1;
+ }
+
/*
* Make the code with the on_load function current.
*/
@@ -821,19 +834,21 @@ BIF_RETTYPE finish_after_on_load_2(BIF_ALIST_2)
ep->beam[1] = 0;
} else {
if (ep->addressv[code_ix] == ep->beam &&
- ep->beam[0] == (BeamInstr) em_apply_bif) {
+ BeamIsOpCode(ep->beam[0], op_apply_bif)) {
continue;
}
- ep->addressv[code_ix] = ep->beam;
- ep->beam[0] = (BeamInstr) em_call_error_handler;
+ ep->addressv[code_ix] = ep->beam;
+ ep->beam[0] = BeamOpCodeAddr(op_call_error_handler);
}
}
modp->curr.code_hdr->on_load_function_ptr = NULL;
- set_default_trace_pattern(BIF_ARG_1);
- #ifdef HIPE
- hipe_redirect_to_module(modp);
- #endif
- } else if (BIF_ARG_2 == am_false) {
+
+ mods[0].modp = modp;
+ mods[0].module = BIF_ARG_1;
+ mods[0].exception = THE_NON_VALUE;
+ return staging_epilogue(BIF_P, 1, am_true, is_blocking, mods, 1, 0);
+ }
+ else if (BIF_ARG_2 == am_false) {
int i, num_exps;
/*
@@ -847,14 +862,12 @@ BIF_RETTYPE finish_after_on_load_2(BIF_ALIST_2)
if (ep == NULL || ep->info.mfa.module != BIF_ARG_1) {
continue;
}
- if (ep->beam[0] == (BeamInstr) em_apply_bif) {
+ if (BeamIsOpCode(ep->beam[0], op_apply_bif)) {
continue;
}
ep->beam[1] = 0;
}
}
- erts_thr_progress_unblock();
- erts_proc_lock(BIF_P, ERTS_PROC_LOCK_MAIN);
erts_release_code_write_permission();
BIF_RET(am_true);
}
@@ -890,15 +903,59 @@ static void hfrag_literal_copy(Eterm **hpp, ErlOffHeap *ohp,
Eterm *start, Eterm *end,
char *lit_start, Uint lit_size);
+static ERTS_INLINE void
+msg_copy_literal_area(ErtsMessage *msgp, int *redsp,
+ char *literals, Uint lit_bsize)
+{
+ ErlHeapFragment *hfrag, *hf;
+ Uint lit_sz = 0;
+
+ *redsp += 1;
+
+ if (!ERTS_SIG_IS_INTERNAL_MSG(msgp) || !msgp->data.attached)
+ return;
+
+ if (msgp->data.attached == ERTS_MSG_COMBINED_HFRAG)
+ hfrag = &msgp->hfrag;
+ else
+ hfrag = msgp->data.heap_frag;
+
+ for (hf = hfrag; hf; hf = hf->next) {
+ lit_sz += hfrag_literal_size(&hf->mem[0],
+ &hf->mem[hf->used_size],
+ literals, lit_bsize);
+ *redsp += 1;
+ }
+
+ *redsp += lit_sz / 16; /* Better value needed... */
+ if (lit_sz > 0) {
+ ErlHeapFragment *bp = new_message_buffer(lit_sz);
+ Eterm *hp = bp->mem;
+
+ for (hf = hfrag; hf; hf = hf->next) {
+ hfrag_literal_copy(&hp, &bp->off_heap,
+ &hf->mem[0],
+ &hf->mem[hf->used_size],
+ literals, lit_bsize);
+ hfrag = hf;
+ }
+
+ /* link new hfrag last */
+ ASSERT(hfrag->next == NULL);
+ hfrag->next = bp;
+ bp->next = NULL;
+ }
+}
+
Eterm
erts_proc_copy_literal_area(Process *c_p, int *redsp, int fcalls, int gc_allowed)
{
ErtsLiteralArea *la;
- ErtsMessage *msgp;
struct erl_off_heap_header* oh;
char *literals;
Uint lit_bsize;
ErlHeapFragment *hfrag;
+ ErtsMessage *mfp;
la = ERTS_COPY_LITERAL_AREA();
if (!la)
@@ -916,46 +973,13 @@ erts_proc_copy_literal_area(Process *c_p, int *redsp, int fcalls, int gc_allowed
*/
erts_proc_lock(c_p, ERTS_PROC_LOCK_MSGQ);
- ERTS_MSGQ_MV_INQ2PRIVQ(c_p);
+ erts_proc_sig_fetch(c_p);
erts_proc_unlock(c_p, ERTS_PROC_LOCK_MSGQ);
- for (msgp = c_p->msg.first; msgp; msgp = msgp->next) {
- ErlHeapFragment *hf;
- Uint lit_sz = 0;
-
- *redsp += 1;
-
- if (msgp->data.attached == ERTS_MSG_COMBINED_HFRAG)
- hfrag = &msgp->hfrag;
- else if (is_value(ERL_MESSAGE_TERM(msgp)) && msgp->data.heap_frag)
- hfrag = msgp->data.heap_frag;
- else
- continue; /* Content on heap or in external term format... */
-
- for (hf = hfrag; hf; hf = hf->next) {
- lit_sz += hfrag_literal_size(&hf->mem[0], &hf->mem[hf->used_size],
- literals, lit_bsize);
- *redsp += 1;
- }
-
- *redsp += lit_sz / 16; /* Better value needed... */
- if (lit_sz > 0) {
- ErlHeapFragment *bp = new_message_buffer(lit_sz);
- Eterm *hp = bp->mem;
-
- for (hf = hfrag; hf; hf = hf->next) {
- hfrag_literal_copy(&hp, &bp->off_heap,
- &hf->mem[0], &hf->mem[hf->used_size],
- literals, lit_bsize);
- hfrag = hf;
- }
-
- /* link new hfrag last */
- ASSERT(hfrag->next == NULL);
- hfrag->next = bp;
- bp->next = NULL;
- }
- }
+ ERTS_FOREACH_SIG_PRIVQS(c_p, msgp, msg_copy_literal_area(msgp,
+ redsp,
+ literals,
+ lit_bsize));
if (gc_allowed) {
/*
@@ -1030,8 +1054,8 @@ erts_proc_copy_literal_area(Process *c_p, int *redsp, int fcalls, int gc_allowed
* process off heap structure.
* - Check for literals
*/
- for (msgp = c_p->msg_frag; msgp; msgp = msgp->next) {
- hfrag = erts_message_to_heap_frag(msgp);
+ for (mfp = c_p->msg_frag; mfp; mfp = mfp->next) {
+ hfrag = erts_message_to_heap_frag(mfp);
for (; hfrag; hfrag = hfrag->next) {
Eterm *hp, *hp_end;
@@ -1764,22 +1788,23 @@ delete_code(Module* modp)
Export *ep = export_list(i, code_ix);
if (ep != NULL && (ep->info.mfa.module == module)) {
if (ep->addressv[code_ix] == ep->beam) {
- if (ep->beam[0] == (BeamInstr) em_apply_bif) {
+ if (BeamIsOpCode(ep->beam[0], op_apply_bif)) {
continue;
}
- else if (ep->beam[0] ==
- (BeamInstr) BeamOp(op_i_generic_breakpoint)) {
+ else if (BeamIsOpCode(ep->beam[0], 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);
}
- else ASSERT(ep->beam[0] == (BeamInstr) em_call_error_handler
- || !erts_initialized);
- }
+ else {
+ ASSERT(BeamIsOpCode(ep->beam[0], op_call_error_handler) ||
+ !erts_initialized);
+ }
+ }
ep->addressv[code_ix] = ep->beam;
- ep->beam[0] = (BeamInstr) em_call_error_handler;
+ ep->beam[0] = BeamOpCodeAddr(op_call_error_handler);
ep->beam[1] = 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 49ec59c989..0832b3f374 100644
--- a/erts/emulator/beam/beam_bp.c
+++ b/erts/emulator/beam/beam_bp.c
@@ -203,7 +203,7 @@ erts_bp_match_functions(BpFunctions* f, ErtsCodeMFA *mfa, int specified)
for (fi = 0; fi < num_functions; fi++) {
ci = code_hdr->functions[fi];
- ASSERT(ci->op == (BeamInstr) BeamOp(op_i_func_info_IaaI));
+ ASSERT(BeamIsOpCode(ci->op, op_i_func_info_IaaI));
if (erts_is_function_native(ci)) {
continue;
}
@@ -265,11 +265,11 @@ erts_bp_match_export(BpFunctions* f, ErtsCodeMFA *mfa, int specified)
pc = ep->beam;
if (ep->addressv[code_ix] == pc) {
- if ((*pc == (BeamInstr) em_apply_bif ||
- *pc == (BeamInstr) em_call_error_handler)) {
- continue;
+ if (BeamIsOpCode(*pc, op_apply_bif) ||
+ BeamIsOpCode(*pc, op_call_error_handler)) {
+ continue;
}
- ASSERT(*pc == (BeamInstr) BeamOp(op_i_generic_breakpoint));
+ ASSERT(BeamIsOpCode(*pc, op_i_generic_breakpoint));
} else if (erts_is_function_native(erts_code_to_codeinfo(ep->addressv[code_ix]))) {
continue;
}
@@ -366,8 +366,8 @@ consolidate_bp_data(Module* modp, ErtsCodeInfo *ci, int local)
}
ASSERT(modp->curr.num_breakpoints >= 0);
ASSERT(modp->curr.num_traced_exports >= 0);
- ASSERT(*erts_codeinfo_to_code(ci) !=
- (BeamInstr) BeamOp(op_i_generic_breakpoint));
+ ASSERT(! BeamIsOpCode(*erts_codeinfo_to_code(ci),
+ op_i_generic_breakpoint));
}
ci->u.gen_bp = NULL;
Free(g);
@@ -415,13 +415,15 @@ erts_install_breakpoints(BpFunctions* f)
{
Uint i;
Uint n = f->matched;
- BeamInstr br = (BeamInstr) BeamOp(op_i_generic_breakpoint);
+ BeamInstr br = BeamOpCodeAddr(op_i_generic_breakpoint);
for (i = 0; i < n; i++) {
ErtsCodeInfo* ci = f->matching[i].ci;
- BeamInstr *pc = erts_codeinfo_to_code(ci);
GenericBp* g = ci->u.gen_bp;
- if (*pc != br && g) {
+ BeamInstr volatile *pc = erts_codeinfo_to_code(ci);
+ BeamInstr instr = *pc;
+
+ if (!BeamIsOpCode(instr, op_i_generic_breakpoint) && g) {
Module* modp = f->matching[i].mod;
/*
@@ -435,11 +437,16 @@ erts_install_breakpoints(BpFunctions* f)
/*
* The following write is not protected by any lock. We
* assume that the hardware guarantees that a write of an
- * aligned word-size (or half-word) writes is atomic
- * (i.e. that other processes executing this code will not
- * see a half pointer).
+ * aligned word-size writes is atomic (i.e. that other
+ * processes executing this code will not see a half
+ * pointer).
+ *
+ * The contents of *pc is marked 'volatile' to ensure that
+ * the compiler will do a single full-word write, and not
+ * try any fancy optimizations to write a half word.
*/
- *pc = br;
+ instr = BeamSetCodeAddr(instr, br);
+ *pc = instr;
modp->curr.num_breakpoints++;
}
}
@@ -460,7 +467,7 @@ static void
uninstall_breakpoint(ErtsCodeInfo *ci)
{
BeamInstr *pc = erts_codeinfo_to_code(ci);
- if (*pc == (BeamInstr) BeamOp(op_i_generic_breakpoint)) {
+ if (BeamIsOpCode(*pc, op_i_generic_breakpoint)) {
GenericBp* g = ci->u.gen_bp;
if (g->data[erts_active_bp_ix()].flags == 0) {
/*
@@ -634,6 +641,49 @@ erts_clear_export_break(Module* modp, ErtsCodeInfo *ci)
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.
+ *
+ * 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.
+ */
+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;
+ }
+ }
+ c_p->cp = (BeamInstr *) cp_val(*cpp);
+ ASSERT(is_CP(*cpp));
+ }
+}
+
BeamInstr
erts_generic_breakpoint(Process* c_p, ErtsCodeInfo *info, Eterm* reg)
{
@@ -642,7 +692,7 @@ erts_generic_breakpoint(Process* c_p, ErtsCodeInfo *info, Eterm* reg)
Uint bp_flags;
ErtsBpIndex ix = erts_active_bp_ix();
- ASSERT(info->op == (BeamInstr) BeamOp(op_i_func_info_IaaI));
+ ASSERT(BeamIsOpCode(info->op, op_i_func_info_IaaI));
g = info->u.gen_bp;
bp = &g->data[ix];
@@ -695,9 +745,9 @@ erts_generic_breakpoint(Process* c_p, ErtsCodeInfo *info, Eterm* reg)
Eterm w;
erts_trace_time_call(c_p, info, bp->time);
w = (BeamInstr) *c_p->cp;
- if (! (w == (BeamInstr) BeamOp(op_i_return_time_trace) ||
- w == (BeamInstr) BeamOp(op_return_trace) ||
- w == (BeamInstr) BeamOp(op_i_return_to_trace)) ) {
+ 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) {
@@ -717,7 +767,7 @@ erts_generic_breakpoint(Process* c_p, ErtsCodeInfo *info, Eterm* reg)
}
if (bp_flags & ERTS_BPF_DEBUG) {
- return (BeamInstr) BeamOp(op_i_debug_breakpoint);
+ return BeamOpCodeAddr(op_i_debug_breakpoint);
} else {
return g->orig_instr;
}
@@ -744,6 +794,7 @@ erts_bif_trace(int bif_index, Process* p, Eterm* args, BeamInstr* I)
GenericBp* g;
GenericBpData* bp = NULL;
Uint bp_flags = 0;
+ int return_to_trace = 0;
ERTS_CHK_HAVE_ONLY_MAIN_PROC_LOCK(p);
@@ -759,6 +810,8 @@ erts_bif_trace(int bif_index, Process* p, Eterm* args, BeamInstr* I)
*/
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)) {
@@ -937,49 +990,20 @@ static ErtsTracer
do_call_trace(Process* c_p, ErtsCodeInfo* info, Eterm* reg,
int local, Binary* ms, ErtsTracer tracer)
{
- Eterm* cpp;
int return_to_trace = 0;
- BeamInstr w;
BeamInstr *cp_save = c_p->cp;
Uint32 flags;
Uint need = 0;
Eterm* E = c_p->stop;
- w = *c_p->cp;
- if (w == (BeamInstr) BeamOp(op_return_trace)) {
- cpp = &E[2];
- } else if (w == (BeamInstr) BeamOp(op_i_return_to_trace)) {
- return_to_trace = 1;
- cpp = &E[0];
- } else if (w == (BeamInstr) BeamOp(op_i_return_time_trace)) {
- cpp = &E[0];
- } else {
- cpp = NULL;
- }
- if (cpp) {
- for (;;) {
- BeamInstr w = *cp_val(*cpp);
- if (w == (BeamInstr) BeamOp(op_return_trace)) {
- cpp += 3;
- } else if (w == (BeamInstr) BeamOp(op_i_return_to_trace)) {
- return_to_trace = 1;
- cpp += 1;
- } else if (w == (BeamInstr) BeamOp(op_i_return_time_trace)) {
- cpp += 2;
- } else {
- break;
- }
- }
- cp_save = c_p->cp;
- c_p->cp = (BeamInstr *) cp_val(*cpp);
- ASSERT(is_CP(*cpp));
- }
+ 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);
- if (cpp) {
- c_p->cp = cp_save;
- }
+
+ /* restore cp after potential fixup */
+ c_p->cp = cp_save;
ASSERT(!ERTS_PROC_IS_EXITING(c_p));
if ((flags & MATCH_SET_RETURN_TO_TRACE) && !return_to_trace) {
@@ -1293,7 +1317,7 @@ erts_find_local_func(ErtsCodeMFA *mfa) {
n = (BeamInstr) code_hdr->num_functions;
for (i = 0; i < n; ++i) {
ci = code_hdr->functions[i];
- ASSERT(((BeamInstr) BeamOp(op_i_func_info_IaaI)) == ci->op);
+ ASSERT(BeamIsOpCode(ci->op, op_i_func_info_IaaI));
ASSERT(mfa->module == ci->mfa.module || is_nil(ci->mfa.module));
if (mfa->function == ci->mfa.function &&
mfa->arity == ci->mfa.arity) {
@@ -1708,7 +1732,7 @@ check_break(ErtsCodeInfo *ci, Uint break_flags)
{
GenericBp* g = ci->u.gen_bp;
- ASSERT(ci->op == (BeamInstr) BeamOp(op_i_func_info_IaaI));
+ ASSERT(BeamIsOpCode(ci->op, op_i_func_info_IaaI));
if (erts_is_function_native(ci)) {
return 0;
}
diff --git a/erts/emulator/beam/beam_debug.c b/erts/emulator/beam/beam_debug.c
index 4e91bfffe8..5eb68b817e 100644
--- a/erts/emulator/beam/beam_debug.c
+++ b/erts/emulator/beam/beam_debug.c
@@ -40,6 +40,7 @@
#include "erl_binary.h"
#include "erl_thr_progress.h"
#include "erl_nfunc_sched.h"
+#include "beam_catches.h"
#ifdef ARCH_64
# define HEXF "%016bpX"
@@ -55,6 +56,7 @@ static int print_op(fmtfn_t to, void *to_arg, int op, int size, BeamInstr* addr)
static void print_bif_name(fmtfn_t to, void* to_arg, BifFunction bif);
static BeamInstr* f_to_addr(BeamInstr* base, int op, BeamInstr* ap);
static BeamInstr* f_to_addr_packed(BeamInstr* base, int op, Sint32* ap);
+static void print_byte_string(fmtfn_t to, void *to_arg, byte* str, Uint bytes);
BIF_RETTYPE
erts_debug_same_2(BIF_ALIST_2)
@@ -199,9 +201,9 @@ void debug_dump_code(BeamInstr *I, int num)
erts_print(ERTS_PRINT_DSBUF, (void *) dsbufp, HEXF ": ", code_ptr);
instr = (BeamInstr) code_ptr[0];
for (i = 0; i < NUM_SPECIFIC_OPS; i++) {
- if (instr == (BeamInstr) BeamOp(i) && opc[i].name[0] != '\0') {
+ if (BeamIsOpCode(instr, i) && opc[i].name[0] != '\0') {
code_ptr += print_op(ERTS_PRINT_DSBUF, (void *) dsbufp,
- i, opc[i].sz-1, code_ptr+1) + 1;
+ i, opc[i].sz-1, code_ptr) + 1;
break;
}
}
@@ -226,11 +228,11 @@ erts_debug_instructions_0(BIF_ALIST_0)
Eterm res = NIL;
for (i = 0; i < num_instructions; i++) {
- needed += 2*strlen(opc[i].name);
+ needed += 2*sys_strlen(opc[i].name);
}
hp = HAlloc(BIF_P, needed);
for (i = num_instructions-1; i >= 0; i--) {
- Eterm s = erts_bld_string_n(&hp, 0, opc[i].name, strlen(opc[i].name));
+ Eterm s = erts_bld_string_n(&hp, 0, opc[i].name, sys_strlen(opc[i].name));
res = erts_bld_cons(&hp, 0, s, res);
}
return res;
@@ -319,9 +321,9 @@ erts_debug_disassemble_1(BIF_ALIST_1)
erts_print(ERTS_PRINT_DSBUF, (void *) dsbufp, HEXF ": ", code_ptr);
instr = (BeamInstr) code_ptr[0];
for (i = 0; i < NUM_SPECIFIC_OPS; i++) {
- if (instr == (BeamInstr) BeamOp(i) && opc[i].name[0] != '\0') {
+ if (BeamIsOpCode(instr, i) && opc[i].name[0] != '\0') {
code_ptr += print_op(ERTS_PRINT_DSBUF, (void *) dsbufp,
- i, opc[i].sz-1, code_ptr+1) + 1;
+ i, opc[i].sz-1, code_ptr) + 1;
break;
}
}
@@ -396,6 +398,7 @@ print_op(fmtfn_t to, void *to_arg, int op, int size, BeamInstr* addr)
BeamInstr args[8]; /* Arguments for this instruction. */
BeamInstr* ap; /* Pointer to arguments. */
BeamInstr* unpacked; /* Unpacked arguments */
+ BeamInstr* first_arg; /* First argument */
start_prog = opc[op].pack;
@@ -405,8 +408,14 @@ print_op(fmtfn_t to, void *to_arg, int op, int size, BeamInstr* addr)
* Avoid copying because instructions containing bignum operands
* are bigger than actually declared.
*/
- ap = (BeamInstr *) addr;
+ addr++;
+ ap = addr;
} else {
+#if defined(ARCH_64) && defined(CODE_MODEL_SMALL)
+ BeamInstr instr_word = addr[0];
+#endif
+ addr++;
+
/*
* Copy all arguments to a local buffer for the unpacking.
*/
@@ -422,7 +431,7 @@ print_op(fmtfn_t to, void *to_arg, int op, int size, BeamInstr* addr)
* the packing program backwards and in reverse.
*/
- prog = start_prog + strlen(start_prog);
+ prog = start_prog + sys_strlen(start_prog);
while (start_prog < prog) {
prog--;
switch (*prog) {
@@ -431,23 +440,22 @@ print_op(fmtfn_t to, void *to_arg, int op, int size, BeamInstr* addr)
case 'q':
*ap++ = *--sp;
break;
- case 'i': /* Initialize packing accumulator. */
- *ap++ = packed;
- break;
- case 's':
- *ap++ = packed & 0x3ff;
- packed >>= 10;
+#ifdef ARCH_64
+ case '1': /* Tightest shift */
+ *ap++ = (packed & BEAM_TIGHTEST_MASK) << 3;
+ packed >>= BEAM_TIGHTEST_SHIFT;
break;
- case '0': /* Tight shift */
+#endif
+ case '2': /* Tight shift */
*ap++ = packed & BEAM_TIGHT_MASK;
packed >>= BEAM_TIGHT_SHIFT;
break;
- case '6': /* Shift 16 steps */
+ case '3': /* Loose shift */
*ap++ = packed & BEAM_LOOSE_MASK;
packed >>= BEAM_LOOSE_SHIFT;
break;
#ifdef ARCH_64
- case 'w': /* Shift 32 steps */
+ case '4': /* Shift 32 steps */
*ap++ = packed & BEAM_WIDE_MASK;
packed >>= BEAM_WIDE_SHIFT;
break;
@@ -458,13 +466,25 @@ print_op(fmtfn_t to, void *to_arg, int op, int size, BeamInstr* addr)
case 'P':
packed = *--sp;
break;
+#if defined(ARCH_64) && defined(CODE_MODEL_SMALL)
+ case '#': /* -1 */
+ case '$': /* -2 */
+ case '%': /* -3 */
+ case '&': /* -4 */
+ case '\'': /* -5 */
+ case '(': /* -6 */
+ packed = (packed << BEAM_WIDE_SHIFT) | BeamExtraData(instr_word);
+ break;
+#endif
default:
- ASSERT(0);
+ erts_exit(ERTS_ERROR_EXIT, "beam_debug: invalid packing op: %c\n", *prog);
}
}
ap = args;
}
+ first_arg = ap;
+
/*
* Print the name and all operands of the instructions.
*/
@@ -555,24 +575,60 @@ print_op(fmtfn_t to, void *to_arg, int op, int size, BeamInstr* addr)
}
break;
}
+ case op_i_make_fun_Wt:
+ if (*sign == 'W') {
+ ErlFunEntry* fe = (ErlFunEntry *) *ap;
+ ErtsCodeMFA* cmfa = find_function_from_pc(fe->address);
+ erts_print(to, to_arg, "%T:%T/%bpu", cmfa->module,
+ cmfa->function, cmfa->arity);
+ } else {
+ erts_print(to, to_arg, "%d", *ap);
+ }
+ break;
+ case op_i_bs_match_string_xfWW:
+ if (ap - first_arg < 3) {
+ erts_print(to, to_arg, "%d", *ap);
+ } else {
+ Uint bits = ap[-1];
+ Uint bytes = (bits+7)/8;
+ byte* str = (byte *) *ap;
+ print_byte_string(to, to_arg, str, bytes);
+ }
+ break;
+ case op_bs_put_string_WW:
+ if (ap - first_arg == 0) {
+ erts_print(to, to_arg, "%d", *ap);
+ } else {
+ Uint bytes = ap[-1];
+ byte* str = (byte *) ap[0];
+ print_byte_string(to, to_arg, str, bytes);
+ }
+ break;
default:
erts_print(to, to_arg, "%d", *ap);
}
ap++;
break;
case 'f': /* Destination label */
- {
- BeamInstr* target = f_to_addr(addr, op, ap);
- ErtsCodeMFA* cmfa = find_function_from_pc(target);
- if (!cmfa || erts_codemfa_to_code(cmfa) != target) {
- erts_print(to, to_arg, "f(" HEXF ")", target);
- } else {
- erts_print(to, to_arg, "%T:%T/%bpu", cmfa->module,
- cmfa->function, cmfa->arity);
- }
- ap++;
- }
- break;
+ switch (op) {
+ case op_catch_yf:
+ erts_print(to, to_arg, "f(" HEXF ")", catch_pc((BeamInstr)*ap));
+ break;
+ default:
+ {
+ BeamInstr* target = f_to_addr(addr, op, ap);
+ ErtsCodeMFA* cmfa = find_function_from_pc(target);
+ if (!cmfa || erts_codemfa_to_code(cmfa) != target) {
+ erts_print(to, to_arg, "f(" HEXF ")", target);
+ } else {
+ erts_print(to, to_arg, "%T:%T/%bpu", cmfa->module,
+ cmfa->function, cmfa->arity);
+ }
+ ap++;
+ }
+ break;
+ }
+ break;
case 'p': /* Pointer (to label) */
{
BeamInstr* target = f_to_addr(addr, op, ap);
@@ -629,13 +685,20 @@ print_op(fmtfn_t to, void *to_arg, int op, int size, BeamInstr* addr)
unpacked = ap;
ap = addr + size;
+
+ /*
+ * In the code below, never use ap[-1], ap[-2], ...
+ * (will not work if the arguments have been packed).
+ *
+ * Instead use unpacked[-1], unpacked[-2], ...
+ */
switch (op) {
case op_i_select_val_lins_xfI:
case op_i_select_val_lins_yfI:
case op_i_select_val_bins_xfI:
case op_i_select_val_bins_yfI:
{
- int n = ap[-1];
+ int n = unpacked[-1];
int ix = n;
Sint32* jump_tab = (Sint32 *)(ap + n);
@@ -656,7 +719,7 @@ print_op(fmtfn_t to, void *to_arg, int op, int size, BeamInstr* addr)
case op_i_select_tuple_arity_xfI:
case op_i_select_tuple_arity_yfI:
{
- int n = ap[-1];
+ int n = unpacked[-1];
int ix = n - 1; /* without sentinel */
Sint32* jump_tab = (Sint32 *)(ap + n);
@@ -698,7 +761,7 @@ print_op(fmtfn_t to, void *to_arg, int op, int size, BeamInstr* addr)
case op_i_jump_on_val_xfIW:
case op_i_jump_on_val_yfIW:
{
- int n = ap[-2];
+ int n = unpacked[-2];
Sint32* jump_tab = (Sint32 *) ap;
size += (n+1) / 2;
@@ -712,7 +775,7 @@ print_op(fmtfn_t to, void *to_arg, int op, int size, BeamInstr* addr)
case op_i_jump_on_val_zero_xfI:
case op_i_jump_on_val_zero_yfI:
{
- int n = ap[-1];
+ int n = unpacked[-1];
Sint32* jump_tab = (Sint32 *) ap;
size += (n+1) / 2;
@@ -737,7 +800,7 @@ print_op(fmtfn_t to, void *to_arg, int op, int size, BeamInstr* addr)
erts_print(to, to_arg, " x(%d)", loader_x_reg_index(ap[0]));
break;
case LOADER_Y_REG:
- erts_print(to, to_arg, " x(%d)", loader_y_reg_index(ap[0]));
+ erts_print(to, to_arg, " y(%d)", loader_y_reg_index(ap[0]) - CP_SIZE);
break;
default:
erts_print(to, to_arg, " %T", (Eterm) ap[0]);
@@ -758,7 +821,7 @@ print_op(fmtfn_t to, void *to_arg, int op, int size, BeamInstr* addr)
erts_print(to, to_arg, " x(%d)", loader_x_reg_index(ap[0]));
break;
case LOADER_Y_REG:
- erts_print(to, to_arg, " x(%d)", loader_y_reg_index(ap[0]));
+ erts_print(to, to_arg, " y(%d)", loader_y_reg_index(ap[0]) - CP_SIZE);
break;
default:
erts_print(to, to_arg, " %T", (Eterm) ap[0]);
@@ -781,7 +844,7 @@ print_op(fmtfn_t to, void *to_arg, int op, int size, BeamInstr* addr)
erts_print(to, to_arg, " x(%d)", loader_x_reg_index(ap[0]));
break;
case LOADER_Y_REG:
- erts_print(to, to_arg, " y(%d)", loader_y_reg_index(ap[0]));
+ erts_print(to, to_arg, " y(%d)", loader_y_reg_index(ap[0]) - CP_SIZE);
break;
default:
erts_print(to, to_arg, " %T", (Eterm) ap[0]);
@@ -826,6 +889,14 @@ static BeamInstr* f_to_addr_packed(BeamInstr* base, int op, Sint32* ap)
return base - 1 + opc[op].adjust + *ap;
}
+static void print_byte_string(fmtfn_t to, void *to_arg, byte* str, Uint bytes)
+{
+ Uint i;
+
+ for (i = 0; i < bytes; i++) {
+ erts_print(to, to_arg, "%02X", str[i]);
+ }
+}
/*
* Dirty BIF testing.
diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c
index 81c4417b1e..ee287243a4 100644
--- a/erts/emulator/beam/beam_emu.c
+++ b/erts/emulator/beam/beam_emu.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1996-2017. All Rights Reserved.
+ * Copyright Ericsson AB 1996-2018. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -44,21 +44,23 @@
#include "hipe_bif1.h"
#endif
#include "dtrace-wrapper.h"
+#include "erl_proc_sig_queue.h"
/* #define HARDDEBUG 1 */
#if defined(NO_JUMP_TABLE)
# define OpCase(OpCode) case op_##OpCode
# define CountCase(OpCode) case op_count_##OpCode
-# define OpCode(OpCode) ((Uint*)op_##OpCode)
-# define Goto(Rel) {Go = (int)(UWord)(Rel); goto emulator_loop;}
-# define LabelAddr(Addr) &&##Addr
+# define IsOpCode(InstrWord, OpCode) (BeamCodeAddr(InstrWord) == (BeamInstr)op_##OpCode)
+# define Goto(Rel) {Go = BeamCodeAddr(Rel); goto emulator_loop;}
+# define GotoPF(Rel) Goto(Rel)
#else
# define OpCase(OpCode) lb_##OpCode
# define CountCase(OpCode) lb_count_##OpCode
-# define Goto(Rel) goto *((void *)Rel)
-# define LabelAddr(Label) &&Label
-# define OpCode(OpCode) (&&lb_##OpCode)
+# define IsOpCode(InstrWord, OpCode) (BeamCodeAddr(InstrWord) == (BeamInstr)&&lb_##OpCode)
+# define Goto(Rel) goto *((void *)BeamCodeAddr(Rel))
+# define GotoPF(Rel) goto *((void *)Rel)
+# define LabelAddr(Label) &&Label
#endif
#ifdef ERTS_ENABLE_LOCK_CHECK
@@ -107,6 +109,8 @@ 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)
@@ -130,11 +134,11 @@ do { \
/* We don't check the range if an ordinary switch is used */
#ifdef NO_JUMP_TABLE
-#define VALID_INSTR(IP) ((UWord)(IP) < (NUMBER_OF_OPCODES*2+10))
+# define VALID_INSTR(IP) (BeamCodeAddr(IP) < (NUMBER_OF_OPCODES*2+10))
#else
-#define VALID_INSTR(IP) \
- ((SWord)LabelAddr(emulator_loop) <= (SWord)(IP) && \
- (SWord)(IP) < (SWord)LabelAddr(end_emulator_loop))
+# define VALID_INSTR(IP) \
+ ((BeamInstr)LabelAddr(emulator_loop) <= BeamCodeAddr(IP) && \
+ BeamCodeAddr(IP) < (BeamInstr)LabelAddr(end_emulator_loop))
#endif /* NO_JUMP_TABLE */
#define SET_CP(p, ip) \
@@ -159,11 +163,6 @@ BeamInstr beam_apply[2];
BeamInstr beam_exit[1];
BeamInstr beam_continue_exit[1];
-BeamInstr* em_call_error_handler;
-BeamInstr* em_apply_bif;
-BeamInstr* em_call_nif;
-BeamInstr* em_call_bif_e;
-
/* NOTE These should be the only variables containing trace instructions.
** Sometimes tests are form the instruction value, and sometimes
@@ -238,15 +237,18 @@ void** beam_ops;
#define fb(N) ((Sint)(Sint32)(N))
#define jb(N) ((Sint)(Sint32)(N))
#define tb(N) (N)
-#define xb(N) (*(Eterm *) (((unsigned char *)reg) + (N)))
-#define yb(N) (*(Eterm *) (((unsigned char *)E) + (N)))
+#define xb(N) (*ADD_BYTE_OFFSET(reg, N))
+#define yb(N) (*ADD_BYTE_OFFSET(E, N))
#define Sb(N) (*REG_TARGET_PTR(N))
#define lb(N) (*(double *) (((unsigned char *)&(freg[0].fd)) + (N)))
#define Qb(N) (N)
#define Ib(N) (N)
+
#define x(N) reg[N]
#define y(N) E[N]
#define r(N) x(N)
+#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
@@ -255,8 +257,8 @@ void** beam_ops;
#define DispatchMacro() \
do { \
- BeamInstr* dis_next; \
- dis_next = (BeamInstr *) *I; \
+ BeamInstr dis_next; \
+ dis_next = *I; \
CHECK_ARGS(I); \
if (FCALLS > 0 || FCALLS > neg_o_reds) { \
FCALLS--; \
@@ -264,12 +266,12 @@ void** beam_ops;
} else { \
goto context_switch; \
} \
- } while (0)
+ } while (0) \
#define DispatchMacroFun() \
do { \
- BeamInstr* dis_next; \
- dis_next = (BeamInstr *) *I; \
+ BeamInstr dis_next; \
+ dis_next = *I; \
CHECK_ARGS(I); \
if (FCALLS > 0 || FCALLS > neg_o_reds) { \
FCALLS--; \
@@ -279,23 +281,23 @@ void** beam_ops;
} \
} while (0)
-#define DispatchMacrox() \
- do { \
- if (FCALLS > 0) { \
- Eterm* dis_next; \
- SET_I(((Export *) Arg(0))->addressv[erts_active_code_ix()]); \
- dis_next = (Eterm *) *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; \
- } \
+#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
@@ -382,6 +384,7 @@ do { \
* The following functions are called directly by process_main().
* Don't inline them.
*/
+static void init_emulator_finish(void) NOINLINE;
static ErtsCodeMFA *ubif2mfa(void* uf) NOINLINE;
static ErtsCodeMFA *gcbif2mfa(void* gcf) NOINLINE;
static BeamInstr* handle_error(Process* c_p, BeamInstr* pc,
@@ -398,12 +401,13 @@ static BeamInstr* apply_fun(Process* p, Eterm fun,
Eterm args, Eterm* reg) NOINLINE;
static Eterm new_fun(Process* p, Eterm* reg,
ErlFunEntry* fe, int num_free) NOINLINE;
-static Eterm new_map(Process* p, Eterm* reg, Uint live, Uint n, BeamInstr* ptr) NOINLINE;
-static Eterm new_small_map_lit(Process* p, Eterm* reg, Eterm keys_literal,
+static Eterm erts_gc_new_map(Process* p, Eterm* reg, Uint live,
+ Uint n, BeamInstr* ptr) NOINLINE;
+static Eterm erts_gc_new_small_map_lit(Process* p, Eterm* reg, Eterm keys_literal,
Uint live, BeamInstr* ptr) NOINLINE;
-static Eterm update_map_assoc(Process* p, Eterm* reg, Uint live,
+static Eterm erts_gc_update_map_assoc(Process* p, Eterm* reg, Uint live,
Uint n, BeamInstr* new_p) NOINLINE;
-static Eterm update_map_exact(Process* p, Eterm* reg, Uint live,
+static Eterm erts_gc_update_map_exact(Process* p, Eterm* reg, Uint live,
Uint n, Eterm* new_p) NOINLINE;
static Eterm get_map_element(Eterm map, Eterm key);
static Eterm get_map_element_hash(Eterm map, Eterm key, Uint32 hx);
@@ -627,7 +631,7 @@ void process_main(Eterm * x_reg_array, FloatDef* f_reg_array)
#ifndef NO_JUMP_TABLE
static void* opcodes[] = { DEFINE_OPCODES };
#else
- int Go;
+ register BeamInstr Go;
#endif
#endif
@@ -703,7 +707,7 @@ void process_main(Eterm * x_reg_array, FloatDef* f_reg_array)
{
int reds;
Eterm* argp;
- BeamInstr *next;
+ BeamInstr next;
int i;
argp = c_p->arg_reg;
@@ -735,7 +739,7 @@ void process_main(Eterm * x_reg_array, FloatDef* f_reg_array)
ERTS_DBG_CHK_REDS(c_p, FCALLS);
- next = (BeamInstr *) *I;
+ next = *I;
SWAPIN;
ASSERT(VALID_INSTR(next));
@@ -746,7 +750,7 @@ void process_main(Eterm * x_reg_array, FloatDef* f_reg_array)
dtrace_proc_str(c_p, process_buf);
if (ERTS_PROC_IS_EXITING(c_p)) {
- strcpy(fun_buf, "<exiting>");
+ sys_strcpy(fun_buf, "<exiting>");
} else {
ErtsCodeMFA *cmfa = find_function_from_pc(c_p->i);
if (cmfa) {
@@ -963,9 +967,6 @@ void process_main(Eterm * x_reg_array, FloatDef* f_reg_array)
init_emulator:
{
- int i;
- Export* ep;
-
#ifndef NO_JUMP_TABLE
#ifdef ERTS_OPCODE_COUNTER_SUPPORT
#ifdef DEBUG
@@ -977,35 +978,8 @@ void process_main(Eterm * x_reg_array, FloatDef* f_reg_array)
beam_ops = opcodes;
#endif /* ERTS_OPCODE_COUNTER_SUPPORT */
#endif /* NO_JUMP_TABLE */
-
- em_call_error_handler = OpCode(call_error_handler);
- em_apply_bif = OpCode(apply_bif);
- em_call_nif = OpCode(call_nif);
- em_call_bif_e = OpCode(call_bif_e);
-
- beam_apply[0] = (BeamInstr) OpCode(i_apply);
- beam_apply[1] = (BeamInstr) OpCode(normal_exit);
- beam_exit[0] = (BeamInstr) OpCode(error_action_code);
- beam_continue_exit[0] = (BeamInstr) OpCode(continue_exit);
- beam_return_to_trace[0] = (BeamInstr) OpCode(i_return_to_trace);
- beam_return_trace[0] = (BeamInstr) OpCode(return_trace);
- beam_exception_trace[0] = (BeamInstr) OpCode(return_trace); /* UGLY */
- beam_return_time_trace[0] = (BeamInstr) OpCode(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] = (BeamInstr) OpCode(apply_bif);
- ep->beam[1] = (BeamInstr) bif_table[i].f;
- /* XXX: set func info for bifs */
- ep->info.op = (BeamInstr) BeamOp(op_i_func_info_IaaI);
- }
+ init_emulator_finish();
return;
}
#ifdef NO_JUMP_TABLE
@@ -1017,19 +991,65 @@ void process_main(Eterm * x_reg_array, FloatDef* f_reg_array)
save_calls1:
{
- Eterm* dis_next;
+ BeamInstr dis_next;
save_calls(c_p, (Export *) Arg(0));
SET_I(((Export *) Arg(0))->addressv[erts_active_code_ix()]);
- dis_next = (Eterm *) *I;
+ dis_next = *I;
FCALLS--;
Goto(dis_next);
}
}
/*
+ * One-time initialization of emulator. Does not need to be
+ * in process_main().
+ */
+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);
+ }
+ }
+#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);
+
+ /*
+ * 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);
+ }
+}
+
+/*
* erts_dirty_process_main() is what dirty schedulers execute. Since they handle
* only NIF calls they do not need to be able to execute all BEAM
* instructions.
@@ -1210,7 +1230,7 @@ void erts_dirty_process_main(ErtsSchedulerData *esdp)
dtrace_proc_str(c_p, process_buf);
if (ERTS_PROC_IS_EXITING(c_p)) {
- strcpy(fun_buf, "<exiting>");
+ sys_strcpy(fun_buf, "<exiting>");
} else {
ErtsCodeMFA *cmfa = find_function_from_pc(c_p->i);
if (cmfa) {
@@ -1250,12 +1270,12 @@ void erts_dirty_process_main(ErtsSchedulerData *esdp)
ERTS_UNREQ_PROC_MAIN_LOCK(c_p);
ASSERT(!ERTS_PROC_IS_EXITING(c_p));
- if (em_apply_bif == (BeamInstr *) *I) {
+ if (BeamIsOpCode(*I, op_apply_bif)) {
exiting = erts_call_dirty_bif(esdp, c_p, I, reg);
}
else {
- ASSERT(em_call_nif == (BeamInstr *) *I);
- exiting = erts_call_dirty_nif(esdp, c_p, I, reg);
+ ASSERT(BeamIsOpCode(*I, op_call_nif));
+ exiting = erts_call_dirty_nif(esdp, c_p, I, reg);
}
ASSERT(!(c_p->flags & F_HIBERNATE_SCHED));
@@ -1435,6 +1455,7 @@ handle_error(Process* c_p, BeamInstr* pc, Eterm* reg, ErtsCodeMFA *bif_mfa)
reg[3] = c_p->ftrace;
if ((new_pc = next_catch(c_p, reg))) {
c_p->cp = 0; /* To avoid keeping stale references. */
+ ERTS_RECV_MARK_CLEAR(c_p); /* No longer safe to use this position */
return new_pc;
}
if (c_p->catches > 0) erts_exit(ERTS_ERROR_EXIT, "Catch not found");
@@ -2090,8 +2111,8 @@ apply_bif_error_adjustment(Process *p, Export *ep,
* and apply_last_IP.
*/
if (I
- && ep->beam[0] == (BeamInstr) em_apply_bif
- && (ep == bif_export[BIF_error_1]
+ && 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])) {
@@ -2399,8 +2420,7 @@ erts_hibernate(Process* c_p, Eterm* reg)
* shrink the heap.
*/
erts_proc_lock(c_p, ERTS_PROC_LOCK_MSGQ|ERTS_PROC_LOCK_STATUS);
- ERTS_MSGQ_MV_INQ2PRIVQ(c_p);
- if (!c_p->msg.len) {
+ if (!erts_proc_sig_fetch(c_p)) {
erts_proc_unlock(c_p, ERTS_PROC_LOCK_MSGQ|ERTS_PROC_LOCK_STATUS);
c_p->fvalue = NIL;
PROCESS_MAIN_CHK_LOCKS(c_p);
@@ -2408,8 +2428,7 @@ erts_hibernate(Process* c_p, Eterm* reg)
ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p);
PROCESS_MAIN_CHK_LOCKS(c_p);
erts_proc_lock(c_p, ERTS_PROC_LOCK_MSGQ|ERTS_PROC_LOCK_STATUS);
- ERTS_MSGQ_MV_INQ2PRIVQ(c_p);
- if (!c_p->msg.len)
+ if (!erts_proc_sig_fetch(c_p))
erts_atomic32_read_band_relb(&c_p->state, ~ERTS_PSFLG_ACTIVE);
ASSERT(!ERTS_PROC_IS_EXITING(c_p));
}
@@ -2737,7 +2756,7 @@ do { \
static Eterm
-new_map(Process* p, Eterm* reg, Uint live, Uint n, BeamInstr* ptr)
+erts_gc_new_map(Process* p, Eterm* reg, Uint live, Uint n, BeamInstr* ptr)
{
Uint i;
Uint need = n + 1 /* hdr */ + 1 /*size*/ + 1 /* ptr */ + 1 /* arity */;
@@ -2794,7 +2813,8 @@ new_map(Process* p, Eterm* reg, Uint live, Uint n, BeamInstr* ptr)
}
static Eterm
-new_small_map_lit(Process* p, Eterm* reg, Eterm keys_literal, Uint live, BeamInstr* ptr)
+erts_gc_new_small_map_lit(Process* p, Eterm* reg, Eterm keys_literal,
+ Uint live, BeamInstr* ptr)
{
Eterm* keys = tuple_val(keys_literal);
Uint n = arityval(*keys);
@@ -2828,7 +2848,8 @@ new_small_map_lit(Process* p, Eterm* reg, Eterm keys_literal, Uint live, BeamIns
}
static Eterm
-update_map_assoc(Process* p, Eterm* reg, Uint live, Uint n, BeamInstr* new_p)
+erts_gc_update_map_assoc(Process* p, Eterm* reg, Uint live,
+ Uint n, BeamInstr* new_p)
{
Uint num_old;
Uint num_updates;
@@ -2874,7 +2895,7 @@ update_map_assoc(Process* p, Eterm* reg, Uint live, Uint n, BeamInstr* new_p)
*/
if (num_old == 0) {
- return new_map(p, reg, live, n, new_p);
+ return erts_gc_new_map(p, reg, live, n, new_p);
}
/*
@@ -3030,7 +3051,7 @@ update_map_assoc(Process* p, Eterm* reg, Uint live, Uint n, BeamInstr* new_p)
*/
static Eterm
-update_map_exact(Process* p, Eterm* reg, Uint live, Uint n, Eterm* new_p)
+erts_gc_update_map_exact(Process* p, Eterm* reg, Uint live, Uint n, Eterm* new_p)
{
Uint i;
Uint num_old;
@@ -3181,13 +3202,16 @@ erts_is_builtin(Eterm Mod, Eterm Name, int arity)
Export e;
Export* ep;
- if (Mod == am_erlang && Name == am_apply && arity == 3) {
- /*
- * Special case. apply/3 is built-in (implemented in C),
- * but implemented in a different way than all other
- * BIFs.
- */
- return 1;
+ if (Mod == am_erlang) {
+ /*
+ * Special case for built-in functions that are implemented
+ * as instructions as opposed to SNIFs.
+ */
+ if (Name == am_apply && (arity == 2 || arity == 3)) {
+ return 1;
+ } else if (Name == am_yield && arity == 0) {
+ return 1;
+ }
}
e.info.mfa.module = Mod;
@@ -3197,8 +3221,8 @@ erts_is_builtin(Eterm Mod, Eterm Name, int arity)
if ((ep = export_get(&e)) == NULL) {
return 0;
}
- return ep->addressv[erts_active_code_ix()] == ep->beam
- && (ep->beam[0] == (BeamInstr) em_apply_bif);
+ return ep->addressv[erts_active_code_ix()] == ep->beam &&
+ BeamIsOpCode(ep->beam[0], op_apply_bif);
}
diff --git a/erts/emulator/beam/beam_load.c b/erts/emulator/beam/beam_load.c
index 3f9dc2c1aa..e61199a8fd 100644
--- a/erts/emulator/beam/beam_load.c
+++ b/erts/emulator/beam/beam_load.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1996-2017. All Rights Reserved.
+ * Copyright Ericsson AB 1996-2018. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -40,6 +40,7 @@
#include "erl_zlib.h"
#include "erl_map.h"
#include "erl_process_dict.h"
+#include "erl_unicode.h"
#ifdef HIPE
#include "hipe_bif0.h"
@@ -55,12 +56,6 @@ ErlDrvBinary* erts_gzinflate_buffer(char*, int);
#define DEFINED 1
#define EXPORTED 2
-#ifdef NO_JUMP_TABLE
-# define BeamOpCode(Op) ((BeamInstr)(Op))
-#else
-# define BeamOpCode(Op) ((BeamInstr)beam_ops[Op])
-#endif
-
#if defined(WORDS_BIGENDIAN)
# define NATIVE_ENDIAN(F) \
if ((F).val & BSF_NATIVE) { \
@@ -464,7 +459,7 @@ typedef struct LoaderState {
#ifdef DEBUG
# define GARBAGE 0xCC
-# define DEBUG_INIT_GENOP(Dst) memset(Dst, GARBAGE, sizeof(GenOp))
+# define DEBUG_INIT_GENOP(Dst) sys_memset(Dst, GARBAGE, sizeof(GenOp))
#else
# define DEBUG_INIT_GENOP(Dst)
#endif
@@ -559,6 +554,7 @@ static Eterm get_module_info(Process* p, ErtsCodeIndex code_ix,
static Eterm exported_from_module(Process* p, ErtsCodeIndex code_ix,
Eterm mod);
static Eterm functions_in_module(Process* p, BeamCodeHeader*);
+static Eterm nifs_in_module(Process* p, Eterm module);
static Eterm attributes_for_module(Process* p, BeamCodeHeader*);
static Eterm compilation_info_for_module(Process* p, BeamCodeHeader*);
static Eterm md5_of_module(Process* p, BeamCodeHeader*);
@@ -812,11 +808,6 @@ erts_finish_loading(Binary* magic, Process* c_p,
struct erl_module_instance* inst_p;
Uint size;
- /*
- * No other process may run since we will update the export
- * table which is not protected by any locks.
- */
-
ERTS_LC_ASSERT(erts_initialized == 0 || erts_has_code_write_permission() ||
erts_thr_progress_is_blocking());
/*
@@ -849,10 +840,9 @@ erts_finish_loading(Binary* magic, Process* c_p,
continue;
}
if (ep->addressv[code_ix] == ep->beam) {
- if (ep->beam[0] == (BeamInstr) em_apply_bif) {
+ if (BeamIsOpCode(ep->beam[0], op_apply_bif)) {
continue;
- } else if (ep->beam[0] ==
- (BeamInstr) BeamOp(op_i_generic_breakpoint)) {
+ } else if (BeamIsOpCode(ep->beam[0], 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);
@@ -1433,7 +1423,7 @@ load_atom_table(LoaderState* stp, ErtsAtomEncoding enc)
Atom* ap;
ap = atom_tab(atom_val(stp->atom[1]));
- memcpy(sbuf, ap->name, ap->len);
+ sys_memcpy(sbuf, ap->name, ap->len);
sbuf[ap->len] = '\0';
LoadError1(stp, "module name in object code is %s", sbuf);
}
@@ -1482,7 +1472,7 @@ load_import_table(LoaderState* stp)
* the BIF function.
*/
if ((e = erts_active_export_entry(mod, func, arity)) != NULL) {
- if (e->beam[0] == (BeamInstr) em_apply_bif) {
+ if (BeamIsOpCode(e->beam[0], op_apply_bif)) {
stp->import[i].bf = (BifFunction) e->beam[1];
if (func == am_load_nif && mod == am_erlang && arity == 2) {
stp->may_load_nif = 1;
@@ -1576,7 +1566,7 @@ is_bif(Eterm mod, Eterm func, unsigned arity)
if (e == NULL) {
return 0;
}
- if (e->beam[0] != (BeamInstr) em_apply_bif) {
+ if (! BeamIsOpCode(e->beam[0], op_apply_bif)) {
return 0;
}
if (mod == am_erlang && func == am_apply && arity == 3) {
@@ -1818,7 +1808,7 @@ read_line_table(LoaderState* stp)
GetInt(stp, 2, n);
GetString(stp, fname, n);
- stp->fname[i] = erts_atom_put(fname, n, ERTS_ATOM_ENC_LATIN1, 1);
+ stp->fname[i] = erts_atom_put(fname, n, ERTS_ATOM_ENC_UTF8, 1);
}
}
@@ -2100,7 +2090,7 @@ load_code(LoaderState* stp)
erts_alloc(ERTS_ALC_T_LOADER_TMP,
(arity+last_op->a[arg].val)
*sizeof(GenOpArg));
- memcpy(last_op->a, last_op->def_args,
+ sys_memcpy(last_op->a, last_op->def_args,
arity*sizeof(GenOpArg));
arity += last_op->a[arg].val;
break;
@@ -2323,7 +2313,7 @@ load_code(LoaderState* stp)
stp->specific_op = specific;
CodeNeed(opc[stp->specific_op].sz+16); /* Extra margin for packing */
last_instr_start = ci + opc[stp->specific_op].adjust;
- code[ci++] = BeamOpCode(stp->specific_op);
+ code[ci++] = BeamOpCodeAddr(stp->specific_op);
}
/*
@@ -2396,8 +2386,18 @@ load_code(LoaderState* stp)
code[ci++] = NIL;
break;
case TAG_q:
- new_literal_patch(stp, ci);
- code[ci++] = tmp_op->a[arg].val;
+ {
+ BeamInstr val = tmp_op->a[arg].val;
+ Eterm term = stp->literals[val].term;
+ new_literal_patch(stp, ci);
+ code[ci++] = val;
+ switch (loader_tag(term)) {
+ case LOADER_X_REG:
+ case LOADER_Y_REG:
+ LoadError1(stp, "the term '%T' would be confused "
+ "with a register", term);
+ }
+ }
break;
default:
LoadError1(stp, "bad tag %d for general source",
@@ -2585,23 +2585,31 @@ load_code(LoaderState* stp)
sp++;
}
break;
- case 'i': /* Initialize packing accumulator. */
- packed = code[--ci];
+#ifdef ARCH_64
+ case '1': /* Tightest shift (always 10 bits) */
+ ci--;
+ ASSERT((code[ci] & ~0x1FF8ull) == 0); /* Fits in 10 bits */
+ packed = (packed << BEAM_TIGHTEST_SHIFT);
+ packed |= code[ci] >> 3;
+ if (packed_label) {
+ packed_label->packed++;
+ }
break;
- case '0': /* Tight shift */
+#endif
+ case '2': /* Tight shift (10 or 16 bits) */
packed = (packed << BEAM_TIGHT_SHIFT) | code[--ci];
if (packed_label) {
packed_label->packed++;
}
break;
- case '6': /* Shift 16 steps */
+ case '3': /* Loose shift (16 bits) */
packed = (packed << BEAM_LOOSE_SHIFT) | code[--ci];
if (packed_label) {
packed_label->packed++;
}
break;
#ifdef ARCH_64
- case 'w': /* Shift 32 steps */
+ case '4': /* Wide shift (32 bits) */
{
Uint w = code[--ci];
@@ -2643,18 +2651,41 @@ load_code(LoaderState* stp)
}
ci++;
break;
- case 'P': /* Put packed operands. */
+ case 'P': /* Put packed operands (on the stack). */
sp->instr = packed;
sp->patch_pos = 0;
- sp++;
- packed = 0;
if (packed_label) {
- packed_label->pos = ci;
+ sp->patch_pos = &packed_label->pos;
packed_label = 0;
}
+ sp++;
+ packed = 0;
+ break;
+#if defined(ARCH_64) && defined(CODE_MODEL_SMALL)
+ case '#': /* -1 */
+ case '$': /* -2 */
+ case '%': /* -3 */
+ case '&': /* -4 */
+ case '\'': /* -5 */
+ case '(': /* -6 */
+ /* Pack accumulator contents into instruction word. */
+ {
+ Sint pos = ci - (*prog - '#' + 1);
+ /* Are the high 32 bits of the instruction word zero? */
+ ASSERT((code[pos] & ~((1ull << BEAM_WIDE_SHIFT)-1)) == 0);
+ code[pos] |= packed << BEAM_WIDE_SHIFT;
+ if (packed_label) {
+ ASSERT(packed_label->packed == 1);
+ packed_label->pos = pos;
+ packed_label->packed = 2;
+ packed_label = 0;
+ }
+ packed >>= BEAM_WIDE_SHIFT;
+ }
break;
+#endif
default:
- ASSERT(0);
+ erts_exit(ERTS_ERROR_EXIT, "beam_load: invalid packing op: %c\n", *prog);
}
}
ASSERT(sp == stack); /* Incorrect program? */
@@ -4491,6 +4522,19 @@ is_empty_map(LoaderState* stp, GenOpArg Lit)
}
/*
+ * Predicate to test whether the given literal is an export.
+ */
+static int
+literal_is_export(LoaderState* stp, GenOpArg Lit)
+{
+ Eterm term;
+
+ ASSERT(Lit.type == TAG_q);
+ term = stp->literals[Lit.val].term;
+ return is_export(term);
+}
+
+/*
* Pseudo predicate map_key_sort that will sort the Rest operand for
* map instructions as a side effect.
*/
@@ -4885,7 +4929,9 @@ freeze_code(LoaderState* stp)
line_items[i] = codev + stp->ci - 1;
line_tab->fname_ptr = (Eterm*) &line_items[i + 1];
- memcpy(line_tab->fname_ptr, stp->fname, stp->num_fnames*sizeof(Eterm));
+ if (stp->num_fnames)
+ sys_memcpy(line_tab->fname_ptr, stp->fname,
+ stp->num_fnames*sizeof(Eterm));
line_tab->loc_size = stp->loc_size;
if (stp->loc_size == 2) {
@@ -5003,7 +5049,7 @@ freeze_code(LoaderState* stp)
*/
codev[pos] = (BeamInstr) (codev + value);
} else {
-#ifdef DEBUG
+#if defined(DEBUG) && defined(BEAM_WIDE_MASK)
Uint w;
#endif
Sint32 rel = lp->offset + value;
@@ -5079,7 +5125,7 @@ final_touch(LoaderState* stp, struct erl_module_instance* inst_p)
while (index != 0) {
BeamInstr next = codev[index];
BeamInstr* abs_addr;
- codev[index] = BeamOpCode(op_catch_yf);
+ codev[index] = BeamOpCodeAddr(op_catch_yf);
/* We must make the address of the label absolute again. */
abs_addr = (BeamInstr *)codev + index + codev[index+2];
catches = beam_catches_cons(abs_addr, catches);
@@ -5468,8 +5514,8 @@ transform_engine(LoaderState* st)
case TOP_store_rest_args:
{
GENOP_ARITY(instr, instr->arity+num_rest_args);
- memcpy(instr->a, instr->def_args, ap*sizeof(GenOpArg));
- memcpy(instr->a+ap, rest_args, num_rest_args*sizeof(GenOpArg));
+ sys_memcpy(instr->a, instr->def_args, ap*sizeof(GenOpArg));
+ sys_memcpy(instr->a+ap, rest_args, num_rest_args*sizeof(GenOpArg));
ap += num_rest_args;
}
break;
@@ -5922,6 +5968,8 @@ get_module_info(Process* p, ErtsCodeIndex code_ix, BeamCodeHeader* code_hdr,
return exported_from_module(p, code_ix, module);
} else if (what == am_functions) {
return functions_in_module(p, code_hdr);
+ } else if (what == am_nifs) {
+ return nifs_in_module(p, module);
} else if (what == am_attributes) {
return attributes_for_module(p, code_hdr);
} else if (what == am_compile) {
@@ -5975,6 +6023,46 @@ functions_in_module(Process* p, /* Process whose heap to use. */
}
/*
+ * Builds a list of all NIFs in the given module:
+ * [{Name, Arity},...]
+ */
+Eterm
+nifs_in_module(Process* p, Eterm module)
+{
+ Eterm nif_list, *hp;
+ Module *mod;
+
+ mod = erts_get_module(module, erts_active_code_ix());
+ nif_list = NIL;
+
+ if (mod->curr.nif != NULL) {
+ int func_count, func_ix;
+ ErlNifFunc *funcs;
+
+ func_count = erts_nif_get_funcs(mod->curr.nif, &funcs);
+ hp = HAlloc(p, func_count * 5);
+
+ for (func_ix = func_count - 1; func_ix >= 0; func_ix--) {
+ Eterm name, arity, pair;
+ ErlNifFunc *func;
+
+ func = &funcs[func_ix];
+
+ name = am_atom_put(func->name, sys_strlen(func->name));
+ arity = make_small(func->arity);
+
+ pair = TUPLE2(hp, name, arity);
+ hp += 3;
+
+ nif_list = CONS(hp, pair, nif_list);
+ hp += 2;
+ }
+ }
+
+ return nif_list;
+}
+
+/*
* Returns 'true' if mod has any native compiled functions, otherwise 'false'
*/
@@ -5999,13 +6087,36 @@ erts_release_literal_area(ErtsLiteralArea* literal_area)
return;
oh = literal_area->off_heap;
-
+
while (oh) {
- Binary* bptr;
- ASSERT(thing_subtag(oh->thing_word) == REFC_BINARY_SUBTAG);
- bptr = ((ProcBin*)oh)->val;
- erts_bin_release(bptr);
- oh = oh->next;
+ switch (thing_subtag(oh->thing_word)) {
+ case REFC_BINARY_SUBTAG:
+ {
+ Binary* bptr = ((ProcBin*)oh)->val;
+ erts_bin_release(bptr);
+ break;
+ }
+ case FUN_SUBTAG:
+ {
+ ErlFunEntry* fe = ((ErlFunThing*)oh)->fe;
+ if (erts_refc_dectest(&fe->refc, 0) == 0) {
+ erts_erase_fun_entry(fe);
+ }
+ break;
+ }
+ case REF_SUBTAG:
+ {
+ ErtsMagicBinary *bptr;
+ ASSERT(is_magic_ref_thing(oh));
+ bptr = ((ErtsMRefThing *) oh)->mb;
+ erts_bin_release((Binary *) bptr);
+ break;
+ }
+ default:
+ ASSERT(is_external_header(oh->thing_word));
+ erts_deref_node_entry(((ExternalThing*)oh)->node);
+ }
+ oh = oh->next;
}
erts_free(ERTS_ALC_T_LITERAL, literal_area);
}
@@ -6033,9 +6144,9 @@ int
erts_is_function_native(ErtsCodeInfo *ci)
{
#ifdef HIPE
- ASSERT(ci->op == (BeamInstr) BeamOp(op_i_func_info_IaaI));
- return erts_codeinfo_to_code(ci)[0] == (BeamInstr) BeamOp(op_hipe_trap_call)
- || erts_codeinfo_to_code(ci)[0] == (BeamInstr) BeamOp(op_hipe_trap_call_closure);
+ ASSERT(BeamIsOpCode(ci->op, op_i_func_info_IaaI));
+ return BeamIsOpCode(erts_codeinfo_to_code(ci)[0], op_hipe_trap_call) ||
+ BeamIsOpCode(erts_codeinfo_to_code(ci)[0], op_hipe_trap_call_closure);
#else
return 0;
#endif
@@ -6104,7 +6215,7 @@ exported_from_module(Process* p, /* Process whose heap to use. */
Eterm tuple;
if (ep->addressv[code_ix] == ep->beam &&
- ep->beam[0] == (BeamInstr) em_call_error_handler) {
+ BeamIsOpCode(ep->beam[0], op_call_error_handler)) {
/* There is a call to the function, but it does not exist. */
continue;
}
@@ -6202,8 +6313,7 @@ erts_build_mfa_item(FunctionInfo* fi, Eterm* hp, Eterm args, Eterm* mfa_p)
file_term = buf_to_intlist(&hp, ".erl", 4, NIL);
file_term = buf_to_intlist(&hp, (char*)ap->name, ap->len, file_term);
} else {
- Atom* ap = atom_tab(atom_val((fi->fname_ptr)[file-1]));
- file_term = buf_to_intlist(&hp, (char*)ap->name, ap->len, NIL);
+ file_term = erts_atom_to_string(&hp, (fi->fname_ptr)[file-1]);
}
tuple = TUPLE2(hp, am_line, make_small(line));
@@ -6375,7 +6485,7 @@ make_stub(ErtsCodeInfo* info, Eterm mod, Eterm func, Uint arity, Uint native, Be
{
DBG_TRACE_MFA(mod,func,arity,"make beam stub at %p", erts_codeinfo_to_code(info));
ASSERT(WORDS_PER_FUNCTION == 6);
- info->op = (BeamInstr) BeamOp(op_i_func_info_IaaI);
+ info->op = BeamOpCodeAddr(op_i_func_info_IaaI);
info->u.ncallee = (void (*)(void)) native;
info->mfa.module = mod;
info->mfa.function = func;
@@ -6395,7 +6505,7 @@ stub_copy_info(LoaderState* stp,
Sint decoded_size;
Uint size = stp->chunks[chunk].size;
if (size != 0) {
- memcpy(info, stp->chunks[chunk].start, size);
+ sys_memcpy(info, stp->chunks[chunk].start, size);
*ptr_word = info;
decoded_size = erts_decode_ext_size(info, size);
if (decoded_size < 0) {
@@ -6480,7 +6590,7 @@ stub_final_touch(LoaderState* stp, ErtsCodeInfo* ci)
for (i = 0, lp = stp->lambdas; i < n; i++, lp++) {
ErlFunEntry* fe = stp->lambdas[i].fe;
if (lp->function == ci->mfa.function && lp->arity == ci->mfa.arity) {
- *erts_codeinfo_to_code(ci) = (Eterm) BeamOpCode(op_hipe_trap_call_closure);
+ *erts_codeinfo_to_code(ci) = BeamOpCodeAddr(op_hipe_trap_call_closure);
fe->address = erts_codeinfo_to_code(ci);
}
}
@@ -6804,7 +6914,7 @@ erts_make_stub_module(Process* p, Eterm hipe_magic_bin, Eterm Beam, Eterm Info)
* as the body until we know what kind of trap we should put there.
*/
code_hdr->functions[i] = (ErtsCodeInfo*)fp;
- op = (Eterm) BeamOpCode(op_hipe_trap_call); /* Might be changed later. */
+ op = BeamOpCodeAddr(op_hipe_trap_call); /* Might be changed later. */
fp = make_stub((ErtsCodeInfo*)fp, hipe_stp->module, func, arity,
(Uint)native_address, op);
}
@@ -6814,7 +6924,7 @@ erts_make_stub_module(Process* p, Eterm hipe_magic_bin, Eterm Beam, Eterm Info)
*/
code_hdr->functions[i] = (ErtsCodeInfo*)fp;
- *fp++ = (BeamInstr) BeamOp(op_int_code_end);
+ *fp++ = BeamOpCodeAddr(op_int_code_end);
/*
* Copy attributes and compilation information.
@@ -6932,6 +7042,8 @@ int erts_commit_hipe_patch_load(Eterm hipe_magic_bin)
hipe_stp->new_hipe_refs = NULL;
hipe_stp->new_hipe_sdesc = NULL;
+ hipe_redirect_to_module(modp);
+
return 1;
}
@@ -6963,8 +7075,8 @@ void dbg_set_traced_mfa(const char* m, const char* f, Uint a)
{
unsigned i = dbg_trace_ix++;
ASSERT(i < MFA_MAX);
- dbg_trace_m[i] = am_atom_put(m, strlen(m));
- dbg_trace_f[i] = am_atom_put(f, strlen(f));
+ dbg_trace_m[i] = am_atom_put(m, sys_strlen(m));
+ dbg_trace_f[i] = am_atom_put(f, sys_strlen(f));
dbg_trace_a[i] = a;
}
diff --git a/erts/emulator/beam/beam_load.h b/erts/emulator/beam/beam_load.h
index c4a90d3f3a..156c3c45e2 100644
--- a/erts/emulator/beam/beam_load.h
+++ b/erts/emulator/beam/beam_load.h
@@ -37,11 +37,6 @@ typedef struct gen_op_entry {
extern const GenOpEntry gen_opc[];
-extern BeamInstr beam_debug_apply[];
-extern BeamInstr* em_call_error_handler;
-extern BeamInstr* em_apply_bif;
-extern BeamInstr* em_call_nif;
-
struct ErtsLiteralArea_;
/*
diff --git a/erts/emulator/beam/beam_ranges.c b/erts/emulator/beam/beam_ranges.c
index 6e373a3480..f0c9496341 100644
--- a/erts/emulator/beam/beam_ranges.c
+++ b/erts/emulator/beam/beam_ranges.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2012-2016. All Rights Reserved.
+ * Copyright Ericsson AB 2012-2018. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -26,12 +26,22 @@
#include "erl_vm.h"
#include "global.h"
#include "beam_load.h"
+#include "erl_unicode.h"
typedef struct {
BeamInstr* start; /* Pointer to start of module. */
erts_atomic_t end; /* (BeamInstr*) Points one word beyond last function in module. */
} Range;
+/*
+ * Used for crash dumping of literals. The size of erts_dump_lit_areas is
+ * always twice the number of active ranges (to allow for literals in both
+ * current and old code).
+ */
+
+ErtsLiteralArea** erts_dump_lit_areas;
+Uint erts_dump_num_lit_areas;
+
/* Range 'end' needs to be atomic as we purge module
by setting end=start in active code_ix */
#define RANGE_END(R) ((BeamInstr*)erts_atomic_read_nob(&(R)->end))
@@ -97,6 +107,11 @@ erts_init_ranges(void)
r[i].allocated = 0;
erts_atomic_init_nob(&r[i].mid, 0);
}
+
+ erts_dump_num_lit_areas = 8;
+ erts_dump_lit_areas = (ErtsLiteralArea **)
+ erts_alloc(ERTS_ALC_T_CRASH_DUMP,
+ erts_dump_num_lit_areas * sizeof(ErtsLiteralArea*));
}
void
@@ -164,6 +179,14 @@ erts_end_staging_ranges(int commit)
erts_atomic_set_nob(&r[dst].mid,
(erts_aint_t) (r[dst].modules +
r[dst].n / 2));
+
+ if (r[dst].allocated * 2 > erts_dump_num_lit_areas) {
+ erts_dump_num_lit_areas *= 2;
+ erts_dump_lit_areas = (ErtsLiteralArea **)
+ erts_realloc(ERTS_ALC_T_CRASH_DUMP,
+ (void *) erts_dump_lit_areas,
+ erts_dump_num_lit_areas * sizeof(ErtsLiteralArea*));
+ }
}
}
@@ -319,8 +342,7 @@ lookup_loc(FunctionInfo* fi, const BeamInstr* pc,
Atom* mod_atom = atom_tab(atom_val(fi->mfa->module));
fi->needed += 2*(mod_atom->len+4);
} else {
- Atom* ap = atom_tab(atom_val((fi->fname_ptr)[file-1]));
- fi->needed += 2*ap->len;
+ fi->needed += 2*erts_atom_to_string_length((fi->fname_ptr)[file-1]);
}
return;
} else {
diff --git a/erts/emulator/beam/bif.c b/erts/emulator/beam/bif.c
index 80f391e91e..79244b8544 100644
--- a/erts/emulator/beam/bif.c
+++ b/erts/emulator/beam/bif.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1996-2017. All Rights Reserved.
+ * Copyright Ericsson AB 1996-2018. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -46,11 +46,12 @@
#include "erl_bif_unique.h"
#include "erl_map.h"
#include "erl_msacc.h"
+#include "erl_proc_sig_queue.h"
Export *erts_await_result;
+static Export await_exit_trap;
static Export* flush_monitor_messages_trap = NULL;
static Export* set_cpu_topology_trap = NULL;
-static Export* await_proc_exit_trap = NULL;
static Export* await_port_send_result_trap = NULL;
Export* erts_format_cpu_topology_trap = NULL;
static Export dsend_continue_trap_export;
@@ -59,6 +60,7 @@ Export *erts_convert_time_unit_trap = NULL;
static Export *await_msacc_mod_trap = NULL;
static erts_atomic32_t msacc;
+static Export *system_flag_scheduler_wall_time_trap;
static Export *await_sched_wall_time_mod_trap;
static erts_atomic32_t sched_wall_time;
@@ -91,83 +93,51 @@ BIF_RETTYPE spawn_3(BIF_ALIST_3)
/* Utility to add a new link between processes p and another internal
* process (rpid). Process p must be the currently executing process.
*/
-static int insert_internal_link(Process* p, Eterm rpid)
-{
- Process *rp;
- ErtsProcLocks rp_locks = ERTS_PROC_LOCK_LINK;
-
- ASSERT(is_internal_pid(rpid));
-
- if (IS_TRACED(p)
- && (ERTS_TRACE_FLAGS(p) & (F_TRACE_SOL|F_TRACE_SOL1))) {
- rp_locks = ERTS_PROC_LOCKS_ALL;
- }
-
- erts_proc_lock(p, ERTS_PROC_LOCK_LINK);
-
- /* get a pointer to the process struct of the linked process */
- rp = erts_pid2proc_opt(p, ERTS_PROC_LOCK_MAIN|ERTS_PROC_LOCK_LINK,
- rpid, rp_locks,
- ERTS_P2P_FLG_ALLOW_OTHER_X);
-
- if (!rp) {
- erts_proc_unlock(p, ERTS_PROC_LOCK_LINK);
- return 0;
- }
-
- if (p != rp) {
- erts_add_link(&ERTS_P_LINKS(p), LINK_PID, rp->common.id);
- erts_add_link(&ERTS_P_LINKS(rp), LINK_PID, p->common.id);
-
- ASSERT(IS_TRACER_VALID(ERTS_TRACER(p)));
-
- if (IS_TRACED(p)) {
- if (ERTS_TRACE_FLAGS(p) & (F_TRACE_SOL|F_TRACE_SOL1)) {
- ERTS_TRACE_FLAGS(rp) |= (ERTS_TRACE_FLAGS(p) & TRACEE_FLAGS);
- erts_tracer_replace(&rp->common, ERTS_TRACER(p));
- if (ERTS_TRACE_FLAGS(p) & F_TRACE_SOL1) { /* maybe override */
- ERTS_TRACE_FLAGS(rp) &= ~(F_TRACE_SOL1 | F_TRACE_SOL);
- ERTS_TRACE_FLAGS(p) &= ~(F_TRACE_SOL1 | F_TRACE_SOL);
- }
- }
- }
- }
- if (IS_TRACED_FL(rp, F_TRACE_PROCS))
- trace_proc(p, p == rp ? rp_locks : ERTS_PROC_LOCK_MAIN|ERTS_PROC_LOCK_LINK,
- rp, am_getting_linked, p->common.id);
-
- if (p == rp)
- erts_proc_unlock(p, rp_locks & ~ERTS_PROC_LOCK_MAIN);
- else {
- erts_proc_unlock(p, ERTS_PROC_LOCK_LINK);
- erts_proc_unlock(rp, rp_locks);
- }
-
- return 1;
-}
-
/* create a link to the process */
BIF_RETTYPE link_1(BIF_ALIST_1)
{
- DistEntry *dep;
-
if (IS_TRACED_FL(BIF_P, F_TRACE_PROCS)) {
trace_proc(BIF_P, ERTS_PROC_LOCK_MAIN, BIF_P, am_link, BIF_ARG_1);
}
/* check that the pid or port which is our argument is OK */
if (is_internal_pid(BIF_ARG_1)) {
- if (insert_internal_link(BIF_P, BIF_ARG_1)) {
- BIF_RET(am_true);
- }
- else {
- goto res_no_proc;
- }
+ int created;
+ ErtsLinkData *ldp;
+ ErtsLink *lnk;
+
+ if (BIF_P->common.id == BIF_ARG_1)
+ BIF_RET(am_true);
+
+ if (!erts_proc_lookup(BIF_ARG_1))
+ goto res_no_proc;
+
+ lnk = erts_link_tree_lookup_create(&ERTS_P_LINKS(BIF_P),
+ &created,
+ ERTS_LNK_TYPE_PROC,
+ BIF_P->common.id,
+ BIF_ARG_1);
+ if (!created)
+ BIF_RET(am_true);
+
+ ldp = erts_link_to_data(lnk);
+
+
+ if (erts_proc_sig_send_link(BIF_P, BIF_ARG_1, &ldp->b))
+ BIF_RET(am_true);
+
+ erts_link_tree_delete(&ERTS_P_LINKS(BIF_P), lnk);
+ erts_link_release_both(ldp);
+ goto res_no_proc;
}
if (is_internal_port(BIF_ARG_1)) {
- int send_link_signal = 0;
+ int created;
+ ErtsLinkData *ldp;
+ ErtsLink *lnk;
+ Eterm ref;
+ Eterm *refp;
Port *prt = erts_port_lookup(BIF_ARG_1,
(erts_port_synchronous_ops
? ERTS_PORT_SFLGS_INVALID_DRIVER_LOOKUP
@@ -176,31 +146,31 @@ BIF_RETTYPE link_1(BIF_ALIST_1)
goto res_no_proc;
}
- erts_proc_lock(BIF_P, ERTS_PROC_LOCK_LINK);
-
- if (erts_add_link(&ERTS_P_LINKS(BIF_P), LINK_PID, BIF_ARG_1) >= 0)
- send_link_signal = 1;
- /* else: already linked */
-
- erts_proc_unlock(BIF_P, ERTS_PROC_LOCK_LINK);
+ lnk = erts_link_tree_lookup_create(&ERTS_P_LINKS(BIF_P),
+ &created,
+ ERTS_LNK_TYPE_PORT,
+ BIF_P->common.id,
+ BIF_ARG_1);
+ if (!created)
+ BIF_RET(am_true);
- if (send_link_signal) {
- Eterm ref;
- Eterm *refp = erts_port_synchronous_ops ? &ref : NULL;
+ ldp = erts_link_to_data(lnk);
+ refp = erts_port_synchronous_ops ? &ref : NULL;
- switch (erts_port_link(BIF_P, prt, BIF_P->common.id, refp)) {
- case ERTS_PORT_OP_DROPPED:
- case ERTS_PORT_OP_BADARG:
- goto res_no_proc;
- case ERTS_PORT_OP_SCHEDULED:
- if (refp) {
- ASSERT(is_internal_ordinary_ref(ref));
- BIF_TRAP3(await_port_send_result_trap, BIF_P, ref, am_true, am_true);
- }
- default:
- break;
- }
- }
+ switch (erts_port_link(BIF_P, prt, &ldp->b, refp)) {
+ case ERTS_PORT_OP_DROPPED:
+ case ERTS_PORT_OP_BADARG:
+ erts_link_tree_delete(&ERTS_P_LINKS(BIF_P), lnk);
+ erts_link_release_both(ldp);
+ goto res_no_proc;
+ case ERTS_PORT_OP_SCHEDULED:
+ if (refp) {
+ ASSERT(is_internal_ordinary_ref(ref));
+ BIF_TRAP3(await_port_send_result_trap, BIF_P, ref, am_true, am_true);
+ }
+ default:
+ break;
+ }
BIF_RET(am_true);
}
else if (is_external_port(BIF_ARG_1)
@@ -209,296 +179,203 @@ BIF_RETTYPE link_1(BIF_ALIST_1)
}
if (is_external_pid(BIF_ARG_1)) {
+ ErtsLinkData *ldp;
+ int created;
+ DistEntry *dep;
+ ErtsLink *lnk;
+ int code;
+ ErtsDSigData dsd;
+
+ dep = external_pid_dist_entry(BIF_ARG_1);
+ if (dep == erts_this_dist_entry)
+ goto res_no_proc;
- erts_proc_lock(BIF_P, ERTS_PROC_LOCK_LINK);
-
- /* We may earn time by checking first that we're not linked already */
- if (erts_lookup_link(ERTS_P_LINKS(BIF_P), BIF_ARG_1) != NULL) {
- erts_proc_unlock(BIF_P, ERTS_PROC_LOCK_LINK);
- BIF_RET(am_true);
- }
- else {
- ErtsLink *lnk;
- int code;
- ErtsDSigData dsd;
- dep = external_pid_dist_entry(BIF_ARG_1);
- if (dep == erts_this_dist_entry) {
- erts_proc_unlock(BIF_P, ERTS_PROC_LOCK_LINK);
- goto res_no_proc;
- }
-
- code = erts_dsig_prepare(&dsd, dep, BIF_P, ERTS_DSP_RLOCK, 0);
- switch (code) {
- case ERTS_DSIG_PREP_NOT_ALIVE:
- /* Let the dlink trap handle it */
- case ERTS_DSIG_PREP_NOT_CONNECTED:
- erts_proc_unlock(BIF_P, ERTS_PROC_LOCK_LINK);
- BIF_TRAP1(dlink_trap, BIF_P, BIF_ARG_1);
-
- case ERTS_DSIG_PREP_CONNECTED:
- /* We are connected. Setup link and send link signal */
-
- erts_de_links_lock(dep);
-
- erts_add_link(&ERTS_P_LINKS(BIF_P), LINK_PID, BIF_ARG_1);
- lnk = erts_add_or_lookup_link(&(dep->nlinks),
- LINK_PID,
- BIF_P->common.id);
- ASSERT(lnk != NULL);
- erts_add_link(&ERTS_LINK_ROOT(lnk), LINK_PID, BIF_ARG_1);
-
- erts_de_links_unlock(dep);
- erts_de_runlock(dep);
- erts_proc_unlock(BIF_P, ERTS_PROC_LOCK_LINK);
-
- code = erts_dsig_send_link(&dsd, BIF_P->common.id, BIF_ARG_1);
- if (code == ERTS_DSIG_SEND_YIELD)
- ERTS_BIF_YIELD_RETURN(BIF_P, am_true);
- BIF_RET(am_true);
- default:
- ASSERT(! "Invalid dsig prepare result");
- BIF_ERROR(BIF_P, EXC_INTERNAL_ERROR);
- }
- }
+ lnk = erts_link_tree_lookup_create(&ERTS_P_LINKS(BIF_P),
+ &created,
+ ERTS_LNK_TYPE_DIST_PROC,
+ BIF_P->common.id,
+ BIF_ARG_1);
+
+ if (!created)
+ BIF_RET(am_true); /* Already present... */
+
+ ldp = erts_link_to_data(lnk);
+
+ code = erts_dsig_prepare(&dsd, dep, BIF_P,
+ ERTS_PROC_LOCK_MAIN,
+ ERTS_DSP_RLOCK, 0, 1);
+ switch (code) {
+ case ERTS_DSIG_PREP_NOT_ALIVE:
+ case ERTS_DSIG_PREP_NOT_CONNECTED:
+ erts_link_set_dead_dist(&ldp->b, dep->sysname);
+ erts_proc_sig_send_link_exit(NULL, BIF_ARG_1, &ldp->b,
+ am_noconnection, NIL);
+ BIF_RET(am_true);
+
+ case ERTS_DSIG_PREP_PENDING:
+ case ERTS_DSIG_PREP_CONNECTED: {
+ /*
+ * We have (pending) connection.
+ * Setup link and enqueue link signal.
+ */
+#ifdef DEBUG
+ int inserted =
+#endif
+ erts_link_dist_insert(&ldp->b, dep->mld);
+ ASSERT(inserted);
+ erts_de_runlock(dep);
+
+ code = erts_dsig_send_link(&dsd, BIF_P->common.id, BIF_ARG_1);
+ if (code == ERTS_DSIG_SEND_YIELD)
+ ERTS_BIF_YIELD_RETURN(BIF_P, am_true);
+ BIF_RET(am_true);
+ break;
+ }
+ default:
+ ERTS_ASSERT(! "Invalid dsig prepare result");
+ }
}
BIF_ERROR(BIF_P, BADARG);
-res_no_proc: {
- erts_aint32_t state = erts_atomic32_read_nob(&BIF_P->state);
- if (state & ERTS_PSFLG_TRAP_EXIT) {
- ErtsProcLocks locks = ERTS_PROC_LOCK_MAIN;
- erts_deliver_exit_message(BIF_ARG_1, BIF_P, &locks, am_noproc, NIL);
- erts_proc_unlock(BIF_P, ~ERTS_PROC_LOCK_MAIN & locks);
- BIF_RET(am_true);
- }
- else
- BIF_ERROR(BIF_P, EXC_NOPROC);
+res_no_proc:
+ if (BIF_P->flags & F_TRAP_EXIT) {
+ ErtsProcLocks locks = ERTS_PROC_LOCK_MAIN;
+ erts_deliver_exit_message(BIF_ARG_1, BIF_P, &locks, am_noproc, NIL);
+ erts_proc_unlock(BIF_P, ~ERTS_PROC_LOCK_MAIN & locks);
+ BIF_RET(am_true);
+ }
+ else {
+ /*
+ * This behaviour is *really* sad but link/1 has
+ * behaved like this for ages (and this behaviour is
+ * actually documented)... :'-(
+ *
+ * The proper behavior would have been to
+ * send calling process an exit signal..
+ */
+ BIF_ERROR(BIF_P, EXC_NOPROC);
}
}
-/* This function is allowed to return range of values handled by demonitor/1-2
- * Namely: atoms true, false, yield, internal_error, badarg or THE_NON_VALUE
- */
static Eterm
-remote_demonitor(Process *c_p, DistEntry *dep, Eterm ref, Eterm to)
+demonitor(Process *c_p, Eterm ref, Eterm *multip)
{
- ErtsDSigData dsd;
- ErtsMonitor *dmon;
- ErtsMonitor *mon;
- int code;
- Eterm res = am_false;
-
- ERTS_LC_ASSERT((ERTS_PROC_LOCK_MAIN|ERTS_PROC_LOCK_LINK)
- == erts_proc_lc_my_proc_locks(c_p));
-
- code = erts_dsig_prepare(&dsd, dep, c_p, ERTS_DSP_RLOCK, 0);
- switch (code) {
- case ERTS_DSIG_PREP_NOT_ALIVE:
- case ERTS_DSIG_PREP_NOT_CONNECTED:
- /*
- * In the smp case this is possible if the node goes
- * down just before the call to demonitor.
- */
- if (dep) {
- erts_de_links_lock(dep);
- dmon = erts_remove_monitor(&dep->monitors, ref);
- erts_de_links_unlock(dep);
- if (dmon)
- erts_destroy_monitor(dmon);
- }
- mon = erts_remove_monitor(&ERTS_P_MONITORS(c_p), ref);
- erts_proc_unlock(c_p, ERTS_PROC_LOCK_LINK);
-
- res = am_true;
- break;
-
- case ERTS_DSIG_PREP_CONNECTED:
-
- erts_de_links_lock(dep);
- mon = erts_remove_monitor(&ERTS_P_MONITORS(c_p), ref);
- dmon = erts_remove_monitor(&dep->monitors, ref);
- erts_de_links_unlock(dep);
- erts_de_runlock(dep);
- erts_proc_unlock(c_p, ERTS_PROC_LOCK_LINK);
-
- if (!dmon) {
- /*
- * This is possible when smp support is enabled.
- * 'DOWN' message just arrived.
- */
- res = am_true;
- }
- else {
- /*
- * Soft (no force) send, use ->data in dist slot
- * monitor list since in case of monitor name
- * the atom is stored there. Yield if necessary.
- */
- code = erts_dsig_send_demonitor(&dsd,
- c_p->common.id,
- (mon->name != NIL
- ? mon->name
- : mon->u.pid),
- ref,
- 0);
- res = (code == ERTS_DSIG_SEND_YIELD ? am_yield : am_true);
- erts_destroy_monitor(dmon);
- }
- break;
- default:
- ASSERT(! "Invalid dsig prepare result");
- return am_internal_error;
- }
+ ErtsMonitor *mon; /* The monitor entry to delete */
+ *multip = am_false;
- /*
- * We aren't allowed to destroy 'mon' until now, since 'to'
- * may refer into 'mon' (external pid).
- */
- ASSERT(mon); /* Since link lock wasn't released between
- lookup and remove */
- erts_destroy_monitor(mon);
-
- ERTS_LC_ASSERT(ERTS_PROC_LOCK_MAIN == erts_proc_lc_my_proc_locks(c_p));
- return res;
-}
+ if (is_not_internal_ref(ref)) {
+ if (is_external_ref(ref)
+ && (erts_this_dist_entry
+ == external_ref_dist_entry(ref))) {
+ return am_false;
+ }
+ return am_badarg; /* Not monitored by this monitor's ref */
+ }
-static ERTS_INLINE void
-demonitor_local_process(Process *c_p, Eterm ref, Eterm to, Eterm *res)
-{
- Process *rp = erts_pid2proc_opt(c_p,
- ERTS_PROC_LOCK_MAIN|ERTS_PROC_LOCK_LINK,
- to,
- ERTS_PROC_LOCK_LINK,
- ERTS_P2P_FLG_ALLOW_OTHER_X);
- ErtsMonitor *mon = erts_remove_monitor(&ERTS_P_MONITORS(c_p), ref);
-
- if (!mon)
- *res = am_false;
- else
- {
- *res = am_true;
- erts_destroy_monitor(mon);
- }
- if (rp) {
- ErtsMonitor *rmon;
- rmon = erts_remove_monitor(&ERTS_P_MONITORS(rp), ref);
- if (rp != c_p)
- erts_proc_unlock(rp, ERTS_PROC_LOCK_LINK);
- if (rmon != NULL)
- erts_destroy_monitor(rmon);
- }
- else {
- ERTS_ASSERT_IS_NOT_EXITING(c_p);
- }
-}
+ mon = erts_monitor_tree_lookup(ERTS_P_MONITORS(c_p), ref);
+ if (!mon)
+ return am_false;
-static ERTS_INLINE BIF_RETTYPE
-demonitor_local_port(Process *origin, Eterm ref, Eterm target)
-{
- BIF_RETTYPE res = am_false;
- Port *port = erts_port_lookup_raw(target);
+ if (!erts_monitor_is_origin(mon))
+ return am_badarg;
- if (!port) {
- BIF_ERROR(origin, BADARG);
- }
- erts_proc_unlock(origin, ERTS_PROC_LOCK_LINK);
+ erts_monitor_tree_delete(&ERTS_P_MONITORS(c_p), mon);
- if (port) {
- Eterm trap_ref;
- switch (erts_port_demonitor(origin, ERTS_PORT_DEMONITOR_NORMAL,
- port, ref, &trap_ref)) {
- case ERTS_PORT_OP_DROPPED:
- case ERTS_PORT_OP_BADARG:
- break;
- case ERTS_PORT_OP_SCHEDULED:
- BIF_TRAP3(await_port_send_result_trap, origin, trap_ref,
- am_busy_port, am_true);
- /* the busy_port atom will never be returned, because it cannot be
- * returned from erts_port_(de)monitor, but just in case if in future
- * internal API changes - you may see this atom */
- default:
- break;
- }
- }
- else {
- ERTS_ASSERT_IS_NOT_EXITING(origin);
- }
- BIF_RET(res);
-}
+ switch (mon->type) {
-/* Can return atom true, false, yield, internal_error, badarg or
- * THE_NON_VALUE if error occurred or trap has been set up
- */
-static
-BIF_RETTYPE demonitor(Process *c_p, Eterm ref, Eterm *multip)
-{
- ErtsMonitor *mon = NULL; /* The monitor entry to delete */
- Eterm to = NIL; /* Monitor link traget */
- DistEntry *dep = NULL; /* Target's distribution entry */
- BIF_RETTYPE res = am_false;
- int unlock_link = 1;
+ case ERTS_MON_TYPE_TIME_OFFSET:
+ *multip = am_true;
+ erts_demonitor_time_offset(mon);
+ return am_true;
+
+ case ERTS_MON_TYPE_PORT: {
+ Port *prt;
+ ASSERT(is_internal_port(mon->other.item));
+ prt = erts_port_lookup(mon->other.item, ERTS_PORT_SFLGS_DEAD);
+ if (!prt || erts_port_demonitor(c_p, prt, mon) == ERTS_PORT_OP_DROPPED)
+ erts_monitor_release(mon);
+ return am_true;
+ }
- erts_proc_lock(c_p, ERTS_PROC_LOCK_LINK);
+ case ERTS_MON_TYPE_PROC:
+ erts_proc_sig_send_demonitor(mon);
+ return am_true;
- if (is_not_internal_ref(ref)) {
- res = am_badarg;
- goto done; /* Cannot be this monitor's ref */
- }
+ case ERTS_MON_TYPE_DIST_PROC: {
+ ErtsMonitorData *mdp = erts_monitor_to_data(mon);
+ Eterm to = mon->other.item;
+ DistEntry *dep;
+ int code = ERTS_DSIG_SEND_OK;
+ int deleted;
+ ErtsDSigData dsd;
- mon = erts_lookup_monitor(ERTS_P_MONITORS(c_p), ref);
- if (!mon) {
- goto done;
- }
+ ASSERT(is_external_pid(to) || is_node_name_atom(to));
- switch (mon->type) {
- case MON_TIME_OFFSET:
- *multip = am_true;
- erts_demonitor_time_offset(ref);
- res = am_true;
- break;
- case MON_ORIGIN:
- to = mon->u.pid;
- *multip = am_false;
- if (is_atom(to)) {
+ if (is_external_pid(to))
+ dep = external_pid_dist_entry(to);
+ else {
/* Monitoring a name at node to */
- ASSERT(is_node_name_atom(to));
dep = erts_sysname_to_connected_dist_entry(to);
ASSERT(dep != erts_this_dist_entry);
- } else if (is_port(to)) {
- if (port_dist_entry(to) != erts_this_dist_entry) {
- goto badarg;
+ if (!dep) {
+ erts_monitor_release(mon);
+ return am_false;
}
- res = demonitor_local_port(c_p, ref, to);
- unlock_link = 0;
- goto done;
- } else {
- ASSERT(is_pid(to));
- dep = pid_dist_entry(to);
}
- if (dep != erts_this_dist_entry) {
- res = remote_demonitor(c_p, dep, ref, to);
- /* remote_demonitor() unlocks link lock on c_p */
- unlock_link = 0;
+
+ code = erts_dsig_prepare(&dsd, dep, c_p, ERTS_PROC_LOCK_MAIN,
+ ERTS_DSP_RLOCK, 0, 0);
+
+ deleted = erts_monitor_dist_delete(&mdp->target);
+
+ switch (code) {
+ case ERTS_DSIG_PREP_NOT_ALIVE:
+ case ERTS_DSIG_PREP_NOT_CONNECTED:
+ /*
+ * In the smp case this is possible if the node goes
+ * down just before the call to demonitor.
+ */
+ break;
+
+ case ERTS_DSIG_PREP_PENDING:
+ case ERTS_DSIG_PREP_CONNECTED: {
+ Eterm watched;
+
+ erts_de_runlock(dep);
+
+ if (mon->flags & ERTS_ML_FLG_NAME)
+ watched = ((ErtsMonitorDataExtended *) mdp)->u.name;
+ else
+ watched = to;
+
+ /*
+ * Soft (no force) send, use ->data in dist slot
+ * monitor list since in case of monitor name
+ * the atom is stored there. Yield if necessary.
+ */
+ code = erts_dsig_send_demonitor(&dsd, c_p->common.id,
+ watched, mdp->ref, 0);
+ break;
}
- else { /* Local monitor */
- demonitor_local_process(c_p, ref, to, &res);
+
+ default:
+ ERTS_INTERNAL_ERROR("invalid result from erts_dsig_prepare()");
+ break;
}
- break;
- default /* case */ :
-badarg:
- res = am_badarg; /* will be converted to error by caller */
- *multip = am_false;
- break;
- }
-done:
- if (unlock_link)
- erts_proc_unlock(c_p, ERTS_PROC_LOCK_LINK);
+ if (deleted)
+ erts_monitor_release(&mdp->target);
+
+ erts_monitor_release(mon);
+ return code == ERTS_DSIG_SEND_YIELD ? am_yield : am_true;
+ }
- ERTS_LC_ASSERT(ERTS_PROC_LOCK_MAIN == erts_proc_lc_my_proc_locks(c_p));
- BIF_RET(res);
+ default:
+ ERTS_INTERNAL_ERROR("Unexpected monitor type");
+ return am_false;
+ }
}
BIF_RETTYPE demonitor_1(BIF_ALIST_1)
@@ -506,25 +383,23 @@ BIF_RETTYPE demonitor_1(BIF_ALIST_1)
Eterm multi;
switch (demonitor(BIF_P, BIF_ARG_1, &multi)) {
case am_false:
- case am_true: BIF_RET(am_true);
- case THE_NON_VALUE: BIF_RET(THE_NON_VALUE);
- case am_yield: ERTS_BIF_YIELD_RETURN(BIF_P, am_true);
- case am_badarg: BIF_ERROR(BIF_P, BADARG);
-
- case am_internal_error:
+ case am_true:
+ BIF_RET(am_true);
+ case am_yield:
+ ERTS_BIF_YIELD_RETURN(BIF_P, am_true);
+ case am_badarg:
default:
- ASSERT(! "demonitor(): internal error");
- BIF_ERROR(BIF_P, EXC_INTERNAL_ERROR);
+ BIF_ERROR(BIF_P, BADARG);
}
}
BIF_RETTYPE demonitor_2(BIF_ALIST_2)
{
- BIF_RETTYPE res = am_true;
- Eterm multi = am_false;
- int info = 0;
- int flush = 0;
- Eterm list = BIF_ARG_2;
+ BIF_RETTYPE res;
+ Eterm multi = am_false;
+ int info = 0;
+ int flush = 0;
+ Eterm list = BIF_ARG_2;
while (is_list(list)) {
Eterm* consp = list_val(list);
@@ -544,10 +419,9 @@ BIF_RETTYPE demonitor_2(BIF_ALIST_2)
if (is_not_nil(list))
goto badarg;
+ res = am_true;
switch (demonitor(BIF_P, BIF_ARG_1, &multi)) {
- case THE_NON_VALUE:
- /* If other error occurred or trap has been set up - pass through */
- BIF_RET(THE_NON_VALUE);
+
case am_false:
if (info)
res = am_false;
@@ -556,20 +430,29 @@ flush_messages:
BIF_TRAP3(flush_monitor_messages_trap, BIF_P,
BIF_ARG_1, multi, res);
}
+ /* Fall through... */
+
case am_true:
if (multi == am_true && flush)
goto flush_messages;
BIF_RET(res);
+
case am_yield:
- ERTS_BIF_YIELD_RETURN(BIF_P, am_true);
+ /* return true after yield... */
+ if (flush) {
+ ERTS_VBUMP_ALL_REDS(BIF_P);
+ goto flush_messages;
+ }
+ ERTS_BIF_YIELD_RETURN(BIF_P, am_true);
+
case am_badarg:
-badarg:
- BIF_ERROR(BIF_P, BADARG);
- case am_internal_error:
default:
- ASSERT(! "demonitor(): internal error");
- BIF_ERROR(BIF_P, EXC_INTERNAL_ERROR);
+ break;
+
}
+
+badarg:
+ BIF_ERROR(BIF_P, BADARG);
}
/* Type must be atomic object! */
@@ -609,297 +492,212 @@ erts_queue_monitor_message(Process *p,
erts_queue_message(p, *p_locksp, msgp, tup, am_system);
}
-static Eterm
-local_pid_monitor(Process *p, Eterm target, Eterm mon_ref, int boolean)
+BIF_RETTYPE monitor_2(BIF_ALIST_2)
{
- Eterm ret = mon_ref;
- Process *rp;
- ErtsProcLocks p_locks = ERTS_PROC_LOCK_MAIN|ERTS_PROC_LOCK_LINK;
-
- if (target == p->common.id) {
- return ret;
- }
+ Eterm target = BIF_ARG_2;
+ Eterm tmp_heap[3];
+ Eterm ref, id, name;
+ ErtsMonitorData *mdp;
+
+ if (BIF_ARG_1 == am_process) {
+ DistEntry *dep;
+ int byname;
+
+ if (is_internal_pid(target)) {
+ name = NIL;
+ id = target;
+
+ local_process:
+
+ ref = erts_make_ref(BIF_P);
+ if (id != BIF_P->common.id) {
+ mdp = erts_monitor_create(ERTS_MON_TYPE_PROC,
+ ref, BIF_P->common.id,
+ id, name);
+ erts_monitor_tree_insert(&ERTS_P_MONITORS(BIF_P),
+ &mdp->origin);
+
+ if (!erts_proc_sig_send_monitor(&mdp->target, id))
+ erts_proc_sig_send_monitor_down(&mdp->target,
+ am_noproc);
+ }
+ BIF_RET(ref);
+ }
- erts_proc_lock(p, ERTS_PROC_LOCK_LINK);
- rp = erts_pid2proc_opt(p, p_locks,
- target, ERTS_PROC_LOCK_LINK,
- ERTS_P2P_FLG_ALLOW_OTHER_X);
- if (!rp) {
- erts_proc_unlock(p, ERTS_PROC_LOCK_LINK);
- p_locks &= ~ERTS_PROC_LOCK_LINK;
- if (boolean)
- ret = am_false;
- else
- erts_queue_monitor_message(p, &p_locks,
- mon_ref, am_process, target, am_noproc);
- }
- else {
- ASSERT(rp != p);
+ if (is_atom(target)) {
+ local_named_process:
+ name = target;
+ id = erts_whereis_name_to_id(BIF_P, target);
+ if (is_internal_pid(id))
+ goto local_process;
+ target = TUPLE2(&tmp_heap[0], name,
+ erts_this_dist_entry->sysname);
+ goto noproc;
+ }
- if (boolean)
- ret = am_true;
+ if (is_external_pid(target)) {
+ ErtsDSigData dsd;
+ int code;
+
+ dep = external_pid_dist_entry(target);
+ if (dep == erts_this_dist_entry)
+ goto noproc;
+
+ id = target;
+ name = NIL;
+ byname = 0;
+
+ remote_process:
+
+ ref = erts_make_ref(BIF_P);
+ mdp = erts_monitor_create(ERTS_MON_TYPE_DIST_PROC, ref,
+ BIF_P->common.id, id, name);
+ erts_monitor_tree_insert(&ERTS_P_MONITORS(BIF_P), &mdp->origin);
+
+ code = erts_dsig_prepare(&dsd, dep,
+ BIF_P, ERTS_PROC_LOCK_MAIN,
+ ERTS_DSP_RLOCK, 0, 1);
+ switch (code) {
+ case ERTS_DSIG_PREP_NOT_ALIVE:
+ case ERTS_DSIG_PREP_NOT_CONNECTED:
+ erts_monitor_set_dead_dist(&mdp->target, dep->sysname);
+ erts_proc_sig_send_monitor_down(&mdp->target, am_noconnection);
+ code = ERTS_DSIG_SEND_OK;
+ break;
- erts_add_monitor(&ERTS_P_MONITORS(p), MON_ORIGIN, mon_ref, target, NIL);
- erts_add_monitor(&ERTS_P_MONITORS(rp), MON_TARGET, mon_ref, p->common.id, NIL);
+ case ERTS_DSIG_PREP_PENDING:
+ case ERTS_DSIG_PREP_CONNECTED: {
+#ifdef DEBUG
+ int inserted =
+#endif
- erts_proc_unlock(rp, ERTS_PROC_LOCK_LINK);
- }
+ erts_monitor_dist_insert(&mdp->target, dep->mld);
+ ASSERT(inserted);
+ erts_de_runlock(dep);
- erts_proc_unlock(p, p_locks & ~ERTS_PROC_LOCK_MAIN);
+ code = erts_dsig_send_monitor(&dsd, BIF_P->common.id, target, ref);
+ break;
+ }
- return ret;
-}
+ default:
+ ERTS_ASSERT(! "Invalid dsig prepare result");
+ code = ERTS_DSIG_SEND_OK;
+ break;
+ }
-static BIF_RETTYPE
-local_port_monitor(Process *origin, Eterm target)
-{
- BIF_RETTYPE ref = erts_make_ref(origin);
- Port *port = erts_sig_lookup_port(origin, target);
- ErtsProcLocks p_locks = ERTS_PROC_LOCK_MAIN;
+ if (byname)
+ erts_deref_dist_entry(dep);
- if (!port) {
-res_no_proc:
- /* Send the DOWN message immediately. Ref is made on the fly because
- * caller has never seen it yet. */
- erts_queue_monitor_message(origin, &p_locks, ref,
- am_port, target, am_noproc);
- }
- else {
- switch (erts_port_monitor(origin, port, target, &ref)) {
- case ERTS_PORT_OP_DROPPED:
- case ERTS_PORT_OP_BADARG:
- goto res_no_proc;
- case ERTS_PORT_OP_SCHEDULED:
- BIF_TRAP3(await_port_send_result_trap, origin, ref,
- am_busy_port, ref);
- /* the busy_port atom will never be returned, because it cannot be
- * returned from erts_port_monitor, but just in case if in future
- * internal API changes - you may see this atom */
- default:
- break;
+ if (code == ERTS_DSIG_SEND_YIELD)
+ ERTS_BIF_YIELD_RETURN(BIF_P, ref);
+ BIF_RET(ref);
}
- }
- erts_proc_unlock(origin, p_locks & ~ERTS_PROC_LOCK_MAIN);
- BIF_RET(ref);
-}
-/* Type = process | port :: atom(), 1st argument passed to erlang:monitor/2
- */
-static BIF_RETTYPE
-local_name_monitor(Process *self, Eterm type, Eterm target_name)
-{
- BIF_RETTYPE ret = erts_make_ref(self);
+ if (is_tuple(target)) {
+ Eterm *tpl = tuple_val(target);
+ if (arityval(tpl[0]) != 2)
+ goto badarg;
+ if (is_not_atom(tpl[1]) || is_not_atom(tpl[2]))
+ goto badarg;
+ if (!erts_is_alive && tpl[2] != am_Noname)
+ goto badarg;
+ target = tpl[1];
+ dep = erts_find_or_insert_dist_entry(tpl[2]);
+ if (dep == erts_this_dist_entry) {
+ erts_deref_dist_entry(dep);
+ goto local_named_process;
+ }
- ErtsProcLocks p_locks = ERTS_PROC_LOCK_MAIN | ERTS_PROC_LOCK_LINK;
- Process *proc = NULL;
- Port *port = NULL;
+ id = dep->sysname;
+ name = target;
+ byname = 1;
+ goto remote_process;
+ }
- erts_proc_lock(self, ERTS_PROC_LOCK_LINK);
+ /* badarg... */
+ }
+ else if (BIF_ARG_1 == am_port) {
+
+ if (is_internal_port(target)) {
+ Port *prt;
+ name = NIL;
+ id = target;
+ local_port:
+ ref = erts_make_ref(BIF_P);
+ mdp = erts_monitor_create(ERTS_MON_TYPE_PORT, ref,
+ BIF_P->common.id, id, name);
+ erts_monitor_tree_insert(&ERTS_P_MONITORS(BIF_P), &mdp->origin);
+ prt = erts_port_lookup(id, ERTS_PORT_SFLGS_INVALID_LOOKUP);
+ if (!prt || erts_port_monitor(BIF_P, prt, &mdp->target) == ERTS_PORT_OP_DROPPED)
+ erts_proc_sig_send_monitor_down(&mdp->target, am_noproc);
+ BIF_RET(ref);
+ }
- erts_whereis_name(self, p_locks, target_name,
- &proc, ERTS_PROC_LOCK_LINK,
- ERTS_P2P_FLG_ALLOW_OTHER_X,
- &port, 0);
+ if (is_atom(target)) {
+ local_named_port:
+ name = target;
+ id = erts_whereis_name_to_id(BIF_P, target);
+ if (is_internal_port(id))
+ goto local_port;
+ target = TUPLE2(&tmp_heap[0], name,
+ erts_this_dist_entry->sysname);
+ goto noproc;
+ }
- /* If the name is not registered,
- * or if we asked for proc and got a port,
- * or if we asked for port and got a proc,
- * we just send the 'DOWN' message.
- */
- if ((!proc && !port) ||
- (type == am_process && port) ||
- (type == am_port && proc)) {
- DeclareTmpHeap(lhp,3,self);
- Eterm item;
- UseTmpHeap(3,self);
-
- erts_proc_unlock(self, ERTS_PROC_LOCK_LINK);
- p_locks &= ~ERTS_PROC_LOCK_LINK;
-
- item = TUPLE2(lhp, target_name, erts_this_dist_entry->sysname);
- erts_queue_monitor_message(self, &p_locks,
- ret,
- type, /* = process|port :: atom() */
- item, am_noproc);
- UnUseTmpHeap(3,self);
- }
- else if (port) {
- erts_proc_unlock(self, p_locks & ~ERTS_PROC_LOCK_MAIN);
- p_locks &= ~ERTS_PROC_LOCK_MAIN;
-
- switch (erts_port_monitor(self, port, target_name, &ret)) {
- case ERTS_PORT_OP_DONE:
- return ret;
- case ERTS_PORT_OP_SCHEDULED: { /* Scheduled a signal */
- ASSERT(is_internal_ordinary_ref(ret));
- BIF_TRAP3(await_port_send_result_trap, self,
- ret, am_true, ret);
- /* bif_trap returns */
- } break;
- default:
+ if (is_external_port(target)) {
+ if (erts_this_dist_entry == external_port_dist_entry(target))
+ goto noproc;
goto badarg;
}
- }
- else if (proc != self) {
- erts_add_monitor(&ERTS_P_MONITORS(self), MON_ORIGIN, ret,
- proc->common.id, target_name);
- erts_add_monitor(&ERTS_P_MONITORS(proc), MON_TARGET, ret,
- self->common.id, target_name);
- erts_proc_unlock(proc, ERTS_PROC_LOCK_LINK);
- }
-
- if (p_locks) {
- erts_proc_unlock(self, p_locks & ~ERTS_PROC_LOCK_MAIN);
- }
- BIF_RET(ret);
-badarg:
- if (p_locks) {
- erts_proc_unlock(self, p_locks & ~ERTS_PROC_LOCK_MAIN);
- }
- BIF_ERROR(self, BADARG);
-}
-static BIF_RETTYPE
-remote_monitor(Process *p, Eterm bifarg1, Eterm bifarg2,
- DistEntry *dep, Eterm target, int byname)
-{
- ErtsDSigData dsd;
- BIF_RETTYPE ret;
- int code;
-
- erts_proc_lock(p, ERTS_PROC_LOCK_LINK);
- code = erts_dsig_prepare(&dsd, dep, p, ERTS_DSP_RLOCK, 0);
- switch (code) {
- case ERTS_DSIG_PREP_NOT_ALIVE:
- /* Let the dmonitor_p trap handle it */
- case ERTS_DSIG_PREP_NOT_CONNECTED:
- erts_proc_unlock(p, ERTS_PROC_LOCK_LINK);
- ERTS_BIF_PREP_TRAP2(ret, dmonitor_p_trap, p, bifarg1, bifarg2);
- break;
- case ERTS_DSIG_PREP_CONNECTED:
- if (!(dep->flags & DFLAG_DIST_MONITOR)
- || (byname && !(dep->flags & DFLAG_DIST_MONITOR_NAME))) {
- erts_de_runlock(dep);
- erts_proc_unlock(p, ERTS_PROC_LOCK_LINK);
- ERTS_BIF_PREP_ERROR(ret, p, BADARG);
- }
- else {
- Eterm p_trgt, p_name, d_name, mon_ref;
+ if (is_tuple(target)) {
+ Eterm *tpl = tuple_val(target);
+ if (arityval(tpl[0]) != 2)
+ goto badarg;
+ if (is_not_atom(tpl[1]) || is_not_atom(tpl[2]))
+ goto badarg;
+ if (tpl[2] == erts_this_dist_entry->sysname) {
+ target = tpl[1];
+ goto local_named_port;
+ }
+ }
- mon_ref = erts_make_ref(p);
+ /* badarg... */
+ }
+ else if (BIF_ARG_1 == am_time_offset) {
- if (byname) {
- p_trgt = dep->sysname;
- p_name = target;
- d_name = target;
- }
- else {
- p_trgt = target;
- p_name = NIL;
- d_name = NIL;
- }
+ if (target != am_clock_service)
+ goto badarg;
+ ref = erts_make_ref(BIF_P);
+ mdp = erts_monitor_create(ERTS_MON_TYPE_TIME_OFFSET,
+ ref, BIF_P->common.id,
+ am_clock_service, NIL);
+ erts_monitor_tree_insert(&ERTS_P_MONITORS(BIF_P), &mdp->origin);
- erts_de_links_lock(dep);
+ erts_monitor_time_offset(&mdp->target);
- erts_add_monitor(&ERTS_P_MONITORS(p), MON_ORIGIN, mon_ref, p_trgt,
- p_name);
- erts_add_monitor(&(dep->monitors), MON_TARGET, mon_ref, p->common.id,
- d_name);
+ BIF_RET(ref);
+ }
- erts_de_links_unlock(dep);
- erts_de_runlock(dep);
- erts_proc_unlock(p, ERTS_PROC_LOCK_LINK);
+badarg:
- code = erts_dsig_send_monitor(&dsd, p->common.id, target, mon_ref);
- if (code == ERTS_DSIG_SEND_YIELD)
- ERTS_BIF_PREP_YIELD_RETURN(ret, p, mon_ref);
- else
- ERTS_BIF_PREP_RET(ret, mon_ref);
- }
- break;
- default:
- ASSERT(! "Invalid dsig prepare result");
- ERTS_BIF_PREP_ERROR(ret, p, EXC_INTERNAL_ERROR);
- break;
- }
+ BIF_ERROR(BIF_P, BADARG);
- BIF_RET(ret);
-}
-
-BIF_RETTYPE monitor_2(BIF_ALIST_2)
-{
- Eterm target = BIF_ARG_2;
- BIF_RETTYPE ret;
- DistEntry *dep = NULL;
+noproc: {
+ ErtsProcLocks locks = ERTS_PROC_LOCK_MAIN;
- /* Only process monitors are implemented */
- switch (BIF_ARG_1) {
- case am_time_offset: {
- Eterm ref;
- if (BIF_ARG_2 != am_clock_service) {
- goto badarg;
- }
- ref = erts_make_ref(BIF_P);
- erts_proc_lock(BIF_P, ERTS_PROC_LOCK_LINK);
- erts_add_monitor(&ERTS_P_MONITORS(BIF_P), MON_TIME_OFFSET,
- ref, am_clock_service, NIL);
- erts_proc_unlock(BIF_P, ERTS_PROC_LOCK_LINK);
- erts_monitor_time_offset(BIF_P->common.id, ref);
- BIF_RET(ref);
- }
- case am_process:
- case am_port:
- break;
- default:
- goto badarg;
- }
+ ref = erts_make_ref(BIF_P);
+ erts_queue_monitor_message(BIF_P,
+ &locks,
+ ref,
+ BIF_ARG_1,
+ target,
+ am_noproc);
+ if (locks != ERTS_PROC_LOCK_MAIN)
+ erts_proc_unlock(BIF_P, locks & ~ERTS_PROC_LOCK_MAIN);
- if (is_internal_pid(target) && BIF_ARG_1 == am_process) {
-local_pid:
- ret = local_pid_monitor(BIF_P, target, erts_make_ref(BIF_P), 0);
- } else if (is_external_pid(target) && BIF_ARG_1 == am_process) {
- dep = external_pid_dist_entry(target);
- if (dep == erts_this_dist_entry)
- goto local_pid;
- ret = remote_monitor(BIF_P, BIF_ARG_1, BIF_ARG_2, dep, target, 0);
- } else if (is_internal_port(target) && BIF_ARG_1 == am_port) {
-local_port:
- ret = local_port_monitor(BIF_P, target);
- } else if (is_external_port(target) && BIF_ARG_1 == am_port) {
- dep = external_port_dist_entry(target);
- if (dep == erts_this_dist_entry) {
- goto local_port;
- }
- goto badarg; /* No want remote port */
- } else if (is_atom(target)) {
- ret = local_name_monitor(BIF_P, BIF_ARG_1, target);
- } else if (is_tuple(target)) {
- Eterm *tp = tuple_val(target);
- Eterm remote_node;
- Eterm name;
- if (arityval(*tp) != 2) {
- goto badarg;
- }
- remote_node = tp[2];
- name = tp[1];
- if (!is_atom(remote_node) || !is_atom(name)) {
- goto badarg;
- }
- if (!erts_is_alive && remote_node != am_Noname) {
- goto badarg; /* Remote monitor from (this) undistributed node */
- }
- dep = erts_sysname_to_connected_dist_entry(remote_node);
- if (dep == erts_this_dist_entry) {
- ret = local_name_monitor(BIF_P, BIF_ARG_1, name);
- } else {
- ret = remote_monitor(BIF_P, BIF_ARG_1, BIF_ARG_2, dep, name, 1);
- }
- } else {
-badarg:
- ERTS_BIF_PREP_ERROR(ret, BIF_P, BADARG);
+ BIF_RET(ref);
}
-
- return ret;
}
/**********************************************************************/
@@ -1072,173 +870,102 @@ BIF_RETTYPE spawn_opt_1(BIF_ALIST_1)
/* remove a link from a process */
BIF_RETTYPE unlink_1(BIF_ALIST_1)
{
- Process *rp;
- DistEntry *dep;
- ErtsLink *l = NULL, *rl = NULL;
- ErtsProcLocks cp_locks = ERTS_PROC_LOCK_MAIN;
-
- /*
- * SMP specific note concerning incoming exit signals:
- * We have to have at least the status lock during removal of
- * the link half on current process, and check for and handle
- * a present pending exit while the status lock is held. This
- * in order to ensure that we wont be exited by a link after
- * it has been removed.
- *
- * (We also have to have the link lock, of course, in order to
- * be allowed to remove the link...)
- */
-
if (IS_TRACED_FL(BIF_P, F_TRACE_PROCS)) {
- trace_proc(BIF_P, cp_locks, BIF_P, am_unlink, BIF_ARG_1);
+ trace_proc(BIF_P, ERTS_PROC_LOCK_MAIN,
+ BIF_P, am_unlink, BIF_ARG_1);
}
- if (is_internal_port(BIF_ARG_1)) {
- erts_proc_lock(BIF_P, ERTS_PROC_LOCK_LINK|ERTS_PROC_LOCK_STATUS);
- if (ERTS_PROC_PENDING_EXIT(BIF_P))
- goto handle_pending_exit;
-
- l = erts_remove_link(&ERTS_P_LINKS(BIF_P), BIF_ARG_1);
+ if (is_internal_pid(BIF_ARG_1)) {
+ ErtsLink *lnk = erts_link_tree_lookup(ERTS_P_LINKS(BIF_P), BIF_ARG_1);
+ if (lnk) {
+ erts_link_tree_delete(&ERTS_P_LINKS(BIF_P), lnk);
+ erts_proc_sig_send_unlink(BIF_P, lnk);
+ }
+ BIF_RET(am_true);
+ }
- erts_proc_unlock(BIF_P, ERTS_PROC_LOCK_LINK|ERTS_PROC_LOCK_STATUS);
+ if (is_internal_port(BIF_ARG_1)) {
+ ErtsLink *lnk = erts_link_tree_lookup(ERTS_P_LINKS(BIF_P), BIF_ARG_1);
- if (l) {
+ if (lnk) {
+ Eterm ref;
+ Eterm *refp = erts_port_synchronous_ops ? &ref : NULL;
+ ErtsPortOpResult res = ERTS_PORT_OP_DROPPED;
Port *prt;
- erts_destroy_link(l);
+ erts_link_tree_delete(&ERTS_P_LINKS(BIF_P), lnk);
/* Send unlink signal */
prt = erts_port_lookup(BIF_ARG_1, ERTS_PORT_SFLGS_DEAD);
if (prt) {
- ErtsPortOpResult res;
- Eterm ref;
- Eterm *refp = erts_port_synchronous_ops ? &ref : NULL;
#ifdef DEBUG
ref = NIL;
#endif
- res = erts_port_unlink(BIF_P, prt, BIF_P->common.id, refp);
+ res = erts_port_unlink(BIF_P, prt, lnk, refp);
- if (refp && res == ERTS_PORT_OP_SCHEDULED) {
- ASSERT(is_internal_ordinary_ref(ref));
- BIF_TRAP3(await_port_send_result_trap, BIF_P, ref, am_true, am_true);
- }
}
+
+ if (res == ERTS_PORT_OP_DROPPED)
+ erts_link_release(lnk);
+ else if (refp && res == ERTS_PORT_OP_SCHEDULED) {
+ ASSERT(is_internal_ordinary_ref(ref));
+ BIF_TRAP3(await_port_send_result_trap, BIF_P, ref, am_true, am_true);
+ }
}
BIF_RET(am_true);
}
- else if (is_external_port(BIF_ARG_1)
- && external_port_dist_entry(BIF_ARG_1) == erts_this_dist_entry) {
- BIF_RET(am_true);
- }
-
- if (is_not_pid(BIF_ARG_1))
- BIF_ERROR(BIF_P, BADARG);
if (is_external_pid(BIF_ARG_1)) {
- ErtsDistLinkData dld;
+ ErtsLink *lnk, *dlnk;
+ ErtsLinkData *ldp;
+ DistEntry *dep;
int code;
ErtsDSigData dsd;
- /* Blind removal, we might have trapped or anything, this leaves
- us in a state where monitors might be inconsistent, but the dist
- code should take care of it. */
- erts_proc_lock(BIF_P, ERTS_PROC_LOCK_LINK|ERTS_PROC_LOCK_STATUS);
- if (ERTS_PROC_PENDING_EXIT(BIF_P))
- goto handle_pending_exit;
- l = erts_remove_link(&ERTS_P_LINKS(BIF_P), BIF_ARG_1);
-
- erts_proc_unlock(BIF_P,
- ERTS_PROC_LOCK_LINK|ERTS_PROC_LOCK_STATUS);
-
- if (l)
- erts_destroy_link(l);
dep = external_pid_dist_entry(BIF_ARG_1);
- if (dep == erts_this_dist_entry) {
+ if (dep == erts_this_dist_entry)
BIF_RET(am_true);
- }
- code = erts_dsig_prepare(&dsd, dep, BIF_P, ERTS_DSP_NO_LOCK, 0);
+ lnk = erts_link_tree_lookup(ERTS_P_LINKS(BIF_P), BIF_ARG_1);
+ if (!lnk)
+ BIF_RET(am_true);
+
+ erts_link_tree_delete(&ERTS_P_LINKS(BIF_P), lnk);
+ dlnk = erts_link_to_other(lnk, &ldp);
+
+ if (erts_link_dist_delete(dlnk))
+ erts_link_release_both(ldp);
+ else
+ erts_link_release(lnk);
+
+ code = erts_dsig_prepare(&dsd, dep, BIF_P, ERTS_PROC_LOCK_MAIN,
+ ERTS_DSP_NO_LOCK, 0, 0);
switch (code) {
case ERTS_DSIG_PREP_NOT_ALIVE:
case ERTS_DSIG_PREP_NOT_CONNECTED:
-#if 1
BIF_RET(am_true);
-#else
- /*
- * This is how we used to do it, but the link is obviously not
- * active, so I see no point in setting up a connection.
- * /Rickard
- */
- BIF_TRAP1(dunlink_trap, BIF_P, BIF_ARG_1);
-#endif
-
+ case ERTS_DSIG_PREP_PENDING:
case ERTS_DSIG_PREP_CONNECTED:
- erts_remove_dist_link(&dld, BIF_P->common.id, BIF_ARG_1, dep);
code = erts_dsig_send_unlink(&dsd, BIF_P->common.id, BIF_ARG_1);
- erts_destroy_dist_link(&dld);
if (code == ERTS_DSIG_SEND_YIELD)
ERTS_BIF_YIELD_RETURN(BIF_P, am_true);
- BIF_RET(am_true);
-
+ break;
default:
ASSERT(! "Invalid dsig prepare result");
BIF_ERROR(BIF_P, EXC_INTERNAL_ERROR);
}
- }
-
- /* Internal pid... */
-
- erts_proc_lock(BIF_P, ERTS_PROC_LOCK_LINK|ERTS_PROC_LOCK_STATUS);
-
- cp_locks |= ERTS_PROC_LOCK_LINK|ERTS_PROC_LOCK_STATUS;
-
- /* get process struct */
- rp = erts_pid2proc_opt(BIF_P, cp_locks,
- BIF_ARG_1, ERTS_PROC_LOCK_LINK,
- ERTS_P2P_FLG_ALLOW_OTHER_X);
- if (ERTS_PROC_PENDING_EXIT(BIF_P)) {
- if (rp && rp != BIF_P)
- erts_proc_unlock(rp, ERTS_PROC_LOCK_LINK);
- goto handle_pending_exit;
+ BIF_RET(am_true);
}
- /* unlink and ignore errors */
- l = erts_remove_link(&ERTS_P_LINKS(BIF_P), BIF_ARG_1);
- if (l != NULL)
- erts_destroy_link(l);
-
- if (!rp) {
- ERTS_ASSERT_IS_NOT_EXITING(BIF_P);
- }
- else {
- rl = erts_remove_link(&ERTS_P_LINKS(rp), BIF_P->common.id);
- if (rl != NULL)
- erts_destroy_link(rl);
-
- if (IS_TRACED_FL(rp, F_TRACE_PROCS) && rl != NULL) {
- erts_proc_unlock(BIF_P, ERTS_PROC_LOCK_STATUS);
- cp_locks &= ~ERTS_PROC_LOCK_STATUS;
- trace_proc(BIF_P, (ERTS_PROC_LOCK_MAIN | ERTS_PROC_LOCK_LINK),
- rp, am_getting_unlinked, BIF_P->common.id);
- }
-
- if (rp != BIF_P)
- erts_proc_unlock(rp, ERTS_PROC_LOCK_LINK);
+ if (is_external_port(BIF_ARG_1)) {
+ if (external_port_dist_entry(BIF_ARG_1) == erts_this_dist_entry)
+ BIF_RET(am_true);
+ /* Links to Remote ports not supported... */
}
-
- erts_proc_unlock(BIF_P, cp_locks & ~ERTS_PROC_LOCK_MAIN);
-
- BIF_RET(am_true);
- handle_pending_exit:
- erts_handle_pending_exit(BIF_P, (ERTS_PROC_LOCK_MAIN
- | ERTS_PROC_LOCK_LINK
- | ERTS_PROC_LOCK_STATUS));
- ASSERT(ERTS_PROC_IS_EXITING(BIF_P));
- erts_proc_unlock(BIF_P, ERTS_PROC_LOCK_LINK|ERTS_PROC_LOCK_STATUS);
- ERTS_BIF_EXITED(BIF_P);
+ BIF_ERROR(BIF_P, BADARG);
}
BIF_RETTYPE hibernate_3(BIF_ALIST_3)
@@ -1423,6 +1150,13 @@ BIF_RETTYPE raise_3(BIF_ALIST_3)
/* Create stacktrace and store */
if (erts_backtrace_depth < depth) {
depth = erts_backtrace_depth;
+ if (depth == 0) {
+ /*
+ * For consistency with stacktraces generated
+ * automatically, always include one element.
+ */
+ depth = 1;
+ }
must_copy = 1;
}
if (must_copy) {
@@ -1483,22 +1217,69 @@ BIF_RETTYPE raise_3(BIF_ALIST_3)
return am_badarg;
}
+static BIF_RETTYPE
+erts_internal_await_exit_trap(BIF_ALIST_0)
+{
+ /*
+ * We have sent ourselves an exit signal which will
+ * terminate ourselves. Handle all signals until
+ * terminated in order to ensure that signal order
+ * is preserved. Yield if necessary.
+ */
+ erts_aint32_t state;
+ int reds = ERTS_BIF_REDS_LEFT(BIF_P);
+ (void) erts_proc_sig_handle_incoming(BIF_P, &state, &reds,
+ reds, !0);
+ BUMP_REDS(BIF_P, reds);
+ if (state & ERTS_PSFLG_EXITING)
+ ERTS_BIF_EXITED(BIF_P);
+
+ ERTS_BIF_YIELD0(&await_exit_trap, BIF_P);
+}
+
/**********************************************************************/
-/* send an exit message to another process (if trapping exits) or
- exit the other process */
+/* send an exit signal to another process */
-BIF_RETTYPE exit_2(BIF_ALIST_2)
+static BIF_RETTYPE send_exit_signal_bif(Process *c_p, Eterm id, Eterm reason, int exit2)
{
- Process *rp;
+ BIF_RETTYPE ret_val;
- /*
- * If the first argument is not a pid, or a local port it is an error.
- */
+ /*
+ * 'id' not a process id, nor a local port id is a 'badarg' error.
+ */
- if (is_internal_port(BIF_ARG_1)) {
+ if (is_internal_pid(id)) {
+ /*
+ * Preserve the very old and *very strange* behaviour
+ * of erlang:exit/2...
+ *
+ * - terminate ourselves even though exit reason
+ * is normal (unless we trap exit)
+ * - terminate ourselves before exit/2 return
+ */
+ int exit2_suicide = (exit2
+ && c_p->common.id == id
+ && (reason == am_kill
+ || !(c_p->flags & F_TRAP_EXIT)));
+ erts_proc_sig_send_exit(c_p, c_p->common.id, id,
+ reason, NIL, exit2_suicide);
+ if (!exit2_suicide)
+ ERTS_BIF_PREP_RET(ret_val, am_true);
+ else {
+ erts_proc_lock(c_p, ERTS_PROC_LOCK_MSGQ);
+ erts_proc_sig_fetch(c_p);
+ erts_proc_unlock(c_p, ERTS_PROC_LOCK_MSGQ);
+ ERTS_BIF_PREP_TRAP0(ret_val, &await_exit_trap, c_p);
+ }
+ }
+ else if (is_internal_port(id)) {
Eterm ref, *refp;
Uint32 invalid_flags;
Port *prt;
+ ErtsPortOpResult res = ERTS_PORT_OP_DONE;
+#ifdef DEBUG
+ ref = NIL;
+#endif
if (erts_port_synchronous_ops) {
refp = &ref;
@@ -1509,106 +1290,75 @@ BIF_RETTYPE exit_2(BIF_ALIST_2)
invalid_flags = ERTS_PORT_SFLGS_INVALID_LOOKUP;
}
- prt = erts_port_lookup(BIF_ARG_1, invalid_flags);
-
- if (prt) {
- ErtsPortOpResult res;
-
-#ifdef DEBUG
- ref = NIL;
-#endif
-
- res = erts_port_exit(BIF_P, 0, prt, BIF_P->common.id, BIF_ARG_2, refp);
-
- ERTS_BIF_CHK_EXITED(BIF_P);
-
- if (refp && res == ERTS_PORT_OP_SCHEDULED) {
- ASSERT(is_internal_ordinary_ref(ref));
- BIF_TRAP3(await_port_send_result_trap, BIF_P, ref, am_true, am_true);
- }
-
- }
-
- BIF_RET(am_true);
+ prt = erts_port_lookup(id, invalid_flags);
+ if (prt)
+ res = erts_port_exit(c_p, 0, prt, c_p->common.id, reason, refp);
+
+ if (!refp || res != ERTS_PORT_OP_SCHEDULED)
+ ERTS_BIF_PREP_RET(ret_val, am_true);
+ else {
+ ASSERT(is_internal_ordinary_ref(ref));
+ ERTS_BIF_PREP_TRAP3(ret_val, await_port_send_result_trap,
+ c_p, ref, am_true, am_true);
+ }
}
- else if(is_external_port(BIF_ARG_1)
- && external_port_dist_entry(BIF_ARG_1) == erts_this_dist_entry)
- BIF_RET(am_true);
-
- /*
- * If it is a remote pid, send a signal to the remote node.
- */
-
- if (is_external_pid(BIF_ARG_1)) {
- int code;
- ErtsDSigData dsd;
- DistEntry *dep;
-
- dep = external_pid_dist_entry(BIF_ARG_1);
- if(dep == erts_this_dist_entry)
- BIF_RET(am_true);
-
- code = erts_dsig_prepare(&dsd, dep, BIF_P, ERTS_DSP_NO_LOCK, 0);
- switch (code) {
- case ERTS_DSIG_PREP_NOT_ALIVE:
- case ERTS_DSIG_PREP_NOT_CONNECTED:
- BIF_TRAP2(dexit_trap, BIF_P, BIF_ARG_1, BIF_ARG_2);
- case ERTS_DSIG_PREP_CONNECTED:
- code = erts_dsig_send_exit2(&dsd, BIF_P->common.id, BIF_ARG_1, BIF_ARG_2);
- if (code == ERTS_DSIG_SEND_YIELD)
- ERTS_BIF_YIELD_RETURN(BIF_P, am_true);
- BIF_RET(am_true);
- default:
- ASSERT(! "Invalid dsig prepare result");
- BIF_ERROR(BIF_P, EXC_INTERNAL_ERROR);
- }
+ else if (is_external_pid(id)) {
+ DistEntry *dep = external_pid_dist_entry(id);
+ if (dep == erts_this_dist_entry)
+ ERTS_BIF_PREP_RET(ret_val, am_true); /* Old incarnation of this node... */
+ else {
+ int code;
+ ErtsDSigData dsd;
+
+ code = erts_dsig_prepare(&dsd, dep, c_p, ERTS_PROC_LOCK_MAIN,
+ ERTS_DSP_NO_LOCK, 0, 1);
+ switch (code) {
+ case ERTS_DSIG_PREP_NOT_ALIVE:
+ case ERTS_DSIG_PREP_NOT_CONNECTED:
+ ERTS_BIF_PREP_RET(ret_val, am_true);
+ break;
+ case ERTS_DSIG_PREP_PENDING:
+ case ERTS_DSIG_PREP_CONNECTED:
+ code = erts_dsig_send_exit2(&dsd, c_p->common.id, id, reason);
+ if (code == ERTS_DSIG_SEND_YIELD)
+ ERTS_BIF_PREP_YIELD_RETURN(ret_val, c_p, am_true);
+ else
+ ERTS_BIF_PREP_RET(ret_val, am_true);
+ break;
+ default:
+ ASSERT(! "Invalid dsig prepare result");
+ ERTS_BIF_PREP_ERROR(ret_val, c_p, EXC_INTERNAL_ERROR);
+ break;
+ }
+ }
}
- else if (is_not_internal_pid(BIF_ARG_1)) {
- BIF_ERROR(BIF_P, BADARG);
+ else if (is_external_port(id)) {
+ DistEntry *dep = external_port_dist_entry(id);
+ if(dep == erts_this_dist_entry)
+ ERTS_BIF_PREP_RET(ret_val, am_true); /* Old incarnation of this node... */
+ else
+ ERTS_BIF_PREP_ERROR(ret_val, c_p, BADARG);
}
else {
- /*
- * The pid is internal. Verify that it refers to an existing process.
- */
- ErtsProcLocks rp_locks;
-
- if (BIF_ARG_1 == BIF_P->common.id) {
- rp_locks = ERTS_PROC_LOCKS_ALL;
- rp = BIF_P;
- erts_proc_lock(rp, ERTS_PROC_LOCKS_ALL_MINOR);
- }
- else {
- rp_locks = ERTS_PROC_LOCKS_XSIG_SEND;
- rp = erts_pid2proc(BIF_P, ERTS_PROC_LOCK_MAIN,
- BIF_ARG_1, rp_locks);
- if (!rp) {
- BIF_RET(am_true);
- }
- }
+ /* Not an id of a process or a port... */
- /*
- * Send an exit signal.
- */
- erts_send_exit_signal(BIF_P,
- BIF_P->common.id,
- rp,
- &rp_locks,
- BIF_ARG_2,
- NIL,
- NULL,
- BIF_P == rp ? ERTS_XSIG_FLG_NO_IGN_NORMAL : 0);
- if (rp == BIF_P)
- rp_locks &= ~ERTS_PROC_LOCK_MAIN;
- if (rp_locks)
- erts_proc_unlock(rp, rp_locks);
- /*
- * We may have exited ourselves and may have to take action.
- */
- ERTS_BIF_CHK_EXITED(BIF_P);
- BIF_RET(am_true);
+ ERTS_BIF_PREP_ERROR(ret_val, c_p, BADARG);
}
+
+ return ret_val;
}
+BIF_RETTYPE exit_2(BIF_ALIST_2)
+{
+ return send_exit_signal_bif(BIF_P, BIF_ARG_1, BIF_ARG_2, !0);
+}
+
+BIF_RETTYPE exit_signal_2(BIF_ALIST_2)
+{
+ return send_exit_signal_bif(BIF_P, BIF_ARG_1, BIF_ARG_2, 0);
+}
+
+
/**********************************************************************/
/* this sets some process info- trapping exits or the error handler */
@@ -1697,42 +1447,18 @@ BIF_RETTYPE process_flag_2(BIF_ALIST_2)
BIF_RET(old_value);
}
else if (BIF_ARG_1 == am_trap_exit) {
- erts_aint32_t state;
- Uint trap_exit;
- if (BIF_ARG_2 == am_true) {
- trap_exit = 1;
- } else if (BIF_ARG_2 == am_false) {
- trap_exit = 0;
- } else {
- goto error;
- }
- /*
- * NOTE: It is important that we check for pending exit signals
- * and handle them before returning if trap_exit is set to
- * true. For more info, see implementation of
- * erts_send_exit_signal().
- */
- erts_proc_lock(BIF_P, ERTS_PROC_LOCKS_XSIG_SEND);
- if (trap_exit)
- state = erts_atomic32_read_bor_mb(&BIF_P->state,
- ERTS_PSFLG_TRAP_EXIT);
+ old_value = (BIF_P->flags & F_TRAP_EXIT) ? am_true : am_false;
+ if (BIF_ARG_2 == am_true)
+ BIF_P->flags |= F_TRAP_EXIT;
+ else if (BIF_ARG_2 == am_false)
+ BIF_P->flags &= ~F_TRAP_EXIT;
else
- state = erts_atomic32_read_band_mb(&BIF_P->state,
- ~ERTS_PSFLG_TRAP_EXIT);
- erts_proc_unlock(BIF_P, ERTS_PROC_LOCKS_XSIG_SEND);
-
- if (state & ERTS_PSFLG_PENDING_EXIT) {
- erts_handle_pending_exit(BIF_P, ERTS_PROC_LOCK_MAIN);
- ERTS_BIF_EXITED(BIF_P);
- }
-
- old_value = (state & ERTS_PSFLG_TRAP_EXIT) ? am_true : am_false;
+ goto error;
BIF_RET(old_value);
}
else if (BIF_ARG_1 == am_scheduler) {
ErtsRunQueue *old, *new, *curr;
Sint sched;
- erts_aint32_t state;
if (!is_small(BIF_ARG_2))
goto error;
@@ -1741,23 +1467,23 @@ BIF_RETTYPE process_flag_2(BIF_ALIST_2)
goto error;
if (sched == 0) {
+ old = erts_bind_runq_proc(BIF_P, 0);
new = NULL;
- state = erts_atomic32_read_band_mb(&BIF_P->state,
- ~ERTS_PSFLG_BOUND);
}
else {
+ int bound = !0;
new = erts_schedid2runq(sched);
- erts_atomic_set_nob(&BIF_P->run_queue, (erts_aint_t) new);
- state = erts_atomic32_read_bor_mb(&BIF_P->state,
- ERTS_PSFLG_BOUND);
+ old = erts_set_runq_proc(BIF_P, new, &bound);
+ if (!bound)
+ old = NULL;
}
+ old_value = old ? make_small(old->ix+1) : make_small(0);
+
curr = erts_proc_sched_data(BIF_P)->run_queue;
- old = (ERTS_PSFLG_BOUND & state) ? curr : NULL;
ASSERT(!old || old == curr);
- old_value = old ? make_small(old->ix+1) : make_small(0);
if (new && new != curr)
ERTS_BIF_YIELD_RETURN_X(BIF_P, old_value, am_scheduler);
else
@@ -1964,7 +1690,7 @@ ebif_bang_2(BIF_ALIST_2)
* Send a message to Process, Port or Registered Process.
* Returns non-negative reduction bump or negative result code.
*/
-#define SEND_TRAP (-1)
+#define SEND_NOCONNECT (-1)
#define SEND_YIELD (-2)
#define SEND_YIELD_RETURN (-3)
#define SEND_BADARG (-4)
@@ -1980,20 +1706,22 @@ static Sint remote_send(Process *p, DistEntry *dep,
{
Sint res;
int code;
-
ASSERT(is_atom(to) || is_external_pid(to));
ctx->dep = dep;
- code = erts_dsig_prepare(&ctx->dsd, dep, p, ERTS_DSP_NO_LOCK, !ctx->suspend);
+ code = erts_dsig_prepare(&ctx->dsd, dep, p, ERTS_PROC_LOCK_MAIN,
+ ERTS_DSP_NO_LOCK,
+ !ctx->suspend, ctx->connect);
switch (code) {
case ERTS_DSIG_PREP_NOT_ALIVE:
case ERTS_DSIG_PREP_NOT_CONNECTED:
- res = SEND_TRAP;
+ res = SEND_NOCONNECT;
break;
case ERTS_DSIG_PREP_WOULD_SUSPEND:
ASSERT(!ctx->suspend);
res = SEND_YIELD;
break;
+ case ERTS_DSIG_PREP_PENDING:
case ERTS_DSIG_PREP_CONNECTED: {
if (is_atom(to))
@@ -2129,9 +1857,6 @@ do_send(Process *p, Eterm to, Eterm msg, Eterm *refp, ErtsSendContext *ctx)
}
switch (erts_port_command(p, ps_flags, pt, msg, refp)) {
- case ERTS_PORT_OP_CALLER_EXIT:
- /* We are exiting... */
- return SEND_USER_ERROR;
case ERTS_PORT_OP_BUSY:
/* Nothing has been sent */
if (ctx->suspend)
@@ -2170,6 +1895,7 @@ do_send(Process *p, Eterm to, Eterm msg, Eterm *refp, ErtsSendContext *ctx)
}
return ret_val;
} else if (is_tuple(to)) { /* Remote send */
+ int deref_dep = 0;
int ret;
tp = tuple_val(to);
if (*tp != make_arityval(2))
@@ -2177,11 +1903,10 @@ do_send(Process *p, Eterm to, Eterm msg, Eterm *refp, ErtsSendContext *ctx)
if (is_not_atom(tp[1]) || is_not_atom(tp[2]))
return SEND_BADARG;
- /* sysname_to_connected_dist_entry will return NULL if there
- is no dist_entry or the dist_entry has no port,
+ /* erts_find_dist_entry will return NULL if there is no dist_entry
but remote_send() will handle that. */
- dep = erts_sysname_to_connected_dist_entry(tp[2]);
+ dep = erts_find_dist_entry(tp[2]);
if (dep == erts_this_dist_entry) {
Eterm id;
@@ -2205,13 +1930,20 @@ do_send(Process *p, Eterm to, Eterm msg, Eterm *refp, ErtsSendContext *ctx)
}
return 0;
}
+ if (dep == NULL) {
+ dep = erts_find_or_insert_dist_entry(tp[2]);
+ ASSERT(dep != erts_this_dist_entry);
+ deref_dep = 1;
+ }
+ ctx->dsd.node = tp[2];
ret = remote_send(p, dep, tp[1], to, msg, ctx);
if (ret == SEND_YIELD_CONTINUE) {
- if (dep)
- erts_ref_dist_entry(dep);
- ctx->dep_to_deref = dep;
+ erts_ref_dist_entry(ctx->dep);
+ ctx->deref_dep = 1;
}
+ if (deref_dep)
+ erts_deref_dist_entry(dep);
return ret;
} else {
if (IS_TRACED_FL(p, F_TRACE_SEND))
@@ -2223,20 +1955,15 @@ do_send(Process *p, Eterm to, Eterm msg, Eterm *refp, ErtsSendContext *ctx)
send_message: {
ErtsProcLocks rp_locks = 0;
- Sint res;
if (p == rp)
rp_locks |= ERTS_PROC_LOCK_MAIN;
/* send to local process */
- res = erts_send_message(p, rp, &rp_locks, msg, 0);
- if (erts_use_sender_punish)
- res *= 4;
- else
- res = 0;
+ erts_send_message(p, rp, &rp_locks, msg, 0);
erts_proc_unlock(rp,
p == rp
? (rp_locks & ~ERTS_PROC_LOCK_MAIN)
: rp_locks);
- return res;
+ return 0;
}
}
@@ -2251,7 +1978,6 @@ BIF_RETTYPE send_3(BIF_ALIST_3)
Eterm msg = BIF_ARG_2;
Eterm opts = BIF_ARG_3;
- int connect = !0;
Eterm l = opts;
Sint result;
@@ -2262,14 +1988,15 @@ BIF_RETTYPE send_3(BIF_ALIST_3)
UseTmpHeap(sizeof(ErtsSendContext)/sizeof(Eterm), BIF_P);
ctx->suspend = !0;
- ctx->dep_to_deref = NULL;
+ ctx->connect = !0;
+ ctx->deref_dep = 0;
ctx->return_term = am_ok;
ctx->dss.reds = (Sint) (ERTS_BIF_REDS_LEFT(p) * TERM_TO_BINARY_LOOP_FACTOR);
ctx->dss.phase = ERTS_DSIG_SEND_PHASE_INIT;
while (is_list(l)) {
if (CAR(list_val(l)) == am_noconnect) {
- connect = 0;
+ ctx->connect = 0;
} else if (CAR(list_val(l)) == am_nosuspend) {
ctx->suspend = 0;
} else {
@@ -2291,8 +2018,8 @@ BIF_RETTYPE send_3(BIF_ALIST_3)
result = do_send(p, to, msg, &ref, ctx);
ERTS_MSACC_POP_STATE_M_X();
- if (result > 0) {
- ERTS_VBUMP_REDS(p, result);
+ if (result >= 0) {
+ ERTS_VBUMP_REDS(p, 4);
if (ERTS_IS_PROC_OUT_OF_REDS(p))
goto yield_return;
ERTS_BIF_PREP_RET(retval, am_ok);
@@ -2300,15 +2027,9 @@ BIF_RETTYPE send_3(BIF_ALIST_3)
}
switch (result) {
- case 0:
- /* May need to yield even though we do not bump reds here... */
- if (ERTS_IS_PROC_OUT_OF_REDS(p))
- goto yield_return;
- ERTS_BIF_PREP_RET(retval, am_ok);
- break;
- case SEND_TRAP:
- if (connect) {
- ERTS_BIF_PREP_TRAP3(retval, dsend3_trap, p, to, msg, opts);
+ case SEND_NOCONNECT:
+ if (ctx->connect) {
+ ERTS_BIF_PREP_RET(retval, am_ok);
} else {
ERTS_BIF_PREP_RET(retval, am_noconnect);
}
@@ -2412,7 +2133,8 @@ Eterm erl_send(Process *p, Eterm to, Eterm msg)
ref = NIL;
#endif
ctx->suspend = !0;
- ctx->dep_to_deref = NULL;
+ ctx->connect = !0;
+ ctx->deref_dep = 0;
ctx->return_term = msg;
ctx->dss.reds = (Sint) (ERTS_BIF_REDS_LEFT(p) * TERM_TO_BINARY_LOOP_FACTOR);
ctx->dss.phase = ERTS_DSIG_SEND_PHASE_INIT;
@@ -2421,8 +2143,8 @@ Eterm erl_send(Process *p, Eterm to, Eterm msg)
ERTS_MSACC_POP_STATE_M_X();
- if (result > 0) {
- ERTS_VBUMP_REDS(p, result);
+ if (result >= 0) {
+ ERTS_VBUMP_REDS(p, 4);
if (ERTS_IS_PROC_OUT_OF_REDS(p))
goto yield_return;
ERTS_BIF_PREP_RET(retval, msg);
@@ -2430,15 +2152,9 @@ Eterm erl_send(Process *p, Eterm to, Eterm msg)
}
switch (result) {
- case 0:
- /* May need to yield even though we do not bump reds here... */
- if (ERTS_IS_PROC_OUT_OF_REDS(p))
- goto yield_return;
+ case SEND_NOCONNECT:
ERTS_BIF_PREP_RET(retval, msg);
break;
- case SEND_TRAP:
- ERTS_BIF_PREP_TRAP2(retval, dsend2_trap, p, to, msg);
- break;
case SEND_YIELD:
ERTS_BIF_PREP_YIELD2(retval, bif_export[BIF_send_2], p, to, msg);
break;
@@ -3097,7 +2813,7 @@ BIF_RETTYPE list_to_integer_2(BIF_ALIST_2)
static int do_float_to_charbuf(Process *p, Eterm efloat, Eterm list,
char *fbuf, int sizeof_fbuf) {
- const static int arity_two = make_arityval(2);
+ Eterm arity_two = make_arityval(2);
int decimals = SYS_DEFAULT_FLOAT_DECIMALS;
int compact = 0;
enum fmt_type_ {
@@ -3461,7 +3177,7 @@ BIF_RETTYPE binary_to_float_1(BIF_ALIST_1)
if (bit_offs)
erts_copy_bits(bytes, bit_offs, 1, buf, 0, 1, size*8);
else
- memcpy(buf, bytes, size);
+ sys_memcpy(buf, bytes, size);
buf[size] = '\0';
@@ -4195,12 +3911,12 @@ BIF_RETTYPE list_to_port_1(BIF_ALIST_1)
buf[i] = '\0'; /* null terminal */
cp = &buf[0];
- if (strncmp("#Port<", cp, 6) != 0)
+ if (sys_strncmp("#Port<", cp, 6) != 0)
goto bad;
- cp += 6; /* strlen("#Port<") */
+ cp += 6; /* sys_strlen("#Port<") */
- if (sscanf(cp, "%u.%u>", &n, &p) < 2)
+ if (sscanf(cp, "%u.%u>", (unsigned int*)&n, (unsigned int*)&p) < 2)
goto bad;
if (p > ERTS_MAX_PORT_NUMBER)
@@ -4369,14 +4085,89 @@ BIF_RETTYPE group_leader_0(BIF_ALIST_0)
}
/**********************************************************************/
-/* arg1 == leader, arg2 == new member */
+/* set group leader */
-BIF_RETTYPE group_leader_2(BIF_ALIST_2)
+int
+erts_set_group_leader(Process *proc, Eterm new_gl)
{
- Process* new_member;
+
+ erts_aint32_t state;
- if (is_not_pid(BIF_ARG_1)) {
- BIF_ERROR(BIF_P, BADARG);
+ ASSERT(is_pid(new_gl));
+
+ state = erts_atomic32_read_nob(&proc->state);
+
+ if (state & ERTS_PSFLG_EXITING)
+ return 0;
+
+ ERTS_LC_ASSERT(ERTS_PROC_LOCK_MAIN & erts_proc_lc_my_proc_locks(proc));
+
+ if (!(state & ERTS_PSFLG_DIRTY_RUNNING))
+ proc->group_leader = STORE_NC_IN_PROC(proc, new_gl);
+ else {
+ ErlHeapFragment *bp;
+ Eterm *hp;
+ /*
+ * Currently executing on a dirty scheduler,
+ * so we are not allowed to write to its heap.
+ * Store group leader pid in heap fragment.
+ */
+ bp = new_message_buffer(NC_HEAP_SIZE(new_gl));
+ hp = bp->mem;
+ proc->group_leader = STORE_NC(&hp,
+ &proc->off_heap,
+ new_gl);
+ bp->next = proc->mbuf;
+ proc->mbuf = bp;
+ proc->mbuf_sz += bp->used_size;
+ }
+
+ return !0;
+}
+
+BIF_RETTYPE erts_internal_group_leader_3(BIF_ALIST_3)
+{
+ if (is_not_pid(BIF_ARG_1))
+ BIF_ERROR(BIF_P, BADARG);
+ if (is_not_internal_pid(BIF_ARG_2))
+ BIF_ERROR(BIF_P, BADARG);
+ if (is_not_internal_ref(BIF_ARG_3))
+ BIF_ERROR(BIF_P, BADARG);
+
+ erts_proc_sig_send_group_leader(BIF_P,
+ BIF_ARG_2,
+ BIF_ARG_1,
+ BIF_ARG_3);
+ BIF_RET(am_ok);
+}
+
+BIF_RETTYPE erts_internal_group_leader_2(BIF_ALIST_2)
+{
+ if (is_not_pid(BIF_ARG_1))
+ BIF_RET(am_badarg);
+
+ if (is_internal_pid(BIF_ARG_2)) {
+ Process *rp;
+ int res;
+
+ if (BIF_ARG_2 == BIF_P->common.id)
+ rp = BIF_P;
+ else {
+ rp = erts_try_lock_sig_free_proc(BIF_ARG_2,
+ ERTS_PROC_LOCK_MAIN,
+ NULL);
+ if (!rp)
+ BIF_RET(am_badarg);
+ if (rp == ERTS_PROC_LOCK_BUSY)
+ BIF_RET(am_false);
+ }
+
+ res = erts_set_group_leader(rp, BIF_ARG_1);
+
+ if (rp != BIF_P)
+ erts_proc_unlock(rp, ERTS_PROC_LOCK_MAIN);
+
+ BIF_RET(res ? am_true : am_badarg);
}
if (is_external_pid(BIF_ARG_2)) {
@@ -4384,95 +4175,30 @@ BIF_RETTYPE group_leader_2(BIF_ALIST_2)
int code;
ErtsDSigData dsd;
dep = external_pid_dist_entry(BIF_ARG_2);
+ ERTS_ASSERT(dep);
if(dep == erts_this_dist_entry)
BIF_ERROR(BIF_P, BADARG);
- code = erts_dsig_prepare(&dsd, dep, BIF_P, ERTS_DSP_NO_LOCK, 0);
+ code = erts_dsig_prepare(&dsd, dep, BIF_P, ERTS_PROC_LOCK_MAIN,
+ ERTS_DSP_NO_LOCK, 0, 1);
switch (code) {
case ERTS_DSIG_PREP_NOT_ALIVE:
- BIF_RET(am_true);
case ERTS_DSIG_PREP_NOT_CONNECTED:
- BIF_TRAP2(dgroup_leader_trap, BIF_P, BIF_ARG_1, BIF_ARG_2);
+ BIF_RET(am_true);
+ case ERTS_DSIG_PREP_PENDING:
case ERTS_DSIG_PREP_CONNECTED:
code = erts_dsig_send_group_leader(&dsd, BIF_ARG_1, BIF_ARG_2);
if (code == ERTS_DSIG_SEND_YIELD)
ERTS_BIF_YIELD_RETURN(BIF_P, am_true);
BIF_RET(am_true);
default:
- ASSERT(! "Invalid dsig prepare result");
- BIF_ERROR(BIF_P, EXC_INTERNAL_ERROR);
+ ERTS_ASSERT(! "Invalid dsig prepare result");
}
}
- else if (is_internal_pid(BIF_ARG_2)) {
- int await_x;
- ErtsProcLocks locks = ERTS_PROC_LOCK_MAIN|ERTS_PROC_LOCK_STATUS;
- new_member = erts_pid2proc_nropt(BIF_P, ERTS_PROC_LOCK_MAIN,
- BIF_ARG_2, locks);
- if (!new_member)
- BIF_ERROR(BIF_P, BADARG);
-
- if (new_member == ERTS_PROC_LOCK_BUSY)
- ERTS_BIF_YIELD2(bif_export[BIF_group_leader_2], BIF_P,
- BIF_ARG_1, BIF_ARG_2);
-
- await_x = (new_member != BIF_P
- && ERTS_PROC_PENDING_EXIT(new_member));
- if (!await_x) {
- if (is_immed(BIF_ARG_1))
- new_member->group_leader = BIF_ARG_1;
- else {
- locks &= ~ERTS_PROC_LOCK_STATUS;
- erts_proc_unlock(new_member, ERTS_PROC_LOCK_STATUS);
- if (new_member == BIF_P
- || !(erts_atomic32_read_nob(&new_member->state)
- & ERTS_PSFLG_DIRTY_RUNNING)) {
- new_member->group_leader = STORE_NC_IN_PROC(new_member,
- BIF_ARG_1);
- }
- else {
- ErlHeapFragment *bp;
- Eterm *hp;
- /*
- * Other process executing on a dirty scheduler,
- * so we are not allowed to write to its heap.
- * Store in heap fragment.
- */
-
- bp = new_message_buffer(NC_HEAP_SIZE(BIF_ARG_1));
- hp = bp->mem;
- new_member->group_leader = STORE_NC(&hp,
- &new_member->off_heap,
- BIF_ARG_1);
- bp->next = new_member->mbuf;
- new_member->mbuf = bp;
- new_member->mbuf_sz += bp->used_size;
- }
- }
- }
-
- if (new_member == BIF_P)
- locks &= ~ERTS_PROC_LOCK_MAIN;
- if (locks)
- erts_proc_unlock(new_member, locks);
-
- if (await_x) {
- /* Wait for new_member to terminate; then badarg */
- Eterm args[2] = {BIF_ARG_1, BIF_ARG_2};
- ERTS_BIF_AWAIT_X_APPLY_TRAP(BIF_P,
- BIF_ARG_2,
- am_erlang,
- am_group_leader,
- args,
- 2);
- }
- BIF_RET(am_true);
- }
- else {
- BIF_ERROR(BIF_P, BADARG);
- }
+ BIF_RET(am_badarg);
}
-
+
BIF_RETTYPE system_flag_2(BIF_ALIST_2)
{
Sint n;
@@ -4657,7 +4383,6 @@ BIF_RETTYPE system_flag_2(BIF_ALIST_2)
BIF_RET(ret);
} else if (BIF_ARG_1 == make_small(1)) {
int i, max;
- ErtsMessage* mp;
erts_proc_unlock(BIF_P, ERTS_PROC_LOCK_MAIN);
erts_thr_progress_block();
@@ -4672,16 +4397,8 @@ BIF_RETTYPE system_flag_2(BIF_ALIST_2)
#endif
p->seq_trace_clock = 0;
p->seq_trace_lastcnt = 0;
- ERTS_MSGQ_MV_INQ2PRIVQ(p);
- mp = p->msg.first;
- while(mp != NULL) {
-#ifdef USE_VM_PROBES
- ERL_MESSAGE_TOKEN(mp) = (ERL_MESSAGE_DT_UTAG(mp) != NIL) ? am_have_dt_utag : NIL;
-#else
- ERL_MESSAGE_TOKEN(mp) = NIL;
-#endif
- mp = mp->next;
- }
+
+ erts_proc_sig_clear_seq_trace_tokens(p);
}
}
@@ -4690,17 +4407,9 @@ BIF_RETTYPE system_flag_2(BIF_ALIST_2)
BIF_RET(am_true);
} else if (BIF_ARG_1 == am_scheduler_wall_time) {
- if (BIF_ARG_2 == am_true || BIF_ARG_2 == am_false) {
- erts_aint32_t new = BIF_ARG_2 == am_true ? 1 : 0;
- erts_aint32_t old = erts_atomic32_xchg_nob(&sched_wall_time,
- new);
- Eterm ref = erts_sched_wall_time_request(BIF_P, 1, new, 0, 0);
- ASSERT(is_value(ref));
- BIF_TRAP2(await_sched_wall_time_mod_trap,
- BIF_P,
- ref,
- old ? am_true : am_false);
- }
+ if (BIF_ARG_2 == am_true || BIF_ARG_2 == am_false)
+ BIF_TRAP1(system_flag_scheduler_wall_time_trap,
+ BIF_P, BIF_ARG_2);
} else if (BIF_ARG_1 == am_dirty_cpu_schedulers_online) {
Sint old_no;
if (!is_small(BIF_ARG_2))
@@ -4807,11 +4516,24 @@ BIF_RETTYPE system_flag_2(BIF_ALIST_2)
"scheduled for removal in Erlang/OTP 18. For more\n"
"information see the erlang:system_flag/2 documentation.\n");
return erts_bind_schedulers(BIF_P, BIF_ARG_2);
+ } else if (ERTS_IS_ATOM_STR("erts_alloc", BIF_ARG_1)) {
+ return erts_alloc_set_dyn_param(BIF_P, BIF_ARG_2);
}
error:
BIF_ERROR(BIF_P, BADARG);
}
+BIF_RETTYPE erts_internal_scheduler_wall_time_1(BIF_ALIST_1)
+{
+ erts_aint32_t new = BIF_ARG_1 == am_true ? 1 : 0;
+ erts_aint32_t old = erts_atomic32_xchg_nob(&sched_wall_time,
+ new);
+ Eterm ref = erts_sched_wall_time_request(BIF_P, 1, new, 0, 0);
+ ASSERT(is_value(ref));
+ BIF_TRAP2(await_sched_wall_time_mod_trap,
+ BIF_P, ref, old ? am_true : am_false);
+}
+
/**********************************************************************/
BIF_RETTYPE phash_2(BIF_ALIST_2)
@@ -4939,85 +4661,6 @@ static BIF_RETTYPE bif_return_trap(BIF_ALIST_2)
BIF_RET(res);
}
-/*
- * NOTE: The erts_bif_prep_await_proc_exit_*() functions are
- * tightly coupled with the implementation of erlang:await_proc_exit/3.
- * The erts_bif_prep_await_proc_exit_*() functions can safely call
- * skip_current_msgq() since they know that erlang:await_proc_exit/3
- * unconditionally will do a monitor and then unconditionally will
- * wait for the corresponding 'DOWN' message in a receive, and no other
- * receive is done before this receive. This optimization removes an
- * unnecessary scan of the currently existing message queue (which
- * can be large). If the erlang:await_proc_exit/3 implementation
- * is changed so that the above isn't true, nasty bugs in later
- * receives, etc, may appear.
- */
-
-static ERTS_INLINE int
-skip_current_msgq(Process *c_p)
-{
- int res;
-#if defined(ERTS_ENABLE_LOCK_CHECK)
- erts_proc_lc_chk_only_proc_main(c_p);
-#endif
-
- erts_proc_lock(c_p, ERTS_PROC_LOCKS_MSG_RECEIVE);
- if (ERTS_PROC_PENDING_EXIT(c_p)) {
- KILL_CATCHES(c_p);
- c_p->freason = EXC_EXIT;
- res = 0;
- }
- else {
- ERTS_MSGQ_MV_INQ2PRIVQ(c_p);
- c_p->msg.save = c_p->msg.last;
- res = 1;
- }
- erts_proc_unlock(c_p, ERTS_PROC_LOCKS_MSG_RECEIVE);
- return res;
-}
-
-void
-erts_bif_prep_await_proc_exit_data_trap(Process *c_p, Eterm pid, Eterm ret)
-{
- if (skip_current_msgq(c_p)) {
- ERTS_BIF_PREP_TRAP3_NO_RET(await_proc_exit_trap, c_p, pid, am_data, ret);
- }
-}
-
-void
-erts_bif_prep_await_proc_exit_reason_trap(Process *c_p, Eterm pid)
-{
- if (skip_current_msgq(c_p)) {
- ERTS_BIF_PREP_TRAP3_NO_RET(await_proc_exit_trap, c_p,
- pid, am_reason, am_undefined);
- }
-}
-
-void
-erts_bif_prep_await_proc_exit_apply_trap(Process *c_p,
- Eterm pid,
- Eterm module,
- Eterm function,
- Eterm args[],
- int nargs)
-{
- ASSERT(is_atom(module) && is_atom(function));
- if (skip_current_msgq(c_p)) {
- Eterm term;
- Eterm *hp;
- int i;
-
- hp = HAlloc(c_p, 4+2*nargs);
- term = NIL;
- for (i = nargs-1; i >= 0; i--) {
- term = CONS(hp, args[i], term);
- hp += 2;
- }
- term = TUPLE3(hp, module, function, term);
- ERTS_BIF_PREP_TRAP3_NO_RET(await_proc_exit_trap, c_p, pid, am_apply, term);
- }
-}
-
Export bif_return_trap_export;
void erts_init_trap_export(Export* ep, Eterm m, Eterm f, Uint a,
@@ -5031,7 +4674,7 @@ void erts_init_trap_export(Export* ep, Eterm m, Eterm f, Uint a,
ep->info.mfa.module = m;
ep->info.mfa.function = f;
ep->info.mfa.arity = a;
- ep->beam[0] = (BeamInstr) em_apply_bif;
+ ep->beam[0] = BeamOpCodeAddr(op_apply_bif);
ep->beam[1] = (BeamInstr) bif;
}
@@ -5053,6 +4696,9 @@ void erts_init_bif(void)
am_erts_internal, am_dsend_continue_trap, 1,
dsend_continue_trap_1);
+ erts_init_trap_export(&await_exit_trap, am_erts_internal,
+ am_await_exit, 0, erts_internal_await_exit_trap);
+
flush_monitor_messages_trap = erts_export_put(am_erts_internal,
am_flush_monitor_messages,
3);
@@ -5067,11 +4713,12 @@ void erts_init_bif(void)
erts_format_cpu_topology_trap = erts_export_put(am_erlang,
am_format_cpu_topology,
1);
- await_proc_exit_trap = erts_export_put(am_erlang,am_await_proc_exit,3);
await_port_send_result_trap
= erts_export_put(am_erts_internal, am_await_port_send_result, 3);
+ system_flag_scheduler_wall_time_trap
+ = erts_export_put(am_erts_internal, am_system_flag_scheduler_wall_time, 1);
await_sched_wall_time_mod_trap
- = erts_export_put(am_erlang, am_await_sched_wall_time_modifications, 2);
+ = erts_export_put(am_erts_internal, am_await_sched_wall_time_modifications, 2);
await_msacc_mod_trap
= erts_export_put(am_erts_internal, am_await_microstate_accounting_modifications, 3);
@@ -5096,7 +4743,7 @@ schedule(Process *c_p, Process *dirty_shadow_proc,
{
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, (BeamInstr) em_apply_bif,
+ mfa, pc, BeamOpCodeAddr(op_apply_bif),
dfunc, ifunc,
module, function,
argc, argv);
@@ -5145,7 +4792,6 @@ static BIF_RETTYPE dirty_bif_exception(BIF_ALIST_2)
}
-extern BeamInstr* em_call_bif_e;
static BIF_RETTYPE call_bif(Process *c_p, Eterm *reg, BeamInstr *I);
BIF_RETTYPE
@@ -5218,13 +4864,13 @@ erts_schedule_bif(Process *proc,
mfa = &exp->info.mfa;
}
#endif
- else if (em_call_bif_e == (BeamInstr *) *i) {
+ else if (BeamIsOpCode(*i, op_call_bif_e)) {
/* Pointer to bif export in i+1 */
exp = (Export *) i[1];
pc = i;
mfa = &exp->info.mfa;
}
- else if (em_apply_bif == (BeamInstr *) *i) {
+ else if (BeamIsOpCode(*i, op_apply_bif)) {
/* Pointer to bif in i+1, and mfa in i-3 */
pc = c_p->cp;
mfa = erts_code_to_codemfa(i);
diff --git a/erts/emulator/beam/bif.h b/erts/emulator/beam/bif.h
index a2bc883dbe..a47339253e 100644
--- a/erts/emulator/beam/bif.h
+++ b/erts/emulator/beam/bif.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1996-2016. All Rights Reserved.
+ * Copyright Ericsson AB 1996-2018. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -306,7 +306,7 @@ do { \
(Proc)->freason = TRAP; \
} while (0)
-#define BIF_TRAP0(p, Trap_) do { \
+#define BIF_TRAP0(Trap_, p) do { \
(p)->arity = 0; \
(p)->i = (BeamInstr*) ((Trap_)->addressv[erts_active_code_ix()]); \
(p)->freason = TRAP; \
@@ -404,7 +404,7 @@ do { \
#define ERTS_BIF_YIELD0(TRP, P) \
do { \
ERTS_VBUMP_ALL_REDS((P)); \
- BIF_TRAP0((TRP), (P)); \
+ BIF_TRAP0((TRP), (P)); \
} while (0)
#define ERTS_BIF_YIELD1(TRP, P, A0) \
@@ -425,10 +425,16 @@ do { \
BIF_TRAP3((TRP), (P), (A0), (A1), (A2)); \
} while (0)
+#define ERTS_BIF_PREP_EXITED(RET, PROC) \
+do { \
+ KILL_CATCHES((PROC)); \
+ ERTS_BIF_PREP_ERROR((RET), (PROC), EXTAG_EXIT); \
+} while (0)
+
#define ERTS_BIF_EXITED(PROC) \
do { \
KILL_CATCHES((PROC)); \
- BIF_ERROR((PROC), EXC_EXIT); \
+ BIF_ERROR((PROC), EXTAG_EXIT); \
} while (0)
#define ERTS_BIF_CHK_EXITED(PROC) \
@@ -437,67 +443,6 @@ do { \
ERTS_BIF_EXITED((PROC)); \
} while (0)
-/*
- * The ERTS_BIF_*_AWAIT_X_*_TRAP makros either exits the caller, or
- * sets up a trap to erlang:await_proc_exit/3.
- *
- * The caller is acquired to hold the 'main' lock on C_P. No other locks
- * are allowed to be held.
- */
-
-#define ERTS_BIF_PREP_AWAIT_X_DATA_TRAP(RET, C_P, PID, DATA) \
-do { \
- erts_bif_prep_await_proc_exit_data_trap((C_P), (PID), (DATA)); \
- (RET) = THE_NON_VALUE; \
-} while (0)
-
-#define ERTS_BIF_PREP_AWAIT_X_REASON_TRAP(RET, C_P, PID) \
-do { \
- erts_bif_prep_await_proc_exit_reason_trap((C_P), (PID)); \
- (RET) = THE_NON_VALUE; \
-} while (0)
-
-#define ERTS_BIF_PREP_AWAIT_X_APPLY_TRAP(RET, C_P, PID, M, F, A, AN) \
-do { \
- erts_bif_prep_await_proc_exit_apply_trap((C_P), (PID), \
- (M), (F), (A), (AN)); \
- (RET) = THE_NON_VALUE; \
-} while (0)
-
-#define ERTS_BIF_AWAIT_X_DATA_TRAP(C_P, PID, DATA) \
-do { \
- erts_bif_prep_await_proc_exit_data_trap((C_P), (PID), (DATA)); \
- return THE_NON_VALUE; \
-} while (0)
-
-#define ERTS_BIF_AWAIT_X_REASON_TRAP(C_P, PID) \
-do { \
- erts_bif_prep_await_proc_exit_reason_trap((C_P), (PID)); \
- return THE_NON_VALUE; \
-} while (0)
-
-#define ERTS_BIF_AWAIT_X_APPLY_TRAP(C_P, PID, M, F, A, AN) \
-do { \
- erts_bif_prep_await_proc_exit_apply_trap((C_P), (PID), \
- (M), (F), (A), (AN)); \
- return THE_NON_VALUE; \
-} while (0)
-
-void
-erts_bif_prep_await_proc_exit_data_trap(Process *c_p,
- Eterm pid,
- Eterm data);
-void
-erts_bif_prep_await_proc_exit_reason_trap(Process *c_p,
- Eterm pid);
-void
-erts_bif_prep_await_proc_exit_apply_trap(Process *c_p,
- Eterm pid,
- Eterm module,
- Eterm function,
- Eterm args[],
- int nargs);
-
int erts_call_dirty_bif(ErtsSchedulerData *esdp, Process *c_p,
BeamInstr *I, Eterm *reg);
diff --git a/erts/emulator/beam/bif.tab b/erts/emulator/beam/bif.tab
index f7b4451890..bcaf9277bf 100644
--- a/erts/emulator/beam/bif.tab
+++ b/erts/emulator/beam/bif.tab
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1996-2017. All Rights Reserved.
+# Copyright Ericsson AB 1996-2018. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -62,6 +62,7 @@ bif erlang:erase/0
bif erlang:erase/1
bif erlang:exit/1
bif erlang:exit/2
+bif erlang:exit_signal/2
bif erlang:external_size/1
bif erlang:external_size/2
gcbif erlang:float/1
@@ -73,7 +74,8 @@ bif erlang:get/0
bif erlang:get/1
bif erlang:get_keys/1
bif erlang:group_leader/0
-bif erlang:group_leader/2
+bif erts_internal:group_leader/2
+bif erts_internal:group_leader/3
bif erlang:halt/2
bif erlang:phash/2
bif erlang:phash2/1
@@ -153,14 +155,12 @@ bif erlang:whereis/1
bif erlang:spawn_opt/1
bif erlang:setnode/2
bif erlang:setnode/3
-bif erlang:dist_exit/3
bif erlang:dist_get_stat/1
bif erlang:dist_ctrl_input_handler/2
bif erlang:dist_ctrl_put_data/2
bif erlang:dist_ctrl_get_data/1
bif erlang:dist_ctrl_get_data_notification/1
-
# Static native functions in erts_internal
bif erts_internal:port_info/1
bif erts_internal:port_info/2
@@ -187,6 +187,10 @@ bif erts_internal:system_check/1
bif erts_internal:release_literal_area_switch/0
+bif erts_internal:scheduler_wall_time/1
+
+bif erts_internal:dirty_process_handle_signals/1
+
# inet_db support
bif erlang:port_set_data/2
bif erlang:port_get_data/1
@@ -262,6 +266,7 @@ bif erlang:demonitor/1
bif erlang:demonitor/2
bif erlang:is_process_alive/1
+bif erts_internal:is_process_alive/2
bif erlang:error/1 error_1
bif erlang:error/2 error_2
@@ -376,9 +381,10 @@ bif ets:match_spec_run_r/3
# Bifs in os module.
#
-bif os:putenv/2
-bif os:getenv/0
-bif os:getenv/1
+bif os:get_env_var/1
+bif os:set_env_var/2
+bif os:unset_env_var/1
+bif os:list_env_vars/0
bif os:getpid/0
bif os:timestamp/0
bif os:system_time/0
@@ -434,13 +440,6 @@ bif erts_debug:dirty_io/2
bif erts_debug:dirty/3
#
-# Monitor testing bif's...
-#
-bif erts_debug:dump_monitors/1
-bif erts_debug:dump_links/1
-
-
-#
# Lock counter bif's
#
bif erts_debug:lcnt_control/2
@@ -550,9 +549,6 @@ bif binary:last/1
bif binary:at/2
bif binary:part/2 binary_binary_part_2
bif binary:part/3 binary_binary_part_3
-bif binary:bin_to_list/1
-bif binary:bin_to_list/2
-bif binary:bin_to_list/3
bif binary:list_to_bin/1
bif binary:copy/1
bif binary:copy/2
@@ -619,7 +615,6 @@ bif erlang:float_to_binary/2
bif erlang:binary_to_float/1
bif io:printable_range/0
-bif os:unsetenv/1
#
# New in 17.0
@@ -629,7 +624,6 @@ bif re:inspect/2
ubif erlang:is_map/1
gcbif erlang:map_size/1
-bif maps:to_list/1
bif maps:find/2
bif maps:get/2
bif maps:from_list/1
@@ -684,10 +678,20 @@ bif math:floor/1
bif math:ceil/1
bif math:fmod/2
bif os:set_signal/2
-bif erts_internal:maps_to_list/2
#
# New in 20.1
#
-
bif erlang:iolist_to_iovec/1
+
+#
+# New in 21.0
+#
+
+bif erts_internal:get_dflags/0
+bif erts_internal:new_connection/1
+bif erts_internal:abort_connection/2
+bif erts_internal:map_next/3
+bif ets:whereis/1
+bif erts_internal:gather_alloc_histograms/1
+bif erts_internal:gather_carrier_info/1
diff --git a/erts/emulator/beam/bif_instrs.tab b/erts/emulator/beam/bif_instrs.tab
index 0932b8b985..0f074280db 100644
--- a/erts/emulator/beam/bif_instrs.tab
+++ b/erts/emulator/beam/bif_instrs.tab
@@ -432,9 +432,17 @@ nif_bif.call_nif() {
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);
+
+ ASSERT((c_p->scheduler_data)->current_nif == NULL);
+ (c_p->scheduler_data)->current_nif = &env;
+
nif_bif_result = (*fp)(&env, bif_nif_arity, reg);
if (env.exception_thrown)
nif_bif_result = THE_NON_VALUE;
+
+ ASSERT((c_p->scheduler_data)->current_nif == &env);
+ (c_p->scheduler_data)->current_nif = NULL;
+
erts_post_nif(&env);
ERTS_CHK_MBUF_SZ(c_p);
diff --git a/erts/emulator/beam/big.c b/erts/emulator/beam/big.c
index 5eaf262cd8..c5cb268f09 100644
--- a/erts/emulator/beam/big.c
+++ b/erts/emulator/beam/big.c
@@ -2549,12 +2549,17 @@ int term_equals_2pow32(Eterm x)
}
}
+static ERTS_INLINE int c2int_is_valid_char(byte ch, int base) {
+ if (base <= 10)
+ return (ch >= '0' && ch < ('0' + base));
+ else
+ return (ch >= '0' && ch <= '9')
+ || (ch >= 'A' && ch < ('A' + base - 10))
+ || (ch >= 'a' && ch < ('a' + base - 10));
+}
+
static ERTS_INLINE int c2int_is_invalid_char(byte ch, int base) {
- return (ch < '0'
- || (ch > ('0' + base - 1)
- && !(base > 10
- && ((ch >= 'a' && ch < ('a' + base - 10))
- || (ch >= 'A' && ch < ('A' + base - 10))))));
+ return !c2int_is_valid_char(ch, base);
}
static ERTS_INLINE byte c2int_digit_from_base(byte ch) {
diff --git a/erts/emulator/beam/big.h b/erts/emulator/beam/big.h
index 48efce20e7..7556205063 100644
--- a/erts/emulator/beam/big.h
+++ b/erts/emulator/beam/big.h
@@ -70,7 +70,20 @@ typedef Uint dsize_t; /* Vector size type */
/* Check for small */
#define IS_USMALL(sgn,x) ((sgn) ? ((x) <= MAX_SMALL+1) : ((x) <= MAX_SMALL))
-#define IS_SSMALL(x) (((x) >= MIN_SMALL) && ((x) <= MAX_SMALL))
+
+/*
+ * It seems that both clang and gcc will generate sub-optimal code
+ * for the more obvious way to write the range check:
+ *
+ * #define IS_SSMALL(x) (((x) >= MIN_SMALL) && ((x) <= MAX_SMALL))
+ *
+ * Note that IS_SSMALL() may be used in the 32-bit emulator with
+ * a Uint64 argument. Therefore, we must test the size of the argument
+ * to ensure that the cast does not discard the high-order 32 bits.
+ */
+#define _IS_SSMALL32(x) (((Uint32) ((((x)) >> (SMALL_BITS-1)) + 1)) < 2)
+#define _IS_SSMALL64(x) (((Uint64) ((((x)) >> (SMALL_BITS-1)) + 1)) < 2)
+#define IS_SSMALL(x) (sizeof(x) == sizeof(Uint32) ? _IS_SSMALL32(x) : _IS_SSMALL64(x))
/* The heap size needed for a bignum */
#define BIG_NEED_SIZE(x) ((x) + 1)
diff --git a/erts/emulator/beam/binary.c b/erts/emulator/beam/binary.c
index ca3e48e205..d53f75c279 100644
--- a/erts/emulator/beam/binary.c
+++ b/erts/emulator/beam/binary.c
@@ -60,14 +60,36 @@ erts_init_binary(void)
}
+static ERTS_INLINE
+Eterm build_proc_bin(ErlOffHeap* ohp, Eterm* hp, Binary* bptr)
+{
+ ProcBin* pb = (ProcBin *) hp;
+ pb->thing_word = HEADER_PROC_BIN;
+ pb->size = bptr->orig_size;
+ pb->next = ohp->first;
+ ohp->first = (struct erl_off_heap_header*)pb;
+ pb->val = bptr;
+ pb->bytes = (byte*) bptr->orig_bytes;
+ pb->flags = 0;
+ OH_OVERHEAD(ohp, pb->size / sizeof(Eterm));
+
+ return make_binary(pb);
+}
+
+/** @brief Initiate a ProcBin for a full Binary.
+ * @param hp must point to PROC_BIN_SIZE available heap words.
+ */
+Eterm erts_build_proc_bin(ErlOffHeap* ohp, Eterm* hp, Binary* bptr)
+{
+ return build_proc_bin(ohp, hp, bptr);
+}
+
/*
* Create a brand new binary from scratch.
*/
-
Eterm
new_binary(Process *p, byte *buf, Uint len)
{
- ProcBin* pb;
Binary* bptr;
if (len <= ERL_ONHEAP_BIN_LIMIT) {
@@ -88,32 +110,49 @@ new_binary(Process *p, byte *buf, Uint len)
sys_memcpy(bptr->orig_bytes, buf, len);
}
- /*
- * Now allocate the ProcBin on the heap.
- */
- pb = (ProcBin *) HAlloc(p, PROC_BIN_SIZE);
- pb->thing_word = HEADER_PROC_BIN;
- pb->size = len;
- pb->next = MSO(p).first;
- MSO(p).first = (struct erl_off_heap_header*)pb;
- pb->val = bptr;
- pb->bytes = (byte*) bptr->orig_bytes;
- pb->flags = 0;
+ return build_proc_bin(&MSO(p), HAlloc(p, PROC_BIN_SIZE), bptr);
+}
+
+Eterm
+erts_heap_factory_new_binary(ErtsHeapFactory *hfact, byte *buf, Uint len,
+ Uint reserve_size)
+{
+ Eterm *hp;
+ Binary* bptr;
+
+ if (len <= ERL_ONHEAP_BIN_LIMIT) {
+ ErlHeapBin* hb;
+ hp = erts_produce_heap(hfact, heap_bin_size(len), reserve_size);
+ hb = (ErlHeapBin *) hp;
+ hb->thing_word = header_heap_bin(len);
+ hb->size = len;
+ if (buf != NULL) {
+ sys_memcpy(hb->data, buf, len);
+ }
+ return make_binary(hb);
+ }
/*
- * Miscellaneous updates. Return the tagged binary.
+ * Allocate the binary struct itself.
*/
- OH_OVERHEAD(&(MSO(p)), pb->size / sizeof(Eterm));
- return make_binary(pb);
+ bptr = erts_bin_nrml_alloc(len);
+ if (buf != NULL) {
+ sys_memcpy(bptr->orig_bytes, buf, len);
+ }
+
+ hp = erts_produce_heap(hfact, PROC_BIN_SIZE, reserve_size);
+
+ return build_proc_bin(hfact->off_heap, hp, bptr);
}
+
+
/*
* When heap binary is not desired...
*/
Eterm erts_new_mso_binary(Process *p, byte *buf, Uint len)
{
- ProcBin* pb;
Binary* bptr;
/*
@@ -124,23 +163,7 @@ Eterm erts_new_mso_binary(Process *p, byte *buf, Uint len)
sys_memcpy(bptr->orig_bytes, buf, len);
}
- /*
- * Now allocate the ProcBin on the heap.
- */
- pb = (ProcBin *) HAlloc(p, PROC_BIN_SIZE);
- pb->thing_word = HEADER_PROC_BIN;
- pb->size = len;
- pb->next = MSO(p).first;
- MSO(p).first = (struct erl_off_heap_header*)pb;
- pb->val = bptr;
- pb->bytes = (byte*) bptr->orig_bytes;
- pb->flags = 0;
-
- /*
- * Miscellaneous updates. Return the tagged binary.
- */
- OH_OVERHEAD(&(MSO(p)), pb->size / sizeof(Eterm));
- return make_binary(pb);
+ return build_proc_bin(&MSO(p), HAlloc(p, PROC_BIN_SIZE), bptr);
}
/*
@@ -964,7 +987,10 @@ HIPE_WRAPPER_BIF_DISABLE_GC(iolist_to_binary, 1)
BIF_RETTYPE iolist_to_binary_1(BIF_ALIST_1)
{
if (is_binary(BIF_ARG_1)) {
- BIF_RET(BIF_ARG_1);
+ if (binary_bitsize(BIF_ARG_1) == 0) {
+ BIF_RET(BIF_ARG_1);
+ }
+ BIF_ERROR(BIF_P, BADARG);
}
return erts_list_to_binary_bif(BIF_P, BIF_ARG_1, bif_export[BIF_iolist_to_binary_1]);
}
diff --git a/erts/emulator/beam/break.c b/erts/emulator/beam/break.c
index 35b2365655..9ff52c92b8 100644
--- a/erts/emulator/beam/break.c
+++ b/erts/emulator/beam/break.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1996-2017. All Rights Reserved.
+ * Copyright Ericsson AB 1996-2018. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -36,9 +36,9 @@
#include "hash.h"
#include "atom.h"
#include "beam_load.h"
-#include "erl_instrument.h"
#include "erl_hl_timer.h"
#include "erl_thr_progress.h"
+#include "erl_proc_sig_queue.h"
/* Forward declarations -- should really appear somewhere else */
static void process_killer(void);
@@ -58,6 +58,8 @@ static void dump_attributes(fmtfn_t to, void *to_arg, byte* ptr, int size);
extern char* erts_system_version[];
+#define WRITE_BUFFER_SIZE (64*1024)
+
static void
port_info(fmtfn_t to, void *to_arg)
{
@@ -105,36 +107,11 @@ process_killer(void)
if ((j = sys_get_key(0)) <= 0)
erts_exit(0, "");
switch(j) {
- case 'k': {
- ErtsProcLocks rp_locks = ERTS_PROC_LOCKS_XSIG_SEND;
- erts_aint32_t state;
- erts_proc_inc_refc(rp);
- erts_proc_lock(rp, rp_locks);
- state = erts_atomic32_read_acqb(&rp->state);
- if (state & (ERTS_PSFLG_FREE
- | ERTS_PSFLG_EXITING
- | ERTS_PSFLG_ACTIVE
- | ERTS_PSFLG_ACTIVE_SYS
- | ERTS_PSFLG_IN_RUNQ
- | ERTS_PSFLG_RUNNING
- | ERTS_PSFLG_RUNNING_SYS
- | ERTS_PSFLG_DIRTY_RUNNING
- | ERTS_PSFLG_DIRTY_RUNNING_SYS)) {
- erts_printf("Can only kill WAITING processes this way\n");
- }
- else {
- (void) erts_send_exit_signal(NULL,
- NIL,
- rp,
- &rp_locks,
- am_kill,
- NIL,
- NULL,
- 0);
- }
- erts_proc_unlock(rp, rp_locks);
- erts_proc_dec_refc(rp);
- }
+ case 'k':
+ /* Send a 'kill' exit signal from init process */
+ erts_proc_sig_send_exit(NULL, erts_init_process_id,
+ rp->common.id, am_kill, NIL,
+ 0);
case 'n': br = 1; break;
case 'r': return;
default: return;
@@ -159,47 +136,64 @@ static void doit_print_link(ErtsLink *lnk, void *vpcontext)
if (pcontext->is_first) {
pcontext->is_first = 0;
- erts_print(to, to_arg, "%T", lnk->pid);
+ erts_print(to, to_arg, "%T", lnk->other.item);
} else {
- erts_print(to, to_arg, ", %T", lnk->pid);
+ erts_print(to, to_arg, ", %T", lnk->other.item);
}
}
static void doit_print_monitor(ErtsMonitor *mon, void *vpcontext)
{
+ ErtsMonitorData *mdp;
PrintMonitorContext *pcontext = vpcontext;
fmtfn_t to = pcontext->to;
void *to_arg = pcontext->to_arg;
char *prefix = ", ";
- if (pcontext->is_first) {
- pcontext->is_first = 0;
- prefix = "";
- }
-
+ mdp = erts_monitor_to_data(mon);
switch (mon->type) {
- case MON_ORIGIN:
- if (is_atom(mon->u.pid)) { /* dist by name */
- ASSERT(is_node_name_atom(mon->u.pid));
- erts_print(to, to_arg, "%s{to,{%T,%T},%T}", prefix, mon->name,
- mon->u.pid, mon->ref);
- } else if (is_atom(mon->name)){ /* local by name */
- erts_print(to, to_arg, "%s{to,{%T,%T},%T}", prefix, mon->name,
- erts_this_dist_entry->sysname, mon->ref);
- } else { /* local and distributed by pid */
- erts_print(to, to_arg, "%s{to,%T,%T}", prefix, mon->u.pid, mon->ref);
- }
- break;
- case MON_TARGET:
- erts_print(to, to_arg, "%s{from,%T,%T}", prefix, mon->u.pid, mon->ref);
- break;
- case MON_NIF_TARGET: {
- ErtsResource* rsrc = mon->u.resource;
- erts_print(to, to_arg, "%s{from,{%T,%T},%T}", prefix, rsrc->type->module,
- rsrc->type->name, mon->ref);
- break;
- }
+ case ERTS_MON_TYPE_PROC:
+ case ERTS_MON_TYPE_PORT:
+ case ERTS_MON_TYPE_TIME_OFFSET:
+ case ERTS_MON_TYPE_DIST_PROC:
+ case ERTS_MON_TYPE_RESOURCE:
+ case ERTS_MON_TYPE_NODE:
+
+ if (pcontext->is_first) {
+ pcontext->is_first = 0;
+ prefix = "";
+ }
+
+ if (erts_monitor_is_target(mon)) {
+ if (mon->type != ERTS_MON_TYPE_RESOURCE)
+ erts_print(to, to_arg, "%s{from,%T,%T}", prefix, mon->other.item, mdp->ref);
+ else {
+ ErtsResource* rsrc = mon->other.ptr;
+ erts_print(to, to_arg, "%s{from,{%T,%T},%T}", prefix, rsrc->type->module,
+ rsrc->type->name, mdp->ref);
+ }
+ }
+ else {
+ if (!(mon->flags & ERTS_ML_FLG_NAME))
+ erts_print(to, to_arg, "%s{to,%T,%T}", prefix, mon->other.item, mdp->ref);
+ else {
+ ErtsMonitorDataExtended *mdep = (ErtsMonitorDataExtended *) mdp;
+ Eterm node;
+ if (mdep->dist)
+ node = mdep->dist->nodename;
+ else
+ node = erts_this_dist_entry->sysname;
+ erts_print(to, to_arg, "%s{to,{%T,%T},%T}", prefix, mdep->u.name,
+ node, mdp->ref);
+ }
+ }
+
+ break;
+
+ default:
+ /* ignore other monitors... */
+ break;
}
}
@@ -207,9 +201,9 @@ static void doit_print_monitor(ErtsMonitor *mon, void *vpcontext)
void
print_process_info(fmtfn_t to, void *to_arg, Process *p)
{
- time_t approx_started;
int garbing = 0;
int running = 0;
+ Sint len;
struct saved_calls *scb;
erts_aint32_t state;
@@ -256,17 +250,22 @@ print_process_info(fmtfn_t to, void *to_arg, Process *p)
}
erts_print(to, to_arg, "Spawned by: %T\n", p->parent);
- approx_started = (time_t) p->approx_started;
- erts_print(to, to_arg, "Started: %s", ctime(&approx_started));
- ERTS_MSGQ_MV_INQ2PRIVQ(p);
- erts_print(to, to_arg, "Message queue length: %d\n", p->msg.len);
+
+ erts_proc_lock(p, ERTS_PROC_LOCK_MSGQ);
+ len = erts_proc_sig_fetch(p);
+ erts_proc_unlock(p, ERTS_PROC_LOCK_MSGQ);
+ erts_print(to, to_arg, "Message queue length: %d\n", len);
/* display the message queue only if there is anything in it */
- if (!ERTS_IS_CRASH_DUMPING && p->msg.first != NULL && !garbing) {
- ErtsMessage* mp;
+ if (!ERTS_IS_CRASH_DUMPING && p->sig_qs.first != NULL && !garbing) {
erts_print(to, to_arg, "Message queue: [");
- for (mp = p->msg.first; mp; mp = mp->next)
- erts_print(to, to_arg, mp->next ? "%T," : "%T", ERL_MESSAGE_TERM(mp));
+ ERTS_FOREACH_SIG_PRIVQS(
+ p, mp,
+ {
+ if (ERTS_SIG_IS_NON_MSG((ErtsSignal *) mp))
+ erts_print(to, to_arg, mp->next ? "%T," : "%T",
+ ERL_MESSAGE_TERM(mp));
+ });
erts_print(to, to_arg, "]\n");
}
@@ -307,11 +306,12 @@ print_process_info(fmtfn_t to, void *to_arg, Process *p)
}
/* display the links only if there are any*/
- if (ERTS_P_LINKS(p) || ERTS_P_MONITORS(p)) {
+ if (ERTS_P_LINKS(p) || ERTS_P_MONITORS(p) || ERTS_P_LT_MONITORS(p)) {
PrintMonitorContext context = {1, to, to_arg};
erts_print(to, to_arg,"Link list: [");
- erts_doforall_links(ERTS_P_LINKS(p), &doit_print_link, &context);
- erts_doforall_monitors(ERTS_P_MONITORS(p), &doit_print_monitor, &context);
+ erts_link_tree_foreach(ERTS_P_LINKS(p), doit_print_link, &context);
+ erts_monitor_tree_foreach(ERTS_P_MONITORS(p), doit_print_monitor, &context);
+ erts_monitor_list_foreach(ERTS_P_LT_MONITORS(p), doit_print_monitor, &context);
erts_print(to, to_arg,"]\n");
}
@@ -334,6 +334,12 @@ print_process_info(fmtfn_t to, void *to_arg, Process *p)
erts_print(to, to_arg, "Heap unused: %bpu\n", (p->hend - p->htop));
erts_print(to, to_arg, "OldHeap unused: %bpu\n",
(OLD_HEAP(p) == NULL) ? 0 : (OLD_HEND(p) - OLD_HTOP(p)) );
+ erts_print(to, to_arg, "BinVHeap: %b64u\n", p->off_heap.overhead);
+ erts_print(to, to_arg, "OldBinVHeap: %b64u\n", BIN_OLD_VHEAP(p));
+ erts_print(to, to_arg, "BinVHeap unused: %b64u\n",
+ BIN_VHEAP_SZ(p) - p->off_heap.overhead);
+ erts_print(to, to_arg, "OldBinVHeap unused: %b64u\n",
+ BIN_OLD_VHEAP_SZ(p) - BIN_OLD_VHEAP(p));
erts_print(to, to_arg, "Memory: %beu\n", erts_process_memory(p, !0));
if (garbing) {
@@ -356,7 +362,7 @@ print_process_info(fmtfn_t to, void *to_arg, Process *p)
static void
print_garb_info(fmtfn_t to, void *to_arg, Process* p)
{
- /* ERTS_SMP: A scheduler is probably concurrently doing gc... */
+ /* A scheduler is probably concurrently doing gc... */
if (!ERTS_IS_CRASH_DUMPING)
return;
erts_print(to, to_arg, "New heap start: %bpX\n", p->heap);
@@ -486,9 +492,7 @@ loaded(fmtfn_t to, void *to_arg)
static void
dump_attributes(fmtfn_t to, void *to_arg, byte* ptr, int size)
{
- while (size-- > 0) {
- erts_print(to, to_arg, "%02X", *ptr++);
- }
+ erts_print_base64(to, to_arg, ptr, size);
erts_print(to, to_arg, "\n");
}
@@ -503,7 +507,7 @@ do_break(void)
/* check if we're in console mode and, if so,
halt immediately if break is called */
mode = erts_read_env("ERL_CONSOLE_MODE");
- if (mode && strcmp(mode, "window") != 0)
+ if (mode && sys_strcmp(mode, "window") != 0)
erts_exit(0, "");
erts_free_read_env(mode);
#endif /* __WIN32__ */
@@ -673,18 +677,28 @@ bin_check(void)
static Sint64 crash_dump_limit = ERTS_SINT64_MAX;
static Sint64 crash_dump_written = 0;
-static int crash_dump_limited_writer(void* vfdp, char* buf, size_t len)
+typedef struct LimitedWriterInfo_ {
+ fmtfn_t to;
+ void* to_arg;
+} LimitedWriterInfo;
+
+static int
+crash_dump_limited_writer(void* vfdp, char* buf, size_t len)
{
const char stop_msg[] = "\n=abort:CRASH DUMP SIZE LIMIT REACHED\n";
+ LimitedWriterInfo* lwi = (LimitedWriterInfo *) vfdp;
crash_dump_written += len;
if (crash_dump_written <= crash_dump_limit) {
- return erts_write_fd(vfdp, buf, len);
+ return lwi->to(lwi->to_arg, buf, len);
}
len -= (crash_dump_written - crash_dump_limit);
- erts_write_fd(vfdp, buf, len);
- erts_write_fd(vfdp, (char*)stop_msg, sizeof(stop_msg)-1);
+ lwi->to(lwi->to_arg, buf, len);
+ lwi->to(lwi->to_arg, (char*)stop_msg, sizeof(stop_msg)-1);
+ if (lwi->to == &erts_write_fp) {
+ fclose((FILE *) lwi->to_arg);
+ }
/* We assume that crash dump was called from erts_exit_vv() */
erts_exit_epilogue();
@@ -707,6 +721,9 @@ erl_crash_dump_v(char *file, int line, char* fmt, va_list args)
int i;
fmtfn_t to = &erts_write_fd;
void* to_arg;
+ FILE* fp = 0;
+ LimitedWriterInfo lwi;
+ static char* write_buffer; /* 'static' to avoid a leak warning in valgrind */
if (ERTS_SOMEONE_IS_CRASH_DUMPING)
return;
@@ -762,16 +779,16 @@ erl_crash_dump_v(char *file, int line, char* fmt, va_list args)
* - write dump until alarm or file is written completely
*/
- if (erts_sys_getenv__("ERL_CRASH_DUMP_SECONDS", env, &envsz) != 0) {
- env_erl_crash_dump_seconds_set = 0;
- secs = -1;
+ if (erts_sys_explicit_8bit_getenv("ERL_CRASH_DUMP_SECONDS", env, &envsz) == 1) {
+ env_erl_crash_dump_seconds_set = 1;
+ secs = atoi(env);
} else {
- env_erl_crash_dump_seconds_set = 1;
- secs = atoi(env);
+ env_erl_crash_dump_seconds_set = 0;
+ secs = -1;
}
if (secs == 0) {
- return;
+ return;
}
/* erts_sys_prepare_crash_dump returns 1 if heart port is found, otherwise 0
@@ -787,7 +804,7 @@ erl_crash_dump_v(char *file, int line, char* fmt, va_list args)
crash_dump_limit = ERTS_SINT64_MAX;
envsz = sizeof(env);
- if (erts_sys_getenv__("ERL_CRASH_DUMP_BYTES", env, &envsz) == 0) {
+ if (erts_sys_explicit_8bit_getenv("ERL_CRASH_DUMP_BYTES", env, &envsz) == 1) {
Sint64 limit;
char* endptr;
errno = 0;
@@ -800,7 +817,7 @@ erl_crash_dump_v(char *file, int line, char* fmt, va_list args)
}
}
- if (erts_sys_getenv__("ERL_CRASH_DUMP",&dumpnamebuf[0],&dumpnamebufsize) != 0)
+ if (erts_sys_explicit_8bit_getenv("ERL_CRASH_DUMP",&dumpnamebuf[0],&dumpnamebufsize) != 1)
dumpname = "erl_crash.dump";
else
dumpname = &dumpnamebuf[0];
@@ -810,9 +827,30 @@ erl_crash_dump_v(char *file, int line, char* fmt, va_list args)
fd = open(dumpname,O_WRONLY | O_CREAT | O_TRUNC,0640);
if (fd < 0)
return; /* Can't create the crash dump, skip it */
- to_arg = (void*)&fd;
+
+ /*
+ * Wrap into a FILE* so that we can use buffered output. Set an
+ * explicit buffer to make sure the first write does not fail because
+ * of a failure to allocate a buffer.
+ */
+ write_buffer = (char *) erts_alloc_fnf(ERTS_ALC_T_TMP, WRITE_BUFFER_SIZE);
+ if (write_buffer && (fp = fdopen(fd, "w")) != NULL) {
+ setvbuf(fp, write_buffer, _IOFBF, WRITE_BUFFER_SIZE);
+ lwi.to = &erts_write_fp;
+ lwi.to_arg = (void*)fp;
+ } else {
+ lwi.to = &erts_write_fd;
+ lwi.to_arg = (void*)&fd;
+ }
+ if (to == &crash_dump_limited_writer) {
+ to_arg = (void *) &lwi;
+ } else {
+ to = lwi.to;
+ to_arg = lwi.to_arg;
+ }
+
time(&now);
- erts_cbprintf(to, to_arg, "=erl_crash_dump:0.3\n%s", ctime(&now));
+ erts_cbprintf(to, to_arg, "=erl_crash_dump:0.5\n%s", ctime(&now));
if (file != NULL)
erts_cbprintf(to, to_arg, "The error occurred in file %s, line %d\n", file, line);
@@ -857,6 +895,21 @@ erl_crash_dump_v(char *file, int line, char* fmt, va_list args)
erts_print_scheduler_info(to, to_arg, ERTS_SCHEDULER_IX(i)),
erts_cbprintf(to, to_arg, "** crashed **\n"));
}
+ for (i = 0; i < erts_no_dirty_cpu_schedulers; i++) {
+ ERTS_SYS_TRY_CATCH(
+ erts_print_scheduler_info(to, to_arg, ERTS_DIRTY_CPU_SCHEDULER_IX(i)),
+ erts_cbprintf(to, to_arg, "** crashed **\n"));
+ }
+ erts_cbprintf(to, to_arg, "=dirty_cpu_run_queue\n");
+ erts_print_run_queue_info(to, to_arg, ERTS_DIRTY_CPU_RUNQ);
+
+ for (i = 0; i < erts_no_dirty_io_schedulers; i++) {
+ ERTS_SYS_TRY_CATCH(
+ erts_print_scheduler_info(to, to_arg, ERTS_DIRTY_IO_SCHEDULER_IX(i)),
+ erts_cbprintf(to, to_arg, "** crashed **\n"));
+ }
+ erts_cbprintf(to, to_arg, "=dirty_io_run_queue\n");
+ erts_print_run_queue_info(to, to_arg, ERTS_DIRTY_IO_RUNQ);
#endif
@@ -901,22 +954,36 @@ erl_crash_dump_v(char *file, int line, char* fmt, va_list args)
erts_cbprintf(to, to_arg, "=atoms\n");
dump_atoms(to, to_arg);
- /* Keep the instrumentation data at the end of the dump */
- if (erts_instr_memory_map || erts_instr_stat) {
- erts_cbprintf(to, to_arg, "=instr_data\n");
-
- if (erts_instr_stat) {
- erts_cbprintf(to, to_arg, "=memory_status\n");
- erts_instr_dump_stat_to(to, to_arg, 0);
- }
- if (erts_instr_memory_map) {
- erts_cbprintf(to, to_arg, "=memory_map\n");
- erts_instr_dump_memory_map_to(to, to_arg);
- }
- }
-
erts_cbprintf(to, to_arg, "=end\n");
+ if (fp) {
+ fclose(fp);
+ }
close(fd);
erts_fprintf(stderr,"done\n");
}
+void
+erts_print_base64(fmtfn_t to, void *to_arg, byte* src, Uint size)
+{
+ static const byte base64_chars[] =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+ while (size >= 3) {
+ erts_putc(to, to_arg, base64_chars[src[0] >> 2]);
+ erts_putc(to, to_arg, base64_chars[((src[0] & 0x03) << 4) | (src[1] >> 4)]);
+ erts_putc(to, to_arg, base64_chars[((src[1] & 0x0f) << 2) | (src[2] >> 6)]);
+ erts_putc(to, to_arg, base64_chars[src[2] & 0x3f]);
+ size -= 3;
+ src += 3;
+ }
+ if (size == 1) {
+ erts_putc(to, to_arg, base64_chars[src[0] >> 2]);
+ erts_putc(to, to_arg, base64_chars[(src[0] & 0x03) << 4]);
+ erts_print(to, to_arg, "==");
+ } else if (size == 2) {
+ erts_putc(to, to_arg, base64_chars[src[0] >> 2]);
+ erts_putc(to, to_arg, base64_chars[((src[0] & 0x03) << 4) | (src[1] >> 4)]);
+ erts_putc(to, to_arg, base64_chars[(src[1] & 0x0f) << 2]);
+ erts_putc(to, to_arg, '=');
+ }
+}
diff --git a/erts/emulator/beam/bs_instrs.tab b/erts/emulator/beam/bs_instrs.tab
index a4d4afe7d4..94e0000c8b 100644
--- a/erts/emulator/beam/bs_instrs.tab
+++ b/erts/emulator/beam/bs_instrs.tab
@@ -364,11 +364,9 @@ i_bs_init_bits_fail := bs_init_bits.fail.verify.execute;
i_bs_init_bits_fail_heap := bs_init_bits.fail_heap.verify.execute;
bs_init_bits.head() {
- Eterm new_binary;
Eterm num_bits_term;
Uint num_bits;
Uint alloc;
- Uint num_bytes;
}
bs_init_bits.plain(NumBits) {
@@ -410,7 +408,9 @@ bs_init_bits.verify(Fail) {
}
bs_init_bits.execute(Live, Dst) {
- num_bytes = ((Uint64)num_bits+(Uint64)7) >> 3;
+ Eterm new_binary;
+ Uint num_bytes = ((Uint64)num_bits+(Uint64)7) >> 3;
+
if (num_bits & 7) {
alloc += ERL_SUB_BIN_SIZE;
}
@@ -709,9 +709,6 @@ i_bs_validate_unicode_retract(Fail, Src, Ms) {
i_bs_start_match2 := bs_start_match.fetch.execute;
bs_start_match.head() {
- Uint slots;
- Uint live;
- Eterm header;
Eterm context;
}
@@ -720,6 +717,9 @@ bs_start_match.fetch(Src) {
}
bs_start_match.execute(Fail, Live, Slots, Dst) {
+ Uint slots;
+ Uint live;
+ Eterm header;
if (!is_boxed(context)) {
$FAIL($Fail);
}
@@ -919,9 +919,23 @@ i_bs_get_integer(Fail, Live, FlagsAndUnit, Ms, Sz, Dst) {
}
i_bs_get_utf8(Ctx, Fail, Dst) {
+ Eterm result;
ErlBinMatchBuffer* mb = ms_matchbuffer($Ctx);
- Eterm result = erts_bs_get_utf8(mb);
+ if (mb->size - mb->offset < 8) {
+ $FAIL($Fail);
+ }
+ if (BIT_OFFSET(mb->offset) != 0) {
+ result = erts_bs_get_utf8(mb);
+ } else {
+ byte b = mb->base[BYTE_OFFSET(mb->offset)];
+ if (b < 128) {
+ result = make_small(b);
+ mb->offset += 8;
+ } else {
+ result = erts_bs_get_utf8(mb);
+ }
+ }
if (is_non_value(result)) {
$FAIL($Fail);
}
@@ -942,13 +956,10 @@ bs_context_to_binary := ctx_to_bin.fetch.execute;
i_bs_get_binary_all_reuse := ctx_to_bin.fetch_bin.execute;
ctx_to_bin.head() {
- Eterm context;
- ErlBinMatchBuffer* mb;
- ErlSubBin* sb;
- Uint size;
- Uint offs;
- Uint orig;
- Uint hole_size;
+ Eterm context;
+ ErlBinMatchBuffer* mb;
+ Uint size;
+ Uint offs;
}
ctx_to_bin.fetch(Src) {
@@ -976,8 +987,12 @@ ctx_to_bin.fetch_bin(Src, Fail, Unit) {
}
ctx_to_bin.execute() {
- orig = mb->orig;
- sb = (ErlSubBin *) boxed_val(context);
+ Uint hole_size;
+ Uint orig = mb->orig;
+ ErlSubBin* sb = (ErlSubBin *) boxed_val(context);
+ /* Since we're going to overwrite the match state with the result, an
+ * ErlBinMatchState must be at least as large as an ErlSubBin. */
+ ERTS_CT_ASSERT(sizeof(ErlSubBin) <= sizeof(ErlBinMatchState));
hole_size = 1 + header_arity(sb->thing_word) - ERL_SUB_BIN_SIZE;
sb->thing_word = HEADER_SUB_BIN;
sb->size = BYTE_OFFSET(size);
diff --git a/erts/emulator/beam/code_ix.h b/erts/emulator/beam/code_ix.h
index 9e3280cd98..42976d2301 100644
--- a/erts/emulator/beam/code_ix.h
+++ b/erts/emulator/beam/code_ix.h
@@ -176,7 +176,7 @@ int erts_has_code_write_permission(void);
ERTS_GLB_INLINE
BeamInstr *erts_codeinfo_to_code(ErtsCodeInfo *ci)
{
- ASSERT(ci->op == (BeamInstr) BeamOp(op_i_func_info_IaaI) || !ci->op);
+ ASSERT(BeamIsOpCode(ci->op, op_i_func_info_IaaI) || !ci->op);
ASSERT_MFA(&ci->mfa);
return (BeamInstr*)(ci + 1);
}
@@ -185,7 +185,7 @@ ERTS_GLB_INLINE
ErtsCodeInfo *erts_code_to_codeinfo(BeamInstr *I)
{
ErtsCodeInfo *ci = ((ErtsCodeInfo *)(((char *)(I)) - sizeof(ErtsCodeInfo)));
- ASSERT(ci->op == (BeamInstr) BeamOp(op_i_func_info_IaaI) || !ci->op);
+ ASSERT(BeamIsOpCode(ci->op, op_i_func_info_IaaI) || !ci->op);
ASSERT_MFA(&ci->mfa);
return ci;
}
diff --git a/erts/emulator/beam/copy.c b/erts/emulator/beam/copy.c
index 10bf197405..7769a914db 100644
--- a/erts/emulator/beam/copy.c
+++ b/erts/emulator/beam/copy.c
@@ -611,7 +611,7 @@ Eterm copy_struct_x(Eterm obj, Uint sz, Eterm** hpp, ErlOffHeap* off_heap, Uint
Eterm* htop;
Eterm* hbot;
Eterm* hp;
- Eterm* objp;
+ Eterm* ERTS_RESTRICT objp;
Eterm* tp;
Eterm res;
Eterm elem;
@@ -1821,7 +1821,8 @@ all_clean:
*
* NOTE: Assumes that term is a tuple (ptr is an untagged tuple ptr).
*/
-Eterm copy_shallow(Eterm* ptr, Uint sz, Eterm** hpp, ErlOffHeap* off_heap)
+Eterm copy_shallow(Eterm* ERTS_RESTRICT ptr, Uint sz, Eterm** hpp,
+ ErlOffHeap* off_heap)
{
Eterm* tp = ptr;
Eterm* hp = *hpp;
@@ -1985,7 +1986,7 @@ move_one_frag(Eterm** hpp, ErlHeapFragment* frag, ErlOffHeap* off_heap, int lite
if (is_header(val)) {
struct erl_off_heap_header* hdr = (struct erl_off_heap_header*)hp;
ASSERT(ptr + header_arity(val) < end);
- move_boxed(&ptr, val, &hp, &dummy_ref);
+ ptr = move_boxed(ptr, val, &hp, &dummy_ref);
switch (val & _HEADER_SUBTAG_MASK) {
case REF_SUBTAG:
if (is_ordinary_ref_thing(hdr))
@@ -2002,7 +2003,7 @@ move_one_frag(Eterm** hpp, ErlHeapFragment* frag, ErlOffHeap* off_heap, int lite
}
else { /* must be a cons cell */
ASSERT(ptr+1 < end);
- move_cons(&ptr, val, &hp, &dummy_ref);
+ move_cons(ptr, val, &hp, &dummy_ref);
ptr += 2;
}
}
diff --git a/erts/emulator/beam/dist.c b/erts/emulator/beam/dist.c
index bc168fc58d..f203d85ca9 100644
--- a/erts/emulator/beam/dist.c
+++ b/erts/emulator/beam/dist.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1996-2017. All Rights Reserved.
+ * Copyright Ericsson AB 1996-2018. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -45,6 +45,7 @@
#include "erl_binary.h"
#include "erl_thr_progress.h"
#include "dtrace-wrapper.h"
+#include "erl_proc_sig_queue.h"
#define DIST_CTL_DEFAULT_SIZE 64
@@ -103,22 +104,12 @@ dist_msg_dbg(ErtsDistExternal *edep, char *what, byte *buf, int sz)
-#define PASS_THROUGH 'p' /* This code should go */
-
int erts_is_alive; /* System must be blocked on change */
int erts_dist_buf_busy_limit;
/* distribution trap functions */
-Export* dsend2_trap = NULL;
-Export* dsend3_trap = NULL;
-/*Export* dsend_nosuspend_trap = NULL;*/
-Export* dlink_trap = NULL;
-Export* dunlink_trap = NULL;
Export* dmonitor_node_trap = NULL;
-Export* dgroup_leader_trap = NULL;
-Export* dexit_trap = NULL;
-Export* dmonitor_p_trap = NULL;
/* local variables */
static Export *dist_ctrl_put_data_trap;
@@ -129,6 +120,7 @@ static void clear_dist_entry(DistEntry*);
static int dsig_send_ctl(ErtsDSigData* dsdp, Eterm ctl, int force_busy);
static void send_nodes_mon_msgs(Process *, Eterm, Eterm, Eterm, Eterm);
static void init_nodes_monitors(void);
+static Sint abort_connection(DistEntry* dep, Uint32 conn_id);
static erts_atomic_t no_caches;
static erts_atomic_t no_nodes;
@@ -192,6 +184,161 @@ get_suspended_on_de(DistEntry *dep, erts_aint32_t unset_qflgs)
}
}
+#define ERTS_MON_LNK_FIRE_LIMIT 100
+
+static void monitor_connection_down(ErtsMonitor *mon, void *unused)
+{
+ if (erts_monitor_is_origin(mon))
+ erts_proc_sig_send_demonitor(mon);
+ else
+ erts_proc_sig_send_monitor_down(mon, am_noconnection);
+}
+
+static void link_connection_down(ErtsLink *lnk, void *vdist)
+{
+ erts_proc_sig_send_link_exit(NULL, THE_NON_VALUE, lnk,
+ am_noconnection, NIL);
+}
+
+typedef enum {
+ ERTS_CML_CLEANUP_STATE_LINKS,
+ ERTS_CML_CLEANUP_STATE_MONITORS,
+ ERTS_CML_CLEANUP_STATE_ONAME_MONITORS,
+ ERTS_CML_CLEANUP_STATE_NODE_MONITORS
+} ErtsConMonLnkCleaupState;
+
+typedef struct {
+ ErtsConMonLnkCleaupState state;
+ ErtsMonLnkDist *dist;
+ void *yield_state;
+ int trigger_node_monitors;
+ Eterm nodename;
+ Eterm visability;
+ Eterm reason;
+ ErlOffHeap oh;
+ Eterm heap[1];
+} ErtsConMonLnkCleanup;
+
+static void
+con_monitor_link_cleanup(void *vcmlcp)
+{
+ ErtsConMonLnkCleanup *cmlcp = vcmlcp;
+ ErtsMonLnkDist *dist = cmlcp->dist;
+ ErtsSchedulerData *esdp;
+ int yield;
+
+ switch (cmlcp->state) {
+ case ERTS_CML_CLEANUP_STATE_LINKS:
+ yield = erts_link_list_foreach_delete_yielding(&dist->links,
+ link_connection_down,
+ NULL, &cmlcp->yield_state,
+ ERTS_MON_LNK_FIRE_LIMIT);
+ if (yield)
+ break;
+
+ ASSERT(!cmlcp->yield_state);
+ cmlcp->state = ERTS_CML_CLEANUP_STATE_MONITORS;
+ case ERTS_CML_CLEANUP_STATE_MONITORS:
+ yield = erts_monitor_list_foreach_delete_yielding(&dist->monitors,
+ monitor_connection_down,
+ NULL, &cmlcp->yield_state,
+ ERTS_MON_LNK_FIRE_LIMIT);
+ if (yield)
+ break;
+
+ ASSERT(!cmlcp->yield_state);
+ cmlcp->state = ERTS_CML_CLEANUP_STATE_ONAME_MONITORS;
+ case ERTS_CML_CLEANUP_STATE_ONAME_MONITORS:
+ yield = erts_monitor_tree_foreach_delete_yielding(&dist->orig_name_monitors,
+ monitor_connection_down,
+ NULL, &cmlcp->yield_state,
+ ERTS_MON_LNK_FIRE_LIMIT/2);
+ if (yield)
+ break;
+
+ cmlcp->dist = NULL;
+ erts_mon_link_dist_dec_refc(dist);
+
+ ASSERT(!cmlcp->yield_state);
+ cmlcp->state = ERTS_CML_CLEANUP_STATE_NODE_MONITORS;
+ case ERTS_CML_CLEANUP_STATE_NODE_MONITORS:
+ if (cmlcp->trigger_node_monitors) {
+ send_nodes_mon_msgs(NULL,
+ am_nodedown,
+ cmlcp->nodename,
+ cmlcp->visability,
+ cmlcp->reason);
+ }
+ erts_cleanup_offheap(&cmlcp->oh);
+ erts_free(ERTS_ALC_T_CML_CLEANUP, vcmlcp);
+ return; /* done */
+ }
+
+ /* yield... */
+
+ esdp = erts_get_scheduler_data();
+ ASSERT(esdp && esdp->type == ERTS_SCHED_NORMAL);
+ erts_schedule_misc_aux_work((int) esdp->no,
+ con_monitor_link_cleanup,
+ (void *) cmlcp);
+}
+
+static void
+schedule_con_monitor_link_cleanup(ErtsMonLnkDist *dist,
+ Eterm nodename,
+ Eterm visability,
+ Eterm reason)
+{
+ if (dist || is_value(nodename)) {
+ ErtsSchedulerData *esdp;
+ ErtsConMonLnkCleanup *cmlcp;
+ Uint rsz, size;
+
+ size = sizeof(ErtsConMonLnkCleanup);
+
+ if (is_non_value(reason) || is_immed(reason)) {
+ rsz = 0;
+ size -= sizeof(Eterm);
+ }
+ else {
+ rsz = size_object(reason);
+ size += sizeof(Eterm) * (rsz - 1);
+ }
+
+ cmlcp = erts_alloc(ERTS_ALC_T_CML_CLEANUP, size);
+
+ ERTS_INIT_OFF_HEAP(&cmlcp->oh);
+
+ cmlcp->yield_state = NULL;
+ cmlcp->dist = dist;
+ if (!dist)
+ cmlcp->state = ERTS_CML_CLEANUP_STATE_NODE_MONITORS;
+ else {
+ cmlcp->state = ERTS_CML_CLEANUP_STATE_LINKS;
+ erts_mtx_lock(&dist->mtx);
+ ASSERT(dist->alive);
+ dist->alive = 0;
+ erts_mtx_unlock(&dist->mtx);
+ }
+
+ cmlcp->trigger_node_monitors = is_value(nodename);
+ cmlcp->nodename = nodename;
+ cmlcp->visability = visability;
+ if (rsz == 0)
+ cmlcp->reason = reason;
+ else {
+ Eterm *hp = &cmlcp->heap[0];
+ cmlcp->reason = copy_struct(reason, rsz, &hp, &cmlcp->oh);
+ }
+
+ esdp = erts_get_scheduler_data();
+ ASSERT(esdp && esdp->type == ERTS_SCHED_NORMAL);
+ erts_schedule_misc_aux_work((int) esdp->no,
+ con_monitor_link_cleanup,
+ (void *) cmlcp);
+ }
+}
+
/*
** A full node name constists of a "n@h"
**
@@ -243,168 +390,6 @@ int is_node_name_atom(Eterm a)
return is_node_name((char*)atom_tab(i)->name, atom_tab(i)->len);
}
-typedef struct {
- DistEntry *dep;
- Eterm *lhp;
-} NetExitsContext;
-
-/*
-** This function is called when a distribution
-** port or process terminates
-*/
-static void doit_monitor_net_exits(ErtsMonitor *mon, void *vnecp)
-{
- Process *rp;
- ErtsMonitor *rmon;
- DistEntry *dep = ((NetExitsContext *) vnecp)->dep;
- ErtsProcLocks rp_locks = ERTS_PROC_LOCK_LINK;
-
- rp = erts_pid2proc(NULL, 0, mon->u.pid, rp_locks);
- if (!rp)
- goto done;
-
- if (mon->type == MON_ORIGIN) {
- /* local pid is being monitored */
- rmon = erts_remove_monitor(&ERTS_P_MONITORS(rp), mon->ref);
- /* ASSERT(rmon != NULL); nope, can happen during process exit */
- if (rmon != NULL) {
- erts_destroy_monitor(rmon);
- }
- } else {
- DeclareTmpHeapNoproc(lhp,3);
- Eterm watched;
- UseTmpHeapNoproc(3);
- ASSERT(mon->type == MON_TARGET);
- rmon = erts_remove_monitor(&ERTS_P_MONITORS(rp), mon->ref);
- /* ASSERT(rmon != NULL); can happen during process exit */
- if (rmon != NULL) {
- ASSERT(rmon->type == MON_ORIGIN);
- ASSERT(is_atom(rmon->name) || is_nil(rmon->name));
- watched = (is_atom(rmon->name)
- ? TUPLE2(lhp, rmon->name, dep->sysname)
- : rmon->u.pid);
- rp_locks |= ERTS_PROC_LOCKS_MSG_SEND;
- erts_proc_lock(rp, ERTS_PROC_LOCKS_MSG_SEND);
- erts_queue_monitor_message(rp, &rp_locks, mon->ref, am_process,
- watched, am_noconnection);
- erts_destroy_monitor(rmon);
- }
- UnUseTmpHeapNoproc(3);
- }
- erts_proc_unlock(rp, rp_locks);
- done:
- erts_destroy_monitor(mon);
-}
-
-typedef struct {
- NetExitsContext *necp;
- ErtsLink *lnk;
-} LinkNetExitsContext;
-
-/*
-** This is the function actually doing the job of sending exit messages
-** for links in a dist entry upon net_exit (the node goes down), NB,
-** only process links, not node monitors are handled here,
-** they reside in a separate tree....
-*/
-static void doit_link_net_exits_sub(ErtsLink *sublnk, void *vlnecp)
-{
- ErtsLink *lnk = ((LinkNetExitsContext *) vlnecp)->lnk; /* the local pid */
- ErtsLink *rlnk;
- Process *rp;
-
- ASSERT(lnk->type == LINK_PID);
- if (is_internal_pid(lnk->pid)) {
- int xres;
- ErtsProcLocks rp_locks = ERTS_PROC_LOCK_LINK|ERTS_PROC_LOCKS_XSIG_SEND;
-
- rp = erts_pid2proc(NULL, 0, lnk->pid, rp_locks);
- if (!rp) {
- goto done;
- }
-
- rlnk = erts_remove_link(&ERTS_P_LINKS(rp), sublnk->pid);
- xres = erts_send_exit_signal(NULL,
- sublnk->pid,
- rp,
- &rp_locks,
- am_noconnection,
- NIL,
- NULL,
- 0);
-
- if (rlnk) {
- erts_destroy_link(rlnk);
- if (xres >= 0 && IS_TRACED_FL(rp, F_TRACE_PROCS)) {
- /* We didn't exit the process and it is traced */
- trace_proc(NULL, 0, rp, am_getting_unlinked, sublnk->pid);
- }
- }
- erts_proc_unlock(rp, rp_locks);
- }
- done:
- erts_destroy_link(sublnk);
-
-}
-
-
-
-
-
-/*
-** This function is called when a distribution
-** port or process terminates, once for each link on the high level,
-** it in turn traverses the link subtree for the specific link node...
-*/
-static void doit_link_net_exits(ErtsLink *lnk, void *vnecp)
-{
- LinkNetExitsContext lnec = {(NetExitsContext *) vnecp, lnk};
- ASSERT(lnk->type == LINK_PID);
- erts_sweep_links(ERTS_LINK_ROOT(lnk), &doit_link_net_exits_sub, (void *) &lnec);
-#ifdef DEBUG
- ERTS_LINK_ROOT(lnk) = NULL;
-#endif
- erts_destroy_link(lnk);
-}
-
-
-static void doit_node_link_net_exits(ErtsLink *lnk, void *vnecp)
-{
- DistEntry *dep = ((NetExitsContext *) vnecp)->dep;
- Eterm name = dep->sysname;
- Process *rp;
- ErtsLink *rlnk;
- Uint i,n;
- ASSERT(lnk->type == LINK_NODE);
- if (is_internal_pid(lnk->pid)) {
- ErtsProcLocks rp_locks = ERTS_PROC_LOCK_LINK;
- ErlOffHeap *ohp;
- rp = erts_proc_lookup(lnk->pid);
- if (!rp)
- goto done;
- erts_proc_lock(rp, rp_locks);
- rlnk = erts_remove_link(&ERTS_P_LINKS(rp), name);
- if (rlnk != NULL) {
- ASSERT(is_atom(rlnk->pid) && (rlnk->type == LINK_NODE));
- erts_destroy_link(rlnk);
- }
- n = ERTS_LINK_REFC(lnk);
- for (i = 0; i < n; ++i) {
- Eterm tup;
- Eterm *hp;
- ErtsMessage *msgp;
-
- msgp = erts_alloc_message_heap(rp, &rp_locks,
- 3, &hp, &ohp);
- tup = TUPLE2(hp, am_nodedown, name);
- erts_queue_message(rp, rp_locks, msgp, tup, am_system);
- }
- erts_proc_unlock(rp, rp_locks);
- }
- done:
- erts_destroy_link(lnk);
-}
-
static void
set_node_not_alive(void *unused)
{
@@ -450,15 +435,8 @@ inc_no_nodes(void)
static void
kill_dist_ctrl_proc(void *vpid)
{
- Eterm pid = (Eterm) vpid;
- ErtsProcLocks rp_locks = ERTS_PROC_LOCKS_XSIG_SEND;
- Process *rp = erts_pid2proc(NULL, 0, pid, rp_locks);
- if (rp) {
- erts_send_exit_signal(NULL, rp->common.id, rp, &rp_locks,
- am_kill, NIL, NULL, 0);
- if (rp_locks)
- erts_proc_unlock(rp, rp_locks);
- }
+ erts_proc_sig_send_exit(NULL, (Eterm) vpid, (Eterm) vpid,
+ am_kill, NIL, 0);
}
static void
@@ -484,41 +462,55 @@ int erts_do_net_exits(DistEntry *dep, Eterm reason)
if (dep == erts_this_dist_entry) { /* Net kernel has died (clean up!!) */
DistEntry *tdep;
- int no_dist_ctrl = 0;
+ int no_dist_ctrl;
+ int no_pending;
Eterm nd_reason = (reason == am_no_network
? am_no_network
: am_net_kernel_terminated);
+ int i = 0;
+ Eterm *dist_ctrl;
+ DistEntry** pending;
+
+ ERTS_UNDEF(dist_ctrl, NULL);
+ ERTS_UNDEF(pending, NULL);
+
erts_rwmtx_rlock(&erts_dist_table_rwmtx);
- for (tdep = erts_hidden_dist_entries; tdep; tdep = tdep->next)
- no_dist_ctrl++;
- for (tdep = erts_visible_dist_entries; tdep; tdep = tdep->next)
- no_dist_ctrl++;
+ no_dist_ctrl = (erts_no_of_hidden_dist_entries +
+ erts_no_of_visible_dist_entries);
+ no_pending = erts_no_of_pending_dist_entries;
/* KILL all port controllers */
- if (no_dist_ctrl == 0)
- erts_rwmtx_runlock(&erts_dist_table_rwmtx);
- else {
- Eterm def_buf[128];
- int i = 0;
- Eterm *dist_ctrl;
-
- if (no_dist_ctrl <= sizeof(def_buf)/sizeof(def_buf[0]))
- dist_ctrl = &def_buf[0];
- else
- dist_ctrl = erts_alloc(ERTS_ALC_T_TMP,
- sizeof(Eterm)*no_dist_ctrl);
+ if (no_dist_ctrl) {
+ dist_ctrl = erts_alloc(ERTS_ALC_T_TMP,
+ sizeof(Eterm)*no_dist_ctrl);
for (tdep = erts_hidden_dist_entries; tdep; tdep = tdep->next) {
ASSERT(is_internal_port(tdep->cid) || is_internal_pid(tdep->cid));
+ ASSERT(i < no_dist_ctrl);
dist_ctrl[i++] = tdep->cid;
}
for (tdep = erts_visible_dist_entries; tdep; tdep = tdep->next) {
ASSERT(is_internal_port(tdep->cid) || is_internal_pid(tdep->cid));
+ ASSERT(i < no_dist_ctrl);
dist_ctrl[i++] = tdep->cid;
}
- erts_rwmtx_runlock(&erts_dist_table_rwmtx);
+ ASSERT(i == no_dist_ctrl);
+ }
+ if (no_pending) {
+ pending = erts_alloc(ERTS_ALC_T_TMP, sizeof(DistEntry*)*no_pending);
+ i = 0;
+ for (tdep = erts_pending_dist_entries; tdep; tdep = tdep->next) {
+ ASSERT(is_nil(tdep->cid));
+ ASSERT(i < no_pending);
+ pending[i++] = tdep;
+ erts_ref_dist_entry(tdep);
+ }
+ ASSERT(i == no_pending);
+ }
+ erts_rwmtx_runlock(&erts_dist_table_rwmtx);
- for (i = 0; i < no_dist_ctrl; i++) {
+ if (no_dist_ctrl) {
+ for (i = 0; i < no_dist_ctrl; i++) {
if (is_internal_pid(dist_ctrl[i]))
schedule_kill_dist_ctrl_proc(dist_ctrl[i]);
else {
@@ -532,11 +524,18 @@ int erts_do_net_exits(DistEntry *dep, Eterm reason)
prt, dist_ctrl[i], nd_reason, NULL);
}
}
- }
+ }
+ erts_free(ERTS_ALC_T_TMP, dist_ctrl);
+ }
+
+ if (no_pending) {
+ for (i = 0; i < no_pending; i++) {
+ abort_connection(pending[i], pending[i]->connection_id);
+ erts_deref_dist_entry(pending[i]);
+ }
+ erts_free(ERTS_ALC_T_TMP, pending);
+ }
- if (dist_ctrl != &def_buf[0])
- erts_free(ERTS_ALC_T_TMP, dist_ctrl);
- }
/*
* When last dist ctrl exits, node will be taken
@@ -557,10 +556,7 @@ int erts_do_net_exits(DistEntry *dep, Eterm reason)
}
}
else { /* Call from distribution controller (port/process) */
- NetExitsContext nec = {dep};
- ErtsLink *nlinks;
- ErtsLink *node_links;
- ErtsMonitor *monitors;
+ ErtsMonLnkDist *mld;
Uint32 flags;
erts_atomic_set_mb(&dep->dist_cmd_scheduled, 1);
@@ -573,27 +569,21 @@ int erts_do_net_exits(DistEntry *dep, Eterm reason)
erts_port_task_abort(&dep->dist_cmd);
}
- if (dep->status & ERTS_DE_SFLG_EXITING) {
+ if (dep->state == ERTS_DE_STATE_EXITING) {
#ifdef DEBUG
ASSERT(erts_atomic32_read_nob(&dep->qflgs) & ERTS_DE_QFLG_EXIT);
#endif
}
else {
- dep->status |= ERTS_DE_SFLG_EXITING;
+ dep->state = ERTS_DE_STATE_EXITING;
erts_mtx_lock(&dep->qlock);
ASSERT(!(erts_atomic32_read_nob(&dep->qflgs) & ERTS_DE_QFLG_EXIT));
erts_atomic32_read_bor_relb(&dep->qflgs, ERTS_DE_QFLG_EXIT);
erts_mtx_unlock(&dep->qlock);
}
- erts_de_links_lock(dep);
- monitors = dep->monitors;
- nlinks = dep->nlinks;
- node_links = dep->node_links;
- dep->monitors = NULL;
- dep->nlinks = NULL;
- dep->node_links = NULL;
- erts_de_links_unlock(dep);
+ mld = dep->mld;
+ dep->mld = NULL;
nodename = dep->sysname;
flags = dep->flags;
@@ -602,18 +592,16 @@ int erts_do_net_exits(DistEntry *dep, Eterm reason)
erts_de_rwunlock(dep);
- erts_sweep_monitors(monitors, &doit_monitor_net_exits, (void *) &nec);
- erts_sweep_links(nlinks, &doit_link_net_exits, (void *) &nec);
- erts_sweep_links(node_links, &doit_node_link_net_exits, (void *) &nec);
-
- send_nodes_mon_msgs(NULL,
- am_nodedown,
- nodename,
- flags & DFLAG_PUBLISHED ? am_visible : am_hidden,
- reason == am_normal ? am_connection_closed : reason);
+ schedule_con_monitor_link_cleanup(mld,
+ nodename,
+ (flags & DFLAG_PUBLISHED
+ ? am_visible
+ : am_hidden),
+ (reason == am_normal
+ ? am_connection_closed
+ : reason));
clear_dist_entry(dep);
-
}
dec_no_nodes();
@@ -627,6 +615,14 @@ trap_function(Eterm func, int arity)
return erts_export_put(am_erlang, func, arity);
}
+/*
+ * Sync with dist_util.erl:
+ *
+ * -record(erts_dflags,
+ * {default, mandatory, addable, rejectable, strict_order}).
+ */
+static Eterm erts_dflags_record;
+
void init_dist(void)
{
init_nodes_monitors();
@@ -638,18 +634,20 @@ void init_dist(void)
erts_atomic_init_nob(&no_caches, 0);
/* Lookup/Install all references to trap functions */
- dsend2_trap = trap_function(am_dsend,2);
- dsend3_trap = trap_function(am_dsend,3);
- /* dsend_nosuspend_trap = trap_function(am_dsend_nosuspend,2);*/
- dlink_trap = trap_function(am_dlink,1);
- dunlink_trap = trap_function(am_dunlink,1);
dmonitor_node_trap = trap_function(am_dmonitor_node,3);
- dgroup_leader_trap = trap_function(am_dgroup_leader,2);
- dexit_trap = trap_function(am_dexit, 2);
- dmonitor_p_trap = trap_function(am_dmonitor_p, 2);
dist_ctrl_put_data_trap = erts_export_put(am_erts_internal,
am_dist_ctrl_put_data,
2);
+ {
+ Eterm* hp = erts_alloc(ERTS_ALC_T_LITERAL, (1+6)*sizeof(Eterm));
+ erts_dflags_record = TUPLE6(hp, am_erts_dflags,
+ make_small(DFLAG_DIST_DEFAULT),
+ make_small(DFLAG_DIST_MANDATORY),
+ make_small(DFLAG_DIST_ADDABLE),
+ make_small(DFLAG_DIST_REJECTABLE),
+ make_small(DFLAG_DIST_STRICT_ORDER));
+ erts_set_literal_tag(&erts_dflags_record, hp, (1+6));
+ }
}
#define ErtsDistOutputBuf2Binary(OB) \
@@ -664,6 +662,7 @@ alloc_dist_obuf(Uint size)
obuf = (ErtsDistOutputBuf *) &bin->orig_bytes[0];
#ifdef DEBUG
obuf->dbg_pattern = ERTS_DIST_OUTPUT_BUF_DBG_PATTERN;
+ obuf->alloc_endp = obuf->data + size;
ASSERT(bin == ErtsDistOutputBuf2Binary(obuf));
#endif
return obuf;
@@ -684,31 +683,11 @@ size_obuf(ErtsDistOutputBuf *obuf)
return bin->orig_size;
}
-static void clear_dist_entry(DistEntry *dep)
+static ErtsDistOutputBuf* clear_de_out_queues(DistEntry* dep)
{
- Sint obufsize = 0;
- ErtsAtomCache *cache;
- ErtsProcList *suspendees;
ErtsDistOutputBuf *obuf;
- erts_de_rwlock(dep);
- erts_atomic_set_nob(&dep->input_handler,
- (erts_aint_t) NIL);
- cache = dep->cache;
- dep->cache = NULL;
-
-#ifdef DEBUG
- erts_de_links_lock(dep);
- ASSERT(!dep->nlinks);
- ASSERT(!dep->node_links);
- ASSERT(!dep->monitors);
- erts_de_links_unlock(dep);
-#endif
-
- erts_mtx_lock(&dep->qlock);
-
- erts_atomic64_set_nob(&dep->in, 0);
- erts_atomic64_set_nob(&dep->out, 0);
+ ERTS_LC_ASSERT(erts_lc_mtx_is_locked(&dep->qlock));
if (!dep->out_queue.last)
obuf = dep->finalized_out_queue.first;
@@ -728,17 +707,13 @@ static void clear_dist_entry(DistEntry *dep)
dep->tmp_out_queue.last = NULL;
dep->finalized_out_queue.first = NULL;
dep->finalized_out_queue.last = NULL;
- dep->status = 0;
- suspendees = get_suspended_on_de(dep, ERTS_DE_QFLGS_ALL);
-
- erts_mtx_unlock(&dep->qlock);
- erts_atomic_set_nob(&dep->dist_cmd_scheduled, 0);
- dep->send = NULL;
- erts_de_rwunlock(dep);
- erts_resume_processes(suspendees);
+ return obuf;
+}
- delete_cache(cache);
+static void free_de_out_queues(DistEntry* dep, ErtsDistOutputBuf *obuf)
+{
+ Sint obufsize = 0;
while (obuf) {
ErtsDistOutputBuf *fobuf;
@@ -750,13 +725,48 @@ static void clear_dist_entry(DistEntry *dep)
if (obufsize) {
erts_mtx_lock(&dep->qlock);
- ASSERT(erts_atomic_read_nob(&dep->qsize) >= obufsize);
+ ASSERT(erts_atomic_read_nob(&dep->qsize) >= obufsize);
erts_atomic_add_nob(&dep->qsize,
(erts_aint_t) -obufsize);
erts_mtx_unlock(&dep->qlock);
}
}
+static void clear_dist_entry(DistEntry *dep)
+{
+ ErtsAtomCache *cache;
+ ErtsProcList *suspendees;
+ ErtsDistOutputBuf *obuf;
+
+ erts_de_rwlock(dep);
+ erts_atomic_set_nob(&dep->input_handler,
+ (erts_aint_t) NIL);
+ cache = dep->cache;
+ dep->cache = NULL;
+
+ erts_mtx_lock(&dep->qlock);
+
+ erts_atomic64_set_nob(&dep->in, 0);
+ erts_atomic64_set_nob(&dep->out, 0);
+
+ obuf = clear_de_out_queues(dep);
+ dep->state = ERTS_DE_STATE_IDLE;
+ suspendees = get_suspended_on_de(dep, ERTS_DE_QFLGS_ALL);
+
+ erts_mtx_unlock(&dep->qlock);
+ erts_atomic_set_nob(&dep->dist_cmd_scheduled, 0);
+ dep->send = NULL;
+ erts_de_rwunlock(dep);
+
+ erts_resume_processes(suspendees);
+
+ delete_cache(cache);
+
+ free_de_out_queues(dep, obuf);
+ if (dep->transcode_ctx)
+ transcode_free_ctx(dep);
+}
+
int erts_dsend_context_dtor(Binary* ctx_bin)
{
ErtsSendContext* ctx = ERTS_MAGIC_BIN_DATA(ctx_bin);
@@ -772,8 +782,8 @@ int erts_dsend_context_dtor(Binary* ctx_bin)
if (ctx->dss.phase >= ERTS_DSIG_SEND_PHASE_ALLOC && ctx->dss.obuf) {
free_dist_obuf(ctx->dss.obuf);
}
- if (ctx->dep_to_deref)
- erts_deref_dist_entry(ctx->dep_to_deref);
+ if (ctx->deref_dep)
+ erts_deref_dist_entry(ctx->dep);
return 1;
}
@@ -842,8 +852,7 @@ erts_dsig_send_unlink(ErtsDSigData *dsdp, Eterm local, Eterm remote)
/* A local process that's being monitored by a remote one exits. We send:
- {DOP_MONITOR_P_EXIT, Local pid or name, Remote pid, ref, reason},
- which is rather sad as only the ref is needed, no pid's... */
+ {DOP_MONITOR_P_EXIT, Local pid or name, Remote pid, ref, reason} */
int
erts_dsig_send_m_exit(ErtsDSigData *dsdp, Eterm watcher, Eterm watched,
Eterm ref, Eterm reason)
@@ -852,17 +861,18 @@ erts_dsig_send_m_exit(ErtsDSigData *dsdp, Eterm watcher, Eterm watched,
DeclareTmpHeapNoproc(ctl_heap,6);
int res;
+ if (~dsdp->dep->flags & (DFLAG_DIST_MONITOR | DFLAG_DIST_MONITOR_NAME)) {
+ /*
+ * Receiver does not support DOP_MONITOR_P_EXIT (see dsig_send_monitor)
+ */
+ return ERTS_DSIG_SEND_OK;
+ }
+
UseTmpHeapNoproc(6);
ctl = TUPLE5(&ctl_heap[0], make_small(DOP_MONITOR_P_EXIT),
watched, watcher, ref, reason);
-#ifdef DEBUG
- erts_de_links_lock(dsdp->dep);
- ASSERT(!erts_lookup_monitor(dsdp->dep->monitors, ref));
- erts_de_links_unlock(dsdp->dep);
-#endif
-
res = dsig_send_ctl(dsdp, ctl, 1);
UnUseTmpHeapNoproc(6);
return res;
@@ -879,6 +889,16 @@ erts_dsig_send_monitor(ErtsDSigData *dsdp, Eterm watcher, Eterm watched,
DeclareTmpHeapNoproc(ctl_heap,5);
int res;
+ if (~dsdp->dep->flags & (DFLAG_DIST_MONITOR | DFLAG_DIST_MONITOR_NAME)) {
+ /*
+ * Receiver does not support DOP_MONITOR_P.
+ * Just avoid sending it and by doing that reduce this monitor
+ * to only supervise the connection. This will work for simple c-nodes
+ * with a 1-to-1 relation between "Erlang process" and OS-process.
+ */
+ return ERTS_DSIG_SEND_OK;
+ }
+
UseTmpHeapNoproc(5);
ctl = TUPLE4(&ctl_heap[0],
make_small(DOP_MONITOR_P),
@@ -891,8 +911,7 @@ erts_dsig_send_monitor(ErtsDSigData *dsdp, Eterm watcher, Eterm watched,
/* A local process monitoring a remote one wants to stop monitoring, either
because of a demonitor bif call or because the local process died. We send
- {DOP_DEMONITOR_P, Local pid, Remote pid or name, ref}, which is once again
- rather redundant as only the ref will be needed on the other side... */
+ {DOP_DEMONITOR_P, Local pid, Remote pid or name, ref} */
int
erts_dsig_send_demonitor(ErtsDSigData *dsdp, Eterm watcher,
Eterm watched, Eterm ref, int force)
@@ -901,6 +920,13 @@ erts_dsig_send_demonitor(ErtsDSigData *dsdp, Eterm watcher,
DeclareTmpHeapNoproc(ctl_heap,5);
int res;
+ if (~dsdp->dep->flags & (DFLAG_DIST_MONITOR | DFLAG_DIST_MONITOR_NAME)) {
+ /*
+ * Receiver does not support DOP_DEMONITOR_P (see dsig_send_monitor)
+ */
+ return ERTS_DSIG_SEND_OK;
+ }
+
UseTmpHeapNoproc(5);
ctl = TUPLE4(&ctl_heap[0],
make_small(DOP_DEMONITOR_P),
@@ -911,6 +937,24 @@ erts_dsig_send_demonitor(ErtsDSigData *dsdp, Eterm watcher,
return res;
}
+static int can_send_seqtrace_token(ErtsSendContext* ctx, Eterm token) {
+ Eterm label;
+
+ if (ctx->dep->flags & DFLAG_BIG_SEQTRACE_LABELS) {
+ /* The other end is capable of handling arbitrary seq_trace labels. */
+ return 1;
+ }
+
+ /* The other end only tolerates smalls, but since we could potentially be
+ * talking to an old 32-bit emulator from a 64-bit one, we have to check
+ * whether the label is small on any emulator. */
+ label = SEQ_TRACE_T_LABEL(token);
+
+ return is_small(label) &&
+ signed_val(label) <= (ERTS_SINT32_MAX >> _TAG_IMMED1_SIZE) &&
+ signed_val(label) >= (ERTS_SINT32_MIN >> _TAG_IMMED1_SIZE);
+}
+
int
erts_dsig_send_msg(Eterm remote, Eterm message, ErtsSendContext* ctx)
{
@@ -944,37 +988,38 @@ erts_dsig_send_msg(Eterm remote, Eterm message, ErtsSendContext* ctx)
"%T", remote);
msize = size_object(message);
if (have_seqtrace(token)) {
- tok_label = signed_val(SEQ_TRACE_T_LABEL(token));
+ tok_label = SEQ_TRACE_T_DTRACE_LABEL(token);
tok_lastcnt = signed_val(SEQ_TRACE_T_LASTCNT(token));
tok_serial = signed_val(SEQ_TRACE_T_SERIAL(token));
}
}
#endif
- if (token != NIL) {
- Eterm el1, el2;
- if (ctx->dep->flags & DFLAG_SEND_SENDER) {
- el1 = make_small(DOP_SEND_SENDER_TT);
- el2 = sender->common.id;
- }
- else {
- el1 = make_small(DOP_SEND_TT);
- el2 = am_Empty;
- }
- ctl = TUPLE4(&ctx->ctl_heap[0], el1, el2, remote, token);
- }
- else {
- Eterm el1, el2;
+ {
+ Eterm dist_op, sender_id;
+ int send_token;
+
+ send_token = (token != NIL && can_send_seqtrace_token(ctx, token));
+
if (ctx->dep->flags & DFLAG_SEND_SENDER) {
- el1 = make_small(DOP_SEND_SENDER);
- el2 = sender->common.id;
+ dist_op = make_small(send_token ?
+ DOP_SEND_SENDER_TT :
+ DOP_SEND_SENDER);
+ sender_id = sender->common.id;
+ } else {
+ dist_op = make_small(send_token ?
+ DOP_SEND_TT :
+ DOP_SEND);
+ sender_id = am_Empty;
}
- else {
- el1 = make_small(DOP_SEND);
- el2 = am_Empty;
+
+ if (send_token) {
+ ctl = TUPLE4(&ctx->ctl_heap[0], dist_op, sender_id, remote, token);
+ } else {
+ ctl = TUPLE3(&ctx->ctl_heap[0], dist_op, sender_id, remote);
}
- ctl = TUPLE3(&ctx->ctl_heap[0], el1, el2, remote);
}
+
DTRACE6(message_send, sender_name, receiver_name,
msize, tok_label, tok_lastcnt, tok_serial);
DTRACE7(message_send_remote, sender_name, node_name, receiver_name,
@@ -1020,19 +1065,20 @@ erts_dsig_send_reg_msg(Eterm remote_name, Eterm message,
"{%T,%s}", remote_name, node_name);
msize = size_object(message);
if (have_seqtrace(token)) {
- tok_label = signed_val(SEQ_TRACE_T_LABEL(token));
+ tok_label = SEQ_TRACE_T_DTRACE_LABEL(token);
tok_lastcnt = signed_val(SEQ_TRACE_T_LASTCNT(token));
tok_serial = signed_val(SEQ_TRACE_T_SERIAL(token));
}
}
#endif
- if (token != NIL)
+ if (token != NIL && can_send_seqtrace_token(ctx, token))
ctl = TUPLE5(&ctx->ctl_heap[0], make_small(DOP_REG_SEND_TT),
sender->common.id, am_Empty, remote_name, token);
else
ctl = TUPLE4(&ctx->ctl_heap[0], make_small(DOP_REG_SEND),
sender->common.id, am_Empty, remote_name);
+
DTRACE6(message_send, sender_name, receiver_name,
msize, tok_label, tok_lastcnt, tok_serial);
DTRACE7(message_send_remote, sender_name, node_name, receiver_name,
@@ -1084,7 +1130,7 @@ erts_dsig_send_exit_tt(ErtsDSigData *dsdp, Eterm local, Eterm remote,
erts_snprintf(reason_str, sizeof(DTRACE_CHARBUF_NAME(reason_str)),
"%T", reason);
if (have_seqtrace(token)) {
- tok_label = signed_val(SEQ_TRACE_T_LABEL(token));
+ tok_label = SEQ_TRACE_T_DTRACE_LABEL(token);
tok_lastcnt = signed_val(SEQ_TRACE_T_LASTCNT(token));
tok_serial = signed_val(SEQ_TRACE_T_SERIAL(token));
}
@@ -1161,7 +1207,7 @@ erts_dsig_send_group_leader(ErtsDSigData *dsdp, Eterm leader, Eterm remote)
# define PURIFY_MSG(msg) \
do { \
char buf__[1]; size_t bufsz__ = sizeof(buf__); \
- if (erts_sys_getenv_raw("VALGRIND_LOG_XML", buf__, &bufsz__) >= 0) { \
+ if (erts_sys_explicit_8bit_getenv("VALGRIND_LOG_XML", buf__, &bufsz__) >= 0) { \
VALGRIND_PRINTF_XML("<erlang_error_log>" \
"%s, line %d: %s</erlang_error_log>\n", \
__FILE__, __LINE__, msg); \
@@ -1207,8 +1253,6 @@ int erts_net_message(Port *prt,
Sint type;
Eterm token;
Eterm token_size;
- ErtsMonitor *mon;
- ErtsLink *lnk;
Uint tuple_arity;
int res;
Uint32 connection_id;
@@ -1306,88 +1350,89 @@ int erts_net_message(Port *prt,
token = NIL;
switch (type = unsigned_val(tuple[1])) {
- case DOP_LINK:
+ case DOP_LINK: {
+ ErtsDSigData dsd;
+ int code;
+
if (tuple_arity != 3) {
goto invalid_message;
}
from = tuple[2];
to = tuple[3]; /* local proc to link to */
- if (is_not_pid(from) || is_not_pid(to)) {
- goto invalid_message;
- }
+ if (is_not_external_pid(from))
+ goto invalid_message;
- rp = erts_pid2proc_opt(NULL, 0,
- to, ERTS_PROC_LOCK_LINK,
- ERTS_P2P_FLG_ALLOW_OTHER_X);
- if (!rp) {
- /* This is tricky (we MUST force a distributed send) */
- ErtsDSigData dsd;
- int code;
- code = erts_dsig_prepare(&dsd, dep, NULL, ERTS_DSP_NO_LOCK, 0);
- if (code == ERTS_DSIG_PREP_CONNECTED) {
- code = erts_dsig_send_exit(&dsd, to, from, am_noproc);
- ASSERT(code == ERTS_DSIG_SEND_OK);
- }
- break;
- }
+ if (dep != external_pid_dist_entry(from))
+ goto invalid_message;
- erts_de_links_lock(dep);
- res = erts_add_link(&ERTS_P_LINKS(rp), LINK_PID, from);
+ if (is_external_pid(to)) {
+ if (external_pid_dist_entry(to) != erts_this_dist_entry)
+ goto invalid_message;
+ /* old incarnation of node; reply noproc... */
+ }
+ else if (is_internal_pid(to)) {
+ ErtsLinkData *ldp = erts_link_create(ERTS_LNK_TYPE_DIST_PROC,
+ from, to);
+ ASSERT(ldp->a.other.item == to);
+ ASSERT(eq(ldp->b.other.item, from));
+#ifdef DEBUG
+ code =
+#endif
+ erts_link_dist_insert(&ldp->a, dep->mld);
+ ASSERT(code);
- if (res < 0) {
- /* It was already there! Lets skip the rest... */
- erts_de_links_unlock(dep);
- erts_proc_unlock(rp, ERTS_PROC_LOCK_LINK);
- break;
- }
- lnk = erts_add_or_lookup_link(&(dep->nlinks), LINK_PID, rp->common.id);
- erts_add_link(&(ERTS_LINK_ROOT(lnk)), LINK_PID, from);
- erts_de_links_unlock(dep);
+ if (erts_proc_sig_send_link(NULL, to, &ldp->b))
+ break; /* done */
- if (IS_TRACED_FL(rp, F_TRACE_PROCS))
- trace_proc(NULL, 0, rp, am_getting_linked, from);
+ /* Failed to send signal; cleanup and reply noproc... */
+
+#ifdef DEBUG
+ code =
+#endif
+ erts_link_dist_delete(&ldp->a);
+ ASSERT(code);
+ erts_link_release_both(ldp);
+ }
+
+ code = erts_dsig_prepare(&dsd, dep, NULL, 0, ERTS_DSP_NO_LOCK, 0, 0);
+ if (code == ERTS_DSIG_PREP_CONNECTED) {
+ code = erts_dsig_send_exit(&dsd, to, from, am_noproc);
+ ASSERT(code == ERTS_DSIG_SEND_OK);
+ }
- erts_proc_unlock(rp, ERTS_PROC_LOCK_LINK);
break;
+ }
case DOP_UNLINK: {
- ErtsDistLinkData dld;
if (tuple_arity != 3) {
goto invalid_message;
}
from = tuple[2];
to = tuple[3];
- if (is_not_pid(from) || is_not_pid(to)) {
+ if (is_not_external_pid(from))
+ goto invalid_message;
+ if (dep != external_pid_dist_entry(from))
goto invalid_message;
- }
-
- rp = erts_pid2proc_opt(NULL, 0,
- to, ERTS_PROC_LOCK_LINK,
- ERTS_P2P_FLG_ALLOW_OTHER_X);
- if (!rp)
- break;
-
- lnk = erts_remove_link(&ERTS_P_LINKS(rp), from);
- if (IS_TRACED_FL(rp, F_TRACE_PROCS) && lnk != NULL) {
- trace_proc(NULL, 0, rp, am_getting_unlinked, from);
- }
+ if (is_external_pid(to)
+ && erts_this_dist_entry == external_pid_dist_entry(from))
+ break;
- erts_proc_unlock(rp, ERTS_PROC_LOCK_LINK);
+ if (is_not_internal_pid(to))
+ goto invalid_message;
- erts_remove_dist_link(&dld, to, from, dep);
- erts_destroy_dist_link(&dld);
- if (lnk)
- erts_destroy_link(lnk);
+ erts_proc_sig_send_dist_unlink(dep, from, to);
break;
}
case DOP_MONITOR_P: {
/* A remote process wants to monitor us, we get:
{DOP_MONITOR_P, Remote pid, local pid or name, ref} */
- Eterm name;
-
+ Eterm pid, name;
+ ErtsDSigData dsd;
+ int code;
+
if (tuple_arity != 4) {
goto invalid_message;
}
@@ -1396,44 +1441,64 @@ int erts_net_message(Port *prt,
watched = tuple[3]; /* local proc to monitor */
ref = tuple[4];
- if (is_not_ref(ref)) {
+ if (is_not_external_pid(watcher))
+ goto invalid_message;
+ else if (external_pid_dist_entry(watcher) != dep)
+ goto invalid_message;
+
+ if (is_not_ref(ref))
goto invalid_message;
- }
- if (is_atom(watched)) {
- name = watched;
- rp = erts_whereis_process(NULL, 0,
- watched, ERTS_PROC_LOCK_LINK,
- ERTS_P2P_FLG_ALLOW_OTHER_X);
- }
- else {
- name = NIL;
- rp = erts_pid2proc_opt(NULL, 0,
- watched, ERTS_PROC_LOCK_LINK,
- ERTS_P2P_FLG_ALLOW_OTHER_X);
- }
+ if (is_internal_pid(watched)) {
+ name = NIL;
+ pid = watched;
+ }
+ else if (is_atom(watched)) {
+ name = watched;
+ pid = erts_whereis_name_to_id(NULL, watched);
+ /* if port or undefined; reply noproc... */
+ }
+ else if (is_external_pid(watched)
+ && external_pid_dist_entry(watched) == erts_this_dist_entry) {
+ name = NIL;
+ pid = am_undefined; /* old incarnation; reply noproc... */
+ }
+ else
+ goto invalid_message;
- if (!rp) {
- ErtsDSigData dsd;
- int code;
- code = erts_dsig_prepare(&dsd, dep, NULL, ERTS_DSP_NO_LOCK, 0);
- if (code == ERTS_DSIG_PREP_CONNECTED) {
- code = erts_dsig_send_m_exit(&dsd, watcher, watched, ref,
- am_noproc);
- ASSERT(code == ERTS_DSIG_SEND_OK);
- }
- }
- else {
- if (is_atom(watched))
- watched = rp->common.id;
- erts_de_links_lock(dep);
- erts_add_monitor(&(dep->monitors), MON_ORIGIN, ref, watched, name);
- erts_add_monitor(&ERTS_P_MONITORS(rp), MON_TARGET, ref, watcher, name);
- erts_de_links_unlock(dep);
- erts_proc_unlock(rp, ERTS_PROC_LOCK_LINK);
- }
+ if (is_internal_pid(pid)) {
+ ErtsMonitorData *mdp;
+ mdp = erts_monitor_create(ERTS_MON_TYPE_DIST_PROC,
+ ref, watcher, pid, name);
- break;
+#ifdef DEBUG
+ code =
+#endif
+ erts_monitor_dist_insert(&mdp->origin, dep->mld);
+ ASSERT(code);
+
+ if (erts_proc_sig_send_monitor(&mdp->target, pid))
+ break; /* done */
+
+ /* Failed to send to local proc; cleanup reply noproc... */
+
+#ifdef DEBUG
+ code =
+#endif
+ erts_monitor_dist_delete(&mdp->origin);
+ ASSERT(code);
+ erts_monitor_release_both(mdp);
+
+ }
+
+ code = erts_dsig_prepare(&dsd, dep, NULL, 0, ERTS_DSP_NO_LOCK, 0, 0);
+ if (code == ERTS_DSIG_PREP_CONNECTED) {
+ code = erts_dsig_send_m_exit(&dsd, watcher, watched, ref,
+ am_noproc);
+ ASSERT(code == ERTS_DSIG_SEND_OK);
+ }
+
+ break;
}
case DOP_DEMONITOR_P:
@@ -1444,36 +1509,43 @@ int erts_net_message(Port *prt,
if (tuple_arity != 4) {
goto invalid_message;
}
- /* watcher = tuple[2]; */
- /* watched = tuple[3]; May be an atom in case of monitor name */
+
+ watcher = tuple[2];
+ watched = tuple[3];
ref = tuple[4];
- if(is_not_ref(ref)) {
+ if (is_not_ref(ref)) {
goto invalid_message;
}
- erts_de_links_lock(dep);
- mon = erts_remove_monitor(&(dep->monitors),ref);
- erts_de_links_unlock(dep);
- /* ASSERT(mon != NULL); can happen in case of broken dist message */
- if (mon == NULL) {
- break;
- }
- watched = mon->u.pid;
- erts_destroy_monitor(mon);
- rp = erts_pid2proc_opt(NULL, 0,
- watched, ERTS_PROC_LOCK_LINK,
- ERTS_P2P_FLG_ALLOW_OTHER_X);
- if (!rp) {
- break;
- }
- mon = erts_remove_monitor(&ERTS_P_MONITORS(rp), ref);
- erts_proc_unlock(rp, ERTS_PROC_LOCK_LINK);
- ASSERT(mon != NULL);
- if (mon == NULL) {
- break;
- }
- erts_destroy_monitor(mon);
+ if (is_not_external_pid(watcher) || external_pid_dist_entry(watcher) != dep)
+ goto invalid_message;
+
+ if (is_internal_pid(watched))
+ erts_proc_sig_send_dist_demonitor(watched, ref);
+ else if (is_external_pid(watched)
+ && external_pid_dist_entry(watched) == erts_this_dist_entry) {
+ /* old incarnation; ignore it */
+ ;
+ }
+ else if (is_atom(watched)) {
+ ErtsMonLnkDist *mld = dep->mld;
+ ErtsMonitor *mon;
+
+ erts_mtx_lock(&mld->mtx);
+
+ mon = erts_monitor_tree_lookup(mld->orig_name_monitors, ref);
+ if (mon)
+ erts_monitor_tree_delete(&mld->orig_name_monitors, mon);
+
+ erts_mtx_unlock(&mld->mtx);
+
+ if (mon)
+ erts_proc_sig_send_demonitor(mon);
+ }
+ else
+ goto invalid_message;
+
break;
case DOP_REG_SEND_TT:
@@ -1600,68 +1672,36 @@ int erts_net_message(Port *prt,
/* We are monitoring a process on the remote node which dies, we get
{DOP_MONITOR_P_EXIT, Remote pid or name, Local pid, ref, reason} */
-
- DeclareTmpHeapNoproc(lhp,3);
- Eterm sysname;
- ErtsProcLocks rp_locks = ERTS_PROC_LOCKS_MSG_SEND|ERTS_PROC_LOCK_LINK;
-
if (tuple_arity != 5) {
goto invalid_message;
}
- /* watched = tuple[2]; */ /* remote proc which died */
- /* watcher = tuple[3]; */
+ watched = tuple[2]; /* remote proc or name which died */
+ watcher = tuple[3];
ref = tuple[4];
reason = tuple[5];
- if(is_not_ref(ref)) {
+ if (is_not_ref(ref))
goto invalid_message;
- }
- erts_de_links_lock(dep);
- sysname = dep->sysname;
- mon = erts_remove_monitor(&(dep->monitors), ref);
- /*
- * If demonitor was performed at the same time as the
- * monitored process exits, monitoring side will have
- * removed info about monitor. In this case, do nothing
- * and everything will be as it should.
- */
- erts_de_links_unlock(dep);
- if (mon == NULL) {
- break;
- }
- rp = erts_pid2proc(NULL, 0, mon->u.pid, rp_locks);
+ if (is_not_external_pid(watched) && is_not_atom(watched))
+ goto invalid_message;
- erts_destroy_monitor(mon);
- if (rp == NULL) {
- break;
- }
-
- mon = erts_remove_monitor(&ERTS_P_MONITORS(rp), ref);
+ if (is_not_internal_pid(watcher)) {
+ if (!is_external_pid(watcher))
+ goto invalid_message;
+ if (erts_this_dist_entry == external_pid_dist_entry(watcher))
+ break;
+ goto invalid_message;
+ }
- if (mon == NULL) {
- erts_proc_unlock(rp, rp_locks);
- break;
- }
- UseTmpHeapNoproc(3);
-
- watched = (is_not_nil(mon->name)
- ? TUPLE2(&lhp[0], mon->name, sysname)
- : mon->u.pid);
-
- erts_queue_monitor_message(rp, &rp_locks,
- ref, am_process, watched, reason);
- erts_proc_unlock(rp, rp_locks);
- erts_destroy_monitor(mon);
- UnUseTmpHeapNoproc(3);
+ erts_proc_sig_send_dist_monitor_down(dep, ref, watched,
+ watcher, reason);
break;
}
case DOP_EXIT_TT:
case DOP_EXIT: {
- ErtsDistLinkData dld;
- ErtsProcLocks rp_locks = ERTS_PROC_LOCK_LINK|ERTS_PROC_LOCKS_XSIG_SEND;
/* 'from', which 'to' is linked to, died */
if (type == DOP_EXIT) {
if (tuple_arity != 4) {
@@ -1681,56 +1721,19 @@ int erts_net_message(Port *prt,
token = tuple[4];
reason = tuple[5];
}
- if (is_not_pid(from) || is_not_internal_pid(to)) {
+ if (is_not_external_pid(from)
+ || dep != external_pid_dist_entry(from)
+ || is_not_internal_pid(to)) {
goto invalid_message;
}
- rp = erts_pid2proc(NULL, 0, to, rp_locks);
- if (!rp)
- lnk = NULL;
- else {
- lnk = erts_remove_link(&ERTS_P_LINKS(rp), from);
-
- /* If lnk == NULL, we have unlinked on this side, i.e.
- * ignore exit.
- */
- if (lnk) {
- int xres;
-#if 0
- /* Arndt: Maybe it should never be 'kill', but it can be,
- namely when a linked process does exit(kill). Until we know
- whether that is incorrect and what should happen instead,
- we leave the assertion out. */
- ASSERT(reason != am_kill); /* should never be kill (killed) */
-#endif
- xres = erts_send_exit_signal(NULL,
- from,
- rp,
- &rp_locks,
- reason,
- token,
- NULL,
- ERTS_XSIG_FLG_IGN_KILL);
- if (xres >= 0 && IS_TRACED_FL(rp, F_TRACE_PROCS)) {
- /* We didn't exit the process and it is traced */
- if (rp_locks & ERTS_PROC_LOCKS_XSIG_SEND) {
- erts_proc_unlock(rp, ERTS_PROC_LOCKS_XSIG_SEND);
- rp_locks &= ~ERTS_PROC_LOCKS_XSIG_SEND;
- }
- trace_proc(NULL, 0, rp, am_getting_unlinked, from);
- }
- }
- erts_proc_unlock(rp, rp_locks);
- }
- erts_remove_dist_link(&dld, to, from, dep);
- if (lnk)
- erts_destroy_link(lnk);
- erts_destroy_dist_link(&dld);
+ erts_proc_sig_send_dist_link_exit(dep,
+ from, to,
+ reason, token);
break;
}
case DOP_EXIT2_TT:
- case DOP_EXIT2: {
- ErtsProcLocks rp_locks = ERTS_PROC_LOCKS_XSIG_SEND;
+ case DOP_EXIT2:
/* 'from' is send an exit signal to 'to' */
if (type == DOP_EXIT2) {
if (tuple_arity != 4) {
@@ -1752,20 +1755,10 @@ int erts_net_message(Port *prt,
if (is_not_pid(from) || is_not_internal_pid(to)) {
goto invalid_message;
}
- rp = erts_pid2proc(NULL, 0, to, rp_locks);
- if (rp) {
- (void) erts_send_exit_signal(NULL,
- from,
- rp,
- &rp_locks,
- reason,
- token,
- NULL,
- 0);
- erts_proc_unlock(rp, rp_locks);
- }
+
+ erts_proc_sig_send_exit(NULL, from, to, reason, token, 0);
break;
- }
+
case DOP_GROUP_LEADER:
if (tuple_arity != 3) {
goto invalid_message;
@@ -1776,11 +1769,7 @@ int erts_net_message(Port *prt,
goto invalid_message;
}
- rp = erts_pid2proc(NULL, 0, to, ERTS_PROC_LOCK_MAIN);
- if (!rp)
- break;
- rp->group_leader = STORE_NC_IN_PROC(rp, from);
- erts_proc_unlock(rp, ERTS_PROC_LOCK_MAIN);
+ (void) erts_proc_sig_send_group_leader(NULL, to, from, NIL);
break;
default:
@@ -1879,33 +1868,32 @@ erts_dsig_send(ErtsDSigData *dsdp, struct erts_dsig_send_context* ctx)
if (ctx->flags & DFLAG_DIST_HDR_ATOM_CACHE) {
ctx->acmp = erts_get_atom_cache_map(ctx->c_p);
- ctx->pass_through_size = 0;
+ ctx->max_finalize_prepend = 0;
}
else {
ctx->acmp = NULL;
- ctx->pass_through_size = 1;
+ ctx->max_finalize_prepend = 3;
}
#ifdef ERTS_DIST_MSG_DBG
- erts_fprintf(stderr, ">>%s CTL: %T\n", ctx->pass_through_size ? "P" : " ", ctx->ctl);
- if (is_value(ctx->msg))
- erts_fprintf(stderr, " MSG: %T\n", ctx->msg);
+ erts_fprintf(stderr, ">> CTL: %T\n", ctx->ctl);
+ if (is_value(ctx->msg))
+ erts_fprintf(stderr, " MSG: %T\n", ctx->msg);
#endif
- ctx->data_size = ctx->pass_through_size;
+ ctx->data_size = ctx->max_finalize_prepend;
erts_reset_atom_cache_map(ctx->acmp);
erts_encode_dist_ext_size(ctx->ctl, ctx->flags, ctx->acmp, &ctx->data_size);
- if (is_value(ctx->msg)) {
- 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;
- } else {
- ctx->phase = ERTS_DSIG_SEND_PHASE_ALLOC;
- }
- break;
+ 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:
if (erts_encode_dist_ext_size_int(ctx->msg, ctx, &ctx->data_size)) {
retval = ERTS_DSIG_SEND_CONTINUE;
@@ -1920,23 +1908,25 @@ erts_dsig_send(ErtsDSigData *dsdp, struct erts_dsig_send_context* ctx)
ctx->data_size += ctx->dhdr_ext_size;
ctx->obuf = alloc_dist_obuf(ctx->data_size);
- ctx->obuf->ext_endp = &ctx->obuf->data[0] + ctx->pass_through_size + ctx->dhdr_ext_size;
+ ctx->obuf->ext_endp = &ctx->obuf->data[0] + ctx->max_finalize_prepend + ctx->dhdr_ext_size;
/* Encode internal version of dist header */
ctx->obuf->extp = erts_encode_ext_dist_header_setup(ctx->obuf->ext_endp, ctx->acmp);
/* Encode control message */
erts_encode_dist_ext(ctx->ctl, &ctx->obuf->ext_endp, ctx->flags, ctx->acmp, NULL, NULL);
- if (is_value(ctx->msg)) {
- ctx->u.ec.flags = ctx->flags;
- ctx->u.ec.level = 0;
- ctx->u.ec.wstack.wstart = NULL;
- ctx->phase = ERTS_DSIG_SEND_PHASE_MSG_ENCODE;
- } else {
- ctx->phase = ERTS_DSIG_SEND_PHASE_FIN;
- }
- break;
-
- case ERTS_DSIG_SEND_PHASE_MSG_ENCODE:
+ 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 (erts_encode_dist_ext(ctx->msg, &ctx->obuf->ext_endp, ctx->flags, ctx->acmp, &ctx->u.ec, &ctx->reds)) {
retval = ERTS_DSIG_SEND_CONTINUE;
goto done;
@@ -1949,11 +1939,12 @@ erts_dsig_send(ErtsDSigData *dsdp, struct erts_dsig_send_context* ctx)
int resume = 0;
ASSERT(ctx->obuf->extp < ctx->obuf->ext_endp);
- ASSERT(&ctx->obuf->data[0] <= ctx->obuf->extp - ctx->pass_through_size);
+ ASSERT(&ctx->obuf->data[0] <= ctx->obuf->extp - ctx->max_finalize_prepend);
ASSERT(ctx->obuf->ext_endp <= &ctx->obuf->data[0] + ctx->data_size);
ctx->data_size = ctx->obuf->ext_endp - ctx->obuf->extp;
+ ctx->obuf->hopefull_flags = ctx->u.ec.hopefull_flags;
/*
* Signal encoded; now verify that the connection still exists,
* and if so enqueue the signal and schedule it for send.
@@ -1961,9 +1952,9 @@ erts_dsig_send(ErtsDSigData *dsdp, struct erts_dsig_send_context* ctx)
ctx->obuf->next = NULL;
erts_de_rlock(dep);
cid = dep->cid;
- if (cid != dsdp->cid
- || dep->connection_id != dsdp->connection_id
- || dep->status & ERTS_DE_SFLG_EXITING) {
+ if (dep->state == ERTS_DE_STATE_EXITING
+ || dep->state == ERTS_DE_STATE_IDLE
+ || dep->connection_id != dsdp->connection_id) {
/* Not the same connection as when we started; drop message... */
erts_de_runlock(dep);
free_dist_obuf(ctx->obuf);
@@ -2037,8 +2028,13 @@ erts_dsig_send(ErtsDSigData *dsdp, struct erts_dsig_send_context* ctx)
}
erts_mtx_unlock(&dep->qlock);
- if (is_internal_port(dep->cid))
- erts_schedule_dist_command(NULL, dep);
+ if (dep->state != ERTS_DE_STATE_PENDING) {
+ if (is_internal_port(dep->cid))
+ erts_schedule_dist_command(NULL, dep);
+ }
+ else {
+ notify_proc = NIL;
+ }
erts_de_runlock(dep);
if (is_internal_pid(notify_proc))
notify_dist_data(ctx->c_p, notify_proc);
@@ -2203,7 +2199,6 @@ dist_port_commandv(Port *prt, ErtsDistOutputBuf *obuf)
#endif
#define ERTS_PORT_REDS_DIST_CMD_START 5
-#define ERTS_PORT_REDS_DIST_CMD_FINALIZE 3
#define ERTS_PORT_REDS_DIST_CMD_EXIT 200
#define ERTS_PORT_REDS_DIST_CMD_RESUMED 5
#define ERTS_PORT_REDS_DIST_CMD_DATA(SZ) \
@@ -2212,10 +2207,10 @@ dist_port_commandv(Port *prt, ErtsDistOutputBuf *obuf)
: ((((Sint) (SZ)) >> 10) & ((Sint) ERTS_PORT_REDS_MASK__)))
int
-erts_dist_command(Port *prt, int reds_limit)
+erts_dist_command(Port *prt, int initial_reds)
{
- Sint reds = ERTS_PORT_REDS_DIST_CMD_START;
- Uint32 status;
+ Sint reds = initial_reds - ERTS_PORT_REDS_DIST_CMD_START;
+ enum dist_entry_state state;
Uint32 flags;
Sint qsize, obufsize = 0;
ErtsDistOutputQueue oq, foq;
@@ -2230,15 +2225,18 @@ erts_dist_command(Port *prt, int reds_limit)
erts_de_rlock(dep);
flags = dep->flags;
- status = dep->status;
+ state = dep->state;
send = dep->send;
erts_de_runlock(dep);
- if (status & ERTS_DE_SFLG_EXITING) {
+ if (state == ERTS_DE_STATE_EXITING) {
erts_deliver_port_exit(prt, prt->common.id, am_killed, 0, 1);
- return reds + ERTS_PORT_REDS_DIST_CMD_EXIT;
+ reds -= ERTS_PORT_REDS_DIST_CMD_EXIT;
+ return initial_reds - reds;
}
+ ASSERT(state != ERTS_DE_STATE_PENDING);
+
ASSERT(send);
/*
@@ -2263,7 +2261,7 @@ erts_dist_command(Port *prt, int reds_limit)
sched_flags = erts_atomic32_read_nob(&prt->sched.flags);
- if (reds > reds_limit)
+ if (reds < 0)
goto preempted;
if (!(sched_flags & ERTS_PTS_FLG_BUSY_PORT) && foq.first) {
@@ -2278,13 +2276,13 @@ erts_dist_command(Port *prt, int reds_limit)
erts_fprintf(stderr, ">> ");
bw(foq.first->extp, size);
#endif
- reds += ERTS_PORT_REDS_DIST_CMD_DATA(size);
- fob = foq.first;
- obufsize += size_obuf(fob);
- foq.first = foq.first->next;
- free_dist_obuf(fob);
+ reds -= ERTS_PORT_REDS_DIST_CMD_DATA(size);
+ fob = foq.first;
+ obufsize += size_obuf(fob);
+ foq.first = foq.first->next;
+ free_dist_obuf(fob);
sched_flags = erts_atomic32_read_nob(&prt->sched.flags);
- preempt = reds > reds_limit || (sched_flags & ERTS_PTS_FLG_EXIT);
+ preempt = reds < 0 || (sched_flags & ERTS_PTS_FLG_EXIT);
if (sched_flags & ERTS_PTS_FLG_BUSY_PORT)
break;
} while (foq.first && !preempt);
@@ -2297,81 +2295,71 @@ erts_dist_command(Port *prt, int reds_limit)
if (sched_flags & ERTS_PTS_FLG_BUSY_PORT) {
if (oq.first) {
ErtsDistOutputBuf *ob;
- int preempt;
+ ErtsDistOutputBuf *last_finalized = NULL;
finalize_only:
- preempt = 0;
ob = oq.first;
ASSERT(ob);
do {
- ob->extp = erts_encode_ext_dist_header_finalize(ob->extp,
- dep->cache,
- flags);
- if (!(flags & DFLAG_DIST_HDR_ATOM_CACHE))
- *--ob->extp = PASS_THROUGH; /* Old node; 'pass through'
- needed */
- ASSERT(&ob->data[0] <= ob->extp && ob->extp < ob->ext_endp);
- reds += ERTS_PORT_REDS_DIST_CMD_FINALIZE;
- preempt = reds > reds_limit;
- if (preempt)
- break;
- ob = ob->next;
+ reds = erts_encode_ext_dist_header_finalize(ob, dep, flags, reds);
+ if (reds < 0)
+ break;
+ last_finalized = ob;
+ ob = ob->next;
} while (ob);
- /*
- * At least one buffer was finalized; if we got preempted,
- * ob points to the last buffer that we finalized.
- */
- if (foq.last)
- foq.last->next = oq.first;
- else
- foq.first = oq.first;
- if (!preempt) {
- /* All buffers finalized */
- foq.last = oq.last;
- oq.first = oq.last = NULL;
- }
- else {
- /* Not all buffers finalized; split oq. */
- foq.last = ob;
- oq.first = ob->next;
- if (oq.first)
- ob->next = NULL;
- else
- oq.last = NULL;
- }
- if (preempt)
- goto preempted;
+ if (last_finalized) {
+ /*
+ * At least one buffer was finalized; if we got preempted,
+ * ob points to the next buffer to continue finalize.
+ */
+ if (foq.last)
+ foq.last->next = oq.first;
+ else
+ foq.first = oq.first;
+ foq.last = last_finalized;
+ if (!ob) {
+ /* All buffers finalized */
+ ASSERT(foq.last == oq.last);
+ ASSERT(foq.last->next == NULL);
+ oq.first = oq.last = NULL;
+ }
+ else {
+ /* Not all buffers finalized; split oq. */
+ ASSERT(foq.last->next == ob);
+ foq.last->next = NULL;
+ oq.first = ob;
+ }
+ }
+ if (reds <= 0)
+ goto preempted;
}
}
else {
int de_busy;
int preempt = 0;
while (oq.first && !preempt) {
- ErtsDistOutputBuf *fob;
- Uint size;
- oq.first->extp
- = erts_encode_ext_dist_header_finalize(oq.first->extp,
- dep->cache,
- flags);
- reds += ERTS_PORT_REDS_DIST_CMD_FINALIZE;
- if (!(flags & DFLAG_DIST_HDR_ATOM_CACHE))
- *--oq.first->extp = PASS_THROUGH; /* Old node; 'pass through'
- needed */
- ASSERT(&oq.first->data[0] <= oq.first->extp
- && oq.first->extp < oq.first->ext_endp);
- size = (*send)(prt, oq.first);
+ ErtsDistOutputBuf *fob;
+ Uint size;
+ reds = erts_encode_ext_dist_header_finalize(oq.first, dep, flags, reds);
+ if (reds < 0) {
+ preempt = 1;
+ break;
+ }
+ ASSERT(&oq.first->data[0] <= 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;
+ esdp->io.out += (Uint64) size;
#ifdef ERTS_RAW_DIST_MSG_DBG
erts_fprintf(stderr, ">> ");
bw(oq.first->extp, size);
#endif
- reds += ERTS_PORT_REDS_DIST_CMD_DATA(size);
- fob = oq.first;
- obufsize += size_obuf(fob);
- oq.first = oq.first->next;
- free_dist_obuf(fob);
+ reds -= ERTS_PORT_REDS_DIST_CMD_DATA(size);
+ fob = oq.first;
+ obufsize += size_obuf(fob);
+ oq.first = oq.first->next;
+ free_dist_obuf(fob);
sched_flags = erts_atomic32_read_nob(&prt->sched.flags);
- preempt = reds > reds_limit || (sched_flags & ERTS_PTS_FLG_EXIT);
+ preempt = reds <= 0 || (sched_flags & ERTS_PTS_FLG_EXIT);
if ((sched_flags & ERTS_PTS_FLG_BUSY_PORT) && oq.first && !preempt)
goto finalize_only;
}
@@ -2411,7 +2399,7 @@ erts_dist_command(Port *prt, int reds_limit)
erts_mtx_unlock(&dep->qlock);
resumed = erts_resume_processes(suspendees);
- reds += resumed*ERTS_PORT_REDS_DIST_CMD_RESUMED;
+ reds -= resumed*ERTS_PORT_REDS_DIST_CMD_RESUMED;
}
else
erts_mtx_unlock(&dep->qlock);
@@ -2434,8 +2422,7 @@ erts_dist_command(Port *prt, int reds_limit)
erts_mtx_unlock(&dep->qlock);
}
- ASSERT(foq.first || !foq.last);
- ASSERT(!foq.first || foq.last);
+ ASSERT(!!foq.first == !!foq.last);
ASSERT(!dep->finalized_out_queue.first);
ASSERT(!dep->finalized_out_queue.last);
@@ -2445,10 +2432,10 @@ erts_dist_command(Port *prt, int reds_limit)
}
/* Avoid wrapping reduction counter... */
- if (reds > INT_MAX/2)
- reds = INT_MAX/2;
+ if (reds < INT_MIN/2)
+ reds = INT_MIN/2;
- return reds;
+ return initial_reds - reds;
preempted:
/*
@@ -2456,8 +2443,7 @@ erts_dist_command(Port *prt, int reds_limit)
* since last call to driver.
*/
- ASSERT(oq.first || !oq.last);
- ASSERT(!oq.first || oq.last);
+ ASSERT(!!oq.first == !!oq.last);
if (sched_flags & ERTS_PTS_FLG_EXIT) {
/*
@@ -2481,12 +2467,6 @@ erts_dist_command(Port *prt, int reds_limit)
foq.first = NULL;
foq.last = NULL;
-
-#ifdef DEBUG
- erts_mtx_lock(&dep->qlock);
- ASSERT(erts_atomic_read_nob(&dep->qsize) == obufsize);
- erts_mtx_unlock(&dep->qlock);
-#endif
}
else {
if (oq.first) {
@@ -2776,7 +2756,8 @@ BIF_RETTYPE
dist_ctrl_get_data_1(BIF_ALIST_1)
{
DistEntry *dep = ERTS_PROC_GET_DIST_ENTRY(BIF_P);
- int reds = 1;
+ const Sint initial_reds = ERTS_BIF_REDS_LEFT(BIF_P);
+ Sint reds = initial_reds;
ErtsDistOutputBuf *obuf;
Eterm *hp;
ProcBin *pb;
@@ -2790,7 +2771,7 @@ dist_ctrl_get_data_1(BIF_ALIST_1)
erts_de_rlock(dep);
- if (dep->status & ERTS_DE_SFLG_EXITING)
+ if (dep->state == ERTS_DE_STATE_EXITING)
goto return_none;
ASSERT(dep->cid == BIF_P->common.id);
@@ -2807,6 +2788,7 @@ 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);
@@ -2824,21 +2806,18 @@ dist_ctrl_get_data_1(BIF_ALIST_1)
erts_de_runlock(dep);
BIF_RET(am_none);
}
- else {
- obuf = dep->tmp_out_queue.first;
- dep->tmp_out_queue.first = obuf->next;
- if (!obuf->next)
- dep->tmp_out_queue.last = NULL;
+
+ obuf = dep->tmp_out_queue.first;
+ reds = erts_encode_ext_dist_header_finalize(obuf, dep, dep->flags, reds);
+ if (reds < 0) {
+ erts_de_runlock(dep);
+ ERTS_BIF_YIELD1(bif_export[BIF_dist_ctrl_get_data_1],
+ BIF_P, BIF_ARG_1);
}
- obuf->extp = erts_encode_ext_dist_header_finalize(obuf->extp,
- dep->cache,
- dep->flags);
- reds += ERTS_PORT_REDS_DIST_CMD_FINALIZE;
- if (!(dep->flags & DFLAG_DIST_HDR_ATOM_CACHE))
- *--obuf->extp = PASS_THROUGH; /* 'pass through' needed */
- ASSERT(&obuf->data[0] <= obuf->extp
- && obuf->extp < obuf->ext_endp);
+ dep->tmp_out_queue.first = obuf->next;
+ if (!obuf->next)
+ dep->tmp_out_queue.last = NULL;
}
erts_atomic64_inc_nob(&dep->out);
@@ -2866,11 +2845,11 @@ dist_ctrl_get_data_1(BIF_ALIST_1)
erts_mtx_unlock(&dep->qlock);
if (resume_procs) {
int resumed = erts_resume_processes(resume_procs);
- reds += resumed*ERTS_PORT_REDS_DIST_CMD_RESUMED;
+ reds -= resumed*ERTS_PORT_REDS_DIST_CMD_RESUMED;
}
}
- BIF_RET2(make_binary(pb), reds);
+ BIF_RET2(make_binary(pb), (initial_reds - reds));
}
void
@@ -2892,24 +2871,31 @@ erts_dist_port_not_busy(Port *prt)
erts_schedule_dist_command(prt, NULL);
}
+static void kill_connection(DistEntry *dep)
+{
+ ERTS_LC_ASSERT(erts_lc_is_de_rwlocked(dep));
+ ASSERT(dep->state == ERTS_DE_STATE_CONNECTED);
+
+ dep->state = ERTS_DE_STATE_EXITING;
+ erts_mtx_lock(&dep->qlock);
+ ASSERT(!(erts_atomic32_read_nob(&dep->qflgs) & ERTS_DE_QFLG_EXIT));
+ erts_atomic32_read_bor_nob(&dep->qflgs, ERTS_DE_QFLG_EXIT);
+ erts_mtx_unlock(&dep->qlock);
+
+ if (is_internal_port(dep->cid))
+ erts_schedule_dist_command(NULL, dep);
+ else if (is_internal_pid(dep->cid))
+ schedule_kill_dist_ctrl_proc(dep->cid);
+}
+
void
erts_kill_dist_connection(DistEntry *dep, Uint32 connection_id)
{
erts_de_rwlock(dep);
if (connection_id == dep->connection_id
- && !(dep->status & ERTS_DE_SFLG_EXITING)) {
+ && dep->state == ERTS_DE_STATE_CONNECTED) {
- dep->status |= ERTS_DE_SFLG_EXITING;
-
- erts_mtx_lock(&dep->qlock);
- ASSERT(!(erts_atomic32_read_nob(&dep->qflgs) & ERTS_DE_QFLG_EXIT));
- erts_atomic32_read_bor_nob(&dep->qflgs, ERTS_DE_QFLG_EXIT);
- erts_mtx_unlock(&dep->qlock);
-
- if (is_internal_port(dep->cid))
- erts_schedule_dist_command(NULL, dep);
- else if (is_internal_pid(dep->cid))
- schedule_kill_dist_ctrl_proc(dep->cid);
+ kill_connection(dep);
}
erts_de_rwunlock(dep);
}
@@ -2923,57 +2909,55 @@ static void doit_print_monitor_info(ErtsMonitor *mon, void *vptdp)
{
fmtfn_t to = ((struct print_to_data *) vptdp)->to;
void *arg = ((struct print_to_data *) vptdp)->arg;
- Process *rp;
- ErtsMonitor *rmon;
- rp = erts_proc_lookup(mon->u.pid);
- if (!rp || (rmon = erts_lookup_monitor(ERTS_P_MONITORS(rp), mon->ref)) == NULL) {
- erts_print(to, arg, "Warning, stray monitor for: %T\n", mon->u.pid);
- } else if (mon->type == MON_ORIGIN) {
- /* Local pid is being monitored */
+ ErtsMonitorDataExtended *mdep;
+
+ ASSERT(mon->flags & ERTS_ML_FLG_EXTENDED);
+
+ mdep = (ErtsMonitorDataExtended *) erts_monitor_to_data(mon);
+
+ ASSERT(mdep->dist);
+
+ if (erts_monitor_is_origin(mon)) {
erts_print(to, arg, "Remotely monitored by: %T %T\n",
- mon->u.pid, rmon->u.pid);
- } else {
- erts_print(to, arg, "Remote monitoring: %T ", mon->u.pid);
- if (is_not_atom(rmon->u.pid))
- erts_print(to, arg, "%T\n", rmon->u.pid);
- else
- erts_print(to, arg, "{%T, %T}\n",
- rmon->name,
- rmon->u.pid); /* which in this case is the
- remote system name... */
+ mon->other.item, mdep->md.target.other.item);
+ }
+ else {
+ erts_print(to, arg, "Remote monitoring: %T ", mon->other.item);
+ if (mon->flags & ERTS_ML_FLG_NAME)
+ erts_print(to, arg, "{%T, %T}\n", mdep->u.name, mdep->dist->nodename);
+ else
+ erts_print(to, arg, "%T\n", mdep->md.origin.other.item);
}
}
-static void print_monitor_info(fmtfn_t to, void *arg, ErtsMonitor *mon)
+static void print_monitor_info(fmtfn_t to, void *arg, DistEntry *dep)
{
struct print_to_data ptd = {to, arg};
- erts_doforall_monitors(mon,&doit_print_monitor_info,&ptd);
-}
-
-typedef struct {
- struct print_to_data *ptdp;
- Eterm from;
-} PrintLinkContext;
-
-static void doit_print_link_info2(ErtsLink *lnk, void *vpplc)
-{
- PrintLinkContext *pplc = (PrintLinkContext *) vpplc;
- erts_print(pplc->ptdp->to, pplc->ptdp->arg, "Remote link: %T %T\n",
- pplc->from, lnk->pid);
+ if (dep->mld) {
+ erts_monitor_list_foreach(dep->mld->monitors,
+ doit_print_monitor_info,
+ (void *) &ptd);
+ erts_monitor_tree_foreach(dep->mld->orig_name_monitors,
+ doit_print_monitor_info,
+ (void *) &ptd);
+ }
}
static void doit_print_link_info(ErtsLink *lnk, void *vptdp)
{
- if (is_internal_pid(lnk->pid) && erts_proc_lookup(lnk->pid)) {
- PrintLinkContext plc = {(struct print_to_data *) vptdp, lnk->pid};
- erts_doforall_links(ERTS_LINK_ROOT(lnk), &doit_print_link_info2, &plc);
- }
+ struct print_to_data *ptdp = vptdp;
+ ErtsLink *lnk2 = erts_link_to_other(lnk, NULL);
+ erts_print(ptdp->to, ptdp->arg, "Remote link: %T %T\n",
+ lnk2->other.item, lnk->other.item);
}
-static void print_link_info(fmtfn_t to, void *arg, ErtsLink *lnk)
+static void print_link_info(fmtfn_t to, void *arg, DistEntry *dep)
{
struct print_to_data ptd = {to, arg};
- erts_doforall_links(lnk, &doit_print_link_info, (void *) &ptd);
+ if (dep->mld)
+ erts_link_list_foreach(dep->mld->links,
+ doit_print_link_info,
+ (void *) &ptd);
}
typedef struct {
@@ -2981,23 +2965,6 @@ typedef struct {
Eterm sysname;
} PrintNodeLinkContext;
-
-static void doit_print_nodelink_info(ErtsLink *lnk, void *vpcontext)
-{
- PrintNodeLinkContext *pcontext = vpcontext;
-
- if (is_internal_pid(lnk->pid) && erts_proc_lookup(lnk->pid))
- erts_print(pcontext->ptd.to, pcontext->ptd.arg,
- "Remote monitoring: %T %T\n", lnk->pid, pcontext->sysname);
-}
-
-static void print_nodelink_info(fmtfn_t to, void *arg, ErtsLink *lnk, Eterm sysname)
-{
- PrintNodeLinkContext context = {{to, arg}, sysname};
- erts_doforall_links(lnk, &doit_print_nodelink_info, &context);
-}
-
-
static int
info_dist_entry(fmtfn_t to, void *arg, DistEntry *dep, int visible, int connected)
{
@@ -3028,8 +2995,8 @@ info_dist_entry(fmtfn_t to, void *arg, DistEntry *dep, int visible, int connecte
erts_print(to, arg, "Name: %T", dep->sysname);
erts_print(to, arg, "\n");
if (!connected && is_nil(dep->cid)) {
- if (dep->nlinks) {
- erts_print(to, arg, "Error: Got links to not connected node:%T\n",
+ if (dep->mld) {
+ erts_print(to, arg, "Error: Got links/monitors to not connected node:%T\n",
dep->sysname);
}
return 0;
@@ -3038,9 +3005,8 @@ info_dist_entry(fmtfn_t to, void *arg, DistEntry *dep, int visible, int connecte
erts_print(to, arg, "Controller: %T\n", dep->cid, to);
erts_print_node_info(to, arg, dep->sysname, NULL, NULL);
- print_monitor_info(to, arg, dep->monitors);
- print_link_info(to, arg, dep->nlinks);
- print_nodelink_info(to, arg, dep->node_links, dep->sysname);
+ print_monitor_info(to, arg, dep);
+ print_link_info(to, arg, dep);
return 0;
@@ -3069,6 +3035,10 @@ int distribution_info(fmtfn_t to, void *arg) /* Called by break handler */
info_dist_entry(to, arg, dep, 0, 1);
}
+ for (dep = erts_pending_dist_entries; dep; dep = dep->next) {
+ info_dist_entry(to, arg, dep, 0, 0);
+ }
+
for (dep = erts_not_connected_dist_entries; dep; dep = dep->next) {
if (dep != erts_this_dist_entry) {
info_dist_entry(to, arg, dep, 0, 0);
@@ -3091,7 +3061,6 @@ int distribution_info(fmtfn_t to, void *arg) /* Called by break handler */
monitor_node -- turn on/off node monitoring
node controller only:
- dist_exit/3 -- send exit signals from remote to local process
dist_link/2 -- link a remote process to a local
dist_unlink/2 -- unlink a remote from a local
****************************************************************************/
@@ -3101,15 +3070,6 @@ int distribution_info(fmtfn_t to, void *arg) /* Called by break handler */
/**********************************************************************
** Set the node name of current node fail if node already is set.
** setnode(name@host, Creation)
- ** loads functions pointer to trap_functions from module erlang.
- ** erlang:dsend/2
- ** erlang:dlink/1
- ** erlang:dunlink/1
- ** erlang:dmonitor_node/3
- ** erlang:dgroup_leader/2
- ** erlang:dexit/2
- ** -- are these needed ?
- ** dexit/1
***********************************************************************/
BIF_RETTYPE setnode_2(BIF_ALIST_2)
@@ -3133,15 +3093,7 @@ BIF_RETTYPE setnode_2(BIF_ALIST_2)
goto error;
/* Check that all trap functions are defined !! */
- if (dsend2_trap->addressv[0] == NULL ||
- dsend3_trap->addressv[0] == NULL ||
- /* dsend_nosuspend_trap->address == NULL ||*/
- dlink_trap->addressv[0] == NULL ||
- dunlink_trap->addressv[0] == NULL ||
- dmonitor_node_trap->addressv[0] == NULL ||
- dgroup_leader_trap->addressv[0] == NULL ||
- dmonitor_p_trap->addressv[0] == NULL ||
- dexit_trap->addressv[0] == NULL) {
+ if (dmonitor_node_trap->addressv[0] == NULL) {
goto error;
}
@@ -3218,6 +3170,8 @@ BIF_RETTYPE setnode_3(BIF_ALIST_3)
ErtsProcLocks proc_unlock = 0;
Process *proc;
Port *pp = NULL;
+ Eterm notify_proc;
+ erts_aint32_t qflgs;
/*
* Check and pick out arguments
@@ -3245,21 +3199,25 @@ BIF_RETTYPE setnode_3(BIF_ALIST_3)
if (!is_atom(ic) || !is_atom(oc))
goto badarg;
- /* DFLAG_EXTENDED_REFERENCES is compulsory from R9 and forward */
- if (!(DFLAG_EXTENDED_REFERENCES & flags)) {
+ if (~flags & DFLAG_DIST_MANDATORY) {
erts_dsprintf_buf_t *dsbufp = erts_create_logger_dsbuf();
erts_dsprintf(dsbufp, "%T", BIF_P->common.id);
if (BIF_P->common.u.alive.reg)
erts_dsprintf(dsbufp, " (%T)", BIF_P->common.u.alive.reg->name);
erts_dsprintf(dsbufp,
" attempted to enable connection to node %T "
- "which is not able to handle extended references.\n",
+ "which does not support all mandatory capabilities.\n",
BIF_ARG_1);
erts_send_error_to_logger(BIF_P->group_leader, dsbufp);
goto badarg;
}
/*
+ * ToDo: Should we not pass connection_id as well
+ * to make sure it's the right connection we commit.
+ */
+
+ /*
* Arguments seem to be in order.
*/
@@ -3302,6 +3260,23 @@ BIF_RETTYPE setnode_3(BIF_ALIST_3)
goto badarg;
}
+ if (dep->state == ERTS_DE_STATE_EXITING) {
+ /* Suspend on dist entry waiting for the exit to finish */
+ ErtsProcList *plp = erts_proclist_create(BIF_P);
+ plp->next = NULL;
+ erts_suspend(BIF_P, ERTS_PROC_LOCK_MAIN, NULL);
+ erts_mtx_lock(&dep->qlock);
+ erts_proclist_store_last(&dep->suspended, plp);
+ erts_mtx_unlock(&dep->qlock);
+ goto yield;
+ }
+ if (dep->state != ERTS_DE_STATE_PENDING) {
+ if (dep->state == ERTS_DE_STATE_IDLE)
+ erts_set_dist_entry_pending(dep);
+ else
+ goto badarg;
+ }
+
if (is_not_nil(dep->cid))
goto badarg;
@@ -3334,7 +3309,7 @@ BIF_RETTYPE setnode_3(BIF_ALIST_3)
goto done; /* Already set */
}
- if (dep->status & ERTS_DE_SFLG_EXITING) {
+ if (dep->state == ERTS_DE_STATE_EXITING) {
/* Suspend on dist entry waiting for the exit to finish */
ErtsProcList *plp = erts_proclist_create(BIF_P);
plp->next = NULL;
@@ -3344,8 +3319,12 @@ BIF_RETTYPE setnode_3(BIF_ALIST_3)
erts_mtx_unlock(&dep->qlock);
goto yield;
}
-
- ASSERT(!(dep->status & ERTS_DE_SFLG_EXITING));
+ if (dep->state != ERTS_DE_STATE_PENDING) {
+ if (dep->state == ERTS_DE_STATE_IDLE)
+ erts_set_dist_entry_pending(dep);
+ else
+ goto badarg;
+ }
if (pp->dist_entry || is_not_nil(dep->cid))
goto badarg;
@@ -3376,7 +3355,8 @@ BIF_RETTYPE setnode_3(BIF_ALIST_3)
dep->creation = 0;
#ifdef DEBUG
- ASSERT(erts_atomic_read_nob(&dep->qsize) == 0);
+ ASSERT(erts_atomic_read_nob(&dep->qsize) == 0
+ || (dep->state == ERTS_DE_STATE_PENDING));
#endif
if (flags & DFLAG_DIST_HDR_ATOM_CACHE)
@@ -3384,7 +3364,26 @@ BIF_RETTYPE setnode_3(BIF_ALIST_3)
erts_set_dist_entry_connected(dep, BIF_ARG_2, flags);
+ notify_proc = NIL;
+ if (erts_atomic_read_nob(&dep->qsize)) {
+ if (is_internal_port(dep->cid)) {
+ erts_schedule_dist_command(NULL, dep);
+ }
+ else {
+ qflgs = erts_atomic32_read_nob(&dep->qflgs);
+ if (qflgs & ERTS_DE_QFLG_REQ_INFO) {
+ qflgs = erts_atomic32_read_band_mb(&dep->qflgs,
+ ~ERTS_DE_QFLG_REQ_INFO);
+ if (qflgs & ERTS_DE_QFLG_REQ_INFO) {
+ notify_proc = dep->cid;
+ ASSERT(is_internal_pid(notify_proc));
+ }
+ }
+ }
+ }
erts_de_rwunlock(dep);
+ if (is_internal_pid(notify_proc))
+ notify_dist_data(BIF_P, notify_proc);
ERTS_BIF_PREP_RET(ret, erts_make_dhandle(BIF_P, dep));
@@ -3426,79 +3425,179 @@ BIF_RETTYPE setnode_3(BIF_ALIST_3)
goto done;
}
+BIF_RETTYPE erts_internal_get_dflags_0(BIF_ALIST_0)
+{
+ return erts_dflags_record;
+}
+
+BIF_RETTYPE erts_internal_new_connection_1(BIF_ALIST_1)
+{
+ DistEntry* dep;
+ Uint32 conn_id;
+ Eterm* hp;
+ Eterm dhandle;
-/**********************************************************************/
-/* dist_exit(Local, Term, Remote) -> Bool */
+ if (is_not_atom(BIF_ARG_1)) {
+ BIF_ERROR(BIF_P, BADARG);
+ }
+ dep = erts_find_or_insert_dist_entry(BIF_ARG_1);
-BIF_RETTYPE dist_exit_3(BIF_ALIST_3)
+ if (dep == erts_this_dist_entry) {
+ erts_deref_dist_entry(dep);
+ BIF_ERROR(BIF_P, BADARG);
+ }
+
+ erts_de_rwlock(dep);
+
+ switch (dep->state) {
+ case ERTS_DE_STATE_PENDING:
+ case ERTS_DE_STATE_CONNECTED:
+ conn_id = dep->connection_id;
+ break;
+ case ERTS_DE_STATE_IDLE:
+ erts_set_dist_entry_pending(dep);
+ conn_id = dep->connection_id;
+ break;
+ case ERTS_DE_STATE_EXITING:
+ conn_id = (dep->connection_id + 1) & ERTS_DIST_CON_ID_MASK;
+ break;
+ default:
+ erts_exit(ERTS_ABORT_EXIT, "Invalid dep->state (%d)\n", dep->state);
+ }
+ erts_de_rwunlock(dep);
+ hp = HAlloc(BIF_P, 3 + ERTS_MAGIC_REF_THING_SIZE);
+ dhandle = erts_build_dhandle(&hp, &BIF_P->off_heap, dep);
+ erts_deref_dist_entry(dep);
+ BIF_RET(TUPLE2(hp, make_small(conn_id), dhandle));
+}
+
+static Sint abort_connection(DistEntry* dep, Uint32 conn_id)
{
- Eterm local;
- Eterm remote;
- DistEntry *rdep;
+ erts_de_rwlock(dep);
- local = BIF_ARG_1;
- remote = BIF_ARG_3;
+ if (dep->connection_id != conn_id)
+ ;
+ else if (dep->state == ERTS_DE_STATE_CONNECTED) {
+ kill_connection(dep);
+ }
+ else if (dep->state == ERTS_DE_STATE_PENDING) {
+ ErtsAtomCache *cache;
+ ErtsDistOutputBuf *obuf;
+ ErtsProcList *resume_procs;
+ Sint reds = 0;
+ ErtsMonLnkDist *mld;
- /* Check that remote is a remote process */
- if (is_not_external_pid(remote))
- goto error;
+ ASSERT(is_nil(dep->cid));
- rdep = external_dist_entry(remote);
-
- if(rdep == erts_this_dist_entry)
- goto error;
+ mld = dep->mld;
+ dep->mld = NULL;
- /* Check that local is local */
- if (is_internal_pid(local)) {
- Process *lp;
- ErtsProcLocks lp_locks;
- if (BIF_P->common.id == local) {
- lp_locks = ERTS_PROC_LOCKS_ALL;
- lp = BIF_P;
- erts_proc_lock(BIF_P, ERTS_PROC_LOCKS_ALL_MINOR);
- }
- else {
- lp_locks = ERTS_PROC_LOCKS_XSIG_SEND;
- lp = erts_pid2proc(BIF_P, ERTS_PROC_LOCK_MAIN,
- local, lp_locks);
- if (!lp) {
- BIF_RET(am_true); /* ignore */
- }
- }
-
- (void) erts_send_exit_signal(BIF_P,
- remote,
- lp,
- &lp_locks,
- BIF_ARG_2,
- NIL,
- NULL,
- 0);
- if (lp == BIF_P)
- lp_locks &= ~ERTS_PROC_LOCK_MAIN;
- erts_proc_unlock(lp, lp_locks);
- if (lp == BIF_P) {
- erts_aint32_t state = erts_atomic32_read_acqb(&BIF_P->state);
- /*
- * We may have exited current process and may have to take action.
- */
- if (state & (ERTS_PSFLG_EXITING|ERTS_PSFLG_PENDING_EXIT)) {
- if (state & ERTS_PSFLG_PENDING_EXIT)
- erts_handle_pending_exit(BIF_P, ERTS_PROC_LOCK_MAIN);
- ERTS_BIF_EXITED(BIF_P);
- }
- }
+ cache = dep->cache;
+ dep->cache = NULL;
+ erts_mtx_lock(&dep->qlock);
+ obuf = dep->out_queue.first;
+ dep->out_queue.first = NULL;
+ dep->out_queue.last = NULL;
+ ASSERT(!dep->tmp_out_queue.first);
+ ASSERT(!dep->finalized_out_queue.first);
+ resume_procs = get_suspended_on_de(dep, ERTS_DE_QFLGS_ALL);
+ erts_mtx_unlock(&dep->qlock);
+ erts_atomic_set_nob(&dep->dist_cmd_scheduled, 0);
+ dep->send = NULL;
+
+ erts_set_dist_entry_not_connected(dep);
+
+ erts_de_rwunlock(dep);
+
+ schedule_con_monitor_link_cleanup(mld, THE_NON_VALUE,
+ THE_NON_VALUE, THE_NON_VALUE);
+
+ if (resume_procs) {
+ int resumed = erts_resume_processes(resume_procs);
+ reds += resumed*ERTS_PORT_REDS_DIST_CMD_RESUMED;
+ }
+
+ delete_cache(cache);
+ free_de_out_queues(dep, obuf);
+
+ /*
+ * We wait to make DistEntry idle and accept new connection attempts
+ * until all is cleared and deallocated. This to get some back pressure
+ * against repeated failing connection attempts saturating all CPUs
+ * with cleanup jobs.
+ */
+ erts_de_rwlock(dep);
+ ASSERT(dep->state == ERTS_DE_STATE_EXITING);
+ dep->state = ERTS_DE_STATE_IDLE;
+ erts_de_rwunlock(dep);
+ return reds;
+ }
+ erts_de_rwunlock(dep);
+ return 0;
+}
+
+BIF_RETTYPE erts_internal_abort_connection_2(BIF_ALIST_2)
+{
+ DistEntry* dep;
+ Eterm* tp;
+
+ if (is_not_atom(BIF_ARG_1) || is_not_tuple_arity(BIF_ARG_2, 2)) {
+ BIF_ERROR(BIF_P, BADARG);
}
- else if (is_external_pid(local)
- && external_dist_entry(local) == erts_this_dist_entry) {
- BIF_RET(am_true); /* ignore */
+ tp = tuple_val(BIF_ARG_2);
+ dep = erts_dhandle_to_dist_entry(tp[2]);
+ if (is_not_small(tp[1]) || dep != erts_find_dist_entry(BIF_ARG_1)
+ || dep == erts_this_dist_entry) {
+ BIF_ERROR(BIF_P, BADARG);
+ }
+
+ if (dep) {
+ Sint reds = abort_connection(dep, unsigned_val(tp[1]));
+ BUMP_REDS(BIF_P, reds);
}
- else
- goto error;
BIF_RET(am_true);
+}
- error:
- BIF_ERROR(BIF_P, BADARG);
+int erts_auto_connect(DistEntry* dep, Process *proc, ErtsProcLocks proc_locks)
+{
+ erts_de_rwlock(dep);
+ if (dep->state != ERTS_DE_STATE_IDLE) {
+ erts_de_rwunlock(dep);
+ }
+ else {
+ Process* net_kernel;
+ ErtsProcLocks nk_locks = ERTS_PROC_LOCK_MSGQ;
+ Eterm *hp;
+ ErlOffHeap *ohp;
+ ErtsMessage *mp;
+ Eterm msg, dhandle;
+ Uint32 conn_id;
+
+ erts_set_dist_entry_pending(dep);
+ conn_id = dep->connection_id;
+ erts_de_rwunlock(dep);
+
+ net_kernel = erts_whereis_process(proc, proc_locks,
+ am_net_kernel, nk_locks, 0);
+ if (!net_kernel) {
+ abort_connection(dep, conn_id);
+ return 0;
+ }
+
+ /*
+ * Send {auto_connect, Node, ConnId, DHandle} to net_kernel
+ */
+ mp = erts_alloc_message_heap(net_kernel, &nk_locks,
+ 5 + ERTS_MAGIC_REF_THING_SIZE,
+ &hp, &ohp);
+ dhandle = erts_build_dhandle(&hp, ohp, dep);
+ msg = TUPLE4(hp, am_auto_connect, dep->sysname, make_small(conn_id),
+ dhandle);
+ erts_queue_message(net_kernel, nk_locks, mp, msg, proc->common.id);
+ erts_proc_unlock(net_kernel, nk_locks);
+ }
+
+ return 1;
}
/**********************************************************************/
@@ -3574,9 +3673,11 @@ BIF_RETTYPE nodes_1(BIF_ALIST_1)
ASSERT(erts_no_of_not_connected_dist_entries > 0);
ASSERT(erts_no_of_hidden_dist_entries >= 0);
+ ASSERT(erts_no_of_pending_dist_entries >= 0);
ASSERT(erts_no_of_visible_dist_entries >= 0);
if(not_connected)
- length += (erts_no_of_not_connected_dist_entries - 1);
+ length += ((erts_no_of_not_connected_dist_entries - 1)
+ + erts_no_of_pending_dist_entries);
if(hidden)
length += erts_no_of_hidden_dist_entries;
if(visible)
@@ -3596,13 +3697,18 @@ BIF_RETTYPE nodes_1(BIF_ALIST_1)
#ifdef DEBUG
endp = hp + length*2;
#endif
- if(not_connected)
+ if(not_connected) {
for(dep = erts_not_connected_dist_entries; dep; dep = dep->next) {
if (dep != erts_this_dist_entry) {
result = CONS(hp, dep->sysname, result);
hp += 2;
}
+ }
+ for(dep = erts_pending_dist_entries; dep; dep = dep->next) {
+ result = CONS(hp, dep->sysname, result);
+ hp += 2;
}
+ }
if(hidden)
for(dep = erts_hidden_dist_entries; dep; dep = dep->next) {
result = CONS(hp, dep->sysname, result);
@@ -3644,75 +3750,179 @@ BIF_RETTYPE is_alive_0(BIF_ALIST_0)
static BIF_RETTYPE
monitor_node(Process* p, Eterm Node, Eterm Bool, Eterm Options)
{
- DistEntry *dep;
- ErtsLink *lnk;
+ BIF_RETTYPE ret;
+ DistEntry *dep = NULL;
Eterm l;
+ int async_connect = 1;
for (l = Options; l != NIL && is_list(l); l = CDR(list_val(l))) {
Eterm t = CAR(list_val(l));
- /* allow_passive_connect the only available option right now */
- if (t != am_allow_passive_connect) {
+ if (t == am_allow_passive_connect) {
+ /*
+ * Handle this horrible feature by falling back on old synchronous
+ * auto-connect (if needed)
+ */
+ async_connect = 0;
+ } else {
BIF_ERROR(p, BADARG);
}
}
if (l != NIL) {
BIF_ERROR(p, BADARG);
}
+ if (l != NIL)
+ goto badarg;
- if (is_not_atom(Node) ||
- ((Bool != am_true) && (Bool != am_false)) ||
- ((erts_this_node->sysname == am_Noname)
- && (Node != erts_this_node->sysname))) {
- BIF_ERROR(p, BADARG);
- }
- dep = erts_sysname_to_connected_dist_entry(Node);
- if (!dep) {
- do_trap:
- BIF_TRAP3(dmonitor_node_trap, p, Node, Bool, Options);
- }
- if (dep == erts_this_dist_entry)
- goto done;
+ if (is_not_atom(Node))
+ goto badarg;
- erts_proc_lock(p, ERTS_PROC_LOCK_LINK);
- erts_de_rlock(dep);
- if (ERTS_DE_IS_NOT_CONNECTED(dep)) {
- erts_proc_unlock(p, ERTS_PROC_LOCK_LINK);
- erts_de_runlock(dep);
- goto do_trap;
- }
- erts_de_links_lock(dep);
- erts_de_runlock(dep);
+ if (erts_this_node->sysname == am_Noname && Node != am_Noname)
+ goto badarg;
+
+ switch (Bool) {
- if (Bool == am_true) {
- ASSERT(dep->cid != NIL);
- lnk = erts_add_or_lookup_link(&(dep->node_links), LINK_NODE,
- p->common.id);
- ++ERTS_LINK_REFC(lnk);
- lnk = erts_add_or_lookup_link(&ERTS_P_LINKS(p), LINK_NODE, Node);
- ++ERTS_LINK_REFC(lnk);
+ case am_false: {
+ ErtsMonitor *mon;
+ /*
+ * Before OTP-21, monitor_node(Node, false) triggered
+ * auto-connect and a 'nodedown' message if that failed.
+ * Now it's a simple no-op which feels more reasonable.
+ */
+ mon = erts_monitor_tree_lookup(ERTS_P_MONITORS(p), Node);
+ if (mon) {
+ ErtsMonitorDataExtended *mdep;
+ ASSERT(erts_monitor_is_origin(mon));
+
+ mdep = (ErtsMonitorDataExtended *) erts_monitor_to_data(mon);
+
+ ASSERT((mdep->u.refc > 0));
+ if (--mdep->u.refc == 0) {
+ if (!mdep->uptr.node_monitors)
+ erts_monitor_tree_delete(&ERTS_P_MONITORS(p), mon);
+ else {
+ ErtsMonitor *sub_mon;
+ ErtsMonitorDataExtended *sub_mdep;
+ sub_mon = erts_monitor_list_last(mdep->uptr.node_monitors);
+ erts_monitor_list_delete(&mdep->uptr.node_monitors, sub_mon);
+ sub_mon->flags &= ~ERTS_ML_FLG_IN_SUBTABLE;
+ sub_mdep = ((ErtsMonitorDataExtended *)
+ erts_monitor_to_data(sub_mon));
+ sub_mdep->uptr.node_monitors = mdep->uptr.node_monitors;
+ mdep->uptr.node_monitors = NULL;
+ erts_monitor_tree_replace(&ERTS_P_MONITORS(p), mon, sub_mon);
+ }
+ if (erts_monitor_dist_delete(&mdep->md.target))
+ erts_monitor_release_both((ErtsMonitorData *) mdep);
+ else
+ erts_monitor_release(mon);
+ }
+ }
+ break;
}
- else {
- lnk = erts_lookup_link(dep->node_links, p->common.id);
- if (lnk != NULL) {
- if ((--ERTS_LINK_REFC(lnk)) == 0) {
- erts_destroy_link(erts_remove_link(&(dep->node_links),
- p->common.id));
- }
- }
- lnk = erts_lookup_link(ERTS_P_LINKS(p), Node);
- if (lnk != NULL) {
- if ((--ERTS_LINK_REFC(lnk)) == 0) {
- erts_destroy_link(erts_remove_link(&ERTS_P_LINKS(p),
- Node));
+
+ case am_true: {
+ ErtsDSigData dsd;
+ dsd.node = Node;
+
+ dep = erts_find_or_insert_dist_entry(Node);
+ if (dep == erts_this_dist_entry)
+ break;
+
+ switch (erts_dsig_prepare(&dsd, dep, p,
+ ERTS_PROC_LOCK_MAIN,
+ ERTS_DSP_RLOCK, 0, async_connect)) {
+ case ERTS_DSIG_PREP_NOT_ALIVE:
+ case ERTS_DSIG_PREP_NOT_CONNECTED:
+ /* Trap to either send 'nodedown' or do passive connection attempt */
+ goto do_trap;
+ case ERTS_DSIG_PREP_PENDING:
+ if (!async_connect) {
+ /*
+ * Pending connection may fail, so we must trap
+ * to ensure passive connection attempt
+ */
+ erts_de_runlock(dep);
+ goto do_trap;
}
- }
+ /*fall through*/
+ case ERTS_DSIG_PREP_CONNECTED: {
+ ErtsMonitor *mon;
+ ErtsMonitorDataExtended *mdep;
+ int created;
+
+ mon = erts_monitor_tree_lookup_create(&ERTS_P_MONITORS(p),
+ &created,
+ ERTS_MON_TYPE_NODE,
+ p->common.id,
+ Node);
+ mdep = (ErtsMonitorDataExtended *) erts_monitor_to_data(mon);
+ if (created) {
+#ifdef DEBUG
+ int inserted =
+#endif
+ erts_monitor_dist_insert(&mdep->md.target, dep->mld);
+ ASSERT(inserted);
+ ASSERT(mdep->dist->connection_id == dep->connection_id);
+ }
+ else if (mdep->dist->connection_id != dep->connection_id) {
+ ErtsMonitorDataExtended *mdep2;
+ ErtsMonitor *mon2;
+#ifdef DEBUG
+ int inserted;
+#endif
+ mdep2 = ((ErtsMonitorDataExtended *)
+ erts_monitor_create(ERTS_MON_TYPE_NODE, NIL,
+ p->common.id, Node, NIL));
+ mon2 = &mdep2->md.origin;
+#ifdef DEBUG
+ inserted =
+#endif
+ erts_monitor_dist_insert(&mdep->md.target, dep->mld);
+ ASSERT(inserted);
+ ASSERT(mdep2->dist->connection_id == dep->connection_id);
+
+ mdep2->uptr.node_monitors = mdep->uptr.node_monitors;
+ mdep->uptr.node_monitors = NULL;
+ erts_monitor_tree_replace(&ERTS_P_MONITORS(p), mon, mon2);
+ erts_monitor_list_insert(&mdep2->uptr.node_monitors, mon);
+ mon->flags |= ERTS_ML_FLG_IN_SUBTABLE;
+ mdep = mdep2;
+ }
+
+ mdep->u.refc++;
+
+ break;
+ }
+
+ default:
+ ERTS_ASSERT(! "Invalid dsig prepare result");
+ }
+
+ erts_de_runlock(dep);
+
+ break;
}
- erts_de_links_unlock(dep);
- erts_proc_unlock(p, ERTS_PROC_LOCK_LINK);
+ default:
+ goto badarg;
+ }
- done:
- BIF_RET(am_true);
+ ERTS_BIF_PREP_RET(ret, am_true);
+
+do_return:
+
+ if (dep)
+ erts_deref_dist_entry(dep);
+
+ return ret;
+
+do_trap:
+ ERTS_BIF_PREP_TRAP3(ret, dmonitor_node_trap, p, Node, Bool, Options);
+ goto do_return;
+
+badarg:
+ ERTS_BIF_PREP_ERROR(ret, p, BADARG);
+ goto do_return;
}
BIF_RETTYPE monitor_node_3(BIF_ALIST_3)
@@ -3763,18 +3973,9 @@ BIF_RETTYPE net_kernel_dflag_unicode_io_1(BIF_ALIST_1)
#define ERTS_NODES_MON_OPT_TYPES \
(ERTS_NODES_MON_OPT_TYPE_VISIBLE|ERTS_NODES_MON_OPT_TYPE_HIDDEN)
-typedef struct ErtsNodesMonitor_ ErtsNodesMonitor;
-struct ErtsNodesMonitor_ {
- ErtsNodesMonitor *prev;
- ErtsNodesMonitor *next;
- Process *proc;
- Uint16 opts;
- Uint16 no;
-};
-
static erts_mtx_t nodes_monitors_mtx;
-static ErtsNodesMonitor *nodes_monitors;
-static ErtsNodesMonitor *nodes_monitors_end;
+static ErtsMonitor *nodes_monitors;
+static Uint no_nodes_monitors;
/*
* Nodes monitors are stored in a double linked list. 'nodes_monitors'
@@ -3793,109 +3994,169 @@ init_nodes_monitors(void)
erts_mtx_init(&nodes_monitors_mtx, "nodes_monitors", NIL,
ERTS_LOCK_FLAGS_PROPERTY_STATIC | ERTS_LOCK_FLAGS_CATEGORY_DISTRIBUTION);
nodes_monitors = NULL;
- nodes_monitors_end = NULL;
+ no_nodes_monitors = 0;
}
-static ERTS_INLINE Uint
-nodes_mon_msg_sz(ErtsNodesMonitor *nmp, Eterm what, Eterm reason)
+Eterm
+erts_monitor_nodes(Process *c_p, Eterm on, Eterm olist)
{
- Uint sz;
- if (!nmp->opts) {
- sz = 3;
- }
- else {
- sz = 0;
+ Eterm key, old_value, opts_list = olist;
+ Uint opts = (Uint) 0;
- if (nmp->opts & ERTS_NODES_MON_OPT_TYPES)
- sz += 2 + 3;
+ ASSERT(c_p);
+ ERTS_LC_ASSERT(erts_proc_lc_my_proc_locks(c_p) == ERTS_PROC_LOCK_MAIN);
- if (what == am_nodedown
- && (nmp->opts & ERTS_NODES_MON_OPT_DOWN_REASON)) {
- if (is_not_immed(reason))
- sz += size_object(reason);
- sz += 2 + 3;
+ if (on != am_true && on != am_false)
+ return THE_NON_VALUE;
+
+ if (is_not_nil(opts_list)) {
+ int all = 0, visible = 0, hidden = 0;
+
+ while (is_list(opts_list)) {
+ Eterm *cp = list_val(opts_list);
+ Eterm opt = CAR(cp);
+ opts_list = CDR(cp);
+ if (opt == am_nodedown_reason)
+ opts |= ERTS_NODES_MON_OPT_DOWN_REASON;
+ else if (is_tuple(opt)) {
+ Eterm* tp = tuple_val(opt);
+ if (arityval(tp[0]) != 2)
+ return THE_NON_VALUE;
+ switch (tp[1]) {
+ case am_node_type:
+ switch (tp[2]) {
+ case am_visible:
+ if (hidden || all)
+ return THE_NON_VALUE;
+ opts |= ERTS_NODES_MON_OPT_TYPE_VISIBLE;
+ visible = 1;
+ break;
+ case am_hidden:
+ if (visible || all)
+ return THE_NON_VALUE;
+ opts |= ERTS_NODES_MON_OPT_TYPE_HIDDEN;
+ hidden = 1;
+ break;
+ case am_all:
+ if (visible || hidden)
+ return THE_NON_VALUE;
+ opts |= ERTS_NODES_MON_OPT_TYPES;
+ all = 1;
+ break;
+ default:
+ return THE_NON_VALUE;
+ }
+ break;
+ default:
+ return THE_NON_VALUE;
+ }
+ }
+ else {
+ return THE_NON_VALUE;
+ }
}
- sz += 4;
+ if (is_not_nil(opts_list))
+ return THE_NON_VALUE;
+ }
+
+ key = make_small(opts);
+
+ if (on == am_true) {
+ ErtsMonitorDataExtended *mdep;
+ ErtsMonitor *omon;
+ int created;
+ omon = erts_monitor_tree_lookup_create(&ERTS_P_MONITORS(c_p),
+ &created,
+ ERTS_MON_TYPE_NODES,
+ c_p->common.id,
+ key);
+ mdep = (ErtsMonitorDataExtended *) erts_monitor_to_data(omon);
+ if (created) {
+ erts_mtx_lock(&nodes_monitors_mtx);
+ no_nodes_monitors++;
+ erts_monitor_list_insert(&nodes_monitors, &mdep->md.target);
+ erts_mtx_unlock(&nodes_monitors_mtx);
+ }
+ old_value = mdep->u.refc;
+ mdep->u.refc++;
}
- return sz;
+ else {
+ ErtsMonitorDataExtended *mdep;
+ ErtsMonitor *omon;
+ omon = erts_monitor_tree_lookup(ERTS_P_MONITORS(c_p), key);
+ if (!omon)
+ old_value = 0;
+ else {
+ mdep = (ErtsMonitorDataExtended *) erts_monitor_to_data(omon);
+ old_value = mdep->u.refc;
+ ASSERT(mdep->u.refc > 0);
+ erts_mtx_lock(&nodes_monitors_mtx);
+ ASSERT(no_nodes_monitors > 0);
+ no_nodes_monitors--;
+ ASSERT(erts_monitor_is_in_table(&mdep->md.target));
+ erts_monitor_list_delete(&nodes_monitors, &mdep->md.target);
+ erts_mtx_unlock(&nodes_monitors_mtx);
+ erts_monitor_tree_delete(&ERTS_P_MONITORS(c_p), omon);
+ erts_monitor_release_both((ErtsMonitorData *) mdep);
+ }
+ }
+
+ return erts_make_integer(old_value, c_p);
}
-static ERTS_INLINE void
-send_nodes_mon_msg(Process *rp,
- ErtsProcLocks *rp_locksp,
- ErtsNodesMonitor *nmp,
- Eterm node,
- Eterm what,
- Eterm type,
- Eterm reason,
- Uint sz)
+void
+erts_monitor_nodes_delete(ErtsMonitor *omon)
{
- Eterm msg;
- Eterm *hp;
- ErtsMessage *mp;
- ErlOffHeap *ohp;
-#ifdef DEBUG
- Eterm *hend;
-#endif
+ ErtsMonitorData *mdp;
- mp = erts_alloc_message_heap(rp, rp_locksp, sz, &hp, &ohp);
-#ifdef DEBUG
- hend = hp + sz;
-#endif
+ ASSERT(omon->type == ERTS_MON_TYPE_NODES);
+ ASSERT(erts_monitor_is_origin(omon));
- if (!nmp->opts) {
- msg = TUPLE2(hp, what, node);
-#ifdef DEBUG
- hp += 3;
-#endif
- }
- else {
- Eterm tup;
- Eterm info = NIL;
+ mdp = erts_monitor_to_data(omon);
- if (nmp->opts & (ERTS_NODES_MON_OPT_TYPE_VISIBLE
- | ERTS_NODES_MON_OPT_TYPE_HIDDEN)) {
+ erts_mtx_lock(&nodes_monitors_mtx);
+ ASSERT(erts_monitor_is_in_table(&mdp->target));
+ ASSERT(no_nodes_monitors > 0);
+ no_nodes_monitors--;
+ erts_monitor_list_delete(&nodes_monitors, &mdp->target);
+ erts_mtx_unlock(&nodes_monitors_mtx);
+ erts_monitor_release_both(mdp);
+}
- tup = TUPLE2(hp, am_node_type, type);
- hp += 3;
- info = CONS(hp, tup, info);
- hp += 2;
- }
+typedef struct {
+ Eterm pid;
+ Eterm options;
+} ErtsNodesMonitorData;
- if (what == am_nodedown
- && (nmp->opts & ERTS_NODES_MON_OPT_DOWN_REASON)) {
- Eterm rsn_cpy;
-
- if (is_immed(reason))
- rsn_cpy = reason;
- else {
- Eterm rsn_sz = size_object(reason);
- rsn_cpy = copy_struct(reason, rsn_sz, &hp, ohp);
- }
+typedef struct {
+ ErtsNodesMonitorData *nmdp;
+ Uint i;
+} ErtsNodesMonitorContext;
- tup = TUPLE2(hp, am_nodedown_reason, rsn_cpy);
- hp += 3;
- info = CONS(hp, tup, info);
- hp += 2;
- }
+static void
+save_nodes_monitor(ErtsMonitor *mon, void *vctxt)
+{
+ ErtsNodesMonitorContext *ctxt = vctxt;
+ ErtsMonitorData *mdp = erts_monitor_to_data(mon);
- msg = TUPLE3(hp, what, node, info);
-#ifdef DEBUG
- hp += 4;
-#endif
- }
+ ASSERT(erts_monitor_is_target(mon));
+ ASSERT(mon->type == ERTS_MON_TYPE_NODES);
+
+ ctxt->nmdp[ctxt->i].pid = mon->other.item;
+ ctxt->nmdp[ctxt->i].options = mdp->origin.other.item;
- ASSERT(hend == hp);
- erts_queue_message(rp, *rp_locksp, mp, msg, am_system);
+ ctxt->i++;
}
static void
send_nodes_mon_msgs(Process *c_p, Eterm what, Eterm node, Eterm type, Eterm reason)
{
- ErtsNodesMonitor *nmp;
- ErtsProcLocks rp_locks = 0; /* Init to shut up false warning */
- Process *rp = NULL;
+ Uint opts;
+ Uint i, no, reason_size;
+ ErtsNodesMonitorData def_buf[100];
+ ErtsNodesMonitorData *nmdp = &def_buf[0];
+ ErtsNodesMonitorContext ctxt;
ASSERT(is_immed(what));
ASSERT(is_immed(node));
@@ -3916,31 +4177,44 @@ send_nodes_mon_msgs(Process *c_p, Eterm what, Eterm node, Eterm type, Eterm reas
}
#endif
- ERTS_LC_ASSERT(!c_p
- || (erts_proc_lc_my_proc_locks(c_p)
- == ERTS_PROC_LOCK_MAIN));
+ ctxt.i = 0;
+
+ reason_size = is_immed(reason) ? 0 : size_object(reason);
+
erts_mtx_lock(&nodes_monitors_mtx);
+ if (no_nodes_monitors > sizeof(def_buf)/sizeof(def_buf[0]))
+ nmdp = erts_alloc(ERTS_ALC_T_TMP,
+ no_nodes_monitors*sizeof(ErtsNodesMonitorData));
+ ctxt.nmdp = nmdp;
+ erts_monitor_list_foreach(nodes_monitors,
+ save_nodes_monitor,
+ (void *) &ctxt);
+
+ ASSERT(ctxt.i == no_nodes_monitors);
+ no = no_nodes_monitors;
- for (nmp = nodes_monitors; nmp; nmp = nmp->next) {
- int i;
- Uint16 no;
- Uint sz;
+ erts_mtx_unlock(&nodes_monitors_mtx);
- ASSERT(nmp->proc != NULL);
+ for (i = 0; i < no; i++) {
+ Eterm tmp_heap[3+2+3+2+4 /* max need */];
+ Eterm *hp, msg;
+ Uint hsz;
- if (!nmp->opts) {
+ ASSERT(is_small(nmdp[i].options));
+ opts = (Uint) signed_val(nmdp[i].options);
+ if (!opts) {
if (type != am_visible)
continue;
}
else {
switch (type) {
case am_hidden:
- if (!(nmp->opts & ERTS_NODES_MON_OPT_TYPE_HIDDEN))
+ if (!(opts & ERTS_NODES_MON_OPT_TYPE_HIDDEN))
continue;
break;
case am_visible:
- if ((nmp->opts & ERTS_NODES_MON_OPT_TYPES)
- && !(nmp->opts & ERTS_NODES_MON_OPT_TYPE_VISIBLE))
+ if ((opts & ERTS_NODES_MON_OPT_TYPES)
+ && !(opts & ERTS_NODES_MON_OPT_TYPE_VISIBLE))
continue;
break;
default:
@@ -3948,342 +4222,162 @@ send_nodes_mon_msgs(Process *c_p, Eterm what, Eterm node, Eterm type, Eterm reas
}
}
- if (rp != nmp->proc) {
- if (rp) {
- if (rp == c_p)
- rp_locks &= ~ERTS_PROC_LOCK_MAIN;
- erts_proc_unlock(rp, rp_locks);
- }
-
- rp = nmp->proc;
- rp_locks = 0;
- if (rp == c_p)
- rp_locks |= ERTS_PROC_LOCK_MAIN;
- }
+ hsz = 0;
+ hp = &tmp_heap[0];
- ASSERT(rp);
-
- sz = nodes_mon_msg_sz(nmp, what, reason);
-
- for (i = 0, no = nmp->no; i < no; i++)
- send_nodes_mon_msg(rp,
- &rp_locks,
- nmp,
- node,
- what,
- type,
- reason,
- sz);
- }
+ if (!opts) {
+ msg = TUPLE2(hp, what, node);
+ hp += 3;
+ }
+ else {
+ Eterm tup;
+ Eterm info = NIL;
- if (rp) {
- if (rp == c_p)
- rp_locks &= ~ERTS_PROC_LOCK_MAIN;
- erts_proc_unlock(rp, rp_locks);
- }
+ if (opts & (ERTS_NODES_MON_OPT_TYPE_VISIBLE
+ | ERTS_NODES_MON_OPT_TYPE_HIDDEN)) {
- erts_mtx_unlock(&nodes_monitors_mtx);
-}
+ tup = TUPLE2(hp, am_node_type, type);
+ hp += 3;
+ info = CONS(hp, tup, info);
+ hp += 2;
+ }
-static Eterm
-insert_nodes_monitor(Process *c_p, Uint32 opts)
-{
- Uint16 no = 1;
- Eterm res = am_false;
- ErtsNodesMonitor *xnmp, *nmp;
+ if (what == am_nodedown
+ && (opts & ERTS_NODES_MON_OPT_DOWN_REASON)) {
+ hsz += reason_size;
+ tup = TUPLE2(hp, am_nodedown_reason, reason);
+ hp += 3;
+ info = CONS(hp, tup, info);
+ hp += 2;
+ }
- ERTS_LC_ASSERT(erts_lc_mtx_is_locked(&nodes_monitors_mtx));
- ERTS_LC_ASSERT(erts_proc_lc_my_proc_locks(c_p) & ERTS_PROC_LOCK_MAIN);
+ msg = TUPLE3(hp, what, node, info);
+ hp += 4;
+ }
- xnmp = c_p->nodes_monitors;
- if (xnmp) {
- ASSERT(!xnmp->prev || xnmp->prev->proc != c_p);
+ ASSERT(hp - &tmp_heap[0] <= sizeof(tmp_heap)/sizeof(tmp_heap[0]));
- while (1) {
- ASSERT(xnmp->proc == c_p);
- if (xnmp->opts == opts)
- break;
- if (!xnmp->next || xnmp->next->proc != c_p)
- break;
- xnmp = xnmp->next;
- }
- ASSERT(xnmp);
- ASSERT(xnmp->proc == c_p);
- ASSERT(xnmp->opts == opts
- || !xnmp->next
- || xnmp->next->proc != c_p);
-
- if (xnmp->opts != opts)
- goto alloc_new;
- else {
- res = am_true;
- no = xnmp->no++;
- if (!xnmp->no) {
- /*
- * 'no' wrapped; transfer all prevous monitors to new
- * element (which will be the next element in the list)
- * and set this to one...
- */
- xnmp->no = 1;
- goto alloc_new;
- }
- }
- }
- else {
- alloc_new:
- nmp = erts_alloc(ERTS_ALC_T_NODES_MON, sizeof(ErtsNodesMonitor));
- nmp->proc = c_p;
- nmp->opts = opts;
- nmp->no = no;
-
- if (xnmp) {
- ASSERT(nodes_monitors);
- ASSERT(c_p->nodes_monitors);
- nmp->next = xnmp->next;
- nmp->prev = xnmp;
- xnmp->next = nmp;
- if (nmp->next) {
- ASSERT(nodes_monitors_end != xnmp);
- ASSERT(nmp->next->prev == xnmp);
- nmp->next->prev = nmp;
- }
- else {
- ASSERT(nodes_monitors_end == xnmp);
- nodes_monitors_end = nmp;
- }
- }
- else {
- ASSERT(!c_p->nodes_monitors);
- c_p->nodes_monitors = nmp;
- nmp->next = NULL;
- nmp->prev = nodes_monitors_end;
- if (nodes_monitors_end) {
- ASSERT(nodes_monitors);
- nodes_monitors_end->next = nmp;
- }
- else {
- ASSERT(!nodes_monitors);
- nodes_monitors = nmp;
- }
- nodes_monitors_end = nmp;
- }
- }
- return res;
-}
+ hsz += hp - &tmp_heap[0];
-static Eterm
-remove_nodes_monitors(Process *c_p, Uint32 opts, int all)
-{
- Eterm res = am_false;
- ErtsNodesMonitor *nmp;
-
- ERTS_LC_ASSERT(erts_lc_mtx_is_locked(&nodes_monitors_mtx));
- ERTS_LC_ASSERT(erts_proc_lc_my_proc_locks(c_p) & ERTS_PROC_LOCK_MAIN);
-
- nmp = c_p->nodes_monitors;
- ASSERT(!nmp || !nmp->prev || nmp->prev->proc != c_p);
-
- while (nmp && nmp->proc == c_p) {
- if (!all && nmp->opts != opts)
- nmp = nmp->next;
- else { /* if (all || nmp->opts == opts) */
- ErtsNodesMonitor *free_nmp;
- res = am_true;
- if (nmp->prev) {
- ASSERT(nodes_monitors != nmp);
- nmp->prev->next = nmp->next;
- }
- else {
- ASSERT(nodes_monitors == nmp);
- nodes_monitors = nmp->next;
- }
- if (nmp->next) {
- ASSERT(nodes_monitors_end != nmp);
- nmp->next->prev = nmp->prev;
- }
- else {
- ASSERT(nodes_monitors_end == nmp);
- nodes_monitors_end = nmp->prev;
- }
- free_nmp = nmp;
- nmp = nmp->next;
- if (c_p->nodes_monitors == free_nmp)
- c_p->nodes_monitors = nmp && nmp->proc == c_p ? nmp : NULL;
- erts_free(ERTS_ALC_T_NODES_MON, free_nmp);
- }
+ erts_proc_sig_send_persistent_monitor_msg(ERTS_MON_TYPE_NODES,
+ nmdp[i].options,
+ am_system,
+ nmdp[i].pid,
+ msg,
+ hsz);
}
-
- ASSERT(!all || !c_p->nodes_monitors);
- return res;
-}
-void
-erts_delete_nodes_monitors(Process *c_p, ErtsProcLocks locks)
-{
-#if defined(ERTS_ENABLE_LOCK_CHECK)
- if (c_p) {
- ErtsProcLocks might_unlock = locks & ~ERTS_PROC_LOCK_MAIN;
- if (might_unlock)
- erts_proc_lc_might_unlock(c_p, might_unlock);
- }
-#endif
- if (erts_mtx_trylock(&nodes_monitors_mtx) == EBUSY) {
- ErtsProcLocks unlock_locks = locks & ~ERTS_PROC_LOCK_MAIN;
- if (c_p && unlock_locks)
- erts_proc_unlock(c_p, unlock_locks);
- erts_mtx_lock(&nodes_monitors_mtx);
- if (c_p && unlock_locks)
- erts_proc_lock(c_p, unlock_locks);
- }
- remove_nodes_monitors(c_p, 0, 1);
- erts_mtx_unlock(&nodes_monitors_mtx);
+ if (nmdp != &def_buf[0])
+ erts_free(ERTS_ALC_T_TMP, nmdp);
}
+
-Eterm
-erts_monitor_nodes(Process *c_p, Eterm on, Eterm olist)
-{
+typedef struct {
+ Eterm **hpp;
+ Uint *szp;
Eterm res;
- Eterm opts_list = olist;
- Uint16 opts = (Uint16) 0;
-
- ASSERT(c_p);
- ERTS_LC_ASSERT(erts_proc_lc_my_proc_locks(c_p) == ERTS_PROC_LOCK_MAIN);
+} ErtsNodesMonitorInfoContext;
- if (on != am_true && on != am_false)
- return THE_NON_VALUE;
-
- if (is_not_nil(opts_list)) {
- int all = 0, visible = 0, hidden = 0;
- while (is_list(opts_list)) {
- Eterm *cp = list_val(opts_list);
- Eterm opt = CAR(cp);
- opts_list = CDR(cp);
- if (opt == am_nodedown_reason)
- opts |= ERTS_NODES_MON_OPT_DOWN_REASON;
- else if (is_tuple(opt)) {
- Eterm* tp = tuple_val(opt);
- if (arityval(tp[0]) != 2)
- return THE_NON_VALUE;
- switch (tp[1]) {
- case am_node_type:
- switch (tp[2]) {
- case am_visible:
- if (hidden || all)
- return THE_NON_VALUE;
- opts |= ERTS_NODES_MON_OPT_TYPE_VISIBLE;
- visible = 1;
- break;
- case am_hidden:
- if (visible || all)
- return THE_NON_VALUE;
- opts |= ERTS_NODES_MON_OPT_TYPE_HIDDEN;
- hidden = 1;
- break;
- case am_all:
- if (visible || hidden)
- return THE_NON_VALUE;
- opts |= ERTS_NODES_MON_OPT_TYPES;
- all = 1;
- break;
- default:
- return THE_NON_VALUE;
- }
- break;
- default:
- return THE_NON_VALUE;
- }
- }
- else {
- return THE_NON_VALUE;
- }
- }
-
- if (is_not_nil(opts_list))
- return THE_NON_VALUE;
+static void
+nodes_monitor_info(ErtsMonitor *mon, void *vctxt)
+{
+ ErtsMonitorDataExtended *mdep;
+ ErtsNodesMonitorInfoContext *ctxt = vctxt;
+ Uint no, i, opts, *szp;
+ Eterm **hpp, res;
+
+ hpp = ctxt->hpp;
+ szp = ctxt->szp;
+ res = ctxt->res;
+
+ ASSERT(erts_monitor_is_target(mon));
+ ASSERT(mon->type == ERTS_MON_TYPE_NODES);
+ mdep = (ErtsMonitorDataExtended *) erts_monitor_to_data(mon);
+ no = mdep->u.refc;
+
+ ASSERT(is_small(mdep->md.origin.other.item));
+ opts = (Uint) signed_val(mdep->md.origin.other.item);
+
+ for (i = 0; i < no; i++) {
+ Eterm olist = NIL;
+ if (opts & ERTS_NODES_MON_OPT_TYPES) {
+ Eterm type;
+ switch (opts & ERTS_NODES_MON_OPT_TYPES) {
+ case ERTS_NODES_MON_OPT_TYPES: type = am_all; break;
+ case ERTS_NODES_MON_OPT_TYPE_VISIBLE: type = am_visible; break;
+ case ERTS_NODES_MON_OPT_TYPE_HIDDEN: type = am_hidden; break;
+ default: erts_exit(ERTS_ABORT_EXIT, "Bad node type found\n");
+ }
+ olist = erts_bld_cons(hpp, szp,
+ erts_bld_tuple(hpp, szp, 2,
+ am_node_type,
+ type),
+ olist);
+ }
+ if (opts & ERTS_NODES_MON_OPT_DOWN_REASON)
+ olist = erts_bld_cons(hpp, szp, am_nodedown_reason, olist);
+ res = erts_bld_cons(hpp, szp,
+ erts_bld_tuple(hpp, szp, 2,
+ mon->other.item,
+ olist),
+ res);
}
- erts_mtx_lock(&nodes_monitors_mtx);
-
- if (on == am_true)
- res = insert_nodes_monitor(c_p, opts);
- else
- res = remove_nodes_monitors(c_p, opts, 0);
-
- erts_mtx_unlock(&nodes_monitors_mtx);
-
- return res;
+ ctxt->hpp = hpp;
+ ctxt->szp = szp;
+ ctxt->res = res;
}
-/*
- * Note, this function is only used for debuging.
- */
-
Eterm
erts_processes_monitoring_nodes(Process *c_p)
{
- ErtsNodesMonitor *nmp;
- Eterm res;
+ /*
+ * Note, this function is only used for debugging.
+ */
+ ErtsNodesMonitorInfoContext ctxt;
Eterm *hp;
- Eterm **hpp;
Uint sz;
- Uint *szp;
#ifdef DEBUG
Eterm *hend;
#endif
ASSERT(c_p);
ERTS_LC_ASSERT(erts_proc_lc_my_proc_locks(c_p) == ERTS_PROC_LOCK_MAIN);
+
+ erts_proc_unlock(c_p, ERTS_PROC_LOCK_MAIN);
+ erts_thr_progress_block();
+
erts_mtx_lock(&nodes_monitors_mtx);
sz = 0;
- szp = &sz;
- hpp = NULL;
+ ctxt.szp = &sz;
+ ctxt.hpp = NULL;
- bld_result:
- res = NIL;
-
- for (nmp = nodes_monitors_end; nmp; nmp = nmp->prev) {
- Uint16 i;
- for (i = 0; i < nmp->no; i++) {
- Eterm olist = NIL;
- if (nmp->opts & ERTS_NODES_MON_OPT_TYPES) {
- Eterm type;
- switch (nmp->opts & ERTS_NODES_MON_OPT_TYPES) {
- case ERTS_NODES_MON_OPT_TYPES: type = am_all; break;
- case ERTS_NODES_MON_OPT_TYPE_VISIBLE: type = am_visible; break;
- case ERTS_NODES_MON_OPT_TYPE_HIDDEN: type = am_hidden; break;
- default: erts_exit(ERTS_ABORT_EXIT, "Bad node type found\n");
- }
- olist = erts_bld_cons(hpp, szp,
- erts_bld_tuple(hpp, szp, 2,
- am_node_type,
- type),
- olist);
- }
- if (nmp->opts & ERTS_NODES_MON_OPT_DOWN_REASON)
- olist = erts_bld_cons(hpp, szp, am_nodedown_reason, olist);
- res = erts_bld_cons(hpp, szp,
- erts_bld_tuple(hpp, szp, 2,
- nmp->proc->common.id,
- olist),
- res);
- }
- }
+ while (1) {
+ ctxt.res = NIL;
+
+ erts_monitor_list_foreach(nodes_monitors,
+ nodes_monitor_info,
+ (void *) &ctxt);
+
+ if (ctxt.hpp)
+ break;
- if (!hpp) {
hp = HAlloc(c_p, sz);
#ifdef DEBUG
hend = hp + sz;
#endif
- hpp = &hp;
- szp = NULL;
- goto bld_result;
+ ctxt.hpp = &hp;
+ ctxt.szp = NULL;
}
ASSERT(hp == hend);
erts_mtx_unlock(&nodes_monitors_mtx);
- return res;
+ erts_thr_progress_unblock();
+ erts_proc_lock(c_p, ERTS_PROC_LOCK_MAIN);
+
+ return ctxt.res;
}
diff --git a/erts/emulator/beam/dist.h b/erts/emulator/beam/dist.h
index d4765c50b8..dda2029a4c 100644
--- a/erts/emulator/beam/dist.h
+++ b/erts/emulator/beam/dist.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1996-2017. All Rights Reserved.
+ * Copyright Ericsson AB 1996-2018. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -40,11 +40,53 @@
#define DFLAG_UNICODE_IO 0x1000
#define DFLAG_DIST_HDR_ATOM_CACHE 0x2000
#define DFLAG_SMALL_ATOM_TAGS 0x4000
-#define DFLAG_INTERNAL_TAGS 0x8000
+#define DFLAG_INTERNAL_TAGS 0x8000 /* used by ETS 'compressed' option */
#define DFLAG_UTF8_ATOMS 0x10000
#define DFLAG_MAP_TAG 0x20000
#define DFLAG_BIG_CREATION 0x40000
#define DFLAG_SEND_SENDER 0x80000
+#define DFLAG_BIG_SEQTRACE_LABELS 0x100000
+#define DFLAG_NO_MAGIC 0x200000 /* internal for pending connection */
+
+/* Mandatory flags for distribution */
+#define DFLAG_DIST_MANDATORY (DFLAG_EXTENDED_REFERENCES \
+ | DFLAG_EXTENDED_PIDS_PORTS \
+ | DFLAG_UTF8_ATOMS \
+ | DFLAG_NEW_FUN_TAGS)
+
+/*
+ * Additional optimistic flags when encoding toward pending connection.
+ * If remote node (erl_interface) does not supporting these then we may need
+ * to transcode messages enqueued before connection setup was finished.
+ */
+#define DFLAG_DIST_HOPEFULLY (DFLAG_EXPORT_PTR_TAG \
+ | DFLAG_BIT_BINARIES \
+ | DFLAG_DIST_MONITOR \
+ | DFLAG_DIST_MONITOR_NAME)
+
+/* Our preferred set of flags. Used for connection setup handshake */
+#define DFLAG_DIST_DEFAULT (DFLAG_DIST_MANDATORY | DFLAG_DIST_HOPEFULLY \
+ | DFLAG_FUN_TAGS \
+ | DFLAG_NEW_FLOATS \
+ | DFLAG_UNICODE_IO \
+ | DFLAG_DIST_HDR_ATOM_CACHE \
+ | DFLAG_SMALL_ATOM_TAGS \
+ | DFLAG_UTF8_ATOMS \
+ | DFLAG_MAP_TAG \
+ | DFLAG_BIG_CREATION \
+ | DFLAG_SEND_SENDER \
+ | DFLAG_BIG_SEQTRACE_LABELS)
+
+/* Flags addable by local distr implementations */
+#define DFLAG_DIST_ADDABLE DFLAG_DIST_DEFAULT
+
+/* Flags rejectable by local distr implementation */
+#define DFLAG_DIST_REJECTABLE (DFLAG_DIST_HDR_ATOM_CACHE \
+ | DFLAG_HIDDEN_ATOM_CACHE \
+ | DFLAG_ATOM_CACHE)
+
+/* Flags for all features needing strict order delivery */
+#define DFLAG_DIST_STRICT_ORDER DFLAG_DIST_HDR_ATOM_CACHE
/* All flags that should be enabled when term_to_binary/1 is used. */
#define TERM_TO_BINARY_DFLAGS (DFLAG_EXTENDED_REFERENCES \
@@ -79,52 +121,33 @@
#define DOP_SEND_SENDER_TT 23
/* distribution trap functions */
-extern Export* dsend2_trap;
-extern Export* dsend3_trap;
-extern Export* dlink_trap;
-extern Export* dunlink_trap;
extern Export* dmonitor_node_trap;
-extern Export* dgroup_leader_trap;
-extern Export* dexit_trap;
-extern Export* dmonitor_p_trap;
typedef enum {
ERTS_DSP_NO_LOCK,
- ERTS_DSP_RLOCK,
- ERTS_DSP_RWLOCK
+ ERTS_DSP_RLOCK
} ErtsDSigPrepLock;
typedef struct {
Process *proc;
DistEntry *dep;
+ Eterm node; /* used if dep == NULL */
Eterm cid;
Eterm connection_id;
int no_suspend;
} ErtsDSigData;
-#define ERTS_DE_IS_NOT_CONNECTED(DEP) \
- (ERTS_LC_ASSERT(erts_lc_rwmtx_is_rlocked(&(DEP)->rwmtx) \
- || erts_lc_rwmtx_is_rwlocked(&(DEP)->rwmtx)), \
- (is_nil((DEP)->cid) || ((DEP)->status & ERTS_DE_SFLG_EXITING)))
-
-#define ERTS_DE_IS_CONNECTED(DEP) \
- (!ERTS_DE_IS_NOT_CONNECTED((DEP)))
-
#define ERTS_DE_BUSY_LIMIT (1024*1024)
extern int erts_dist_buf_busy_limit;
extern int erts_is_alive;
/*
* erts_dsig_prepare() prepares a send of a distributed signal.
- * One of the values defined below are returned. If the returned
- * value is another than ERTS_DSIG_PREP_CONNECTED, the
- * distributed signal cannot be sent before appropriate actions
- * have been taken. Appropriate actions would typically be setting
- * up the connection.
+ * One of the values defined below are returned.
*/
-/* Connected; signal can be sent. */
+/* Connected; signals can be enqueued and sent. */
#define ERTS_DSIG_PREP_CONNECTED 0
/* Not connected; connection needs to be set up. */
#define ERTS_DSIG_PREP_NOT_CONNECTED 1
@@ -132,43 +155,80 @@ extern int erts_is_alive;
#define ERTS_DSIG_PREP_WOULD_SUSPEND 2
/* System not alive (distributed) */
#define ERTS_DSIG_PREP_NOT_ALIVE 3
+/* Pending connection; signals can be enqueued */
+#define ERTS_DSIG_PREP_PENDING 4
ERTS_GLB_INLINE int erts_dsig_prepare(ErtsDSigData *,
- DistEntry *,
+ DistEntry*,
Process *,
+ ErtsProcLocks,
ErtsDSigPrepLock,
+ int,
int);
ERTS_GLB_INLINE
void erts_schedule_dist_command(Port *, DistEntry *);
+int erts_auto_connect(DistEntry* dep, Process *proc, ErtsProcLocks proc_locks);
+
#if ERTS_GLB_INLINE_INCL_FUNC_DEF
ERTS_GLB_INLINE int
erts_dsig_prepare(ErtsDSigData *dsdp,
DistEntry *dep,
Process *proc,
+ ErtsProcLocks proc_locks,
ErtsDSigPrepLock dspl,
- int no_suspend)
+ int no_suspend,
+ int connect)
{
- int failure;
+ int res;
+
if (!erts_is_alive)
return ERTS_DSIG_PREP_NOT_ALIVE;
- if (!dep)
- return ERTS_DSIG_PREP_NOT_CONNECTED;
- if (dspl == ERTS_DSP_RWLOCK)
- erts_de_rwlock(dep);
- else
- erts_de_rlock(dep);
- if (ERTS_DE_IS_NOT_CONNECTED(dep)) {
- failure = ERTS_DSIG_PREP_NOT_CONNECTED;
+ if (!dep) {
+ ASSERT(!connect);
+ return ERTS_DSIG_PREP_NOT_CONNECTED;
+ }
+
+#ifdef ERTS_ENABLE_LOCK_CHECK
+ if (connect) {
+ erts_proc_lc_might_unlock(proc, proc_locks);
+ }
+#endif
+
+retry:
+ erts_de_rlock(dep);
+
+ if (dep->state == ERTS_DE_STATE_CONNECTED) {
+ res = ERTS_DSIG_PREP_CONNECTED;
+ }
+ else if (dep->state == ERTS_DE_STATE_PENDING) {
+ res = ERTS_DSIG_PREP_PENDING;
+ }
+ else if (dep->state == ERTS_DE_STATE_EXITING) {
+ res = ERTS_DSIG_PREP_NOT_CONNECTED;
goto fail;
}
+ else if (connect) {
+ ASSERT(dep->state == ERTS_DE_STATE_IDLE);
+ erts_de_runlock(dep);
+ if (!erts_auto_connect(dep, proc, proc_locks)) {
+ return ERTS_DSIG_PREP_NOT_ALIVE;
+ }
+ goto retry;
+ }
+ else {
+ ASSERT(dep->state == ERTS_DE_STATE_IDLE);
+ res = ERTS_DSIG_PREP_NOT_CONNECTED;
+ goto fail;
+ }
+
if (no_suspend) {
- if (erts_atomic32_read_acqb(&dep->qflgs) & ERTS_DE_QFLG_BUSY) {
- failure = ERTS_DSIG_PREP_WOULD_SUSPEND;
+ if (erts_atomic32_read_acqb(&dep->qflgs) & ERTS_DE_QFLG_BUSY) {
+ res = ERTS_DSIG_PREP_WOULD_SUSPEND;
goto fail;
- }
+ }
}
dsdp->proc = proc;
dsdp->dep = dep;
@@ -177,15 +237,11 @@ erts_dsig_prepare(ErtsDSigData *dsdp,
dsdp->no_suspend = no_suspend;
if (dspl == ERTS_DSP_NO_LOCK)
erts_de_runlock(dep);
- return ERTS_DSIG_PREP_CONNECTED;
+ return res;
fail:
- if (dspl == ERTS_DSP_RWLOCK)
- erts_de_rwunlock(dep);
- else
- erts_de_runlock(dep);
- return failure;
-
+ erts_de_runlock(dep);
+ return res;
}
ERTS_GLB_INLINE
@@ -219,58 +275,13 @@ void erts_schedule_dist_command(Port *prt, DistEntry *dist_entry)
#endif
-typedef struct {
- ErtsLink *d_lnk;
- ErtsLink *d_sub_lnk;
-} ErtsDistLinkData;
-
-ERTS_GLB_INLINE void erts_remove_dist_link(ErtsDistLinkData *,
- Eterm,
- Eterm,
- DistEntry *);
-ERTS_GLB_INLINE int erts_was_dist_link_removed(ErtsDistLinkData *);
-ERTS_GLB_INLINE void erts_destroy_dist_link(ErtsDistLinkData *);
-
-#if ERTS_GLB_INLINE_INCL_FUNC_DEF
-
-ERTS_GLB_INLINE void
-erts_remove_dist_link(ErtsDistLinkData *dldp,
- Eterm lid,
- Eterm rid,
- DistEntry *dep)
-{
- erts_de_links_lock(dep);
- dldp->d_lnk = erts_lookup_link(dep->nlinks, lid);
- if (!dldp->d_lnk)
- dldp->d_sub_lnk = NULL;
- else {
- dldp->d_sub_lnk = erts_remove_link(&ERTS_LINK_ROOT(dldp->d_lnk), rid);
- dldp->d_lnk = (ERTS_LINK_ROOT(dldp->d_lnk)
- ? NULL
- : erts_remove_link(&dep->nlinks, lid));
- }
- erts_de_links_unlock(dep);
-}
-
-ERTS_GLB_INLINE int
-erts_was_dist_link_removed(ErtsDistLinkData *dldp)
-{
- return dldp->d_sub_lnk != NULL;
-}
-
-ERTS_GLB_INLINE void
-erts_destroy_dist_link(ErtsDistLinkData *dldp)
-{
- if (dldp->d_lnk)
- erts_destroy_link(dldp->d_lnk);
- if (dldp->d_sub_lnk)
- erts_destroy_link(dldp->d_sub_lnk);
-}
-
+#ifdef DEBUG
+#define ERTS_DBG_CHK_NO_DIST_LNK(D, R, L) \
+ erts_dbg_chk_no_dist_proc_link((D), (R), (L))
+#else
+#define ERTS_DBG_CHK_NO_DIST_LNK(D, R, L)
#endif
-
-
/* Define for testing */
/* #define EXTREME_TTB_TRAPPING 1 */
@@ -291,6 +302,7 @@ typedef struct TTBSizeContext_ {
typedef struct TTBEncodeContext_ {
Uint flags;
+ Uint hopefull_flags;
int level;
byte* ep;
Eterm obj;
@@ -332,7 +344,7 @@ struct erts_dsig_send_context {
Eterm ctl;
Eterm msg;
int force_busy;
- Uint32 pass_through_size;
+ Uint32 max_finalize_prepend;
Uint data_size, dhdr_ext_size;
ErtsAtomCacheMap *acmp;
ErtsDistOutputBuf *obuf;
@@ -346,11 +358,12 @@ struct erts_dsig_send_context {
typedef struct {
int suspend;
+ int connect;
Eterm ctl_heap[6];
ErtsDSigData dsd;
- DistEntry* dep_to_deref;
DistEntry *dep;
+ int deref_dep;
struct erts_dsig_send_context dss;
Eterm return_term;
diff --git a/erts/emulator/beam/erl_afit_alloc.c b/erts/emulator/beam/erl_afit_alloc.c
index 4ebe37ee1d..23efe3bba4 100644
--- a/erts/emulator/beam/erl_afit_alloc.c
+++ b/erts/emulator/beam/erl_afit_alloc.c
@@ -181,7 +181,7 @@ static struct {
static void ERTS_INLINE atom_init(Eterm *atom, char *name)
{
- *atom = am_atom_put(name, strlen(name));
+ *atom = am_atom_put(name, sys_strlen(name));
}
#define AM_INIT(AM) atom_init(&am.AM, #AM)
diff --git a/erts/emulator/beam/erl_alloc.c b/erts/emulator/beam/erl_alloc.c
index 88285d8be6..d99d2ea57b 100644
--- a/erts/emulator/beam/erl_alloc.c
+++ b/erts/emulator/beam/erl_alloc.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2002-2017. All Rights Reserved.
+ * Copyright Ericsson AB 2002-2018. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -38,9 +38,9 @@
#include "erl_db.h"
#include "erl_binary.h"
#include "erl_bits.h"
-#include "erl_instrument.h"
+#include "erl_mtrace.h"
#include "erl_mseg.h"
-#include "erl_monitors.h"
+#include "erl_monitor_link.h"
#include "erl_hl_timer.h"
#include "erl_cpu_topology.h"
#include "erl_thr_queue.h"
@@ -160,7 +160,7 @@ enum allctr_type {
GOODFIT,
BESTFIT,
AFIT,
- AOFIRSTFIT
+ FIRSTFIT
};
struct au_init {
@@ -202,8 +202,6 @@ typedef struct {
int top_pad;
AlcUInit_t alloc_util;
struct {
- int stat;
- int map;
char *mtrace;
char *nodename;
} instr;
@@ -366,16 +364,9 @@ set_default_exec_alloc_opts(struct au_init *ip)
ip->init.util.rmbcmt = 0;
ip->init.util.acul = 0;
-# ifdef ERTS_HAVE_EXEC_MMAPPER
- ip->init.util.mseg_alloc = &erts_alcu_mmapper_mseg_alloc;
- ip->init.util.mseg_realloc = &erts_alcu_mmapper_mseg_realloc;
- ip->init.util.mseg_dealloc = &erts_alcu_mmapper_mseg_dealloc;
- ip->init.util.mseg_mmapper = &erts_exec_mmapper;
-# else
ip->init.util.mseg_alloc = &erts_alcu_exec_mseg_alloc;
ip->init.util.mseg_realloc = &erts_alcu_exec_mseg_realloc;
ip->init.util.mseg_dealloc = &erts_alcu_exec_mseg_dealloc;
-# endif
}
#endif /* ERTS_ALC_A_EXEC */
@@ -435,6 +426,7 @@ set_default_binary_alloc_opts(struct au_init *ip)
#endif
ip->init.util.ts = ERTS_ALC_MTA_BINARY;
ip->init.util.acul = ERTS_ALC_DEFAULT_ACUL;
+ ip->init.util.atags = 1;
}
static void
@@ -471,6 +463,7 @@ set_default_driver_alloc_opts(struct au_init *ip)
#endif
ip->init.util.ts = ERTS_ALC_MTA_DRIVER;
ip->init.util.acul = ERTS_ALC_DEFAULT_ACUL;
+ ip->init.util.atags = 1;
}
static void
@@ -500,13 +493,15 @@ set_default_test_alloc_opts(struct au_init *ip)
SET_DEFAULT_ALLOC_OPTS(ip);
ip->enable = 0; /* Disabled by default */
ip->thr_spec = -1 * erts_no_schedulers;
- ip->atype = AOFIRSTFIT;
- ip->init.aoff.flavor = AOFF_BF;
+ ip->atype = FIRSTFIT;
+ ip->init.aoff.crr_order = FF_AOFF;
+ ip->init.aoff.blk_order = FF_BF;
ip->init.util.name_prefix = "test_";
ip->init.util.alloc_no = ERTS_ALC_A_TEST;
ip->init.util.mmbcs = 0; /* Main carrier size */
ip->init.util.ts = ERTS_ALC_MTA_TEST;
ip->init.util.acul = ERTS_ALC_DEFAULT_ACUL;
+ ip->init.util.atags = 1;
/* Use a constant minimal MBC size */
#if ERTS_SA_MB_CARRIERS
@@ -599,10 +594,10 @@ static ERTS_INLINE int
strategy_support_carrier_migration(struct au_init *auip)
{
/*
- * Currently only aoff, aoffcbf and aoffcaobf support carrier
+ * Currently only aoff* and ageff* support carrier
* migration, i.e, type AOFIRSTFIT.
*/
- return auip->atype == AOFIRSTFIT;
+ return auip->atype == FIRSTFIT;
}
static ERTS_INLINE void
@@ -617,8 +612,9 @@ adjust_carrier_migration_support(struct au_init *auip)
*/
if (!strategy_support_carrier_migration(auip)) {
/* Default to aoffcbf */
- auip->atype = AOFIRSTFIT;
- auip->init.aoff.flavor = AOFF_BF;
+ auip->atype = FIRSTFIT;
+ auip->init.aoff.crr_order = FF_AOFF;
+ auip->init.aoff.blk_order = FF_BF;
}
}
}
@@ -641,10 +637,10 @@ erts_alloc_init(int *argc, char **argv, ErtsAllocInitOpts *eaiop)
fix_type_sizes[ERTS_ALC_FIX_TYPE_IX(ERTS_ALC_T_PROC)]
= sizeof(Process);
- fix_type_sizes[ERTS_ALC_FIX_TYPE_IX(ERTS_ALC_T_MONITOR_SH)]
- = ERTS_MONITOR_SH_SIZE * sizeof(Uint);
- fix_type_sizes[ERTS_ALC_FIX_TYPE_IX(ERTS_ALC_T_NLINK_SH)]
- = ERTS_LINK_SH_SIZE * sizeof(Uint);
+ fix_type_sizes[ERTS_ALC_FIX_TYPE_IX(ERTS_ALC_T_MONITOR)]
+ = sizeof(ErtsMonitorDataHeap);
+ fix_type_sizes[ERTS_ALC_FIX_TYPE_IX(ERTS_ALC_T_LINK)]
+ = sizeof(ErtsLinkData);
fix_type_sizes[ERTS_ALC_FIX_TYPE_IX(ERTS_ALC_T_DRV_SEL_D_STATE)]
= sizeof(ErtsDrvSelectDataState);
fix_type_sizes[ERTS_ALC_FIX_TYPE_IX(ERTS_ALC_T_NIF_SEL_D_STATE)]
@@ -911,7 +907,6 @@ erts_alloc_init(int *argc, char **argv, ErtsAllocInitOpts *eaiop)
&test_alloc_state);
erts_mtrace_install_wrapper_functions();
- extra_block_size += erts_instr_init(init.instr.stat, init.instr.map);
init_aireq_alloc();
@@ -1132,7 +1127,7 @@ start_au_allocator(ErtsAlcType_t alctr_n,
&init->init.af,
&init->init.util);
break;
- case AOFIRSTFIT:
+ case FIRSTFIT:
as = erts_aoffalc_start((AOFFAllctr_t *) as0,
&init->init.aoff,
&init->init.util);
@@ -1217,31 +1212,41 @@ get_bool_value(char *param_end, char** argv, int* ip)
{
char *param = argv[*ip]+1;
char *value = get_value(param_end, argv, ip);
- if (strcmp(value, "true") == 0)
+ if (sys_strcmp(value, "true") == 0)
return 1;
- else if (strcmp(value, "false") == 0)
+ else if (sys_strcmp(value, "false") == 0)
return 0;
else
bad_value(param, param_end, value);
return -1;
}
+static Uint kb_to_bytes(Sint kb, Uint *bytes)
+{
+ const Uint max = ((~((Uint) 0))/1024) + 1;
+
+ if (kb < 0 || (Uint)kb > max)
+ return 0;
+ if ((Uint)kb == max)
+ *bytes = ~((Uint) 0);
+ else
+ *bytes = ((Uint) kb)*1024;
+ return 1;
+}
+
static Uint
get_kb_value(char *param_end, char** argv, int* ip)
{
Sint tmp;
- Uint max = ((~((Uint) 0))/1024) + 1;
+ Uint bytes = 0;
char *rest;
char *param = argv[*ip]+1;
char *value = get_value(param_end, argv, ip);
errno = 0;
tmp = (Sint) ErtsStrToSint(value, &rest, 10);
- if (errno != 0 || rest == value || tmp < 0 || max < ((Uint) tmp))
+ if (errno != 0 || rest == value || !kb_to_bytes(tmp, &bytes))
bad_value(param, param_end, value);
- if (max == (Uint) tmp)
- return ~((Uint) 0);
- else
- return ((Uint) tmp)*1024;
+ return bytes;
}
static UWord
@@ -1328,55 +1333,87 @@ handle_au_arg(struct au_init *auip,
switch (sub_param[0]) {
case 'a':
- if (has_prefix("acul", sub_param)) {
- if (!auip->carrier_migration_allowed) {
- if (!u_switch)
- goto bad_switch;
- else {
- /* ignore */
- (void) get_acul_value(auip, sub_param + 4, argv, ip);
- break;
- }
- }
- auip->init.util.acul = get_acul_value(auip, sub_param + 4, argv, ip);
- }
+ if (sub_param[1] == 'c') { /* Migration parameters "ac*" */
+ UWord value;
+ UWord* wp;
+ if (!auip->carrier_migration_allowed && !u_switch)
+ goto bad_switch;
+
+ if (has_prefix("acul", sub_param)) {
+ value = get_acul_value(auip, sub_param + 4, argv, ip);
+ wp = &auip->init.util.acul;
+ }
+ else if (has_prefix("acnl", sub_param)) {
+ value = get_amount_value(sub_param + 4, argv, ip);
+ wp = &auip->init.util.acnl;
+ }
+ else if (has_prefix("acfml", sub_param)) {
+ value = get_amount_value(sub_param + 5, argv, ip);
+ wp = &auip->init.util.acfml;
+ }
+ else
+ goto bad_switch;
+
+ if (auip->carrier_migration_allowed)
+ *wp = value;
+ }
else if(has_prefix("asbcst", sub_param)) {
auip->init.util.asbcst = get_kb_value(sub_param + 6, argv, ip);
}
else if(has_prefix("as", sub_param)) {
char *alg = get_value(sub_param + 2, argv, ip);
- if (strcmp("bf", alg) == 0) {
+ if (sys_strcmp("bf", alg) == 0) {
auip->atype = BESTFIT;
auip->init.bf.ao = 0;
}
- else if (strcmp("aobf", alg) == 0) {
+ else if (sys_strcmp("aobf", alg) == 0) {
auip->atype = BESTFIT;
auip->init.bf.ao = 1;
}
- else if (strcmp("gf", alg) == 0) {
+ else if (sys_strcmp("gf", alg) == 0) {
auip->atype = GOODFIT;
}
- else if (strcmp("af", alg) == 0) {
+ else if (sys_strcmp("af", alg) == 0) {
auip->atype = AFIT;
}
- else if (strcmp("aoff", alg) == 0) {
- auip->atype = AOFIRSTFIT;
- auip->init.aoff.flavor = AOFF_AOFF;
+ else if (sys_strcmp("aoff", alg) == 0) {
+ auip->atype = FIRSTFIT;
+ auip->init.aoff.crr_order = FF_AOFF;
+ auip->init.aoff.blk_order = FF_AOFF;
}
- else if (strcmp("aoffcbf", alg) == 0) {
- auip->atype = AOFIRSTFIT;
- auip->init.aoff.flavor = AOFF_BF;
+ else if (sys_strcmp("aoffcbf", alg) == 0) {
+ auip->atype = FIRSTFIT;
+ auip->init.aoff.crr_order = FF_AOFF;
+ auip->init.aoff.blk_order = FF_BF;
}
- else if (strcmp("aoffcaobf", alg) == 0) {
- auip->atype = AOFIRSTFIT;
- auip->init.aoff.flavor = AOFF_AOBF;
+ else if (sys_strcmp("aoffcaobf", alg) == 0) {
+ auip->atype = FIRSTFIT;
+ auip->init.aoff.crr_order = FF_AOFF;
+ auip->init.aoff.blk_order = FF_AOBF;
}
+ else if (sys_strcmp("ageffcaoff", alg) == 0) {
+ auip->atype = FIRSTFIT;
+ auip->init.aoff.crr_order = FF_AGEFF;
+ auip->init.aoff.blk_order = FF_AOFF;
+ }
+ else if (sys_strcmp("ageffcbf", alg) == 0) {
+ auip->atype = FIRSTFIT;
+ auip->init.aoff.crr_order = FF_AGEFF;
+ auip->init.aoff.blk_order = FF_BF;
+ }
+ else if (sys_strcmp("ageffcaobf", alg) == 0) {
+ auip->atype = FIRSTFIT;
+ auip->init.aoff.crr_order = FF_AGEFF;
+ auip->init.aoff.blk_order = FF_AOBF;
+ }
else {
bad_value(param, sub_param + 1, alg);
}
if (!strategy_support_carrier_migration(auip))
auip->init.util.acul = 0;
- }
+ } else if (has_prefix("atags", sub_param)) {
+ auip->init.util.atags = get_bool_value(sub_param + 5, argv, ip);
+ }
else
goto bad_switch;
break;
@@ -1526,10 +1563,8 @@ handle_args(int *argc, char **argv, erts_alc_hndl_args_init_t *init)
break;
case 'X':
if (has_prefix("scs", argv[i]+3)) {
-#ifdef ERTS_HAVE_EXEC_MMAPPER
- init->mseg.exec_mmap.scs =
-#endif
- get_mb_value(argv[i]+6, argv, &i);
+ /* Ignore obsolete */
+ (void) get_mb_value(argv[i]+6, argv, &i);
}
else
handle_au_arg(&init->exec_alloc, &argv[i][3], argv, &i, 0);
@@ -1646,7 +1681,7 @@ handle_args(int *argc, char **argv, erts_alc_hndl_args_init_t *init)
}
else if (has_prefix("e", param+2)) {
arg = get_value(param+3, argv, &i);
- if (strcmp("true", arg) != 0)
+ if (sys_strcmp("true", arg) != 0)
bad_value(param, param+3, arg);
}
else
@@ -1658,20 +1693,20 @@ handle_args(int *argc, char **argv, erts_alc_hndl_args_init_t *init)
case 'a': {
int a;
arg = get_value(argv[i]+4, argv, &i);
- if (strcmp("min", arg) == 0) {
+ if (sys_strcmp("min", arg) == 0) {
for (a = 0; a < aui_sz; a++)
aui[a]->enable = 0;
}
- else if (strcmp("max", arg) == 0) {
+ else if (sys_strcmp("max", arg) == 0) {
for (a = 0; a < aui_sz; a++)
aui[a]->enable = 1;
}
- else if (strcmp("config", arg) == 0) {
+ else if (sys_strcmp("config", arg) == 0) {
init->erts_alloc_config = 1;
}
- else if (strcmp("r9c", arg) == 0
- || strcmp("r10b", arg) == 0
- || strcmp("r11b", arg) == 0) {
+ else if (sys_strcmp("r9c", arg) == 0
+ || sys_strcmp("r10b", arg) == 0
+ || sys_strcmp("r11b", arg) == 0) {
set_default_sl_alloc_opts(&init->sl_alloc);
set_default_std_alloc_opts(&init->std_alloc);
set_default_ll_alloc_opts(&init->ll_alloc);
@@ -1683,7 +1718,7 @@ handle_args(int *argc, char **argv, erts_alc_hndl_args_init_t *init)
set_default_driver_alloc_opts(&init->fix_alloc);
init->driver_alloc.enable = 0;
- if (strcmp("r9c", arg) == 0) {
+ if (sys_strcmp("r9c", arg) == 0) {
init->sl_alloc.enable = 0;
init->std_alloc.enable = 0;
init->binary_alloc.enable = 0;
@@ -1708,24 +1743,6 @@ handle_args(int *argc, char **argv, erts_alc_hndl_args_init_t *init)
break;
case 'i':
switch (argv[i][3]) {
- case 's':
- arg = get_value(argv[i]+4, argv, &i);
- if (strcmp("true", arg) == 0)
- init->instr.stat = 1;
- else if (strcmp("false", arg) == 0)
- init->instr.stat = 0;
- else
- bad_value(param, param+3, arg);
- break;
- case 'm':
- arg = get_value(argv[i]+4, argv, &i);
- if (strcmp("true", arg) == 0)
- init->instr.map = 1;
- else if (strcmp("false", arg) == 0)
- init->instr.map = 0;
- else
- bad_value(param, param+3, arg);
- break;
case 't':
init->instr.mtrace = get_value(argv[i]+4, argv, &i);
break;
@@ -1736,9 +1753,9 @@ handle_args(int *argc, char **argv, erts_alc_hndl_args_init_t *init)
case 'l':
if (has_prefix("pm", param+2)) {
arg = get_value(argv[i]+5, argv, &i);
- if (strcmp("all", arg) == 0)
+ if (sys_strcmp("all", arg) == 0)
lock_all_physical_memory = 1;
- else if (strcmp("no", arg) == 0)
+ else if (sys_strcmp("no", arg) == 0)
lock_all_physical_memory = 0;
else
bad_value(param, param+4, arg);
@@ -1784,12 +1801,10 @@ handle_args(int *argc, char **argv, erts_alc_hndl_args_init_t *init)
case '-':
if (argv[i][2] == '\0') {
/* End of system flags reached */
- if (init->instr.mtrace
- /* || init->instr.stat
- || init->instr.map */) {
+ if (init->instr.mtrace) {
while (i < *argc) {
- if(strcmp(argv[i], "-sname") == 0
- || strcmp(argv[i], "-name") == 0) {
+ if(sys_strcmp(argv[i], "-sname") == 0
+ || sys_strcmp(argv[i], "-name") == 0) {
if (i + 1 <*argc) {
init->instr.nodename = argv[i+1];
break;
@@ -2064,7 +2079,7 @@ erts_memory(fmtfn_t *print_to_p, void *print_to_arg, void *proc, Eterm earg)
* NOTE! When updating this function, make sure to also update
* erlang:memory/[0,1] in $ERL_TOP/erts/preloaded/src/erlang.erl
*/
-#define ERTS_MEM_NEED_ALL_ALCU (!erts_instr_stat && want_tot_or_sys)
+#define ERTS_MEM_NEED_ALL_ALCU (want_tot_or_sys)
struct {
int total;
int processes;
@@ -2075,7 +2090,6 @@ erts_memory(fmtfn_t *print_to_p, void *print_to_arg, void *proc, Eterm earg)
int binary;
int code;
int ets;
- int maximum;
} want = {0};
struct {
UWord total;
@@ -2087,7 +2101,6 @@ erts_memory(fmtfn_t *print_to_p, void *print_to_arg, void *proc, Eterm earg)
UWord binary;
UWord code;
UWord ets;
- UWord maximum;
} size = {0};
Eterm atoms[sizeof(size)/sizeof(UWord)];
UWord *uintps[sizeof(size)/sizeof(UWord)];
@@ -2140,12 +2153,6 @@ erts_memory(fmtfn_t *print_to_p, void *print_to_arg, void *proc, Eterm earg)
want.ets = 1;
atoms[length] = am_ets;
uintps[length++] = &size.ets;
-
- want.maximum = erts_instr_stat;
- if (want.maximum) {
- atoms[length] = am_maximum;
- uintps[length++] = &size.maximum;
- }
}
else {
DeclareTmpHeapNoproc(tmp_heap,2);
@@ -2227,18 +2234,6 @@ erts_memory(fmtfn_t *print_to_p, void *print_to_arg, void *proc, Eterm earg)
uintps[length++] = &size.ets;
}
break;
- case am_maximum:
- if (erts_instr_stat) {
- if (!want.maximum) {
- want.maximum = 1;
- atoms[length] = am_maximum;
- uintps[length++] = &size.maximum;
- }
- } else {
- UnUseTmpHeapNoproc(2);
- return am_badarg;
- }
- break;
default:
UnUseTmpHeapNoproc(2);
return am_badarg;
@@ -2337,7 +2332,6 @@ erts_memory(fmtfn_t *print_to_p, void *print_to_arg, void *proc, Eterm earg)
}
tmp += erts_ptab_mem_size(&erts_proc);
tmp += erts_bif_timer_memory_size();
- tmp += erts_tot_link_lh_size();
size.processes = size.processes_used = tmp;
@@ -2348,12 +2342,11 @@ erts_memory(fmtfn_t *print_to_p, void *print_to_arg, void *proc, Eterm earg)
add_fix_values(&size.processes,
&size.processes_used,
fi,
- ERTS_ALC_T_MONITOR_SH);
-
+ ERTS_ALC_T_MONITOR);
add_fix_values(&size.processes,
&size.processes_used,
fi,
- ERTS_ALC_T_NLINK_SH);
+ ERTS_ALC_T_LINK);
add_fix_values(&size.processes,
&size.processes_used,
fi,
@@ -2406,14 +2399,7 @@ erts_memory(fmtfn_t *print_to_p, void *print_to_arg, void *proc, Eterm earg)
size.ets += erts_get_ets_misc_mem_size();
}
- if (erts_instr_stat && (want_tot_or_sys || want.maximum)) {
- if (want_tot_or_sys) {
- size.total = erts_instr_get_total();
- size.system = size.total - size.processes;
- }
- size.maximum = erts_instr_get_max_total();
- }
- else if (want_tot_or_sys) {
+ if (want_tot_or_sys) {
size.system = size.total - size.processes;
}
@@ -2491,18 +2477,6 @@ erts_allocated_areas(fmtfn_t *print_to_p, void *print_to_arg, void *proc)
i = 0;
- if (erts_instr_stat) {
- values[i].arity = 2;
- values[i].name = "total";
- values[i].ui[0] = erts_instr_get_total();
- i++;
-
- values[i].arity = 2;
- values[i].name = "maximum";
- values[i].ui[0] = erts_instr_get_max_total();
- i++;
- }
-
values[i].arity = 2;
values[i].name = "sys_misc";
values[i].ui[0] = erts_sys_misc_mem_sz();
@@ -2584,11 +2558,6 @@ erts_allocated_areas(fmtfn_t *print_to_p, void *print_to_arg, void *proc)
i++;
values[i].arity = 2;
- values[i].name = "link_lh";
- values[i].ui[0] = erts_tot_link_lh_size();
- i++;
-
- values[i].arity = 2;
values[i].name = "process_table";
values[i].ui[0] = erts_ptab_mem_size(&erts_proc);
i++;
@@ -2650,7 +2619,7 @@ erts_allocated_areas(fmtfn_t *print_to_p, void *print_to_arg, void *proc)
Eterm atom;
if (hpp)
atom = am_atom_put(values[i].name,
- (int) strlen(values[i].name));
+ (int) sys_strlen(values[i].name));
else
atom = am_true;
@@ -2791,10 +2760,6 @@ erts_allocator_info(fmtfn_t to, void *arg)
erts_print(to, arg, "=allocator:erts_mmap.literal_mmap\n");
erts_mmap_info(&erts_literal_mmapper, &to, arg, NULL, NULL, &emis);
#endif
-#ifdef ERTS_HAVE_EXEC_MMAPPER
- erts_print(to, arg, "=allocator:erts_mmap.exec_mmap\n");
- erts_mmap_info(&erts_exec_mmapper, &to, arg, NULL, NULL, &emis);
-#endif
}
#endif
@@ -2802,10 +2767,7 @@ erts_allocator_info(fmtfn_t to, void *arg)
erts_alcu_au_info_options(&to, arg, NULL, NULL);
erts_print(to, arg, "=allocator:instr\n");
- erts_print(to, arg, "option m: %s\n",
- erts_instr_memory_map ? "true" : "false");
- erts_print(to, arg, "option s: %s\n",
- erts_instr_stat ? "true" : "false");
+
erts_print(to, arg, "option t: %s\n",
erts_mtrace_enabled ? "true" : "false");
@@ -2841,7 +2803,7 @@ erts_allocator_options(void *proc)
for (a = ERTS_ALC_A_MIN; a <= ERTS_ALC_A_MAX; a++) {
Eterm tmp = NIL;
atoms[length] = am_atom_put((char *) ERTS_ALC_A2AD(a),
- strlen(ERTS_ALC_A2AD(a)));
+ sys_strlen(ERTS_ALC_A2AD(a)));
if (erts_allctrs_info[a].enabled) {
if (erts_allctrs_info[a].alloc_util) {
Allctr_t *allctr;
@@ -2911,16 +2873,12 @@ erts_allocator_options(void *proc)
NULL, hpp, szp);
#endif
{
- Eterm o[3], v[3];
- o[0] = am_atom_put("m", 1);
- v[0] = erts_instr_memory_map ? am_true : am_false;
- o[1] = am_atom_put("s", 1);
- v[1] = erts_instr_stat ? am_true : am_false;
- o[2] = am_atom_put("t", 1);
- v[2] = erts_mtrace_enabled ? am_true : am_false;
+ Eterm o[1], v[1];
+ o[0] = am_atom_put("t", 1);
+ v[0] = erts_mtrace_enabled ? am_true : am_false;
atoms[length] = am_atom_put("instr", 5);
- terms[length++] = erts_bld_2tup_list(hpp, szp, 3, o, v);
+ terms[length++] = erts_bld_2tup_list(hpp, szp, 1, o, v);
}
atoms[length] = am_atom_put("lock_physical_memory", 20);
@@ -2935,7 +2893,7 @@ erts_allocator_options(void *proc)
for (a = ERTS_ALC_A_MIN; a <= ERTS_ALC_A_MAX; a++) {
if (erts_allctrs_info[a].enabled) {
terms[length++] = am_atom_put((char *) ERTS_ALC_A2AD(a),
- strlen(ERTS_ALC_A2AD(a)));
+ sys_strlen(ERTS_ALC_A2AD(a)));
}
}
@@ -2949,9 +2907,6 @@ erts_allocator_options(void *proc)
#if defined(ARCH_64) && defined(ERTS_HAVE_OS_PHYSICAL_MEMORY_RESERVATION)
terms[length++] = ERTS_MAKE_AM("literal_mmap");
#endif
-#ifdef ERTS_HAVE_EXEC_MMAPPER
- terms[length++] = ERTS_MAKE_AM("exec_mmap");
-#endif
features = length ? erts_bld_list(hpp, szp, length, terms) : NIL;
#if defined(__GLIBC__)
@@ -3041,9 +2996,6 @@ reply_alloc_info(void *vair)
# if defined(ARCH_64) && defined(ERTS_HAVE_OS_PHYSICAL_MEMORY_RESERVATION)
struct erts_mmap_info_struct mmap_info_literal;
# endif
-# ifdef ERTS_HAVE_EXEC_MMAPPER
- struct erts_mmap_info_struct mmap_info_exec;
-# endif
#endif
int i;
Eterm (*info_func)(Allctr_t *,
@@ -3172,17 +3124,6 @@ reply_alloc_info(void *vair)
erts_bld_atom(hpp,szp,"literal_mmap"),
ainfo);
# endif
-# ifdef ERTS_HAVE_EXEC_MMAPPER
- ai_list = erts_bld_cons(hpp, szp,
- ainfo, ai_list);
- ainfo = (air->only_sz ? NIL :
- erts_mmap_info(&erts_exec_mmapper, NULL, NULL,
- hpp, szp, &mmap_info_exec));
- ainfo = erts_bld_tuple3(hpp, szp,
- alloc_atom,
- erts_bld_atom(hpp,szp,"exec_mmap"),
- ainfo);
-# endif
#else /* !HAVE_ERTS_MMAP */
ainfo = erts_bld_tuple2(hpp, szp, alloc_atom,
am_false);
@@ -3392,10 +3333,69 @@ erts_request_alloc_info(struct process *c_p,
return 1;
}
+Eterm erts_alloc_set_dyn_param(Process* c_p, Eterm tuple)
+{
+ ErtsAllocatorThrSpec_t *tspec;
+ ErtsAlcType_t ai;
+ Allctr_t* allctr;
+ Eterm* tp;
+ Eterm res;
+
+ if (!is_tuple_arity(tuple, 3))
+ goto badarg;
+
+ tp = tuple_val(tuple);
+
+ /*
+ * Ex: {ets_alloc, sbct, 256000}
+ */
+ if (!is_atom(tp[1]) || !is_atom(tp[2]) || !is_integer(tp[3]))
+ goto badarg;
+
+ for (ai = ERTS_ALC_A_MIN; ai <= ERTS_ALC_A_MAX; ai++)
+ if (erts_is_atom_str(erts_alc_a2ad[ai], tp[1], 0))
+ break;
+
+ if (ai > ERTS_ALC_A_MAX)
+ goto badarg;
+
+ if (!erts_allctrs_info[ai].enabled ||
+ !erts_allctrs_info[ai].alloc_util) {
+ return am_notsup;
+ }
+
+ if (tp[2] == am_sbct) {
+ Uint sbct;
+ int i, ok;
+
+ if (!term_to_Uint(tp[3], &sbct))
+ goto badarg;
+
+ tspec = &erts_allctr_thr_spec[ai];
+ if (tspec->enabled) {
+ ok = 0;
+ for (i = 0; i < tspec->size; i++) {
+ allctr = tspec->allctr[i];
+ ok |= allctr->try_set_dyn_param(allctr, am_sbct, sbct);
+ }
+ }
+ else {
+ allctr = erts_allctrs_info[ai].extra;
+ ok = allctr->try_set_dyn_param(allctr, am_sbct, sbct);
+ }
+ return ok ? am_ok : am_notsup;
+ }
+ return am_notsup;
+
+badarg:
+ ERTS_BIF_PREP_ERROR(res, c_p, EXC_BADARG);
+ return res;
+}
+
/*
* The allocator wrapper prelocking stuff below is about the locking order.
- * It only affects wrappers (erl_mtrace.c and erl_instrument.c) that keep locks
- * during alloc/realloc/free.
+ * It only affects wrappers (erl_mtrace.c) that keep locks during
+ * alloc/realloc/free.
*
* Some query functions in erl_alloc_util.c lock the allocator mutex and then
* use erts_printf that in turn may call the sys allocator through the wrappers.
@@ -3528,7 +3528,7 @@ UWord erts_alc_test(UWord op, UWord a1, UWord a2, UWord a3)
&init.init.af,
&init.init.util);
break;
- case AOFIRSTFIT:
+ case FIRSTFIT:
allctr = erts_aoffalc_start((AOFFAllctr_t *)
erts_alloc(ERTS_ALC_T_UNDEF,
sizeof(AOFFAllctr_t)),
@@ -3622,7 +3622,9 @@ UWord erts_alc_test(UWord op, UWord a1, UWord a2, UWord a3)
case 0xf15: erts_free(ERTS_ALC_T_TEST, (void*)a1); return 0;
- case 0xf16: {
+ case 0xf16: return (UWord) erts_realloc(ERTS_ALC_T_TEST, (void*)a1, (Uint)a2);
+
+ case 0xf17: {
Uint extra_hdr_sz = UNIT_CEILING((Uint)a1);
ErtsAllocatorThrSpec_t* ts = &erts_allctr_thr_spec[ERTS_ALC_A_TEST];
Uint offset = ts->allctr[0]->mbc_header_size;
@@ -3649,7 +3651,7 @@ UWord erts_alc_test(UWord op, UWord a1, UWord a2, UWord a3)
*(void**)a3 = orig_destroying_mbc;
return offset;
}
- case 0xf17: {
+ case 0xf18: {
ErtsAllocatorThrSpec_t* ts = &erts_allctr_thr_spec[ERTS_ALC_A_TEST];
return ts->allctr[0]->largest_mbc_size;
}
@@ -3858,7 +3860,7 @@ set_memory_fence(void *ptr, Uint sz, ErtsAlcType_t n)
*(ui_ptr++) = sz;
*(ui_ptr++) = pattern;
- memcpy((void *) (((char *) ui_ptr)+sz), (void *) &pattern, sizeof(UWord));
+ sys_memcpy((void *) (((char *) ui_ptr)+sz), (void *) &pattern, sizeof(UWord));
#ifdef HARD_DEBUG
*mblkpp = hdbg_alloc((void *) ui_ptr, sz, n);
@@ -3898,7 +3900,7 @@ check_memory_fence(void *ptr, Uint *size, ErtsAlcType_t n, int func)
(UWord) ptr);
}
- memcpy((void *) &post_pattern, (void *) (((char *)ptr)+sz), sizeof(UWord));
+ sys_memcpy((void *) &post_pattern, (void *) (((char *)ptr)+sz), sizeof(UWord));
if (post_pattern != MK_PATTERN(n)
|| pre_pattern != post_pattern) {
diff --git a/erts/emulator/beam/erl_alloc.h b/erts/emulator/beam/erl_alloc.h
index 117f96a4ad..578a3717d9 100644
--- a/erts/emulator/beam/erl_alloc.h
+++ b/erts/emulator/beam/erl_alloc.h
@@ -126,8 +126,10 @@ typedef struct {
void *extra;
} ErtsAllocatorFunctions_t;
-extern ErtsAllocatorFunctions_t erts_allctrs[ERTS_ALC_A_MAX+1];
-extern ErtsAllocatorInfo_t erts_allctrs_info[ERTS_ALC_A_MAX+1];
+extern ErtsAllocatorFunctions_t
+ ERTS_WRITE_UNLIKELY(erts_allctrs[ERTS_ALC_A_MAX+1]);
+extern ErtsAllocatorInfo_t
+ ERTS_WRITE_UNLIKELY(erts_allctrs_info[ERTS_ALC_A_MAX+1]);
typedef struct {
int enabled;
@@ -144,7 +146,7 @@ typedef struct ErtsAllocatorWrapper_t_ {
void (*unlock)(void);
struct ErtsAllocatorWrapper_t_* next;
}ErtsAllocatorWrapper_t;
-ErtsAllocatorWrapper_t *erts_allctr_wrappers;
+extern ErtsAllocatorWrapper_t *erts_allctr_wrappers;
extern int erts_allctr_wrapper_prelocked;
extern erts_tsd_key_t erts_allctr_prelock_tsd_key;
void erts_allctr_wrapper_prelock_init(ErtsAllocatorWrapper_t* wrapper);
@@ -169,6 +171,8 @@ __decl_noreturn void erts_realloc_n_enomem(ErtsAlcType_t,void*,Uint)
__decl_noreturn void erts_alc_fatal_error(int,int,ErtsAlcType_t,...)
__noreturn;
+Eterm erts_alloc_set_dyn_param(struct process*, Eterm);
+
#undef ERTS_HAVE_IS_IN_LITERAL_RANGE
#if defined(ARCH_32) || defined(ERTS_HAVE_OS_PHYSICAL_MEMORY_RESERVATION)
# define ERTS_HAVE_IS_IN_LITERAL_RANGE
@@ -430,7 +434,7 @@ NAME##_free(TYPE *p) \
#ifdef DEBUG
#define ERTS_PRE_ALLOC_SIZE(SZ) ((SZ) < 1000 ? (SZ)/10 + 10 : 100)
-#define ERTS_PRE_ALLOC_CLOBBER(P, T) memset((void *) (P), 0xfd, sizeof(T))
+#define ERTS_PRE_ALLOC_CLOBBER(P, T) sys_memset((void *) (P), 0xfd, sizeof(T))
#else
#define ERTS_PRE_ALLOC_SIZE(SZ) ((SZ) > 1 ? (SZ) : 1)
#define ERTS_PRE_ALLOC_CLOBBER(P, T)
diff --git a/erts/emulator/beam/erl_alloc.types b/erts/emulator/beam/erl_alloc.types
index bf5487d31e..4a6a19b210 100644
--- a/erts/emulator/beam/erl_alloc.types
+++ b/erts/emulator/beam/erl_alloc.types
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2003-2017. All Rights Reserved.
+# Copyright Ericsson AB 2003-2018. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -259,6 +259,9 @@ type MREF_TAB_BKTS STANDARD SYSTEM magic_ref_table_buckets
type MREF_TAB LONG_LIVED SYSTEM magic_ref_table
type MINDIRECTION FIXED_SIZE SYSTEM magic_indirection
type BINARY_FIND SHORT_LIVED PROCESSES binary_find
+type OPEN_PORT_ENV TEMPORARY SYSTEM open_port_env
+type CRASH_DUMP STANDARD SYSTEM crash_dump
+type DIST_TRANSCODE SHORT_LIVED SYSTEM dist_transcode_context
type THR_Q_EL STANDARD SYSTEM thr_q_element
type THR_Q_EL_SL FIXED_SIZE SYSTEM sl_thr_q_element
@@ -279,6 +282,13 @@ type THR_PRGR_IDATA LONG_LIVED SYSTEM thr_prgr_internal_data
type THR_PRGR_DATA LONG_LIVED SYSTEM thr_prgr_data
type T_THR_PRGR_DATA SHORT_LIVED SYSTEM temp_thr_prgr_data
type RELEASE_LAREA SHORT_LIVED SYSTEM release_literal_area
+type SIG_DATA SHORT_LIVED PROCESSES signal_data
+type DIST_DEMONITOR SHORT_LIVED PROCESSES dist_demonitor
+type CML_CLEANUP SHORT_LIVED SYSTEM connection_ml_cleanup
+type ML_YIELD_STATE SHORT_LIVED SYSTEM monitor_link_yield_state
+type ML_DIST STANDARD SYSTEM monitor_link_dist
+
+type ENVIRONMENT SYSTEM SYSTEM environment
#
# Types used for special emulators
@@ -321,8 +331,8 @@ type LCNT_VECTOR SHORT_LIVED SYSTEM lcnt_sample_vector
type DEBUG SHORT_LIVED SYSTEM debugging
type DDLL_PROCESS STANDARD SYSTEM ddll_processes
-type MONITOR_LH STANDARD PROCESSES monitor_lh
-type NLINK_LH STANDARD PROCESSES nlink_lh
+type MONITOR_EXT STANDARD PROCESSES monitor_extended
+type LINK_EXT STANDARD PROCESSES link_extended
type CODE LONG_LIVED CODE code
type LITERAL LITERAL CODE literal
type LITERAL_REF SHORT_LIVED CODE literal_area_ref
@@ -335,8 +345,8 @@ type LL_TEMP_TERM LONG_LIVED SYSTEM ll_temp_term
type NIF_TRAP_EXPORT STANDARD PROCESSES nif_trap_export_entry
type NIF_EXP_TRACE FIXED_SIZE PROCESSES nif_export_trace
type EXPORT LONG_LIVED CODE export_entry
-type MONITOR_SH FIXED_SIZE PROCESSES monitor_sh
-type NLINK_SH FIXED_SIZE PROCESSES nlink_sh
+type MONITOR FIXED_SIZE PROCESSES monitor
+type LINK FIXED_SIZE PROCESSES link
type AINFO_REQ SHORT_LIVED SYSTEM alloc_info_request
type SCHED_WTIME_REQ SHORT_LIVED SYSTEM sched_wall_time_request
type GC_INFO_REQ SHORT_LIVED SYSTEM gc_info_request
@@ -367,8 +377,6 @@ type SYS_READ_BUF TEMPORARY SYSTEM sys_read_buf
type FD_TAB LONG_LIVED SYSTEM fd_tab
type FD_ENTRY_BUF STANDARD SYSTEM fd_entry_buf
type CS_PROG_PATH LONG_LIVED SYSTEM cs_prog_path
-type ENVIRONMENT TEMPORARY SYSTEM environment
-type PUTENV_STR SYSTEM SYSTEM putenv_string
type PRT_REP_EXIT STANDARD SYSTEM port_report_exit
type SYS_BLOCKING STANDARD SYSTEM sys_blocking
@@ -380,9 +388,7 @@ type SYS_WRITE_BUF TEMPORARY SYSTEM sys_write_buf
type DRV_DATA_BUF SYSTEM SYSTEM drv_data_buf
type PRELOADED LONG_LIVED SYSTEM preloaded
-type PUTENV_STR SYSTEM SYSTEM putenv_string
type WAITER_OBJ LONG_LIVED SYSTEM waiter_object
-type ENVIRONMENT SYSTEM SYSTEM environment
type CON_VPRINTF_BUF TEMPORARY SYSTEM con_vprintf_buf
+endif
diff --git a/erts/emulator/beam/erl_alloc_util.c b/erts/emulator/beam/erl_alloc_util.c
index 4d4bddb93f..fdf355d503 100644
--- a/erts/emulator/beam/erl_alloc_util.c
+++ b/erts/emulator/beam/erl_alloc_util.c
@@ -48,6 +48,8 @@
#include "erl_mseg.h"
#include "erl_threads.h"
#include "erl_thr_progress.h"
+#include "erl_bif_unique.h"
+#include "erl_nif.h"
#ifdef ERTS_ENABLE_LOCK_COUNT
#include "erl_lock_count.h"
@@ -139,6 +141,33 @@ MBC after deallocating first block:
[Carrier_t|pad|Block_t 111| udata... ]
*/
+/* Allocation tags ...
+ *
+ * These are added to the footer of every block when enabled. Currently they
+ * consist of the allocation type and an atom identifying the allocating
+ * driver/nif (or 'system' if that can't be determined), but the format is not
+ * supposed to be set in stone.
+ *
+ * The packing scheme requires that the atom values are small enough to fit
+ * into a word with ERTS_ALC_N_BITS to spare. Users must check for overflow
+ * before MAKE_ATAG(). */
+
+typedef UWord alcu_atag_t;
+
+#define MAKE_ATAG(IdAtom, Type) \
+ (ASSERT((Type) >= ERTS_ALC_N_MIN && (Type) <= ERTS_ALC_N_MAX), \
+ ASSERT(atom_val(IdAtom) <= MAX_ATAG_ATOM_ID), \
+ (atom_val(IdAtom) << ERTS_ALC_N_BITS) | (Type))
+
+#define ATAG_ID(AT) (make_atom((AT) >> ERTS_ALC_N_BITS))
+#define ATAG_TYPE(AT) ((AT) & ERTS_ALC_N_MASK)
+
+#define MAX_ATAG_ATOM_ID (ERTS_UWORD_MAX >> ERTS_ALC_N_BITS)
+
+#define DBG_IS_VALID_ATAG(Allocator, AT) \
+ (ATAG_TYPE(AT) >= ERTS_ALC_N_MIN && \
+ ATAG_TYPE(AT) <= ERTS_ALC_N_MAX && \
+ (Allocator)->alloc_no == ERTS_ALC_T2A(ERTS_ALC_N2T(ATAG_TYPE(AT))))
/* Blocks ... */
@@ -153,10 +182,17 @@ MBC after deallocating first block:
#endif
#define FBLK_FTR_SZ (sizeof(FreeBlkFtr_t))
+#define GET_BLK_ATAG(B) \
+ (((alcu_atag_t *) (((char *) (B)) + (BLK_SZ(B))))[-1])
+#define SET_BLK_ATAG(B, T) \
+ (((alcu_atag_t *) (((char *) (B)) + (BLK_SZ(B))))[-1] = (T))
+
+#define BLK_ATAG_SZ(AP) ((AP)->atags ? sizeof(alcu_atag_t) : 0)
+
#define UMEMSZ2BLKSZ(AP, SZ) \
- (ABLK_HDR_SZ + (SZ) <= (AP)->min_block_size \
+ (ABLK_HDR_SZ + BLK_ATAG_SZ(AP) + (SZ) <= (AP)->min_block_size \
? (AP)->min_block_size \
- : UNIT_CEILING(ABLK_HDR_SZ + (SZ)))
+ : UNIT_CEILING(ABLK_HDR_SZ + BLK_ATAG_SZ(AP) + (SZ)))
#define UMEM2BLK(P) ((Block_t *) (((char *) (P)) - ABLK_HDR_SZ))
#define BLK2UMEM(P) ((void *) (((char *) (P)) + ABLK_HDR_SZ))
@@ -362,8 +398,10 @@ do { \
#define ERTS_CRR_ALCTR_FLG_IN_POOL (((erts_aint_t) 1) << 0)
#define ERTS_CRR_ALCTR_FLG_BUSY (((erts_aint_t) 1) << 1)
+#define ERTS_CRR_ALCTR_FLG_HOMECOMING (((erts_aint_t) 1) << 2)
#define ERTS_CRR_ALCTR_FLG_MASK (ERTS_CRR_ALCTR_FLG_IN_POOL | \
- ERTS_CRR_ALCTR_FLG_BUSY)
+ ERTS_CRR_ALCTR_FLG_BUSY | \
+ ERTS_CRR_ALCTR_FLG_HOMECOMING)
#define SBC_HEADER_SIZE \
(UNIT_CEILING(offsetof(Carrier_t, cpool) \
@@ -563,7 +601,7 @@ do { \
DEBUG_CHECK_CARRIER_NO_SZ((AP)); \
} while (0)
-#define STAT_MBC_CPOOL_INSERT(AP, CRR) \
+#define STAT_MBC_ABANDON(AP, CRR) \
do { \
UWord csz__ = CARRIER_SZ((CRR)); \
if (IS_MSEG_CARRIER((CRR))) \
@@ -686,6 +724,62 @@ static void destroy_carrier(Allctr_t *, Block_t *, Carrier_t **);
static void mbc_free(Allctr_t *allctr, void *p, Carrier_t **busy_pcrr_pp);
static void dealloc_block(Allctr_t *, void *, ErtsAlcFixList_t *, int);
+static alcu_atag_t determine_alloc_tag(Allctr_t *allocator, ErtsAlcType_t type)
+{
+ ErtsSchedulerData *esdp;
+ Eterm id;
+
+ ERTS_CT_ASSERT(_unchecked_atom_val(am_system) <= MAX_ATAG_ATOM_ID);
+ ASSERT(allocator->atags);
+
+ esdp = erts_get_scheduler_data();
+ id = am_system;
+
+ if (esdp) {
+ if (esdp->current_nif) {
+ Module *mod = erts_nif_get_module((esdp->current_nif)->mod_nif);
+
+ /* Mod can be NULL if a resource destructor allocates memory after
+ * the module has been unloaded. */
+ if (mod) {
+ id = make_atom(mod->module);
+ }
+ } else if (esdp->current_port) {
+ Port *p = esdp->current_port;
+ id = (p->drv_ptr)->name_atom;
+ }
+
+ /* We fall back to 'system' if we can't pack the driver/NIF name into
+ * the tag. This may be a bit misleading but we've made no promises
+ * that the information is complete.
+ *
+ * This can only happen on 32-bit emulators when a new driver/NIF has
+ * been loaded *after* 16 million atoms have been used, and supporting
+ * that fringe case is not worth an extra word. 64-bit emulators are
+ * unaffected since the atom cache limits atom indexes to 32 bits. */
+ if(MAX_ATOM_TABLE_SIZE > MAX_ATAG_ATOM_ID) {
+ if (atom_val(id) > MAX_ATAG_ATOM_ID) {
+ id = am_system;
+ }
+ }
+ }
+
+ return MAKE_ATAG(id, type);
+}
+
+static void set_alloc_tag(Allctr_t *allocator, void *p, alcu_atag_t tag)
+{
+ Block_t *block;
+
+ ASSERT(DBG_IS_VALID_ATAG(allocator, tag));
+ ASSERT(allocator->atags && p);
+ (void)allocator;
+
+ block = UMEM2BLK(p);
+
+ SET_BLK_ATAG(block, tag);
+}
+
/* internal data... */
#if 0
@@ -872,7 +966,7 @@ erts_alcu_literal_32_mseg_dealloc(Allctr_t *allctr, void *seg, Uint size,
#elif defined(ARCH_64) && defined(ERTS_HAVE_OS_PHYSICAL_MEMORY_RESERVATION)
/* For allocators that have their own mmapper (super carrier),
- * like literal_alloc and exec_alloc on amd64
+ * like literal_alloc.
*/
void*
erts_alcu_mmapper_mseg_alloc(Allctr_t *allctr, Uint *size_p, Uint flags)
@@ -914,10 +1008,10 @@ erts_alcu_mmapper_mseg_dealloc(Allctr_t *allctr, void *seg, Uint size,
}
#endif /* ARCH_64 && ERTS_HAVE_OS_PHYSICAL_MEMORY_RESERVATION */
-#if defined(ERTS_ALC_A_EXEC) && !defined(ERTS_HAVE_EXEC_MMAPPER)
+#if defined(ERTS_ALC_A_EXEC)
/*
- * For exec_alloc on non-amd64 that just need memory with PROT_EXEC
+ * For exec_alloc that need memory with PROT_EXEC
*/
void*
erts_alcu_exec_mseg_alloc(Allctr_t *allctr, Uint *size_p, Uint flags)
@@ -956,7 +1050,7 @@ erts_alcu_exec_mseg_dealloc(Allctr_t *allctr, void *seg, Uint size, Uint flags)
ASSERT(r == 0); (void)r;
erts_alcu_mseg_dealloc(allctr, seg, size, flags);
}
-#endif /* ERTS_ALC_A_EXEC && !ERTS_HAVE_EXEC_MMAPPER */
+#endif /* ERTS_ALC_A_EXEC */
#endif /* HAVE_ERTS_MSEG */
@@ -1153,89 +1247,23 @@ unlink_carrier(CarrierList_t *cl, Carrier_t *crr)
ASSERT(crr->next);
crr->next->prev = crr->prev;
}
-}
-
-
#ifdef DEBUG
-static int is_in_list(ErtsDoubleLink_t* sentinel, ErtsDoubleLink_t* node)
-{
- ErtsDoubleLink_t* p;
-
- ASSERT(node != sentinel);
- for (p = sentinel->next; p != sentinel; p = p->next) {
- if (p == node)
- return 1;
- }
- return 0;
-}
-#endif /* DEBUG */
-
-static ERTS_INLINE void
-link_edl_after(ErtsDoubleLink_t* after_me, ErtsDoubleLink_t* node)
-{
- ErtsDoubleLink_t* before_me = after_me->next;
- ASSERT(node != after_me && node != before_me);
- node->next = before_me;
- node->prev = after_me;
- before_me->prev = node;
- after_me->next = node;
-}
-
-static ERTS_INLINE void
-link_edl_before(ErtsDoubleLink_t* before_me, ErtsDoubleLink_t* node)
-{
- ErtsDoubleLink_t* after_me = before_me->prev;
- ASSERT(node != before_me && node != after_me);
- node->next = before_me;
- node->prev = after_me;
- before_me->prev = node;
- after_me->next = node;
-}
-
-static ERTS_INLINE void
-unlink_edl(ErtsDoubleLink_t* node)
-{
- node->next->prev = node->prev;
- node->prev->next = node->next;
-}
-
-static ERTS_INLINE void
-relink_edl_before(ErtsDoubleLink_t* before_me, ErtsDoubleLink_t* node)
-{
- if (node != before_me && node != before_me->prev) {
- unlink_edl(node);
- link_edl_before(before_me, node);
- }
+ crr->next = crr;
+ crr->prev = crr;
+#endif
}
static ERTS_INLINE int is_abandoned(Carrier_t *crr)
{
- return crr->cpool.abandoned.next != NULL;
-}
-
-static ERTS_INLINE void
-link_abandoned_carrier(ErtsDoubleLink_t* list, Carrier_t *crr)
-{
- ASSERT(!is_abandoned(crr));
-
- link_edl_after(list, &crr->cpool.abandoned);
-
- ASSERT(crr->cpool.abandoned.next != &crr->cpool.abandoned);
- ASSERT(crr->cpool.abandoned.prev != &crr->cpool.abandoned);
+ return crr->cpool.state != ERTS_MBC_IS_HOME;
}
static ERTS_INLINE void
unlink_abandoned_carrier(Carrier_t *crr)
{
- ASSERT(is_in_list(&crr->cpool.orig_allctr->cpool.pooled_list,
- &crr->cpool.abandoned) ||
- is_in_list(&crr->cpool.orig_allctr->cpool.traitor_list,
- &crr->cpool.abandoned));
-
- unlink_edl(&crr->cpool.abandoned);
-
- crr->cpool.abandoned.next = NULL;
- crr->cpool.abandoned.prev = NULL;
+ if (crr->cpool.state == ERTS_MBC_WAS_POOLED) {
+ aoff_remove_pooled_mbc(crr->cpool.orig_allctr, crr);
+ }
}
static ERTS_INLINE void
@@ -1243,24 +1271,19 @@ clear_busy_pool_carrier(Allctr_t *allctr, Carrier_t *crr)
{
if (crr) {
erts_aint_t max_size;
- erts_aint_t new_val;
+ erts_aint_t iallctr;
max_size = (erts_aint_t) allctr->largest_fblk_in_mbc(allctr, crr);
erts_atomic_set_nob(&crr->cpool.max_size, max_size);
- new_val = (((erts_aint_t) allctr)|ERTS_CRR_ALCTR_FLG_IN_POOL);
-
-#ifdef ERTS_ALC_CPOOL_DEBUG
- {
- erts_aint_t old_val = new_val|ERTS_CRR_ALCTR_FLG_BUSY;
+ iallctr = erts_atomic_read_nob(&crr->allctr);
+ ERTS_ALC_CPOOL_ASSERT((iallctr & ~ERTS_CRR_ALCTR_FLG_HOMECOMING)
+ == ((erts_aint_t)allctr |
+ ERTS_CRR_ALCTR_FLG_IN_POOL |
+ ERTS_CRR_ALCTR_FLG_BUSY));
- ERTS_ALC_CPOOL_ASSERT(old_val
- == erts_atomic_xchg_relb(&crr->allctr,
- new_val));
- }
-#else
- erts_atomic_set_relb(&crr->allctr, new_val);
-#endif
+ iallctr &= ~ERTS_CRR_ALCTR_FLG_BUSY;
+ erts_atomic_set_relb(&crr->allctr, iallctr);
}
}
@@ -1658,6 +1681,11 @@ dealloc_mbc(Allctr_t *allctr, Carrier_t *crr)
}
+static void set_new_allctr_abandon_limit(Allctr_t*);
+static void abandon_carrier(Allctr_t*, Carrier_t*);
+static void poolify_my_carrier(Allctr_t*, Carrier_t*);
+static void enqueue_homecoming(Allctr_t*, Carrier_t*);
+
static ERTS_INLINE Allctr_t*
get_pref_allctr(void *extra)
{
@@ -1724,9 +1752,23 @@ get_used_allctr(Allctr_t *pref_allctr, int pref_lock, void *p, UWord *sizep,
erts_aint_t act;
ERTS_ALC_CPOOL_ASSERT(!(iallctr & ERTS_CRR_ALCTR_FLG_BUSY));
- act = erts_atomic_cmpxchg_ddrb(&crr->allctr,
- iallctr|ERTS_CRR_ALCTR_FLG_BUSY,
- iallctr);
+ if (iallctr & ERTS_CRR_ALCTR_FLG_HOMECOMING) {
+ /*
+ * This carrier has just been given back to us by writing
+ * to crr->allctr with a write barrier (see abandon_carrier).
+ *
+ * We need a mathing read barrier to guarantee a correct view
+ * of the carrier for deallocation work.
+ */
+ act = erts_atomic_cmpxchg_rb(&crr->allctr,
+ iallctr|ERTS_CRR_ALCTR_FLG_BUSY,
+ iallctr);
+ }
+ else {
+ act = erts_atomic_cmpxchg_ddrb(&crr->allctr,
+ iallctr|ERTS_CRR_ALCTR_FLG_BUSY,
+ iallctr);
+ }
if (act == iallctr) {
*busy_pcrr_pp = crr;
break;
@@ -1742,13 +1784,6 @@ get_used_allctr(Allctr_t *pref_allctr, int pref_lock, void *p, UWord *sizep,
erts_mtx_unlock(&pref_allctr->mutex);
}
}
-
- ERTS_ALC_CPOOL_ASSERT(
- (((iallctr & ~ERTS_CRR_ALCTR_FLG_MASK) == (erts_aint_t) pref_allctr)
- ? (((iallctr & ERTS_CRR_ALCTR_FLG_MASK) == ERTS_CRR_ALCTR_FLG_IN_POOL)
- || ((iallctr & ERTS_CRR_ALCTR_FLG_MASK) == 0))
- : 1));
-
return used_allctr;
}
}
@@ -2000,9 +2035,9 @@ handle_delayed_fix_dealloc(Allctr_t *allctr, void *ptr)
/* Carrier migrated; need to redirect block to new owner... */
int cinit = used_allctr->dd.ix - allctr->dd.ix;
- ERTS_ALC_CPOOL_ASSERT(!busy_pcrr_p);
+ ERTS_ALC_CPOOL_ASSERT(!busy_pcrr_p);
- DEC_CC(allctr->calls.this_free);
+ DEC_CC(allctr->calls.this_free);
((ErtsAllctrFixDDBlock_t *) ptr)->fix_type = type;
if (ddq_enqueue(&used_allctr->dd.q, ptr, cinit))
@@ -2011,8 +2046,9 @@ handle_delayed_fix_dealloc(Allctr_t *allctr, void *ptr)
}
}
-static void
-schedule_dealloc_carrier(Allctr_t *allctr, Carrier_t *crr);
+static void schedule_dealloc_carrier(Allctr_t*, Carrier_t*);
+static void dealloc_my_carrier(Allctr_t*, Carrier_t*);
+
static ERTS_INLINE int
handle_delayed_dealloc(Allctr_t *allctr,
@@ -2074,39 +2110,61 @@ handle_delayed_dealloc(Allctr_t *allctr,
res = 1;
blk = UMEM2BLK(ptr);
- if (IS_FREE_LAST_MBC_BLK(blk)) {
+ if (blk->bhdr == HOMECOMING_MBC_BLK_HDR) {
/*
* A multiblock carrier that previously has been migrated away
- * from us and now is back to be deallocated. For more info
- * see schedule_dealloc_carrier().
- *
- * Note that we cannot use FBLK_TO_MBC(blk) since it
- * data has been overwritten by the queue.
+ * from us, was sent back to us either because
+ * - it became empty and we need to deallocated it, or
+ * - it was inserted into the pool and we need to update our pooled_tree
*/
- Carrier_t *crr = FIRST_BLK_TO_MBC(allctr, blk);
-
- /* Restore word overwritten by the dd-queue as it will be read
- * if this carrier is pulled from dc_list by cpool_fetch()
- */
- ERTS_ALC_CPOOL_ASSERT(FBLK_TO_MBC(blk) != crr);
- ERTS_CT_ASSERT(sizeof(ErtsAllctrDDBlock_t) == sizeof(void*));
-#ifdef MBC_ABLK_OFFSET_BITS
- blk->u.carrier = crr;
-#else
- blk->carrier = crr;
-#endif
+ Carrier_t *crr = ErtsContainerStruct(blk, Carrier_t,
+ cpool.homecoming_dd.blk);
+ Block_t* first_blk = MBC_TO_FIRST_BLK(allctr, crr);
+ erts_aint_t iallctr;
ERTS_ALC_CPOOL_ASSERT(ERTS_ALC_IS_CPOOL_ENABLED(allctr));
ERTS_ALC_CPOOL_ASSERT(allctr == crr->cpool.orig_allctr);
- ERTS_ALC_CPOOL_ASSERT(((erts_aint_t) allctr)
- != (erts_atomic_read_nob(&crr->allctr)
- & ~ERTS_CRR_ALCTR_FLG_MASK));
- erts_atomic_set_nob(&crr->allctr, ((erts_aint_t) allctr));
-
- schedule_dealloc_carrier(allctr, crr);
+ iallctr = erts_atomic_read_nob(&crr->allctr);
+ ASSERT(iallctr & ERTS_CRR_ALCTR_FLG_HOMECOMING);
+ while (1) {
+ if ((iallctr & (~ERTS_CRR_ALCTR_FLG_MASK |
+ ERTS_CRR_ALCTR_FLG_IN_POOL))
+ == (erts_aint_t)allctr) {
+ /*
+ * Carrier is home (mine and not in pool)
+ */
+ ASSERT(!(iallctr & ERTS_CRR_ALCTR_FLG_BUSY));
+ erts_atomic_set_nob(&crr->allctr, (erts_aint_t)allctr);
+ if (IS_FREE_LAST_MBC_BLK(first_blk))
+ dealloc_my_carrier(allctr, crr);
+ else
+ ASSERT(crr->cpool.state == ERTS_MBC_IS_HOME);
+ }
+ else {
+ erts_aint_t exp = iallctr;
+ erts_aint_t want = iallctr & ~ERTS_CRR_ALCTR_FLG_HOMECOMING;
+
+ iallctr = erts_atomic_cmpxchg_nob(&crr->allctr,
+ want,
+ exp);
+ if (iallctr != exp)
+ continue; /* retry */
+
+ ASSERT(crr->cpool.state != ERTS_MBC_IS_HOME);
+ unlink_abandoned_carrier(crr);
+ if (iallctr & ERTS_CRR_ALCTR_FLG_IN_POOL)
+ poolify_my_carrier(allctr, crr);
+ else
+ crr->cpool.state = ERTS_MBC_WAS_TRAITOR;
+ }
+ break;
+ }
}
else {
+ ASSERT(IS_SBC_BLK(blk) || (ABLK_TO_MBC(blk) !=
+ ErtsContainerStruct(blk, Carrier_t,
+ cpool.homecoming_dd.blk)));
INC_CC(allctr->calls.this_free);
@@ -2148,20 +2206,26 @@ enqueue_dealloc_other_instance(ErtsAlcType_t type,
erts_alloc_notify_delayed_dealloc(allctr->ix);
}
-
-static void
-set_new_allctr_abandon_limit(Allctr_t *allctr);
-static void
-abandon_carrier(Allctr_t *allctr, Carrier_t *crr);
-
+static ERTS_INLINE void
+update_pooled_tree(Allctr_t *allctr, Carrier_t *crr, Uint blk_sz)
+{
+ if (allctr == crr->cpool.orig_allctr && crr->cpool.state == ERTS_MBC_WAS_POOLED) {
+ /*
+ * Update pooled_tree with a potentially new (larger) max_sz
+ */
+ AOFF_RBTree_t* crr_node = &crr->cpool.pooled;
+ if (blk_sz > crr_node->hdr.bhdr) {
+ crr_node->hdr.bhdr = blk_sz;
+ erts_aoff_larger_max_size(crr_node);
+ }
+ }
+}
static ERTS_INLINE void
check_abandon_carrier(Allctr_t *allctr, Block_t *fblk, Carrier_t **busy_pcrr_pp)
{
Carrier_t *crr;
-
- if (busy_pcrr_pp && *busy_pcrr_pp)
- return;
+ UWord ncrr_in_pool, largest_fblk;
if (!ERTS_ALC_IS_CPOOL_ENABLED(allctr))
return;
@@ -2170,8 +2234,7 @@ check_abandon_carrier(Allctr_t *allctr, Block_t *fblk, Carrier_t **busy_pcrr_pp)
if (--allctr->cpool.check_limit_count <= 0)
set_new_allctr_abandon_limit(allctr);
- if (!erts_thr_progress_is_managed_thread())
- return;
+ ASSERT(erts_thr_progress_is_managed_thread());
if (allctr->cpool.disable_abandon)
return;
@@ -2179,6 +2242,9 @@ check_abandon_carrier(Allctr_t *allctr, Block_t *fblk, Carrier_t **busy_pcrr_pp)
if (allctr->mbcs.blocks.curr.size > allctr->cpool.abandon_limit)
return;
+ ncrr_in_pool = erts_atomic_read_nob(&allctr->cpool.stat.no_carriers);
+ if (ncrr_in_pool >= allctr->cpool.in_pool_limit)
+ return;
crr = FBLK_TO_MBC(fblk);
@@ -2189,9 +2255,14 @@ check_abandon_carrier(Allctr_t *allctr, Block_t *fblk, Carrier_t **busy_pcrr_pp)
return;
if (crr->cpool.thr_prgr != ERTS_THR_PRGR_INVALID
- && !erts_thr_progress_has_reached(crr->cpool.thr_prgr))
- return;
+ && !erts_thr_progress_has_reached(crr->cpool.thr_prgr))
+ return;
+
+ largest_fblk = allctr->largest_fblk_in_mbc(allctr, crr);
+ if (largest_fblk < allctr->cpool.fblk_min_limit)
+ return;
+ erts_atomic_set_nob(&crr->cpool.max_size, largest_fblk);
abandon_carrier(allctr, crr);
}
@@ -2237,6 +2308,7 @@ dealloc_block(Allctr_t *allctr, void *ptr, ErtsAlcFixList_t *fix, int dec_cc_on_
else {
Carrier_t *busy_pcrr_p;
Allctr_t *used_allctr;
+
used_allctr = get_used_allctr(allctr, ERTS_ALC_TS_PREF_LOCK_NO, ptr,
NULL, &busy_pcrr_p);
if (used_allctr == allctr) {
@@ -2253,10 +2325,10 @@ dealloc_block(Allctr_t *allctr, void *ptr, ErtsAlcFixList_t *fix, int dec_cc_on_
/* Carrier migrated; need to redirect block to new owner... */
int cinit = used_allctr->dd.ix - allctr->dd.ix;
- ERTS_ALC_CPOOL_ASSERT(!busy_pcrr_p);
+ ERTS_ALC_CPOOL_ASSERT(!busy_pcrr_p);
- if (dec_cc_on_redirect)
- DEC_CC(allctr->calls.this_free);
+ if (dec_cc_on_redirect)
+ DEC_CC(allctr->calls.this_free);
if (ddq_enqueue(&used_allctr->dd.q, ptr, cinit))
erts_alloc_notify_delayed_dealloc(used_allctr->ix);
}
@@ -2500,15 +2572,16 @@ mbc_free(Allctr_t *allctr, void *p, Carrier_t **busy_pcrr_pp)
ASSERT(blk_sz % sizeof(Unit_t) == 0);
ASSERT(IS_MBC_BLK(blk));
- if (is_first_blk
- && is_last_blk
- && allctr->main_carrier != FIRST_BLK_TO_MBC(allctr, blk)) {
- destroy_carrier(allctr, blk, busy_pcrr_pp);
+ if (is_first_blk && is_last_blk && crr != allctr->main_carrier) {
+ destroy_carrier(allctr, blk, busy_pcrr_pp);
}
else {
(*allctr->link_free_block)(allctr, blk);
HARD_CHECK_BLK_CARRIER(allctr, blk);
- check_abandon_carrier(allctr, blk, busy_pcrr_pp);
+ if (busy_pcrr_pp && *busy_pcrr_pp)
+ update_pooled_tree(allctr, crr, blk_sz);
+ else
+ check_abandon_carrier(allctr, blk, busy_pcrr_pp);
}
}
@@ -2542,8 +2615,19 @@ mbc_realloc(Allctr_t *allctr, void *p, Uint size, Uint32 alcu_flgs,
return NULL;
#else /* !MBC_REALLOC_ALWAYS_MOVES */
- if (busy_pcrr_pp && *busy_pcrr_pp)
- goto realloc_move; /* Don't want to use carrier in pool */
+ if (busy_pcrr_pp && *busy_pcrr_pp) {
+ /*
+ * Don't want to use carrier in pool
+ */
+ new_p = mbc_alloc(allctr, size);
+ if (!new_p)
+ return NULL;
+ new_blk = UMEM2BLK(new_p);
+ ASSERT(!(IS_MBC_BLK(new_blk) && ABLK_TO_MBC(new_blk) == *busy_pcrr_pp));
+ sys_memcpy(new_p, p, MIN(size, old_blk_sz - ABLK_HDR_SZ));
+ mbc_free(allctr, p, busy_pcrr_pp);
+ return new_p;
+ }
get_blk_sz = blk_sz = UMEMSZ2BLKSZ(allctr, size);
@@ -2776,7 +2860,7 @@ mbc_realloc(Allctr_t *allctr, void *p, Uint size, Uint32 alcu_flgs,
if (cand_blk_sz < get_blk_sz) {
/* We wont fit in cand_blk get a new one */
- realloc_move:
+
#endif /* !MBC_REALLOC_ALWAYS_MOVES */
new_p = mbc_alloc(allctr, size);
@@ -2880,8 +2964,7 @@ mbc_realloc(Allctr_t *allctr, void *p, Uint size, Uint32 alcu_flgs,
#define ERTS_ALC_MAX_DEALLOC_CARRIER 10
-#define ERTS_ALC_CPOOL_MAX_FETCH_INSPECT 20
-#define ERTS_ALC_CPOOL_MAX_TRAITOR_INSPECT 10
+#define ERTS_ALC_CPOOL_MAX_FETCH_INSPECT 100
#define ERTS_ALC_CPOOL_CHECK_LIMIT_COUNT 100
#define ERTS_ALC_CPOOL_MAX_FAILED_STAT_READS 3
@@ -3045,19 +3128,18 @@ cpool_insert(Allctr_t *allctr, Carrier_t *crr)
ErtsAlcCPoolData_t *cpd1p, *cpd2p;
erts_aint_t val;
ErtsAlcCPoolData_t *sentinel = &carrier_pool[allctr->alloc_no].sentinel;
+ Allctr_t *orig_allctr = crr->cpool.orig_allctr;
ERTS_ALC_CPOOL_ASSERT(allctr->alloc_no == ERTS_ALC_A_INVALID /* testcase */
|| erts_thr_progress_is_managed_thread());
- ERTS_ALC_CPOOL_ASSERT(erts_atomic_read_nob(&crr->allctr)
- == (erts_aint_t) allctr);
- erts_atomic_add_nob(&allctr->cpool.stat.blocks_size,
+ erts_atomic_add_nob(&orig_allctr->cpool.stat.blocks_size,
(erts_aint_t) crr->cpool.blocks_size);
- erts_atomic_add_nob(&allctr->cpool.stat.no_blocks,
+ erts_atomic_add_nob(&orig_allctr->cpool.stat.no_blocks,
(erts_aint_t) crr->cpool.blocks);
- erts_atomic_add_nob(&allctr->cpool.stat.carriers_size,
+ erts_atomic_add_nob(&orig_allctr->cpool.stat.carriers_size,
(erts_aint_t) CARRIER_SZ(crr));
- erts_atomic_inc_nob(&allctr->cpool.stat.no_carriers);
+ erts_atomic_inc_nob(&orig_allctr->cpool.stat.no_carriers);
/*
* We search in 'next' direction and begin by passing
@@ -3118,8 +3200,6 @@ cpool_insert(Allctr_t *allctr, Carrier_t *crr)
(erts_aint_t) &crr->cpool,
(erts_aint_t) cpd1p);
- erts_atomic_set_wb(&crr->allctr,
- ((erts_aint_t) allctr)|ERTS_CRR_ALCTR_FLG_IN_POOL);
LTTNG3(carrier_pool_put, ERTS_ALC_A2AD(allctr->alloc_no), allctr->ix, CARRIER_SZ(crr));
}
@@ -3221,130 +3301,126 @@ cpool_delete(Allctr_t *allctr, Allctr_t *prev_allctr, Carrier_t *crr)
static Carrier_t *
cpool_fetch(Allctr_t *allctr, UWord size)
{
- int i, i_stop, has_passed_sentinel;
+ enum { IGNORANT, HAS_SEEN_SENTINEL, THE_LAST_ONE } loop_state;
+ int i;
Carrier_t *crr;
+ Carrier_t *reinsert_crr = NULL;
ErtsAlcCPoolData_t *cpdp;
- ErtsAlcCPoolData_t *cpool_entrance;
+ ErtsAlcCPoolData_t *cpool_entrance = NULL;
ErtsAlcCPoolData_t *sentinel;
- ErtsDoubleLink_t* dl;
- ErtsDoubleLink_t* first_old_traitor;
ERTS_ALC_CPOOL_ASSERT(allctr->alloc_no == ERTS_ALC_A_INVALID /* testcase */
|| erts_thr_progress_is_managed_thread());
i = ERTS_ALC_CPOOL_MAX_FETCH_INSPECT;
- first_old_traitor = allctr->cpool.traitor_list.next;
- cpool_entrance = NULL;
LTTNG3(carrier_pool_get, ERTS_ALC_A2AD(allctr->alloc_no), allctr->ix, (unsigned long)size);
/*
- * Search my own pooled_list,
+ * Search my own pooled_tree,
* i.e my abandoned carriers that were in the pool last time I checked.
*/
+ do {
+ erts_aint_t exp, act;
+
+ crr = aoff_lookup_pooled_mbc(allctr, size);
+ if (!crr)
+ break;
+
+ ASSERT(crr->cpool.state == ERTS_MBC_WAS_POOLED);
+ ASSERT(crr->cpool.orig_allctr == allctr);
+
+ aoff_remove_pooled_mbc(allctr, crr);
+
+ exp = erts_atomic_read_nob(&crr->allctr);
+ if (exp & ERTS_CRR_ALCTR_FLG_IN_POOL) {
+ ASSERT((exp & ~ERTS_CRR_ALCTR_FLG_MASK) == (erts_aint_t)allctr);
+ if (erts_atomic_read_nob(&crr->cpool.max_size) < size) {
+ /*
+ * This carrier has been fetched and inserted back again
+ * by a foreign allocator. That's why it has a stale search size.
+ */
+ ASSERT(exp & ERTS_CRR_ALCTR_FLG_HOMECOMING);
+ crr->cpool.pooled.hdr.bhdr = erts_atomic_read_nob(&crr->cpool.max_size);
+ aoff_add_pooled_mbc(allctr, crr);
+ INC_CC(allctr->cpool.stat.skip_size);
+ continue;
+ }
+ else if (exp & ERTS_CRR_ALCTR_FLG_BUSY) {
+ /*
+ * This must be our own carrier as part of a realloc call.
+ * Skip it to make things simpler.
+ * Must wait to re-insert to not be found again by lookup.
+ */
+ ASSERT(!reinsert_crr);
+ reinsert_crr = crr;
+ INC_CC(allctr->cpool.stat.skip_busy);
+ continue;
+ }
- dl = allctr->cpool.pooled_list.next;
- while(dl != &allctr->cpool.pooled_list) {
- erts_aint_t exp, act;
- crr = (Carrier_t *) (((char *) dl) - offsetof(Carrier_t, cpool.abandoned));
-
- ASSERT(!is_in_list(&allctr->cpool.traitor_list, dl));
- ASSERT(crr->cpool.orig_allctr == allctr);
- dl = dl->next;
- exp = erts_atomic_read_rb(&crr->allctr);
- if ((exp & ERTS_CRR_ALCTR_FLG_MASK) == ERTS_CRR_ALCTR_FLG_IN_POOL
- && erts_atomic_read_nob(&crr->cpool.max_size) >= size) {
- /* Try to fetch it... */
- act = erts_atomic_cmpxchg_mb(&crr->allctr,
- (erts_aint_t) allctr,
- exp);
- if (act == exp) {
- cpool_delete(allctr, ((Allctr_t *) (act & ~ERTS_CRR_ALCTR_FLG_MASK)), crr);
- unlink_abandoned_carrier(crr);
-
- /* Move sentinel to continue next search from here */
- relink_edl_before(dl, &allctr->cpool.pooled_list);
- return crr;
- }
- exp = act;
- }
- if (exp & ERTS_CRR_ALCTR_FLG_IN_POOL) {
- if (!cpool_entrance)
- cpool_entrance = &crr->cpool;
- }
- else { /* Not in pool, move to traitor_list */
- unlink_abandoned_carrier(crr);
- link_abandoned_carrier(&allctr->cpool.traitor_list, crr);
- }
- if (--i <= 0) {
- /* Move sentinel to continue next search from here */
- relink_edl_before(dl, &allctr->cpool.pooled_list);
- return NULL;
- }
- }
+ /* Try to fetch it... */
+ act = erts_atomic_cmpxchg_mb(&crr->allctr,
+ exp & ~ERTS_CRR_ALCTR_FLG_IN_POOL,
+ exp);
+ if (act == exp) {
+ cpool_delete(allctr, allctr, crr);
+ crr->cpool.state = ERTS_MBC_IS_HOME;
+
+ if (reinsert_crr)
+ aoff_add_pooled_mbc(allctr, reinsert_crr);
+ return crr;
+ }
+ exp = act;
+ INC_CC(allctr->cpool.stat.skip_race);
+ }
+ else
+ INC_CC(allctr->cpool.stat.skip_not_pooled);
- /* Now search traitor_list.
- * i.e carriers employed by other allocators last time I checked.
- * They might have been abandoned since then.
- */
+ /* Not in pool anymore */
+ ASSERT(!(exp & ERTS_CRR_ALCTR_FLG_BUSY));
+ crr->cpool.state = ERTS_MBC_WAS_TRAITOR;
- i_stop = (i < ERTS_ALC_CPOOL_MAX_TRAITOR_INSPECT ?
- 0 : i - ERTS_ALC_CPOOL_MAX_TRAITOR_INSPECT);
- dl = first_old_traitor;
- while(dl != &allctr->cpool.traitor_list) {
- erts_aint_t exp, act;
- crr = (Carrier_t *) (((char *) dl) - offsetof(Carrier_t, cpool.abandoned));
- ASSERT(dl != &allctr->cpool.pooled_list);
- ASSERT(crr->cpool.orig_allctr == allctr);
- dl = dl->next;
- exp = erts_atomic_read_rb(&crr->allctr);
- if (exp & ERTS_CRR_ALCTR_FLG_IN_POOL) {
- if (!(exp & ERTS_CRR_ALCTR_FLG_BUSY)
- && erts_atomic_read_nob(&crr->cpool.max_size) >= size) {
- /* Try to fetch it... */
- act = erts_atomic_cmpxchg_mb(&crr->allctr,
- (erts_aint_t) allctr,
- exp);
- if (act == exp) {
- cpool_delete(allctr, ((Allctr_t *) (act & ~ERTS_CRR_ALCTR_FLG_MASK)), crr);
- unlink_abandoned_carrier(crr);
+ }while (--i > 0);
- /* Move sentinel to continue next search from here */
- relink_edl_before(dl, &allctr->cpool.traitor_list);
- return crr;
- }
- exp = act;
- }
- if (exp & ERTS_CRR_ALCTR_FLG_IN_POOL) {
- if (!cpool_entrance)
- cpool_entrance = &crr->cpool;
+ if (reinsert_crr)
+ aoff_add_pooled_mbc(allctr, reinsert_crr);
- /* Move to pooled_list */
- unlink_abandoned_carrier(crr);
- link_abandoned_carrier(&allctr->cpool.pooled_list, crr);
- }
- }
- if (--i <= i_stop) {
- /* Move sentinel to continue next search from here */
- relink_edl_before(dl, &allctr->cpool.traitor_list);
- if (i > 0)
- break;
- else
- return NULL;
- }
+ /*
+ * Try find a nice cpool_entrance
+ */
+ while (allctr->cpool.pooled_tree) {
+ erts_aint_t iallctr;
+
+ crr = ErtsContainerStruct(allctr->cpool.pooled_tree, Carrier_t, cpool.pooled);
+ iallctr = erts_atomic_read_nob(&crr->allctr);
+ if (iallctr & ERTS_CRR_ALCTR_FLG_IN_POOL) {
+ cpool_entrance = &crr->cpool;
+ break;
+ }
+ /* Not in pool anymore */
+ ASSERT(!(iallctr & ERTS_CRR_ALCTR_FLG_BUSY));
+ aoff_remove_pooled_mbc(allctr, crr);
+ crr->cpool.state = ERTS_MBC_WAS_TRAITOR;
+
+ if (--i <= 0) {
+ INC_CC(allctr->cpool.stat.fail_pooled);
+ return NULL;
+ }
}
+
/*
* Finally search the shared pool and try employ foreign carriers
*/
-
sentinel = &carrier_pool[allctr->alloc_no].sentinel;
if (cpool_entrance) {
- /* We saw a pooled carried above, use it as entrance into the pool
+ /*
+ * We saw a pooled carried above, use it as entrance into the pool
*/
cpdp = cpool_entrance;
}
else {
- /* No pooled carried seen above. Start search at cpool sentinel,
+ /*
+ * No pooled carried seen above. Start search at cpool sentinel,
* but begin by passing one element before trying to fetch.
* This in order to avoid contention with threads inserting elements.
*/
@@ -3354,8 +3430,8 @@ cpool_fetch(Allctr_t *allctr, UWord size)
goto check_dc_list;
}
- has_passed_sentinel = 0;
- while (1) {
+ loop_state = IGNORANT;
+ do {
erts_aint_t exp;
cpdp = cpool_aint2cpd(cpool_read(&cpdp->prev));
if (cpdp == cpool_entrance) {
@@ -3364,38 +3440,52 @@ cpool_fetch(Allctr_t *allctr, UWord size)
if (cpdp == sentinel)
break;
}
- i = 0; /* Last one to inspect */
+ loop_state = THE_LAST_ONE;
}
else if (cpdp == sentinel) {
- if (has_passed_sentinel) {
+ if (loop_state == HAS_SEEN_SENTINEL) {
/* We been here before. cpool_entrance must have been removed */
+ INC_CC(allctr->cpool.stat.entrance_removed);
break;
}
cpdp = cpool_aint2cpd(cpool_read(&cpdp->prev));
if (cpdp == sentinel)
break;
- has_passed_sentinel = 1;
+ loop_state = HAS_SEEN_SENTINEL;
}
- crr = (Carrier_t *)(((char *)cpdp) - offsetof(Carrier_t, cpool));
+ crr = ErtsContainerStruct(cpdp, Carrier_t, cpool);
exp = erts_atomic_read_rb(&crr->allctr);
- if (((exp & (ERTS_CRR_ALCTR_FLG_MASK)) == ERTS_CRR_ALCTR_FLG_IN_POOL)
- && (erts_atomic_read_nob(&cpdp->max_size) >= size)) {
+
+ if (erts_atomic_read_nob(&cpdp->max_size) < size) {
+ INC_CC(allctr->cpool.stat.skip_size);
+ }
+ else if ((exp & (ERTS_CRR_ALCTR_FLG_IN_POOL | ERTS_CRR_ALCTR_FLG_BUSY))
+ == ERTS_CRR_ALCTR_FLG_IN_POOL) {
erts_aint_t act;
- /* Try to fetch it... */
- act = erts_atomic_cmpxchg_mb(&crr->allctr,
- (erts_aint_t) allctr,
- exp);
+ erts_aint_t want = (((erts_aint_t) allctr)
+ | (exp & ERTS_CRR_ALCTR_FLG_HOMECOMING));
+ /* Try to fetch it... */
+ act = erts_atomic_cmpxchg_mb(&crr->allctr, want, exp);
if (act == exp) {
cpool_delete(allctr, ((Allctr_t *) (act & ~ERTS_CRR_ALCTR_FLG_MASK)), crr);
if (crr->cpool.orig_allctr == allctr) {
unlink_abandoned_carrier(crr);
- }
+ crr->cpool.state = ERTS_MBC_IS_HOME;
+ }
return crr;
}
}
- if (--i <= 0)
+
+ if (exp & ERTS_CRR_ALCTR_FLG_BUSY)
+ INC_CC(allctr->cpool.stat.skip_busy);
+ else
+ INC_CC(allctr->cpool.stat.skip_race);
+
+ if (--i <= 0) {
+ INC_CC(allctr->cpool.stat.fail_shared);
return NULL;
- }
+ }
+ }while (loop_state != THE_LAST_ONE);
check_dc_list:
/* Last; check our own pending dealloc carrier list... */
@@ -3404,23 +3494,23 @@ check_dc_list:
if (erts_atomic_read_nob(&crr->cpool.max_size) >= size) {
Block_t* blk;
unlink_carrier(&allctr->cpool.dc_list, crr);
-#ifdef ERTS_ALC_CPOOL_DEBUG
- ERTS_ALC_CPOOL_ASSERT(erts_atomic_xchg_nob(&crr->allctr,
- ((erts_aint_t) allctr))
- == (((erts_aint_t) allctr) & ~ERTS_CRR_ALCTR_FLG_MASK));
-#else
- erts_atomic_set_nob(&crr->allctr, ((erts_aint_t) allctr));
-#endif
+ ERTS_ALC_CPOOL_ASSERT(erts_atomic_read_nob(&crr->allctr)
+ == ((erts_aint_t) allctr));
blk = MBC_TO_FIRST_BLK(allctr, crr);
ASSERT(FBLK_TO_MBC(blk) == crr);
allctr->link_free_block(allctr, blk);
return crr;
}
crr = crr->prev;
- if (--i <= 0)
+ if (--i <= 0) {
+ INC_CC(allctr->cpool.stat.fail_pend_dealloc);
return NULL;
+ }
}
+ if (i != ERTS_ALC_CPOOL_MAX_FETCH_INSPECT)
+ INC_CC(allctr->cpool.stat.fail);
+
return NULL;
}
@@ -3475,9 +3565,6 @@ static void
schedule_dealloc_carrier(Allctr_t *allctr, Carrier_t *crr)
{
Allctr_t *orig_allctr;
- Block_t *blk;
- int check_pending_dealloc;
- erts_aint_t max_size;
ASSERT(IS_MB_CARRIER(crr));
@@ -3488,9 +3575,17 @@ schedule_dealloc_carrier(Allctr_t *allctr, Carrier_t *crr)
orig_allctr = crr->cpool.orig_allctr;
- if (allctr != orig_allctr) {
- int cinit = orig_allctr->dd.ix - allctr->dd.ix;
-
+ if (allctr == orig_allctr) {
+ if (!(erts_atomic_read_nob(&crr->allctr) & ERTS_CRR_ALCTR_FLG_HOMECOMING)) {
+ dealloc_my_carrier(allctr, crr);
+ }
+ /*else
+ * Carrier was abandoned earlier by other thread and
+ * is still waiting for us in dd-queue.
+ * handle_delayed_dealloc() will handle it when crr is dequeued.
+ */
+ }
+ else {
/*
* We send the carrier to its origin for deallocation.
* This in order:
@@ -3499,29 +3594,39 @@ schedule_dealloc_carrier(Allctr_t *allctr, Carrier_t *crr)
* - to ensure that we always only reuse empty carriers
* originating from our own thread specific mseg_alloc
* instance which is beneficial on NUMA systems.
- *
- * The receiver will recognize that this is a carrier to
- * deallocate (and not a block which is the common case)
- * since the block is an mbc block that is free and last
- * in the carrier.
*/
- blk = MBC_TO_FIRST_BLK(allctr, crr);
- ERTS_ALC_CPOOL_ASSERT(IS_FREE_LAST_MBC_BLK(blk));
-
- ERTS_ALC_CPOOL_ASSERT(IS_MBC_FIRST_ABLK(allctr, blk));
- ERTS_ALC_CPOOL_ASSERT(crr == FBLK_TO_MBC(blk));
- ERTS_ALC_CPOOL_ASSERT(crr == FIRST_BLK_TO_MBC(allctr, blk));
- ERTS_ALC_CPOOL_ASSERT(((erts_aint_t) allctr)
- == (erts_atomic_read_nob(&crr->allctr)
- & ~ERTS_CRR_ALCTR_FLG_MASK));
+ erts_aint_t iallctr;
+#ifdef ERTS_ALC_CPOOL_DEBUG
+ Block_t* first_blk = MBC_TO_FIRST_BLK(allctr, crr);
+ ERTS_ALC_CPOOL_ASSERT(IS_FREE_LAST_MBC_BLK(first_blk));
+
+ ERTS_ALC_CPOOL_ASSERT(IS_MBC_FIRST_ABLK(allctr, first_blk));
+ ERTS_ALC_CPOOL_ASSERT(crr == FBLK_TO_MBC(first_blk));
+ ERTS_ALC_CPOOL_ASSERT(crr == FIRST_BLK_TO_MBC(allctr, first_blk));
+ ERTS_ALC_CPOOL_ASSERT((erts_atomic_read_nob(&crr->allctr)
+ & ~ERTS_CRR_ALCTR_FLG_HOMECOMING)
+ == (erts_aint_t) allctr);
+#endif
- if (ddq_enqueue(&orig_allctr->dd.q, BLK2UMEM(blk), cinit))
- erts_alloc_notify_delayed_dealloc(orig_allctr->ix);
- return;
+ iallctr = (erts_aint_t)orig_allctr | ERTS_CRR_ALCTR_FLG_HOMECOMING;
+ if (!(erts_atomic_xchg_nob(&crr->allctr, iallctr)
+ & ERTS_CRR_ALCTR_FLG_HOMECOMING)) {
+ enqueue_homecoming(allctr, crr);
+ }
}
+}
+
+static void dealloc_my_carrier(Allctr_t *allctr, Carrier_t *crr)
+{
+ Block_t *blk;
+ int check_pending_dealloc;
+ erts_aint_t max_size;
- if (is_abandoned(crr))
- unlink_abandoned_carrier(crr);
+ ERTS_ALC_CPOOL_ASSERT(allctr == crr->cpool.orig_allctr);
+ if (is_abandoned(crr)) {
+ unlink_abandoned_carrier(crr);
+ crr->cpool.state = ERTS_MBC_IS_HOME;
+ }
if (crr->cpool.thr_prgr == ERTS_THR_PRGR_INVALID
|| erts_thr_progress_has_reached(crr->cpool.thr_prgr)) {
@@ -3553,6 +3658,7 @@ schedule_dealloc_carrier(Allctr_t *allctr, Carrier_t *crr)
static ERTS_INLINE void
cpool_init_carrier_data(Allctr_t *allctr, Carrier_t *crr)
{
+ crr->cpool.homecoming_dd.blk.bhdr = HOMECOMING_MBC_BLK_HDR;
erts_atomic_init_nob(&crr->cpool.next, ERTS_AINT_NULL);
erts_atomic_init_nob(&crr->cpool.prev, ERTS_AINT_NULL);
crr->cpool.orig_allctr = allctr;
@@ -3571,8 +3677,7 @@ cpool_init_carrier_data(Allctr_t *allctr, Carrier_t *crr)
limit = (csz/100)*allctr->cpool.util_limit;
crr->cpool.abandon_limit = limit;
}
- crr->cpool.abandoned.next = NULL;
- crr->cpool.abandoned.prev = NULL;
+ crr->cpool.state = ERTS_MBC_IS_HOME;
}
static void
@@ -3598,23 +3703,62 @@ set_new_allctr_abandon_limit(Allctr_t *allctr)
static void
abandon_carrier(Allctr_t *allctr, Carrier_t *crr)
{
- erts_aint_t max_size;
+ erts_aint_t iallctr;
- STAT_MBC_CPOOL_INSERT(allctr, crr);
+ STAT_MBC_ABANDON(allctr, crr);
unlink_carrier(&allctr->mbc_list, crr);
- if (crr->cpool.orig_allctr == allctr) {
- link_abandoned_carrier(&allctr->cpool.pooled_list, crr);
+ allctr->remove_mbc(allctr, crr);
+ set_new_allctr_abandon_limit(allctr);
+
+ cpool_insert(allctr, crr);
+
+
+ iallctr = erts_atomic_read_nob(&crr->allctr);
+ if (allctr == crr->cpool.orig_allctr) {
+ /* preserve HOMECOMING flag */
+ ASSERT((iallctr & ~ERTS_CRR_ALCTR_FLG_HOMECOMING) == (erts_aint_t)allctr);
+ erts_atomic_set_wb(&crr->allctr, iallctr | ERTS_CRR_ALCTR_FLG_IN_POOL);
+ poolify_my_carrier(allctr, crr);
}
+ else {
+ ASSERT((iallctr & ~ERTS_CRR_ALCTR_FLG_HOMECOMING) == (erts_aint_t)allctr);
+ iallctr = ((erts_aint_t)crr->cpool.orig_allctr |
+ ERTS_CRR_ALCTR_FLG_HOMECOMING |
+ ERTS_CRR_ALCTR_FLG_IN_POOL);
+ if (!(erts_atomic_xchg_wb(&crr->allctr, iallctr)
+ & ERTS_CRR_ALCTR_FLG_HOMECOMING)) {
+
+ enqueue_homecoming(allctr, crr);
+ }
+ }
+}
- allctr->remove_mbc(allctr, crr);
+static void
+enqueue_homecoming(Allctr_t* allctr, Carrier_t* crr)
+{
+ Allctr_t* orig_allctr = crr->cpool.orig_allctr;
+ const int cinit = orig_allctr->dd.ix - allctr->dd.ix;
+ Block_t* dd_blk = &crr->cpool.homecoming_dd.blk;
- max_size = (erts_aint_t) allctr->largest_fblk_in_mbc(allctr, crr);
- erts_atomic_set_nob(&crr->cpool.max_size, max_size);
+ /*
+ * The receiver will recognize this as a carrier
+ * (and not a block which is the common case)
+ * since the block header is HOMECOMING_MBC_BLK_HDR.
+ */
+ ASSERT(dd_blk->bhdr == HOMECOMING_MBC_BLK_HDR);
+ if (ddq_enqueue(&orig_allctr->dd.q, BLK2UMEM(dd_blk), cinit))
+ erts_alloc_notify_delayed_dealloc(orig_allctr->ix);
+}
- cpool_insert(allctr, crr);
+static void
+poolify_my_carrier(Allctr_t *allctr, Carrier_t *crr)
+{
+ ERTS_ALC_CPOOL_ASSERT(allctr == crr->cpool.orig_allctr);
- set_new_allctr_abandon_limit(allctr);
+ crr->cpool.pooled.hdr.bhdr = erts_atomic_read_nob(&crr->cpool.max_size);
+ aoff_add_pooled_mbc(allctr, crr);
+ crr->cpool.state = ERTS_MBC_WAS_POOLED;
}
static void
@@ -3771,6 +3915,7 @@ create_carrier(Allctr_t *allctr, Uint umem_sz, UWord flags)
crr = cpool_fetch(allctr, blk_sz);
if (crr) {
STAT_MBC_CPOOL_FETCH(allctr, crr);
+ INC_CC(allctr->cpool.stat.fetch);
link_carrier(&allctr->mbc_list, crr);
(*allctr->add_mbc)(allctr, crr);
blk = (*allctr->get_free_block)(allctr, blk_sz, NULL, 0);
@@ -4128,13 +4273,18 @@ destroy_carrier(Allctr_t *allctr, Block_t *blk, Carrier_t **busy_pcrr_pp)
#endif
if (busy_pcrr_pp && *busy_pcrr_pp) {
+ erts_aint_t iallctr = erts_atomic_read_nob(&crr->allctr);
ERTS_ALC_CPOOL_ASSERT(*busy_pcrr_pp == crr);
- *busy_pcrr_pp = NULL;
- ERTS_ALC_CPOOL_ASSERT(erts_atomic_read_nob(&crr->allctr)
- == (((erts_aint_t) allctr)
- | ERTS_CRR_ALCTR_FLG_IN_POOL
- | ERTS_CRR_ALCTR_FLG_BUSY));
- erts_atomic_set_nob(&crr->allctr, ((erts_aint_t) allctr));
+ ERTS_ALC_CPOOL_ASSERT((iallctr & ~ERTS_CRR_ALCTR_FLG_HOMECOMING)
+ == (((erts_aint_t) allctr)
+ | ERTS_CRR_ALCTR_FLG_IN_POOL
+ | ERTS_CRR_ALCTR_FLG_BUSY));
+ ERTS_ALC_CPOOL_ASSERT(allctr == crr->cpool.orig_allctr);
+
+ *busy_pcrr_pp = NULL;
+ erts_atomic_set_nob(&crr->allctr,
+ (iallctr & ~(ERTS_CRR_ALCTR_FLG_IN_POOL |
+ ERTS_CRR_ALCTR_FLG_BUSY)));
cpool_delete(allctr, allctr, crr);
}
else
@@ -4184,7 +4334,7 @@ static struct {
Eterm e;
Eterm t;
Eterm ramv;
- Eterm sbct;
+ Eterm atags;
#if HAVE_ERTS_MSEG
Eterm asbcst;
Eterm rsbcst;
@@ -4201,6 +4351,8 @@ static struct {
Eterm smbcs;
Eterm mbcgs;
Eterm acul;
+ Eterm acnl;
+ Eterm acfml;
#if HAVE_ERTS_MSEG
Eterm mmc;
@@ -4212,6 +4364,17 @@ static struct {
Eterm mbcs;
Eterm mbcs_pool;
+ Eterm fetch;
+ Eterm fail_pooled;
+ Eterm fail_shared;
+ Eterm fail_pend_dealloc;
+ Eterm fail;
+ Eterm skip_size;
+ Eterm skip_busy;
+ Eterm skip_not_pooled;
+ Eterm skip_homecoming;
+ Eterm skip_race;
+ Eterm entrance_removed;
Eterm sbcs;
Eterm sys_alloc_carriers_size;
@@ -4241,11 +4404,11 @@ static struct {
#endif
} am;
-static Eterm fix_type_atoms[ERTS_ALC_NO_FIXED_SIZES];
+static Eterm alloc_type_atoms[ERTS_ALC_N_MAX + 1];
static ERTS_INLINE void atom_init(Eterm *atom, char *name)
{
- *atom = am_atom_put(name, strlen(name));
+ *atom = am_atom_put(name, sys_strlen(name));
}
#define AM_INIT(AM) atom_init(&am.AM, #AM)
@@ -4272,7 +4435,7 @@ init_atoms(Allctr_t *allctr)
AM_INIT(e);
AM_INIT(t);
AM_INIT(ramv);
- AM_INIT(sbct);
+ AM_INIT(atags);
#if HAVE_ERTS_MSEG
AM_INIT(asbcst);
AM_INIT(rsbcst);
@@ -4289,6 +4452,8 @@ init_atoms(Allctr_t *allctr)
AM_INIT(smbcs);
AM_INIT(mbcgs);
AM_INIT(acul);
+ AM_INIT(acnl);
+ AM_INIT(acfml);
#if HAVE_ERTS_MSEG
AM_INIT(mmc);
@@ -4300,6 +4465,17 @@ init_atoms(Allctr_t *allctr)
AM_INIT(mbcs);
AM_INIT(mbcs_pool);
+ AM_INIT(fetch);
+ AM_INIT(fail_pooled);
+ AM_INIT(fail_shared);
+ AM_INIT(fail_pend_dealloc);
+ AM_INIT(fail);
+ AM_INIT(skip_size);
+ AM_INIT(skip_busy);
+ AM_INIT(skip_not_pooled);
+ AM_INIT(skip_homecoming);
+ AM_INIT(skip_race);
+ AM_INIT(entrance_removed);
AM_INIT(sbcs);
AM_INIT(sys_alloc_carriers_size);
@@ -4331,12 +4507,12 @@ init_atoms(Allctr_t *allctr)
}
#endif
- for (ix = 0; ix < ERTS_ALC_NO_FIXED_SIZES; ix++) {
- ErtsAlcType_t n = ERTS_ALC_N_MIN_A_FIXED_SIZE + ix;
- char *name = (char *) ERTS_ALC_N2TD(n);
- size_t len = strlen(name);
- fix_type_atoms[ix] = am_atom_put(name, len);
- }
+ for (ix = ERTS_ALC_N_MIN; ix <= ERTS_ALC_N_MAX; ix++) {
+ const char *name = ERTS_ALC_N2TD(ix);
+ size_t len = sys_strlen(name);
+
+ alloc_type_atoms[ix] = am_atom_put(name, len);
+ }
}
if (allctr && !allctr->atoms_initialized) {
@@ -4449,6 +4625,7 @@ sz_info_fix(Allctr_t *allctr,
ErtsAlcFixList_t *fix = &allctr->fix[ix];
UWord alloced = fix->type_size * fix->u.cpool.allocated;
UWord used = fix->type_size * fix->u.cpool.used;
+ ErtsAlcType_t n = ERTS_ALC_N_MIN_A_FIXED_SIZE + ix;
if (print_to_p) {
fmtfn_t to = *print_to_p;
@@ -4456,15 +4633,14 @@ sz_info_fix(Allctr_t *allctr,
erts_print(to,
arg,
"fix type internal: %s %bpu %bpu\n",
- (char *) ERTS_ALC_N2TD(ERTS_ALC_N_MIN_A_FIXED_SIZE
- + ix),
+ (char *) ERTS_ALC_N2TD(n),
alloced,
used);
}
if (hpp || szp) {
add_3tup(hpp, szp, &res,
- fix_type_atoms[ix],
+ alloc_type_atoms[n],
bld_unstable_uint(hpp, szp, alloced),
bld_unstable_uint(hpp, szp, used));
}
@@ -4477,6 +4653,7 @@ sz_info_fix(Allctr_t *allctr,
ErtsAlcFixList_t *fix = &allctr->fix[ix];
UWord alloced = fix->type_size * fix->u.nocpool.allocated;
UWord used = fix->type_size*fix->u.nocpool.used;
+ ErtsAlcType_t n = ERTS_ALC_N_MIN_A_FIXED_SIZE + ix;
if (print_to_p) {
fmtfn_t to = *print_to_p;
@@ -4484,15 +4661,14 @@ sz_info_fix(Allctr_t *allctr,
erts_print(to,
arg,
"fix type: %s %bpu %bpu\n",
- (char *) ERTS_ALC_N2TD(ERTS_ALC_N_MIN_A_FIXED_SIZE
- + ix),
+ (char *) ERTS_ALC_N2TD(n),
alloced,
used);
}
if (hpp || szp) {
add_3tup(hpp, szp, &res,
- fix_type_atoms[ix],
+ alloc_type_atoms[n],
bld_unstable_uint(hpp, szp, alloced),
bld_unstable_uint(hpp, szp, used));
}
@@ -4583,9 +4759,56 @@ info_cpool(Allctr_t *allctr,
if (hpp || szp) {
res = NIL;
+
+ if (!sz_only) {
+ add_3tup(hpp, szp, &res, am.fail_pooled,
+ bld_unstable_uint(hpp, szp, ERTS_ALC_CC_GIGA_VAL(allctr->cpool.stat.fail_pooled)),
+ bld_unstable_uint(hpp, szp, ERTS_ALC_CC_VAL(allctr->cpool.stat.fail_pooled)));
+
+ add_3tup(hpp, szp, &res, am.fail_shared,
+ bld_unstable_uint(hpp, szp, ERTS_ALC_CC_GIGA_VAL(allctr->cpool.stat.fail_shared)),
+ bld_unstable_uint(hpp, szp, ERTS_ALC_CC_VAL(allctr->cpool.stat.fail_shared)));
+
+ add_3tup(hpp, szp, &res, am.fail_pend_dealloc,
+ bld_unstable_uint(hpp, szp, ERTS_ALC_CC_GIGA_VAL(allctr->cpool.stat.fail_pend_dealloc)),
+ bld_unstable_uint(hpp, szp, ERTS_ALC_CC_VAL(allctr->cpool.stat.fail_pend_dealloc)));
+
+ add_3tup(hpp, szp, &res, am.fail,
+ bld_unstable_uint(hpp, szp, ERTS_ALC_CC_GIGA_VAL(allctr->cpool.stat.fail)),
+ bld_unstable_uint(hpp, szp, ERTS_ALC_CC_VAL(allctr->cpool.stat.fail)));
+
+ add_3tup(hpp, szp, &res, am.fetch,
+ bld_unstable_uint(hpp, szp, ERTS_ALC_CC_GIGA_VAL(allctr->cpool.stat.fetch)),
+ bld_unstable_uint(hpp, szp, ERTS_ALC_CC_VAL(allctr->cpool.stat.fetch)));
+
+ add_3tup(hpp, szp, &res, am.skip_size,
+ bld_unstable_uint(hpp, szp, ERTS_ALC_CC_GIGA_VAL(allctr->cpool.stat.skip_size)),
+ bld_unstable_uint(hpp, szp, ERTS_ALC_CC_VAL(allctr->cpool.stat.skip_size)));
+
+ add_3tup(hpp, szp, &res, am.skip_busy,
+ bld_unstable_uint(hpp, szp, ERTS_ALC_CC_GIGA_VAL(allctr->cpool.stat.skip_busy)),
+ bld_unstable_uint(hpp, szp, ERTS_ALC_CC_VAL(allctr->cpool.stat.skip_busy)));
+
+ add_3tup(hpp, szp, &res, am.skip_not_pooled,
+ bld_unstable_uint(hpp, szp, ERTS_ALC_CC_GIGA_VAL(allctr->cpool.stat.skip_not_pooled)),
+ bld_unstable_uint(hpp, szp, ERTS_ALC_CC_VAL(allctr->cpool.stat.skip_not_pooled)));
+
+ add_3tup(hpp, szp, &res, am.skip_homecoming,
+ bld_unstable_uint(hpp, szp, ERTS_ALC_CC_GIGA_VAL(allctr->cpool.stat.skip_homecoming)),
+ bld_unstable_uint(hpp, szp, ERTS_ALC_CC_VAL(allctr->cpool.stat.skip_homecoming)));
+
+ add_3tup(hpp, szp, &res, am.skip_race,
+ bld_unstable_uint(hpp, szp, ERTS_ALC_CC_GIGA_VAL(allctr->cpool.stat.skip_race)),
+ bld_unstable_uint(hpp, szp, ERTS_ALC_CC_VAL(allctr->cpool.stat.skip_race)));
+
+ add_3tup(hpp, szp, &res, am.entrance_removed,
+ bld_unstable_uint(hpp, szp, ERTS_ALC_CC_GIGA_VAL(allctr->cpool.stat.entrance_removed)),
+ bld_unstable_uint(hpp, szp, ERTS_ALC_CC_VAL(allctr->cpool.stat.entrance_removed)));
+
add_2tup(hpp, szp, &res,
am.carriers_size,
bld_unstable_uint(hpp, szp, csz));
+ }
if (!sz_only)
add_2tup(hpp, szp, &res,
am.carriers,
@@ -4725,20 +4948,20 @@ make_name_atoms(Allctr_t *allctr)
char realloc[] = "realloc";
char free[] = "free";
char buf[MAX_ATOM_CHARACTERS];
- size_t prefix_len = strlen(allctr->name_prefix);
+ size_t prefix_len = sys_strlen(allctr->name_prefix);
if (prefix_len > MAX_ATOM_CHARACTERS + sizeof(realloc) - 1)
erts_exit(ERTS_ERROR_EXIT,"Too long allocator name: %salloc\n",allctr->name_prefix);
- memcpy((void *) buf, (void *) allctr->name_prefix, prefix_len);
+ sys_memcpy((void *) buf, (void *) allctr->name_prefix, prefix_len);
- memcpy((void *) &buf[prefix_len], (void *) alloc, sizeof(alloc) - 1);
+ sys_memcpy((void *) &buf[prefix_len], (void *) alloc, sizeof(alloc) - 1);
allctr->name.alloc = am_atom_put(buf, prefix_len + sizeof(alloc) - 1);
- memcpy((void *) &buf[prefix_len], (void *) realloc, sizeof(realloc) - 1);
+ sys_memcpy((void *) &buf[prefix_len], (void *) realloc, sizeof(realloc) - 1);
allctr->name.realloc = am_atom_put(buf, prefix_len + sizeof(realloc) - 1);
- memcpy((void *) &buf[prefix_len], (void *) free, sizeof(free) - 1);
+ sys_memcpy((void *) &buf[prefix_len], (void *) free, sizeof(free) - 1);
allctr->name.free = am_atom_put(buf, prefix_len + sizeof(free) - 1);
}
@@ -4844,7 +5067,7 @@ info_options(Allctr_t *allctr,
Uint *szp)
{
Eterm res = THE_NON_VALUE;
- int acul;
+ UWord acul, acnl, acfml;
if (!allctr) {
if (print_to_p)
@@ -4857,6 +5080,8 @@ info_options(Allctr_t *allctr,
}
acul = allctr->cpool.util_limit;
+ acnl = allctr->cpool.in_pool_limit;
+ acfml = allctr->cpool.fblk_min_limit;
if (print_to_p) {
char topt[21]; /* Enough for any 64-bit integer */
@@ -4869,6 +5094,7 @@ info_options(Allctr_t *allctr,
"option e: true\n"
"option t: %s\n"
"option ramv: %s\n"
+ "option atags: %s\n"
"option sbct: %beu\n"
#if HAVE_ERTS_MSEG
"option asbcst: %bpu\n"
@@ -4884,9 +5110,10 @@ info_options(Allctr_t *allctr,
"option lmbcs: %beu\n"
"option smbcs: %beu\n"
"option mbcgs: %beu\n"
- "option acul: %d\n",
+ "option acul: %bpu\n",
topt,
allctr->ramv ? "true" : "false",
+ allctr->atags ? "true" : "false",
allctr->sbc_threshold,
#if HAVE_ERTS_MSEG
allctr->mseg_opt.abs_shrink_th,
@@ -4909,9 +5136,15 @@ info_options(Allctr_t *allctr,
hpp, szp);
if (hpp || szp) {
+ add_2tup(hpp, szp, &res,
+ am.acfml,
+ bld_uint(hpp, szp, acfml));
+ add_2tup(hpp, szp, &res,
+ am.acnl,
+ bld_uint(hpp, szp, acnl));
add_2tup(hpp, szp, &res,
am.acul,
- bld_uint(hpp, szp, (UWord) acul));
+ bld_uint(hpp, szp, acul));
add_2tup(hpp, szp, &res,
am.mbcgs,
bld_uint(hpp, szp, allctr->mbc_growth_stages));
@@ -4947,9 +5180,10 @@ info_options(Allctr_t *allctr,
bld_uint(hpp, szp, allctr->mseg_opt.abs_shrink_th));
#endif
add_2tup(hpp, szp, &res,
- am.sbct,
+ am_sbct,
bld_uint(hpp, szp, allctr->sbc_threshold));
add_2tup(hpp, szp, &res, am.ramv, allctr->ramv ? am_true : am_false);
+ add_2tup(hpp, szp, &res, am.atags, allctr->atags ? am_true : am_false);
add_2tup(hpp, szp, &res, am.t, (allctr->t ? am_true : am_false));
add_2tup(hpp, szp, &res, am.e, am_true);
}
@@ -5271,9 +5505,8 @@ erts_alcu_current_size(Allctr_t *allctr, AllctrSize_t *size, ErtsAlcUFixInfo_t *
/* ----------------------------------------------------------------------- */
static ERTS_INLINE void *
-do_erts_alcu_alloc(ErtsAlcType_t type, void *extra, Uint size)
+do_erts_alcu_alloc(ErtsAlcType_t type, Allctr_t *allctr, Uint size)
{
- Allctr_t *allctr = (Allctr_t *) extra;
void *res;
ASSERT(initialized);
@@ -5312,10 +5545,19 @@ do_erts_alcu_alloc(ErtsAlcType_t type, void *extra, Uint size)
void *erts_alcu_alloc(ErtsAlcType_t type, void *extra, Uint size)
{
+ Allctr_t *allctr = (Allctr_t *) extra;
void *res;
+
ASSERT(!"This is not thread safe");
- res = do_erts_alcu_alloc(type, extra, size);
+
+ res = do_erts_alcu_alloc(type, allctr, size);
+
+ if (allctr->atags && res) {
+ set_alloc_tag(allctr, res, determine_alloc_tag(allctr, type));
+ }
+
DEBUG_CHECK_ALIGNMENT(res);
+
return res;
}
@@ -5325,13 +5567,25 @@ void *
erts_alcu_alloc_ts(ErtsAlcType_t type, void *extra, Uint size)
{
Allctr_t *allctr = (Allctr_t *) extra;
+ alcu_atag_t tag = 0;
void *res;
+
+ if (allctr->atags) {
+ tag = determine_alloc_tag(allctr, type);
+ }
+
erts_mtx_lock(&allctr->mutex);
- res = do_erts_alcu_alloc(type, extra, size);
- DEBUG_CHECK_ALIGNMENT(res);
+ res = do_erts_alcu_alloc(type, allctr, size);
+
+ if (allctr->atags && res) {
+ set_alloc_tag(allctr, res, tag);
+ }
erts_mtx_unlock(&allctr->mutex);
+
+ DEBUG_CHECK_ALIGNMENT(res);
+
return res;
}
@@ -5341,6 +5595,7 @@ erts_alcu_alloc_thr_spec(ErtsAlcType_t type, void *extra, Uint size)
{
ErtsAllocatorThrSpec_t *tspec = (ErtsAllocatorThrSpec_t *) extra;
int ix;
+ alcu_atag_t tag = 0;
Allctr_t *allctr;
void *res;
@@ -5350,11 +5605,19 @@ erts_alcu_alloc_thr_spec(ErtsAlcType_t type, void *extra, Uint size)
allctr = tspec->allctr[ix];
+ if (allctr->atags) {
+ tag = determine_alloc_tag(allctr, type);
+ }
+
if (allctr->thread_safe)
erts_mtx_lock(&allctr->mutex);
res = do_erts_alcu_alloc(type, allctr, size);
+ if (allctr->atags && res) {
+ set_alloc_tag(allctr, res, tag);
+ }
+
if (allctr->thread_safe)
erts_mtx_unlock(&allctr->mutex);
@@ -5367,10 +5630,15 @@ void *
erts_alcu_alloc_thr_pref(ErtsAlcType_t type, void *extra, Uint size)
{
Allctr_t *pref_allctr;
+ alcu_atag_t tag = 0;
void *res;
pref_allctr = get_pref_allctr(extra);
+ if (pref_allctr->atags) {
+ tag = determine_alloc_tag(pref_allctr, type);
+ }
+
if (pref_allctr->thread_safe)
erts_mtx_lock(&pref_allctr->mutex);
@@ -5386,12 +5654,15 @@ erts_alcu_alloc_thr_pref(ErtsAlcType_t type, void *extra, Uint size)
res = do_erts_alcu_alloc(type, pref_allctr, size);
}
+ if (pref_allctr->atags && res) {
+ set_alloc_tag(pref_allctr, res, tag);
+ }
+
if (pref_allctr->thread_safe)
erts_mtx_unlock(&pref_allctr->mutex);
DEBUG_CHECK_ALIGNMENT(res);
-
return res;
}
@@ -5400,10 +5671,9 @@ erts_alcu_alloc_thr_pref(ErtsAlcType_t type, void *extra, Uint size)
/* ------------------------------------------------------------------------- */
static ERTS_INLINE void
-do_erts_alcu_free(ErtsAlcType_t type, void *extra, void *p,
+do_erts_alcu_free(ErtsAlcType_t type, Allctr_t *allctr, void *p,
Carrier_t **busy_pcrr_pp)
{
- Allctr_t *allctr = (Allctr_t *) extra;
ASSERT(initialized);
ASSERT(allctr);
@@ -5435,7 +5705,8 @@ do_erts_alcu_free(ErtsAlcType_t type, void *extra, void *p,
void erts_alcu_free(ErtsAlcType_t type, void *extra, void *p)
{
- do_erts_alcu_free(type, extra, p, NULL);
+ Allctr_t *allctr = (Allctr_t *) extra;
+ do_erts_alcu_free(type, allctr, p, NULL);
}
@@ -5444,7 +5715,7 @@ erts_alcu_free_ts(ErtsAlcType_t type, void *extra, void *p)
{
Allctr_t *allctr = (Allctr_t *) extra;
erts_mtx_lock(&allctr->mutex);
- do_erts_alcu_free(type, extra, p, NULL);
+ do_erts_alcu_free(type, allctr, p, NULL);
erts_mtx_unlock(&allctr->mutex);
}
@@ -5481,12 +5752,13 @@ erts_alcu_free_thr_pref(ErtsAlcType_t type, void *extra, void *p)
pref_allctr = get_pref_allctr(extra);
used_allctr = get_used_allctr(pref_allctr, ERTS_ALC_TS_PREF_LOCK_IF_USED,
p, NULL, &busy_pcrr_p);
- if (pref_allctr != used_allctr)
+ if (pref_allctr != used_allctr) {
enqueue_dealloc_other_instance(type,
- used_allctr,
- p,
- (used_allctr->dd.ix
- - pref_allctr->dd.ix));
+ used_allctr,
+ p,
+ (used_allctr->dd.ix
+ - pref_allctr->dd.ix));
+ }
else {
ERTS_ALCU_DBG_CHK_THR_ACCESS(used_allctr);
do_erts_alcu_free(type, used_allctr, p, &busy_pcrr_p);
@@ -5503,13 +5775,12 @@ erts_alcu_free_thr_pref(ErtsAlcType_t type, void *extra, void *p)
static ERTS_INLINE void *
do_erts_alcu_realloc(ErtsAlcType_t type,
- void *extra,
+ Allctr_t *allctr,
void *p,
Uint size,
Uint32 alcu_flgs,
Carrier_t **busy_pcrr_pp)
{
- Allctr_t *allctr = (Allctr_t *) extra;
Block_t *blk;
void *res;
@@ -5523,7 +5794,7 @@ do_erts_alcu_realloc(ErtsAlcType_t type,
ERTS_ALCU_DBG_CHK_THR_ACCESS(allctr);
if (!p) {
- res = do_erts_alcu_alloc(type, extra, size);
+ res = do_erts_alcu_alloc(type, allctr, size);
INC_CC(allctr->calls.this_realloc);
DEC_CC(allctr->calls.this_alloc);
return res;
@@ -5532,7 +5803,7 @@ do_erts_alcu_realloc(ErtsAlcType_t type,
#if ALLOC_ZERO_EQ_NULL
if (!size) {
ASSERT(p);
- do_erts_alcu_free(type, extra, p, busy_pcrr_pp);
+ do_erts_alcu_free(type, allctr, p, busy_pcrr_pp);
INC_CC(allctr->calls.this_realloc);
DEC_CC(allctr->calls.this_free);
return NULL;
@@ -5617,19 +5888,29 @@ do_erts_alcu_realloc(ErtsAlcType_t type,
void *
erts_alcu_realloc(ErtsAlcType_t type, void *extra, void *p, Uint size)
{
+ Allctr_t *allctr = (Allctr_t *)extra;
void *res;
- res = do_erts_alcu_realloc(type, extra, p, size, 0, NULL);
+
+ res = do_erts_alcu_realloc(type, allctr, p, size, 0, NULL);
+
DEBUG_CHECK_ALIGNMENT(res);
+
+ if (allctr->atags && res) {
+ set_alloc_tag(allctr, res, determine_alloc_tag(allctr, type));
+ }
+
return res;
}
void *
erts_alcu_realloc_mv(ErtsAlcType_t type, void *extra, void *p, Uint size)
{
+ Allctr_t *allctr = (Allctr_t *)extra;
void *res;
- res = do_erts_alcu_alloc(type, extra, size);
+
+ res = do_erts_alcu_alloc(type, allctr, size);
if (!res)
- res = erts_alcu_realloc(type, extra, p, size);
+ res = do_erts_alcu_realloc(type, allctr, p, size, 0, NULL);
else {
Block_t *blk;
size_t cpy_size;
@@ -5639,23 +5920,42 @@ erts_alcu_realloc_mv(ErtsAlcType_t type, void *extra, void *p, Uint size)
if (cpy_size > size)
cpy_size = size;
sys_memcpy(res, p, cpy_size);
- do_erts_alcu_free(type, extra, p, NULL);
+ do_erts_alcu_free(type, allctr, p, NULL);
}
+
DEBUG_CHECK_ALIGNMENT(res);
+
+ if (allctr->atags && res) {
+ set_alloc_tag(allctr, res, determine_alloc_tag(allctr, type));
+ }
+
return res;
}
-
void *
erts_alcu_realloc_ts(ErtsAlcType_t type, void *extra, void *ptr, Uint size)
{
Allctr_t *allctr = (Allctr_t *) extra;
+ alcu_atag_t tag = 0;
void *res;
+
+ if (allctr->atags) {
+ tag = determine_alloc_tag(allctr, type);
+ }
+
erts_mtx_lock(&allctr->mutex);
- res = do_erts_alcu_realloc(type, extra, ptr, size, 0, NULL);
+
+ res = do_erts_alcu_realloc(type, allctr, ptr, size, 0, NULL);
+
+ if (allctr->atags && res) {
+ set_alloc_tag(allctr, res, tag);
+ }
+
erts_mtx_unlock(&allctr->mutex);
+
DEBUG_CHECK_ALIGNMENT(res);
+
return res;
}
@@ -5663,11 +5963,17 @@ void *
erts_alcu_realloc_mv_ts(ErtsAlcType_t type, void *extra, void *p, Uint size)
{
Allctr_t *allctr = (Allctr_t *) extra;
+ alcu_atag_t tag = 0;
void *res;
+
+ if (allctr->atags) {
+ tag = determine_alloc_tag(allctr, type);
+ }
+
erts_mtx_lock(&allctr->mutex);
- res = do_erts_alcu_alloc(type, extra, size);
+ res = do_erts_alcu_alloc(type, allctr, size);
if (!res)
- res = erts_alcu_realloc_ts(type, extra, p, size);
+ res = do_erts_alcu_realloc(type, allctr, p, size, 0, NULL);
else {
Block_t *blk;
size_t cpy_size;
@@ -5677,10 +5983,17 @@ erts_alcu_realloc_mv_ts(ErtsAlcType_t type, void *extra, void *p, Uint size)
if (cpy_size > size)
cpy_size = size;
sys_memcpy(res, p, cpy_size);
- do_erts_alcu_free(type, extra, p, NULL);
+ do_erts_alcu_free(type, allctr, p, NULL);
}
+
+ if (allctr->atags && res) {
+ set_alloc_tag(allctr, res, tag);
+ }
+
erts_mtx_unlock(&allctr->mutex);
+
DEBUG_CHECK_ALIGNMENT(res);
+
return res;
}
@@ -5691,6 +6004,7 @@ erts_alcu_realloc_thr_spec(ErtsAlcType_t type, void *extra,
{
ErtsAllocatorThrSpec_t *tspec = (ErtsAllocatorThrSpec_t *) extra;
int ix;
+ alcu_atag_t tag = 0;
Allctr_t *allctr;
void *res;
@@ -5700,11 +6014,19 @@ erts_alcu_realloc_thr_spec(ErtsAlcType_t type, void *extra,
allctr = tspec->allctr[ix];
+ if (allctr->atags) {
+ tag = determine_alloc_tag(allctr, type);
+ }
+
if (allctr->thread_safe)
erts_mtx_lock(&allctr->mutex);
res = do_erts_alcu_realloc(type, allctr, ptr, size, 0, NULL);
+ if (allctr->atags && res) {
+ set_alloc_tag(allctr, res, tag);
+ }
+
if (allctr->thread_safe)
erts_mtx_unlock(&allctr->mutex);
@@ -5719,6 +6041,7 @@ erts_alcu_realloc_mv_thr_spec(ErtsAlcType_t type, void *extra,
{
ErtsAllocatorThrSpec_t *tspec = (ErtsAllocatorThrSpec_t *) extra;
int ix;
+ alcu_atag_t tag = 0;
Allctr_t *allctr;
void *res;
@@ -5728,14 +6051,16 @@ erts_alcu_realloc_mv_thr_spec(ErtsAlcType_t type, void *extra,
allctr = tspec->allctr[ix];
+ if (allctr->atags) {
+ tag = determine_alloc_tag(allctr, type);
+ }
+
if (allctr->thread_safe)
erts_mtx_lock(&allctr->mutex);
res = do_erts_alcu_alloc(type, allctr, size);
if (!res) {
- if (allctr->thread_safe)
- erts_mtx_unlock(&allctr->mutex);
- res = erts_alcu_realloc_thr_spec(type, allctr, ptr, size);
+ res = do_erts_alcu_realloc(type, allctr, ptr, size, 0, NULL);
}
else {
Block_t *blk;
@@ -5747,29 +6072,34 @@ erts_alcu_realloc_mv_thr_spec(ErtsAlcType_t type, void *extra,
cpy_size = size;
sys_memcpy(res, ptr, cpy_size);
do_erts_alcu_free(type, allctr, ptr, NULL);
- if (allctr->thread_safe)
- erts_mtx_unlock(&allctr->mutex);
}
+ if (allctr->atags && res) {
+ set_alloc_tag(allctr, res, tag);
+ }
+
+ if (allctr->thread_safe)
+ erts_mtx_unlock(&allctr->mutex);
+
DEBUG_CHECK_ALIGNMENT(res);
return res;
}
static ERTS_INLINE void *
-realloc_thr_pref(ErtsAlcType_t type, void *extra, void *p, Uint size,
+realloc_thr_pref(ErtsAlcType_t type, Allctr_t *pref_allctr, void *p, Uint size,
int force_move)
{
void *res;
- Allctr_t *pref_allctr, *used_allctr;
+ Allctr_t *used_allctr;
UWord old_user_size;
Carrier_t *busy_pcrr_p;
+ alcu_atag_t tag = 0;
int retried;
- if (!p)
- return erts_alcu_alloc_thr_pref(type, extra, size);
-
- pref_allctr = get_pref_allctr(extra);
+ if (pref_allctr->atags) {
+ tag = determine_alloc_tag(pref_allctr, type);
+ }
if (pref_allctr->thread_safe)
erts_mtx_lock(&pref_allctr->mutex);
@@ -5798,6 +6128,11 @@ restart:
retried = 1;
goto restart;
}
+
+ if (pref_allctr->atags && res) {
+ set_alloc_tag(pref_allctr, res, tag);
+ }
+
if (pref_allctr->thread_safe)
erts_mtx_unlock(&pref_allctr->mutex);
}
@@ -5806,6 +6141,9 @@ restart:
if (!res)
goto unlock_ts_return;
else {
+ if (pref_allctr->atags) {
+ set_alloc_tag(pref_allctr, res, tag);
+ }
DEBUG_CHECK_ALIGNMENT(res);
@@ -5836,24 +6174,69 @@ restart:
}
}
+ DEBUG_CHECK_ALIGNMENT(res);
+
return res;
}
void *
erts_alcu_realloc_thr_pref(ErtsAlcType_t type, void *extra, void *p, Uint size)
{
- return realloc_thr_pref(type, extra, p, size, 0);
+ if (p) {
+ Allctr_t *pref_allctr = get_pref_allctr(extra);
+
+ return realloc_thr_pref(type, pref_allctr, p, size, 0);
+ }
+
+ return erts_alcu_alloc_thr_pref(type, extra, size);
}
void *
erts_alcu_realloc_mv_thr_pref(ErtsAlcType_t type, void *extra,
void *p, Uint size)
{
- return realloc_thr_pref(type, extra, p, size, 1);
+ if (p) {
+ Allctr_t *pref_allctr = get_pref_allctr(extra);
+
+ return realloc_thr_pref(type, pref_allctr, p, size, 1);
+ }
+
+ return erts_alcu_alloc_thr_pref(type, extra, size);
}
+static Uint adjust_sbct(Allctr_t* allctr, Uint sbct)
+{
+#ifndef ARCH_64
+ if (sbct > 0) {
+ Uint max_mbc_block_sz = UNIT_CEILING(sbct - 1 + ABLK_HDR_SZ);
+ if (max_mbc_block_sz + UNIT_FLOOR(allctr->min_block_size - 1) > MBC_ABLK_SZ_MASK
+ || max_mbc_block_sz < sbct) { /* wrap around */
+ /*
+ * By limiting sbc_threshold to (hard limit - min_block_size)
+ * we avoid having to split off free "residue blocks"
+ * smaller than min_block_size.
+ */
+ max_mbc_block_sz = MBC_ABLK_SZ_MASK - UNIT_FLOOR(allctr->min_block_size - 1);
+ sbct = max_mbc_block_sz - ABLK_HDR_SZ + 1;
+ }
+ }
+#endif
+ return sbct;
+}
+
+int erts_alcu_try_set_dyn_param(Allctr_t* allctr, Eterm param, Uint value)
+{
+ const Uint MIN_DYN_SBCT = 4000; /* a lame catastrophe prevention */
+
+ if (param == am_sbct && value >= MIN_DYN_SBCT) {
+ allctr->sbc_threshold = adjust_sbct(allctr, value);
+ return 1;
+ }
+ return 0;
+}
+
/* ------------------------------------------------------------------------- */
int
@@ -5902,6 +6285,7 @@ erts_alcu_start(Allctr_t *allctr, AllctrInit_t *init)
allctr->t = 0;
allctr->ramv = init->ramv;
+ allctr->atags = init->atags;
allctr->main_carrier_size = init->mmbcs;
#if HAVE_ERTS_MSEG
@@ -5941,10 +6325,7 @@ erts_alcu_start(Allctr_t *allctr, AllctrInit_t *init)
allctr->min_block_size = sz;
}
- allctr->cpool.pooled_list.next = &allctr->cpool.pooled_list;
- allctr->cpool.pooled_list.prev = &allctr->cpool.pooled_list;
- allctr->cpool.traitor_list.next = &allctr->cpool.traitor_list;
- allctr->cpool.traitor_list.prev = &allctr->cpool.traitor_list;
+ allctr->cpool.pooled_tree = NULL;
allctr->cpool.dc_list.first = NULL;
allctr->cpool.dc_list.last = NULL;
allctr->cpool.abandon_limit = 0;
@@ -5954,24 +6335,18 @@ erts_alcu_start(Allctr_t *allctr, AllctrInit_t *init)
erts_atomic_init_nob(&allctr->cpool.stat.carriers_size, 0);
erts_atomic_init_nob(&allctr->cpool.stat.no_carriers, 0);
allctr->cpool.check_limit_count = ERTS_ALC_CPOOL_CHECK_LIMIT_COUNT;
- allctr->cpool.util_limit = init->ts ? 0 : init->acul;
-
- allctr->sbc_threshold = init->sbct;
-#ifndef ARCH_64
- if (allctr->sbc_threshold > 0) {
- Uint max_mbc_block_sz = UNIT_CEILING(allctr->sbc_threshold - 1 + ABLK_HDR_SZ);
- if (max_mbc_block_sz + UNIT_FLOOR(allctr->min_block_size - 1) > MBC_ABLK_SZ_MASK
- || max_mbc_block_sz < allctr->sbc_threshold) { /* wrap around */
- /*
- * By limiting sbc_threshold to (hard limit - min_block_size)
- * we avoid having to split off free "residue blocks"
- * smaller than min_block_size.
- */
- max_mbc_block_sz = MBC_ABLK_SZ_MASK - UNIT_FLOOR(allctr->min_block_size - 1);
- allctr->sbc_threshold = max_mbc_block_sz - ABLK_HDR_SZ + 1;
- }
+ if (!init->ts && init->acul && init->acnl) {
+ allctr->cpool.util_limit = init->acul;
+ allctr->cpool.in_pool_limit = init->acnl;
+ allctr->cpool.fblk_min_limit = init->acfml;
+ }
+ else {
+ allctr->cpool.util_limit = 0;
+ allctr->cpool.in_pool_limit = 0;
+ allctr->cpool.fblk_min_limit = 0;
}
-#endif
+
+ allctr->sbc_threshold = adjust_sbct(allctr, init->sbct);
#if HAVE_ERTS_MSEG
if (allctr->mseg_opt.abs_shrink_th > ~((UWord) 0) / 100)
@@ -6022,6 +6397,9 @@ erts_alcu_start(Allctr_t *allctr, AllctrInit_t *init)
allctr->sys_realloc = &erts_alcu_sys_realloc;
allctr->sys_dealloc = &erts_alcu_sys_dealloc;
}
+
+ allctr->try_set_dyn_param = &erts_alcu_try_set_dyn_param;
+
#if HAVE_ERTS_MSEG
if (init->mseg_alloc) {
ASSERT(init->mseg_realloc && init->mseg_dealloc);
@@ -6036,6 +6414,7 @@ erts_alcu_start(Allctr_t *allctr, AllctrInit_t *init)
allctr->mseg_realloc = &erts_alcu_mseg_realloc;
allctr->mseg_dealloc = &erts_alcu_mseg_dealloc;
}
+
/* If a custom carrier alloc function is specified, make sure it's used */
if (init->mseg_alloc && !init->sys_alloc) {
allctr->crr_set_flgs = CFLG_FORCE_MSEG;
@@ -6156,6 +6535,1072 @@ erts_alcu_init(AlcUInit_t *init)
initialized = 1;
}
+/* ------------------------------------------------------------------------- */
+
+/* Allocation histograms and carrier information is gathered by walking through
+ * all carriers associated with each allocator instance. This is done as
+ * aux_yield_work on the scheduler that owns each instance.
+ *
+ * Yielding is implemented by temporarily inserting a "dummy carrier" at the
+ * last position. It's permanently "busy" so it won't get picked up by someone
+ * else when in the carrier pool, and we never make the employer aware of it
+ * through callbacks so we can't accidentally allocate on it.
+ *
+ * Plain malloc/free is used to guarantee we won't allocate with the allocator
+ * we're scanning. */
+
+/* Yield between carriers once this many blocks have been processed. Note that
+ * a single carrier scan may exceed this figure. */
+#ifndef DEBUG
+ #define BLOCKSCAN_REDUCTIONS (8000)
+#else
+ #define BLOCKSCAN_REDUCTIONS (400)
+#endif
+
+/* Abort a single carrier scan after this many blocks to prevent really large
+ * MBCs from blocking forever. */
+#define BLOCKSCAN_BAILOUT_THRESHOLD (16000)
+
+typedef struct alcu_blockscan {
+ /* A per-scheduler list used when multiple scans have been queued. The
+ * current scanner will always run until completion/abort before moving on
+ * to the next. */
+ struct alcu_blockscan *scanner_queue;
+
+ Allctr_t *allocator;
+ Process *process;
+
+ int (*current_op)(struct alcu_blockscan *scanner);
+ int (*next_op)(struct alcu_blockscan *scanner);
+ int reductions;
+
+ ErtsAlcCPoolData_t *cpool_cursor;
+ CarrierList_t *current_clist;
+ Carrier_t *clist_cursor;
+ Carrier_t dummy_carrier;
+
+ /* Called if the process that started this job dies before we're done. */
+ void (*abort)(void *user_data);
+
+ /* Called on each carrier. The callback must return the number of blocks
+ * scanned to yield properly between carriers.
+ *
+ * Note that it's not possible to "yield back" into a carrier. */
+ int (*scan)(Allctr_t *, void *user_data, Carrier_t *);
+
+ /* Called when all carriers have been scanned. The callback may return
+ * non-zero to yield. */
+ int (*finish)(void *user_data);
+
+ void *user_data;
+} blockscan_t;
+
+static Carrier_t *blockscan_restore_clist_cursor(blockscan_t *state)
+{
+ Carrier_t *cursor = state->clist_cursor;
+
+ ASSERT(state->clist_cursor == (state->current_clist)->first ||
+ state->clist_cursor == &state->dummy_carrier);
+
+ if (cursor == &state->dummy_carrier) {
+ cursor = cursor->next;
+
+ unlink_carrier(state->current_clist, state->clist_cursor);
+ }
+
+ return cursor;
+}
+
+static void blockscan_save_clist_cursor(blockscan_t *state, Carrier_t *after)
+{
+ ASSERT(state->clist_cursor == (state->current_clist)->first ||
+ state->clist_cursor == &state->dummy_carrier);
+
+ state->clist_cursor = &state->dummy_carrier;
+
+ (state->clist_cursor)->next = after->next;
+ (state->clist_cursor)->prev = after;
+
+ relink_carrier(state->current_clist, state->clist_cursor);
+}
+
+static int blockscan_clist_yielding(blockscan_t *state)
+{
+ Carrier_t *cursor = blockscan_restore_clist_cursor(state);
+
+ if (ERTS_PROC_IS_EXITING(state->process)) {
+ return 0;
+ }
+
+ while (cursor) {
+ /* Skip dummy carriers inserted by another (concurrent) block scan.
+ * This can happen when scanning thread-safe allocators from multiple
+ * schedulers. */
+ if (CARRIER_SZ(cursor) > 0) {
+ int blocks_scanned = state->scan(state->allocator,
+ state->user_data,
+ cursor);
+
+ state->reductions -= blocks_scanned;
+
+ if (state->reductions <= 0) {
+ blockscan_save_clist_cursor(state, cursor);
+ return 1;
+ }
+ }
+
+ cursor = cursor->next;
+ }
+
+ return 0;
+}
+
+static ErtsAlcCPoolData_t *blockscan_restore_cpool_cursor(blockscan_t *state)
+{
+ ErtsAlcCPoolData_t *cursor;
+
+ cursor = cpool_aint2cpd(cpool_read(&(state->cpool_cursor)->next));
+
+ if (state->cpool_cursor == &state->dummy_carrier.cpool) {
+ cpool_delete(state->allocator, state->allocator, &state->dummy_carrier);
+ }
+
+ return cursor;
+}
+
+static void blockscan_save_cpool_cursor(blockscan_t *state,
+ ErtsAlcCPoolData_t *after)
+{
+ ErtsAlcCPoolData_t *dummy_carrier, *prev_carrier, *next_carrier;
+
+ dummy_carrier = &state->dummy_carrier.cpool;
+
+ next_carrier = cpool_aint2cpd(cpool_mod_mark(&after->next));
+ prev_carrier = cpool_aint2cpd(cpool_mod_mark(&next_carrier->prev));
+
+ cpool_init(&dummy_carrier->next, (erts_aint_t)next_carrier);
+ cpool_init(&dummy_carrier->prev, (erts_aint_t)prev_carrier);
+
+ cpool_set_mod_marked(&prev_carrier->next,
+ (erts_aint_t)dummy_carrier,
+ (erts_aint_t)next_carrier);
+ cpool_set_mod_marked(&next_carrier->prev,
+ (erts_aint_t)dummy_carrier,
+ (erts_aint_t)prev_carrier);
+
+ state->cpool_cursor = dummy_carrier;
+}
+
+static int blockscan_cpool_yielding(blockscan_t *state)
+{
+ ErtsAlcCPoolData_t *sentinel, *cursor;
+
+ sentinel = &carrier_pool[(state->allocator)->alloc_no].sentinel;
+ cursor = blockscan_restore_cpool_cursor(state);
+
+ if (ERTS_PROC_IS_EXITING(state->process)) {
+ return 0;
+ }
+
+ while (cursor != sentinel) {
+ Carrier_t *carrier;
+ erts_aint_t exp;
+
+ /* When a deallocation happens on a pooled carrier it will be routed to
+ * its owner, so the only way to be sure that it isn't modified while
+ * scanning is to skip all carriers that aren't ours. The deallocations
+ * deferred to us will get handled when we're done. */
+ while (cursor->orig_allctr != state->allocator) {
+ cursor = cpool_aint2cpd(cpool_read(&cursor->next));
+
+ if (cursor == sentinel) {
+ return 0;
+ }
+ }
+
+ carrier = ErtsContainerStruct(cursor, Carrier_t, cpool);
+ exp = erts_atomic_read_rb(&carrier->allctr);
+
+ if (exp & ERTS_CRR_ALCTR_FLG_IN_POOL) {
+ ASSERT(state->allocator == (Allctr_t*)(exp & ~ERTS_CRR_ALCTR_FLG_MASK));
+ ASSERT(!(exp & ERTS_CRR_ALCTR_FLG_BUSY));
+
+ if (erts_atomic_cmpxchg_acqb(&carrier->allctr,
+ exp | ERTS_CRR_ALCTR_FLG_BUSY,
+ exp) == exp) {
+ /* Skip dummy carriers inserted by another (concurrent) block
+ * scan. This can happen when scanning thread-safe allocators
+ * from multiple schedulers. */
+ if (CARRIER_SZ(carrier) > 0) {
+ int blocks_scanned = state->scan(state->allocator,
+ state->user_data,
+ carrier);
+
+ state->reductions -= blocks_scanned;
+
+ if (state->reductions <= 0) {
+ blockscan_save_cpool_cursor(state, cursor);
+ erts_atomic_set_relb(&carrier->allctr, exp);
+
+ return 1;
+ }
+ }
+
+ erts_atomic_set_relb(&carrier->allctr, exp);
+ }
+ }
+
+ cursor = cpool_aint2cpd(cpool_read(&cursor->next));
+ }
+
+ return 0;
+}
+
+static int blockscan_yield_helper(blockscan_t *state,
+ int (*yielding_op)(blockscan_t*))
+{
+ /* Note that we don't check whether to abort here; only yielding_op knows
+ * whether the carrier is still in the list/pool. */
+
+ if ((state->allocator)->thread_safe) {
+ /* Locked scans have to be as short as possible. */
+ state->reductions = 1;
+
+ erts_mtx_lock(&(state->allocator)->mutex);
+ } else {
+ state->reductions = BLOCKSCAN_REDUCTIONS;
+ }
+
+ if (yielding_op(state)) {
+ state->next_op = state->current_op;
+ }
+
+ if ((state->allocator)->thread_safe) {
+ erts_mtx_unlock(&(state->allocator)->mutex);
+ }
+
+ return 1;
+}
+
+/* */
+
+static int blockscan_finish(blockscan_t *state)
+{
+ if (ERTS_PROC_IS_EXITING(state->process)) {
+ state->abort(state->user_data);
+ return 0;
+ }
+
+ state->current_op = blockscan_finish;
+
+ return state->finish(state->user_data);
+}
+
+static int blockscan_sweep_sbcs(blockscan_t *state)
+{
+ if (state->current_op != blockscan_sweep_sbcs) {
+ SET_CARRIER_HDR(&state->dummy_carrier, 0, SCH_SBC, state->allocator);
+ state->current_clist = &(state->allocator)->sbc_list;
+ state->clist_cursor = (state->current_clist)->first;
+ }
+
+ state->current_op = blockscan_sweep_sbcs;
+ state->next_op = blockscan_finish;
+
+ return blockscan_yield_helper(state, blockscan_clist_yielding);
+}
+
+static int blockscan_sweep_mbcs(blockscan_t *state)
+{
+ if (state->current_op != blockscan_sweep_mbcs) {
+ SET_CARRIER_HDR(&state->dummy_carrier, 0, SCH_MBC, state->allocator);
+ state->current_clist = &(state->allocator)->mbc_list;
+ state->clist_cursor = (state->current_clist)->first;
+ }
+
+ state->current_op = blockscan_sweep_mbcs;
+ state->next_op = blockscan_sweep_sbcs;
+
+ return blockscan_yield_helper(state, blockscan_clist_yielding);
+}
+
+static int blockscan_sweep_cpool(blockscan_t *state)
+{
+ if (state->current_op != blockscan_sweep_cpool) {
+ ErtsAlcCPoolData_t *sentinel;
+
+ SET_CARRIER_HDR(&state->dummy_carrier, 0, SCH_MBC, state->allocator);
+ sentinel = &carrier_pool[(state->allocator)->alloc_no].sentinel;
+ state->cpool_cursor = sentinel;
+ }
+
+ state->current_op = blockscan_sweep_cpool;
+ state->next_op = blockscan_sweep_mbcs;
+
+ return blockscan_yield_helper(state, blockscan_cpool_yielding);
+}
+
+static int blockscan_get_specific_allocator(int allocator_num,
+ int sched_id,
+ Allctr_t **out)
+{
+ ErtsAllocatorInfo_t *ai;
+ Allctr_t *allocator;
+
+ ASSERT(allocator_num >= ERTS_ALC_A_MIN &&
+ allocator_num <= ERTS_ALC_A_MAX);
+ ASSERT(sched_id >= 0 && sched_id <= erts_no_schedulers);
+
+ ai = &erts_allctrs_info[allocator_num];
+
+ if (!ai->enabled || !ai->alloc_util) {
+ return 0;
+ }
+
+ if (!ai->thr_spec) {
+ if (sched_id != 0) {
+ /* Only thread-specific allocators can be scanned on a specific
+ * scheduler. */
+ return 0;
+ }
+
+ allocator = (Allctr_t*)ai->extra;
+ ASSERT(allocator->thread_safe);
+ } else {
+ ErtsAllocatorThrSpec_t *tspec = (ErtsAllocatorThrSpec_t*)ai->extra;
+
+ ASSERT(sched_id < tspec->size);
+
+ allocator = tspec->allctr[sched_id];
+ }
+
+ *out = allocator;
+
+ return 1;
+}
+
+static void blockscan_sched_trampoline(void *arg)
+{
+ ErtsAlcuBlockscanYieldData *yield;
+ ErtsSchedulerData *esdp;
+ blockscan_t *scanner;
+
+ esdp = erts_get_scheduler_data();
+ scanner = (blockscan_t*)arg;
+
+ yield = ERTS_SCHED_AUX_YIELD_DATA(esdp, alcu_blockscan);
+
+ ASSERT((yield->last == NULL) == (yield->current == NULL));
+
+ if (yield->last != NULL) {
+ blockscan_t *prev_scanner = yield->last;
+
+ ASSERT(prev_scanner->scanner_queue == NULL);
+
+ prev_scanner->scanner_queue = scanner;
+ } else {
+ yield->current = scanner;
+ }
+
+ scanner->scanner_queue = NULL;
+ yield->last = scanner;
+
+ erts_notify_new_aux_yield_work(esdp);
+}
+
+static void blockscan_dispatch(blockscan_t *scanner, Process *owner,
+ Allctr_t *allocator, int sched_id)
+{
+ ASSERT(erts_get_scheduler_id() != 0);
+
+ if (sched_id == 0) {
+ /* Global instances are always handled on the current scheduler. */
+ sched_id = ERTS_ALC_GET_THR_IX();
+ ASSERT(allocator->thread_safe);
+ }
+
+ scanner->allocator = allocator;
+ scanner->process = owner;
+
+ erts_proc_inc_refc(scanner->process);
+
+ cpool_init_carrier_data(scanner->allocator, &scanner->dummy_carrier);
+ erts_atomic_init_nob(&(scanner->dummy_carrier).allctr,
+ (erts_aint_t)allocator | ERTS_CRR_ALCTR_FLG_BUSY);
+
+ if (ERTS_ALC_IS_CPOOL_ENABLED(scanner->allocator)) {
+ scanner->next_op = blockscan_sweep_cpool;
+ } else {
+ scanner->next_op = blockscan_sweep_mbcs;
+ }
+
+ /* Aux yield jobs can only be set up while running on the scheduler that
+ * services them, so we move there before continuing.
+ *
+ * We can't drive the scan itself through this since the scheduler will
+ * always finish *all* misc aux work in one go which makes it impossible to
+ * yield. */
+ erts_schedule_misc_aux_work(sched_id, blockscan_sched_trampoline, scanner);
+}
+
+int erts_handle_yielded_alcu_blockscan(ErtsSchedulerData *esdp,
+ ErtsAlcuBlockscanYieldData *yield)
+{
+ blockscan_t *scanner = yield->current;
+
+ (void)esdp;
+
+ ASSERT((yield->last == NULL) == (yield->current == NULL));
+
+ if (scanner) {
+ if (scanner->next_op(scanner)) {
+ return 1;
+ }
+
+ ASSERT(ERTS_PROC_IS_EXITING(scanner->process) ||
+ scanner->current_op == blockscan_finish);
+
+ yield->current = scanner->scanner_queue;
+
+ if (yield->current == NULL) {
+ ASSERT(scanner == yield->last);
+ yield->last = NULL;
+ }
+
+ erts_proc_dec_refc(scanner->process);
+
+ /* Plain free is intentional. */
+ free(scanner);
+
+ return yield->current != NULL;
+ }
+
+ return 0;
+}
+
+void erts_alcu_sched_spec_data_init(ErtsSchedulerData *esdp)
+{
+ ErtsAlcuBlockscanYieldData *yield;
+
+ yield = ERTS_SCHED_AUX_YIELD_DATA(esdp, alcu_blockscan);
+
+ yield->current = NULL;
+ yield->last = NULL;
+}
+
+/* ------------------------------------------------------------------------- */
+
+static ERTS_INLINE int u64_log2(Uint64 v)
+{
+ static const int log2_tab64[64] = {
+ 63, 0, 58, 1, 59, 47, 53, 2,
+ 60, 39, 48, 27, 54, 33, 42, 3,
+ 61, 51, 37, 40, 49, 18, 28, 20,
+ 55, 30, 34, 11, 43, 14, 22, 4,
+ 62, 57, 46, 52, 38, 26, 32, 41,
+ 50, 36, 17, 19, 29, 10, 13, 21,
+ 56, 45, 25, 31, 35, 16, 9, 12,
+ 44, 24, 15, 8, 23, 7, 6, 5};
+
+ v |= v >> 1;
+ v |= v >> 2;
+ v |= v >> 4;
+ v |= v >> 8;
+ v |= v >> 16;
+ v |= v >> 32;
+
+ return log2_tab64[((Uint64)((v - (v >> 1))*0x07EDD5E59A4E28C2)) >> 58];
+}
+
+/* ------------------------------------------------------------------------- */
+
+typedef struct hist_tree__ {
+ struct hist_tree__ *parent;
+ struct hist_tree__ *left;
+ struct hist_tree__ *right;
+
+ int is_red;
+
+ alcu_atag_t tag;
+ UWord histogram[1];
+} hist_tree_t;
+
+#define ERTS_RBT_PREFIX hist_tree
+#define ERTS_RBT_T hist_tree_t
+#define ERTS_RBT_KEY_T UWord
+#define ERTS_RBT_FLAGS_T int
+#define ERTS_RBT_INIT_EMPTY_TNODE(T) ((void)0)
+#define ERTS_RBT_IS_RED(T) ((T)->is_red)
+#define ERTS_RBT_SET_RED(T) ((T)->is_red = 1)
+#define ERTS_RBT_IS_BLACK(T) (!ERTS_RBT_IS_RED(T))
+#define ERTS_RBT_SET_BLACK(T) ((T)->is_red = 0)
+#define ERTS_RBT_GET_FLAGS(T) ((T)->is_red)
+#define ERTS_RBT_SET_FLAGS(T, F) ((T)->is_red = F)
+#define ERTS_RBT_GET_PARENT(T) ((T)->parent)
+#define ERTS_RBT_SET_PARENT(T, P) ((T)->parent = P)
+#define ERTS_RBT_GET_RIGHT(T) ((T)->right)
+#define ERTS_RBT_SET_RIGHT(T, R) ((T)->right = (R))
+#define ERTS_RBT_GET_LEFT(T) ((T)->left)
+#define ERTS_RBT_SET_LEFT(T, L) ((T)->left = (L))
+#define ERTS_RBT_GET_KEY(T) ((T)->tag)
+#define ERTS_RBT_IS_LT(KX, KY) (KX < KY)
+#define ERTS_RBT_IS_EQ(KX, KY) (KX == KY)
+#define ERTS_RBT_WANT_FOREACH_DESTROY_YIELDING
+#define ERTS_RBT_WANT_FOREACH_DESTROY
+#define ERTS_RBT_WANT_INSERT
+#define ERTS_RBT_WANT_LOOKUP
+#define ERTS_RBT_UNDEF
+
+#include "erl_rbtree.h"
+
+typedef struct {
+ blockscan_t common;
+
+ ErtsIRefStorage iref;
+ Process *process;
+
+ hist_tree_rbt_yield_state_t hist_tree_yield;
+ hist_tree_t *hist_tree;
+ UWord hist_count;
+
+ UWord hist_slot_start;
+ int hist_slot_count;
+
+ UWord unscanned_size;
+
+ ErtsHeapFactory msg_factory;
+ int building_result;
+ Eterm result_list;
+} gather_ahist_t;
+
+static void gather_ahist_update(gather_ahist_t *state, UWord tag, UWord size)
+{
+ hist_tree_t *hist_node;
+ UWord size_interval;
+ int hist_slot;
+
+ hist_node = hist_tree_rbt_lookup(state->hist_tree, tag);
+
+ if (hist_node == NULL) {
+ /* Plain calloc is intentional. */
+ hist_node = (hist_tree_t*)calloc(1, sizeof(hist_tree_t) +
+ (state->hist_slot_count - 1) *
+ sizeof(hist_node->histogram[0]));
+ hist_node->tag = tag;
+
+ hist_tree_rbt_insert(&state->hist_tree, hist_node);
+ state->hist_count++;
+ }
+
+ size_interval = (size / state->hist_slot_start);
+ size_interval = u64_log2(size_interval + 1);
+
+ hist_slot = MIN(size_interval, state->hist_slot_count - 1);
+
+ hist_node->histogram[hist_slot]++;
+}
+
+static int gather_ahist_scan(Allctr_t *allocator,
+ void *user_data,
+ Carrier_t *carrier)
+{
+ gather_ahist_t *state;
+ int blocks_scanned;
+ Block_t *block;
+
+ state = (gather_ahist_t*)user_data;
+ blocks_scanned = 1;
+
+ if (IS_SB_CARRIER(carrier)) {
+ alcu_atag_t tag;
+
+ block = SBC2BLK(allocator, carrier);
+ tag = GET_BLK_ATAG(block);
+
+ ASSERT(DBG_IS_VALID_ATAG(allocator, tag));
+
+ gather_ahist_update(state, tag, SBC_BLK_SZ(block));
+ } else {
+ UWord scanned_bytes = MBC_HEADER_SIZE(allocator);
+
+ ASSERT(IS_MB_CARRIER(carrier));
+
+ block = MBC_TO_FIRST_BLK(allocator, carrier);
+
+ while (1) {
+ UWord block_size = MBC_BLK_SZ(block);
+
+ if (IS_ALLOCED_BLK(block)) {
+ alcu_atag_t tag = GET_BLK_ATAG(block);
+
+ ASSERT(DBG_IS_VALID_ATAG(allocator, tag));
+
+ gather_ahist_update(state, tag, block_size);
+ }
+
+ scanned_bytes += block_size;
+
+ if (blocks_scanned >= BLOCKSCAN_BAILOUT_THRESHOLD) {
+ state->unscanned_size += CARRIER_SZ(carrier) - scanned_bytes;
+ break;
+ } else if (IS_LAST_BLK(block)) {
+ break;
+ }
+
+ block = NXT_BLK(block);
+ blocks_scanned++;
+ }
+ }
+
+ return blocks_scanned;
+}
+
+static void gather_ahist_append_result(hist_tree_t *node, void *arg)
+{
+ gather_ahist_t *state = (gather_ahist_t*)arg;
+
+ Eterm histogram_tuple, tag_tuple;
+
+ Eterm *hp;
+ int ix;
+
+ ASSERT(state->building_result);
+
+ hp = erts_produce_heap(&state->msg_factory, 7 + state->hist_slot_count, 0);
+
+ hp[0] = make_arityval(state->hist_slot_count);
+
+ for (ix = 0; ix < state->hist_slot_count; ix++) {
+ hp[1 + ix] = make_small(node->histogram[ix]);
+ }
+
+ histogram_tuple = make_tuple(hp);
+ hp += 1 + state->hist_slot_count;
+
+ hp[0] = make_arityval(3);
+ hp[1] = ATAG_ID(node->tag);
+ hp[2] = alloc_type_atoms[ATAG_TYPE(node->tag)];
+ hp[3] = histogram_tuple;
+
+ tag_tuple = make_tuple(hp);
+ hp += 4;
+
+ state->result_list = CONS(hp, tag_tuple, state->result_list);
+
+ /* Plain free is intentional. */
+ free(node);
+}
+
+static void gather_ahist_send(gather_ahist_t *state)
+{
+ Eterm result_tuple, unscanned_size, task_ref;
+
+ Uint term_size;
+ Eterm *hp;
+
+ ASSERT((state->result_list == NIL) ^ (state->hist_count > 0));
+ ASSERT(state->building_result);
+
+ term_size = 4 + erts_iref_storage_heap_size(&state->iref);
+ term_size += IS_USMALL(0, state->unscanned_size) ? 0 : BIG_UINT_HEAP_SIZE;
+
+ hp = erts_produce_heap(&state->msg_factory, term_size, 0);
+
+ task_ref = erts_iref_storage_make_ref(&state->iref, &hp,
+ &(state->msg_factory.message)->hfrag.off_heap, 0);
+
+ unscanned_size = bld_unstable_uint(&hp, NULL, state->unscanned_size);
+
+ hp[0] = make_arityval(3);
+ hp[1] = task_ref;
+ hp[2] = unscanned_size;
+ hp[3] = state->result_list;
+
+ result_tuple = make_tuple(hp);
+
+ erts_factory_trim_and_close(&state->msg_factory, &result_tuple, 1);
+
+ erts_queue_message(state->process, 0, state->msg_factory.message,
+ result_tuple, am_system);
+}
+
+static int gather_ahist_finish(void *arg)
+{
+ gather_ahist_t *state = (gather_ahist_t*)arg;
+
+ if (!state->building_result) {
+ ErtsMessage *message;
+ Uint minimum_size;
+ Eterm *hp;
+
+ /* {Ref, unscanned size, [{Tag, {Histogram}} | Rest]} */
+ minimum_size = 4 + erts_iref_storage_heap_size(&state->iref) +
+ state->hist_count * (7 + state->hist_slot_count);
+
+ message = erts_alloc_message(minimum_size, &hp);
+ erts_factory_selfcontained_message_init(&state->msg_factory,
+ message, hp);
+
+ ERTS_RBT_YIELD_STAT_INIT(&state->hist_tree_yield);
+
+ state->result_list = NIL;
+ state->building_result = 1;
+ }
+
+ if (hist_tree_rbt_foreach_destroy_yielding(&state->hist_tree,
+ &gather_ahist_append_result,
+ state,
+ &state->hist_tree_yield,
+ BLOCKSCAN_REDUCTIONS)) {
+ return 1;
+ }
+
+ gather_ahist_send(state);
+
+ return 0;
+}
+
+static void gather_ahist_destroy_result(hist_tree_t *node, void *arg)
+{
+ (void)arg;
+ free(node);
+}
+
+static void gather_ahist_abort(void *arg)
+{
+ gather_ahist_t *state = (gather_ahist_t*)arg;
+
+ if (state->building_result) {
+ erts_factory_undo(&state->msg_factory);
+ }
+
+ hist_tree_rbt_foreach_destroy(&state->hist_tree,
+ &gather_ahist_destroy_result,
+ NULL);
+}
+
+int erts_alcu_gather_alloc_histograms(Process *p, int allocator_num,
+ int sched_id, int hist_width,
+ UWord hist_start, Eterm ref)
+{
+ gather_ahist_t *gather_state;
+ blockscan_t *scanner;
+ Allctr_t *allocator;
+
+ ASSERT(is_internal_ref(ref));
+
+ if (!blockscan_get_specific_allocator(allocator_num,
+ sched_id,
+ &allocator)) {
+ return 0;
+ } else if (!allocator->atags) {
+ return 0;
+ }
+
+ ensure_atoms_initialized(allocator);
+
+ /* Plain calloc is intentional. */
+ gather_state = (gather_ahist_t*)calloc(1, sizeof(gather_ahist_t));
+ scanner = &gather_state->common;
+
+ scanner->abort = gather_ahist_abort;
+ scanner->scan = gather_ahist_scan;
+ scanner->finish = gather_ahist_finish;
+ scanner->user_data = gather_state;
+
+ erts_iref_storage_save(&gather_state->iref, ref);
+ gather_state->hist_slot_start = hist_start;
+ gather_state->hist_slot_count = hist_width;
+ gather_state->process = p;
+
+ blockscan_dispatch(scanner, p, allocator, sched_id);
+
+ return 1;
+}
+
+/* ------------------------------------------------------------------------- */
+
+typedef struct chist_node__ {
+ struct chist_node__ *next;
+
+ 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];
+} chist_node_t;
+
+typedef struct {
+ blockscan_t common;
+
+ ErtsIRefStorage iref;
+ Process *process;
+
+ Eterm allocator_desc;
+
+ chist_node_t *info_list;
+ UWord info_count;
+
+ UWord hist_slot_start;
+ int hist_slot_count;
+
+ ErtsHeapFactory msg_factory;
+ int building_result;
+ Eterm result_list;
+} gather_cinfo_t;
+
+static int gather_cinfo_scan(Allctr_t *allocator,
+ void *user_data,
+ Carrier_t *carrier)
+{
+ gather_cinfo_t *state;
+ chist_node_t *node;
+ int blocks_scanned;
+ Block_t *block;
+
+ state = (gather_cinfo_t*)user_data;
+ node = calloc(1, sizeof(chist_node_t) +
+ (state->hist_slot_count - 1) *
+ sizeof(node->histogram[0]));
+ blocks_scanned = 1;
+
+ /* ERTS_CRR_ALCTR_FLG_BUSY is ignored since we've set it ourselves and it
+ * would be misleading to include it. */
+ node->flags = erts_atomic_read_rb(&carrier->allctr) &
+ (ERTS_CRR_ALCTR_FLG_MASK & ~ERTS_CRR_ALCTR_FLG_BUSY);
+ node->carrier_size = CARRIER_SZ(carrier);
+
+ if (IS_SB_CARRIER(carrier)) {
+ UWord block_size;
+
+ block = SBC2BLK(allocator, carrier);
+ block_size = SBC_BLK_SZ(block);
+
+ node->allocated_size = block_size;
+ node->allocated_count = 1;
+ } else {
+ UWord scanned_bytes = MBC_HEADER_SIZE(allocator);
+
+ block = MBC_TO_FIRST_BLK(allocator, carrier);
+
+ while (1) {
+ UWord block_size = MBC_BLK_SZ(block);
+
+ scanned_bytes += block_size;
+
+ if (IS_ALLOCED_BLK(block)) {
+ node->allocated_size += block_size;
+ node->allocated_count++;
+ } else {
+ UWord size_interval;
+ int hist_slot;
+
+ size_interval = (block_size / state->hist_slot_start);
+ size_interval = u64_log2(size_interval + 1);
+
+ hist_slot = MIN(size_interval, state->hist_slot_count - 1);
+
+ node->histogram[hist_slot]++;
+ }
+
+ if (blocks_scanned >= BLOCKSCAN_BAILOUT_THRESHOLD) {
+ node->unscanned_size += CARRIER_SZ(carrier) - scanned_bytes;
+ break;
+ } else if (IS_LAST_BLK(block)) {
+ break;
+ }
+
+ block = NXT_BLK(block);
+ blocks_scanned++;
+ }
+ }
+
+ node->next = state->info_list;
+ state->info_list = node;
+ state->info_count++;
+
+ return blocks_scanned;
+}
+
+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;
+
+ Uint term_size;
+ Eterm *hp;
+ int ix;
+
+ ASSERT(state->building_result);
+
+ term_size = 11 + state->hist_slot_count;
+ 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;
+
+ hp = erts_produce_heap(&state->msg_factory, term_size, 0);
+
+ hp[0] = make_arityval(state->hist_slot_count);
+
+ for (ix = 0; ix < state->hist_slot_count; ix++) {
+ hp[1 + ix] = make_small(info->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;
+
+ carrier_tuple = make_tuple(hp);
+ hp += 8;
+
+ state->result_list = CONS(hp, carrier_tuple, state->result_list);
+
+ free(info);
+}
+
+static void gather_cinfo_send(gather_cinfo_t *state)
+{
+ Eterm result_tuple, task_ref;
+
+ int term_size;
+ Eterm *hp;
+
+ ASSERT((state->result_list == NIL) ^ (state->info_count > 0));
+ ASSERT(state->building_result);
+
+ term_size = 3 + erts_iref_storage_heap_size(&state->iref);
+ hp = erts_produce_heap(&state->msg_factory, term_size, 0);
+
+ task_ref = erts_iref_storage_make_ref(&state->iref, &hp,
+ &(state->msg_factory.message)->hfrag.off_heap, 0);
+
+ hp[0] = make_arityval(2);
+ hp[1] = task_ref;
+ hp[2] = state->result_list;
+
+ result_tuple = make_tuple(hp);
+
+ erts_factory_trim_and_close(&state->msg_factory, &result_tuple, 1);
+
+ erts_queue_message(state->process, 0, state->msg_factory.message,
+ result_tuple, am_system);
+}
+
+static int gather_cinfo_finish(void *arg)
+{
+ gather_cinfo_t *state = (gather_cinfo_t*)arg;
+ int reductions = BLOCKSCAN_REDUCTIONS;
+
+ if (!state->building_result) {
+ ErtsMessage *message;
+ Uint minimum_size;
+ Eterm *hp;
+
+ /* {Ref, [{Carrier size, unscanned size, allocated size,
+ * allocated block count, {Free block histogram}} | Rest]} */
+ minimum_size = 3 + erts_iref_storage_heap_size(&state->iref) +
+ state->info_count * (11 + state->hist_slot_count);
+
+ message = erts_alloc_message(minimum_size, &hp);
+ erts_factory_selfcontained_message_init(&state->msg_factory,
+ message, hp);
+
+ state->result_list = NIL;
+ state->building_result = 1;
+ }
+
+ while (state->info_list) {
+ chist_node_t *current = state->info_list;
+ state->info_list = current->next;
+
+ gather_cinfo_append_result(state, current);
+
+ if (reductions-- <= 0) {
+ return 1;
+ }
+ }
+
+ gather_cinfo_send(state);
+
+ return 0;
+}
+
+static void gather_cinfo_abort(void *arg)
+{
+ gather_cinfo_t *state = (gather_cinfo_t*)arg;
+
+ if (state->building_result) {
+ erts_factory_undo(&state->msg_factory);
+ }
+
+ while (state->info_list) {
+ chist_node_t *current = state->info_list;
+ state->info_list = current->next;
+
+ free(current);
+ }
+}
+
+int erts_alcu_gather_carrier_info(struct process *p, int allocator_num,
+ int sched_id, int hist_width,
+ UWord hist_start, Eterm ref)
+{
+ gather_cinfo_t *gather_state;
+ blockscan_t *scanner;
+
+ const char *allocator_desc;
+ Allctr_t *allocator;
+
+ ASSERT(is_internal_ref(ref));
+
+ if (!blockscan_get_specific_allocator(allocator_num,
+ sched_id,
+ &allocator)) {
+ return 0;
+ }
+
+ allocator_desc = ERTS_ALC_A2AD(allocator_num);
+
+ /* Plain calloc is intentional. */
+ gather_state = (gather_cinfo_t*)calloc(1, sizeof(gather_cinfo_t));
+ scanner = &gather_state->common;
+
+ scanner->abort = gather_cinfo_abort;
+ scanner->scan = gather_cinfo_scan;
+ 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);
+ erts_iref_storage_save(&gather_state->iref, ref);
+ gather_state->hist_slot_start = hist_start * 2;
+ gather_state->hist_slot_count = hist_width;
+ gather_state->process = p;
+
+ blockscan_dispatch(scanner, p, allocator, sched_id);
+
+ return 1;
+}
+
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\
* NOTE: erts_alcu_test() is only supposed to be used for testing. *
@@ -6277,6 +7722,7 @@ erts_alcu_verify_unused_ts(Allctr_t *allctr)
erts_mtx_unlock(&allctr->mutex);
}
+
#ifdef DEBUG
int is_sbc_blk(Block_t* blk)
{
diff --git a/erts/emulator/beam/erl_alloc_util.h b/erts/emulator/beam/erl_alloc_util.h
index faeb5ef368..ff4d10b206 100644
--- a/erts/emulator/beam/erl_alloc_util.h
+++ b/erts/emulator/beam/erl_alloc_util.h
@@ -50,6 +50,7 @@ typedef struct {
int tspec;
int tpref;
int ramv;
+ int atags;
UWord sbct;
UWord asbcst;
UWord rsbcst;
@@ -61,7 +62,9 @@ typedef struct {
UWord lmbcs;
UWord smbcs;
UWord mbcgs;
- int acul;
+ UWord acul;
+ UWord acnl;
+ UWord acfml;
void *fix;
size_t *fix_type_size;
@@ -104,6 +107,7 @@ typedef struct {
0, /* (bool) tspec: thread specific */\
0, /* (bool) tpref: thread preferred */\
0, /* (bool) ramv: realloc always moves */\
+ 0, /* (bool) atags: tagged allocations */\
512*1024, /* (bytes) sbct: sbc threshold */\
2*1024*2024, /* (amount) asbcst: abs sbc shrink threshold */\
20, /* (%) rsbcst: rel sbc shrink threshold */\
@@ -116,6 +120,8 @@ typedef struct {
1024*1024, /* (bytes) smbcs: smallest mbc size */\
10, /* (amount) mbcgs: mbc growth stages */\
0, /* (%) acul: abandon carrier utilization limit */\
+ 1000, /* (amount) acnl: abandoned carriers number limit */\
+ 0, /* (bytes) acfml: abandoned carrier fblk min limit */\
/* --- Data not options -------------------------------------------- */\
NULL, /* (ptr) fix */\
NULL /* (ptr) fix_type_size */\
@@ -138,6 +144,7 @@ typedef struct {
0, /* (bool) tspec: thread specific */\
0, /* (bool) tpref: thread preferred */\
0, /* (bool) ramv: realloc always moves */\
+ 0, /* (bool) atags: tagged allocations */\
64*1024, /* (bytes) sbct: sbc threshold */\
2*1024*2024, /* (amount) asbcst: abs sbc shrink threshold */\
20, /* (%) rsbcst: rel sbc shrink threshold */\
@@ -149,6 +156,8 @@ typedef struct {
128*1024, /* (bytes) smbcs: smallest mbc size */\
10, /* (amount) mbcgs: mbc growth stages */\
0, /* (%) acul: abandon carrier utilization limit */\
+ 1000, /* (amount) acnl: abandoned carriers number limit */\
+ 0, /* (bytes) acfml: abandoned carrier fblk min limit */\
/* --- Data not options -------------------------------------------- */\
NULL, /* (ptr) fix */\
NULL /* (ptr) fix_type_size */\
@@ -199,7 +208,7 @@ void* erts_alcu_mmapper_mseg_realloc(Allctr_t*, void *seg, Uint old_size, Uint *
void erts_alcu_mmapper_mseg_dealloc(Allctr_t*, void *seg, Uint size, Uint flags);
# endif
-# if defined(ERTS_ALC_A_EXEC) && !defined(ERTS_HAVE_EXEC_MMAPPER)
+# if defined(ERTS_ALC_A_EXEC)
void* erts_alcu_exec_mseg_alloc(Allctr_t*, Uint *size_p, Uint flags);
void* erts_alcu_exec_mseg_realloc(Allctr_t*, void *seg, Uint old_size, Uint *new_size_p);
void erts_alcu_exec_mseg_dealloc(Allctr_t*, void *seg, Uint size, Uint flags);
@@ -216,6 +225,38 @@ void erts_alcu_literal_32_sys_dealloc(Allctr_t*, void *ptr, Uint size, int supe
void erts_lcnt_update_allocator_locks(int enable);
#endif
+int erts_alcu_try_set_dyn_param(Allctr_t*, Eterm param, Uint value);
+
+/* Gathers per-tag allocation histograms from the given allocator number
+ * (ERTS_ALC_A_*) and scheduler id. An id of 0 means the global instance will
+ * be used.
+ *
+ * The results are sent to `p`, and it returns the number of messages to wait
+ * for. */
+int erts_alcu_gather_alloc_histograms(struct process *p, int allocator_num,
+ int sched_id, int hist_width,
+ UWord hist_start, Eterm ref);
+
+/* Gathers per-carrier info from the given allocator number (ERTS_ALC_A_*) and
+ * scheduler id. An id of 0 means the global instance will be used.
+ *
+ * The results are sent to `p`, and it returns the number of messages to wait
+ * for. */
+int erts_alcu_gather_carrier_info(struct process *p, int allocator_num,
+ int sched_id, int hist_width,
+ UWord hist_start, Eterm ref);
+
+struct alcu_blockscan;
+
+typedef struct {
+ struct alcu_blockscan *current;
+ struct alcu_blockscan *last;
+} ErtsAlcuBlockscanYieldData;
+
+int erts_handle_yielded_alcu_blockscan(struct ErtsSchedulerData_ *esdp,
+ ErtsAlcuBlockscanYieldData *yield);
+void erts_alcu_sched_spec_data_init(struct ErtsSchedulerData_ *esdp);
+
#endif /* !ERL_ALLOC_UTIL__ */
#if defined(GET_ERL_ALLOC_UTIL_IMPL) && !defined(ERL_ALLOC_UTIL_IMPL__)
@@ -296,41 +337,7 @@ void erts_lcnt_update_allocator_locks(int enable);
typedef union {char c[ERTS_ALLOC_ALIGN_BYTES]; long l; double d;} Unit_t;
-
-typedef struct ErtsDoubleLink_t_ {
- struct ErtsDoubleLink_t_ *next;
- struct ErtsDoubleLink_t_ *prev;
-}ErtsDoubleLink_t;
-
-typedef struct {
- erts_atomic_t next;
- erts_atomic_t prev;
- Allctr_t *orig_allctr; /* read-only while carrier is alive */
- ErtsThrPrgrVal thr_prgr;
- erts_atomic_t max_size;
- UWord abandon_limit;
- UWord blocks;
- UWord blocks_size;
- ErtsDoubleLink_t abandoned; /* node in pooled_list or traitor_list */
-} ErtsAlcCPoolData_t;
-
-
typedef struct Carrier_t_ Carrier_t;
-struct Carrier_t_ {
- UWord chdr;
- Carrier_t *next;
- Carrier_t *prev;
- erts_atomic_t allctr;
- ErtsAlcCPoolData_t cpool; /* Overwritten by block if sbc */
-};
-
-#define ERTS_ALC_CARRIER_TO_ALLCTR(C) \
- ((Allctr_t *) (erts_atomic_read_nob(&(C)->allctr) & ~FLG_MASK))
-
-typedef struct {
- Carrier_t *first;
- Carrier_t *last;
-} CarrierList_t;
typedef struct {
UWord bhdr;
@@ -344,6 +351,22 @@ typedef struct {
#endif
} Block_t;
+typedef union ErtsAllctrDDBlock_t_ ErtsAllctrDDBlock_t;
+
+union ErtsAllctrDDBlock_t_ {
+ erts_atomic_t atmc_next;
+ ErtsAllctrDDBlock_t *ptr_next;
+};
+
+typedef struct {
+ Block_t blk;
+#if !MBC_ABLK_OFFSET_BITS
+ ErtsAllctrDDBlock_t umem_;
+#endif
+} ErtsFakeDDBlock_t;
+
+
+
#define THIS_FREE_BLK_HDR_FLG (((UWord) 1) << 0)
#define PREV_FREE_BLK_HDR_FLG (((UWord) 1) << 1)
#define LAST_BLK_HDR_FLG (((UWord) 1) << 2)
@@ -352,14 +375,13 @@ typedef struct {
(THIS_FREE_BLK_HDR_FLG | PREV_FREE_BLK_HDR_FLG | LAST_BLK_HDR_FLG)
/*
- * FREE_LAST_MBC_BLK_HDR_FLGS is a special flag combo used for
- * distinguishing empty mbc's from allocated blocks in
- * handle_delayed_dealloc().
+ * HOMECOMING_MBC_BLK_HDR is a special block header combo used for
+ * distinguishing MBC's from allocated blocks in handle_delayed_dealloc().
*/
-#define FREE_LAST_MBC_BLK_HDR_FLGS (THIS_FREE_BLK_HDR_FLG | LAST_BLK_HDR_FLG)
+#define HOMECOMING_MBC_BLK_HDR (THIS_FREE_BLK_HDR_FLG | LAST_BLK_HDR_FLG)
#define IS_FREE_LAST_MBC_BLK(B) \
- (((B)->bhdr & FLG_MASK) == FREE_LAST_MBC_BLK_HDR_FLGS)
+ (((B)->bhdr & FLG_MASK) == (THIS_FREE_BLK_HDR_FLG | LAST_BLK_HDR_FLG))
#define IS_SBC_BLK(B) (((B)->bhdr & FLG_MASK) == SBC_BLK_HDR_FLG)
#define IS_MBC_BLK(B) (!IS_SBC_BLK((B)))
@@ -383,6 +405,57 @@ typedef struct {
typedef UWord FreeBlkFtr_t; /* Footer of a free block */
+/* This AOFF stuff really belong in erl_ao_firstfit_alloc.h */
+typedef struct AOFF_RBTree_t_ AOFF_RBTree_t;
+struct AOFF_RBTree_t_ {
+ Block_t hdr;
+ AOFF_RBTree_t *parent;
+ AOFF_RBTree_t *left;
+ AOFF_RBTree_t *right;
+ Uint32 flags;
+ Uint32 max_sz; /* of all blocks in this sub-tree */
+};
+
+void aoff_add_pooled_mbc(Allctr_t*, Carrier_t*);
+void aoff_remove_pooled_mbc(Allctr_t*, Carrier_t*);
+Carrier_t* aoff_lookup_pooled_mbc(Allctr_t*, Uint size);
+void erts_aoff_larger_max_size(AOFF_RBTree_t *node);
+
+typedef struct {
+ ErtsFakeDDBlock_t homecoming_dd;
+ erts_atomic_t next;
+ erts_atomic_t prev;
+ Allctr_t *orig_allctr; /* read-only while carrier is alive */
+ ErtsThrPrgrVal thr_prgr;
+ erts_atomic_t max_size;
+ UWord abandon_limit;
+ UWord blocks;
+ UWord blocks_size;
+ enum {
+ ERTS_MBC_IS_HOME,
+ ERTS_MBC_WAS_POOLED,
+ ERTS_MBC_WAS_TRAITOR
+ } state;
+ AOFF_RBTree_t pooled; /* node in pooled_tree */
+} ErtsAlcCPoolData_t;
+
+struct Carrier_t_ {
+ UWord chdr;
+ Carrier_t *next;
+ Carrier_t *prev;
+ erts_atomic_t allctr;
+ ErtsAlcCPoolData_t cpool; /* Overwritten by block if sbc */
+};
+
+#define ERTS_ALC_CARRIER_TO_ALLCTR(C) \
+ ((Allctr_t *) (erts_atomic_read_nob(&(C)->allctr) & ~FLG_MASK))
+
+typedef struct {
+ Carrier_t *first;
+ Carrier_t *last;
+} CarrierList_t;
+
+
typedef Uint64 CallCounter_t;
typedef struct {
@@ -419,13 +492,6 @@ typedef struct {
#endif
-typedef union ErtsAllctrDDBlock_t_ ErtsAllctrDDBlock_t;
-
-union ErtsAllctrDDBlock_t_ {
- erts_atomic_t atmc_next;
- ErtsAllctrDDBlock_t *ptr_next;
-};
-
typedef struct {
ErtsAllctrDDBlock_t marker;
erts_atomic_t last;
@@ -515,6 +581,7 @@ struct Allctr_t_ {
/* Options */
int t;
int ramv;
+ int atags;
Uint sbc_threshold;
Uint sbc_move_threshold;
Uint mbc_move_threshold;
@@ -537,25 +604,37 @@ struct Allctr_t_ {
UWord crr_set_flgs;
UWord crr_clr_flgs;
- /* Carriers */
+ /* Carriers *employed* by this allocator */
CarrierList_t mbc_list;
CarrierList_t sbc_list;
struct {
- /* pooled_list, traitor list and dc_list contain only
- carriers _created_ by this allocator */
- ErtsDoubleLink_t pooled_list;
- ErtsDoubleLink_t traitor_list;
+ /* pooled_tree and dc_list contain only
+ carriers *created* by this allocator */
+ AOFF_RBTree_t* pooled_tree;
CarrierList_t dc_list;
UWord abandon_limit;
int disable_abandon;
int check_limit_count;
- int util_limit;
+ UWord util_limit; /* acul */
+ UWord in_pool_limit; /* acnl */
+ UWord fblk_min_limit; /* acmfl */
struct {
erts_atomic_t blocks_size;
erts_atomic_t no_blocks;
erts_atomic_t carriers_size;
erts_atomic_t no_carriers;
+ CallCounter_t fail_pooled;
+ CallCounter_t fail_shared;
+ CallCounter_t fail_pend_dealloc;
+ CallCounter_t fail;
+ CallCounter_t fetch;
+ CallCounter_t skip_size;
+ CallCounter_t skip_busy;
+ CallCounter_t skip_not_pooled;
+ CallCounter_t skip_homecoming;
+ CallCounter_t skip_race;
+ CallCounter_t entrance_removed;
} stat;
} cpool;
@@ -589,6 +668,8 @@ struct Allctr_t_ {
void* (*sys_realloc)(Allctr_t *allctr, void *ptr, Uint *size_p, Uint old_size, int superalign);
void (*sys_dealloc)(Allctr_t *allctr, void *ptr, Uint size, int superalign);
+ int (*try_set_dyn_param)(Allctr_t*, Eterm param, Uint value);
+
void (*init_atoms) (void);
#ifdef ERTS_ALLOC_UTIL_HARD_DEBUG
@@ -637,6 +718,7 @@ struct Allctr_t_ {
#endif
};
+
int erts_alcu_start(Allctr_t *, AllctrInit_t *);
void erts_alcu_stop(Allctr_t *);
diff --git a/erts/emulator/beam/erl_ao_firstfit_alloc.c b/erts/emulator/beam/erl_ao_firstfit_alloc.c
index 05ba1f9891..0c5545401a 100644
--- a/erts/emulator/beam/erl_ao_firstfit_alloc.c
+++ b/erts/emulator/beam/erl_ao_firstfit_alloc.c
@@ -20,7 +20,7 @@
/*
- * Description: An "address order first fit" allocator
+ * Description: A family of "first fit" allocator strategies
* based on a Red-Black (binary search) Tree. The search,
* insert, and delete operations are all O(log n) operations
* on a Red-Black Tree.
@@ -40,6 +40,10 @@
* sorting order. Blocks within the same carrier are sorted
* wrt size instead of address. The 'max_sz' field is maintained
* in order to dismiss entire carriers with too small blocks.
+ * Age Order:
+ * Carriers are ordered by creation time instead of address.
+ * Oldest carrier with a large enough free block is chosen.
+ * No age order supported for blocks.
*
* Authors: Rickard Green/Sverker Eriksson
*/
@@ -53,10 +57,12 @@
#include "erl_ao_firstfit_alloc.h"
#ifdef DEBUG
+# define IS_DEBUG 1
#if 0
#define HARD_DEBUG
#endif
#else
+# define IS_DEBUG 0
#undef HARD_DEBUG
#endif
@@ -92,18 +98,6 @@
#define RBT_ASSERT(x)
#endif
-
-/* Types... */
-typedef struct AOFF_RBTree_t_ AOFF_RBTree_t;
-
-struct AOFF_RBTree_t_ {
- Block_t hdr;
- AOFF_RBTree_t *parent;
- AOFF_RBTree_t *left;
- AOFF_RBTree_t *right;
- Uint32 flags;
- Uint32 max_sz; /* of all blocks in this sub-tree */
-};
#define AOFF_BLK_SZ(B) MBC_FBLK_SZ(&(B)->hdr)
/* BF block nodes keeps list of all with equal size
@@ -121,6 +115,7 @@ typedef struct AOFF_Carrier_t_ AOFF_Carrier_t;
struct AOFF_Carrier_t_ {
Carrier_t crr;
AOFF_RBTree_t rbt_node; /* My node in the carrier tree */
+ Sint64 birth_time;
AOFF_RBTree_t* root; /* Root of my block tree */
};
#define RBT_NODE_TO_MBC(PTR) ErtsContainerStruct((PTR), AOFF_Carrier_t, rbt_node)
@@ -136,12 +131,12 @@ struct AOFF_Carrier_t_ {
*/
#ifdef HARD_DEBUG
-# define HARD_CHECK_IS_MEMBER(ROOT,NODE) rbt_assert_is_member(ROOT,NODE)
-# define HARD_CHECK_TREE(CRR,FLV,ROOT,SZ) check_tree(CRR, FLV, ROOT, SZ)
-static AOFF_RBTree_t * check_tree(Carrier_t* within_crr, enum AOFF_Flavor flavor, AOFF_RBTree_t* root, Uint);
+# define HARD_CHECK_IS_MEMBER(ROOT,NODE) ASSERT(rbt_is_member(ROOT,NODE))
+# define HARD_CHECK_TREE(CRR,ORDER,ROOT,SZ) check_tree(CRR, ORDER, ROOT, SZ)
+static AOFF_RBTree_t * check_tree(Carrier_t*, enum AOFFSortOrder, AOFF_RBTree_t*, Uint);
#else
# define HARD_CHECK_IS_MEMBER(ROOT,NODE)
-# define HARD_CHECK_TREE(CRR,FLV,ROOT,SZ)
+# define HARD_CHECK_TREE(CRR,ORDER,ROOT,SZ)
#endif
@@ -179,25 +174,63 @@ static ERTS_INLINE void lower_max_size(AOFF_RBTree_t *node,
else ASSERT(new_max == old_max);
}
-static ERTS_INLINE SWord cmp_blocks(enum AOFF_Flavor flavor,
+/*
+ * Set possibly new larger 'max_sz' of node and propagate change toward root
+ */
+void erts_aoff_larger_max_size(AOFF_RBTree_t *node)
+{
+ AOFF_RBTree_t* x = node;
+ const Uint new_sz = node->hdr.bhdr;
+
+ ASSERT(!x->left || x->left->max_sz <= x->max_sz);
+ ASSERT(!x->right || x->right->max_sz <= x->max_sz);
+
+ while (new_sz > x->max_sz) {
+ x->max_sz = new_sz;
+ x = x->parent;
+ if (!x)
+ break;
+ }
+}
+
+/* Compare nodes for both carrier and block trees */
+static ERTS_INLINE SWord cmp_blocks(enum AOFFSortOrder order,
AOFF_RBTree_t* lhs, AOFF_RBTree_t* rhs)
{
ASSERT(lhs != rhs);
- ASSERT(flavor == AOFF_AOFF || FBLK_TO_MBC(&lhs->hdr) == FBLK_TO_MBC(&rhs->hdr));
- if (flavor != AOFF_AOFF) {
- SWord diff = (SWord)AOFF_BLK_SZ(lhs) - (SWord)AOFF_BLK_SZ(rhs);
- if (diff || flavor == AOFF_BF) return diff;
+ if (order == FF_AGEFF) {
+ AOFF_Carrier_t* lc = RBT_NODE_TO_MBC(lhs);
+ AOFF_Carrier_t* rc = RBT_NODE_TO_MBC(rhs);
+ Sint64 diff = lc->birth_time - rc->birth_time;
+ #ifdef ARCH_64
+ if (diff)
+ return diff;
+ #else
+ if (diff < 0)
+ return -1;
+ else if (diff > 0)
+ return 1;
+ #endif
+ }
+ else {
+ ASSERT(order == FF_AOFF || FBLK_TO_MBC(&lhs->hdr) == FBLK_TO_MBC(&rhs->hdr));
+ if (order != FF_AOFF) {
+ SWord diff = (SWord)AOFF_BLK_SZ(lhs) - (SWord)AOFF_BLK_SZ(rhs);
+ if (diff || order == FF_BF) return diff;
+ }
}
return (char*)lhs - (char*)rhs;
}
-static ERTS_INLINE SWord cmp_cand_blk(enum AOFF_Flavor flavor,
+/* Compare candidate block. Only for block tree */
+static ERTS_INLINE SWord cmp_cand_blk(enum AOFFSortOrder order,
Block_t* cand_blk, AOFF_RBTree_t* rhs)
{
- if (flavor != AOFF_AOFF) {
+ ASSERT(order != FF_AGEFF);
+ if (order != FF_AOFF) {
if (BLK_TO_MBC(cand_blk) == FBLK_TO_MBC(&rhs->hdr)) {
SWord diff = (SWord)MBC_BLK_SZ(cand_blk) - (SWord)MBC_FBLK_SZ(&rhs->hdr);
- if (diff || flavor == AOFF_BF) return diff;
+ if (diff || order == FF_BF) return diff;
}
}
return (char*)cand_blk - (char*)rhs;
@@ -218,11 +251,8 @@ static UWord aoff_largest_fblk_in_mbc(Allctr_t*, Carrier_t*);
/* Generic tree functions used by both carrier and block trees. */
static void rbt_delete(AOFF_RBTree_t** root, AOFF_RBTree_t* del);
-static void rbt_insert(enum AOFF_Flavor flavor, AOFF_RBTree_t** root, AOFF_RBTree_t* blk);
+static void rbt_insert(enum AOFFSortOrder, AOFF_RBTree_t** root, AOFF_RBTree_t* blk);
static AOFF_RBTree_t* rbt_search(AOFF_RBTree_t* root, Uint size);
-#ifdef HARD_DEBUG
-static int rbt_assert_is_member(AOFF_RBTree_t* root, AOFF_RBTree_t* node);
-#endif
static Eterm info_options(Allctr_t *, char *, fmtfn_t *, void *, Uint **, Uint *);
static void init_atoms(void);
@@ -230,10 +260,17 @@ static void init_atoms(void);
static int atoms_initialized = 0;
+#ifndef ERTS_HAVE_OS_MONOTONIC_TIME_SUPPORT
+static erts_atomic64_t birth_time_counter;
+#endif
+
void
erts_aoffalc_init(void)
{
atoms_initialized = 0;
+#ifndef ERTS_HAVE_OS_MONOTONIC_TIME_SUPPORT
+ erts_atomic64_init_nob(&birth_time_counter, 0);
+#endif
}
Allctr_t *
@@ -254,11 +291,12 @@ erts_aoffalc_start(AOFFAllctr_t *alc,
sys_memcpy((void *) alc, (void *) &zero.allctr, sizeof(AOFFAllctr_t));
- alc->flavor = aoffinit->flavor;
+ alc->blk_order = aoffinit->blk_order;
+ alc->crr_order = aoffinit->crr_order;
allctr->mbc_header_size = sizeof(AOFF_Carrier_t);
allctr->min_mbc_size = MIN_MBC_SZ;
allctr->min_mbc_first_free_size = MIN_MBC_FIRST_FREE_SZ;
- allctr->min_block_size = (aoffinit->flavor == AOFF_BF ?
+ allctr->min_block_size = (aoffinit->blk_order == FF_BF ?
sizeof(AOFF_RBTreeList_t):sizeof(AOFF_RBTree_t));
allctr->vsn_str = ERTS_ALC_AOFF_ALLOC_VSN_STR;
@@ -487,9 +525,9 @@ aoff_unlink_free_block(Allctr_t *allctr, Block_t *blk)
AOFF_Carrier_t *crr = (AOFF_Carrier_t*) FBLK_TO_MBC(&del->hdr);
ASSERT(crr->rbt_node.hdr.bhdr == crr->root->max_sz);
- HARD_CHECK_TREE(&crr->crr, alc->flavor, crr->root, 0);
+ HARD_CHECK_TREE(&crr->crr, alc->blk_order, crr->root, 0);
- if (alc->flavor == AOFF_BF) {
+ if (alc->blk_order == FF_BF) {
ASSERT(del->flags & IS_BF_FLG);
if (IS_LIST_ELEM(del)) {
/* Remove from list */
@@ -510,14 +548,14 @@ aoff_unlink_free_block(Allctr_t *allctr, Block_t *blk)
replace(&crr->root, (AOFF_RBTree_t*)del, LIST_NEXT(del));
- HARD_CHECK_TREE(&crr->crr, alc->flavor, crr->root, 0);
+ HARD_CHECK_TREE(&crr->crr, alc->blk_order, crr->root, 0);
return;
}
}
rbt_delete(&crr->root, (AOFF_RBTree_t*)del);
- HARD_CHECK_TREE(&crr->crr, alc->flavor, crr->root, 0);
+ HARD_CHECK_TREE(&crr->crr, alc->blk_order, crr->root, 0);
/* Update the carrier tree with a potentially new (lower) max_sz
*/
@@ -715,32 +753,33 @@ aoff_link_free_block(Allctr_t *allctr, Block_t *block)
ASSERT(allctr == ERTS_ALC_CARRIER_TO_ALLCTR(&blk_crr->crr));
ASSERT(blk_crr->rbt_node.hdr.bhdr == (blk_crr->root ? blk_crr->root->max_sz : 0));
- HARD_CHECK_TREE(&blk_crr->crr, alc->flavor, blk_crr->root, 0);
+ HARD_CHECK_TREE(&blk_crr->crr, alc->blk_order, blk_crr->root, 0);
- rbt_insert(alc->flavor, &blk_crr->root, blk);
+ rbt_insert(alc->blk_order, &blk_crr->root, blk);
- /* Update the carrier tree with a potentially new (larger) max_sz
- */
+ /*
+ * Update carrier tree with a potentially new (larger) max_sz
+ */
crr_node = &blk_crr->rbt_node;
if (blk_sz > crr_node->hdr.bhdr) {
- ASSERT(blk_sz == blk_crr->root->max_sz);
- crr_node->hdr.bhdr = blk_sz;
- while (blk_sz > crr_node->max_sz) {
- crr_node->max_sz = blk_sz;
- crr_node = crr_node->parent;
- if (!crr_node) break;
- }
+ ASSERT(blk_sz == blk_crr->root->max_sz);
+ crr_node->hdr.bhdr = blk_sz;
+ while (blk_sz > crr_node->max_sz) {
+ crr_node->max_sz = blk_sz;
+ crr_node = crr_node->parent;
+ if (!crr_node) break;
+ }
}
- HARD_CHECK_TREE(&blk_crr->crr, alc->flavor, blk_crr->root, 0);
+ HARD_CHECK_TREE(NULL, alc->crr_order, alc->mbc_root, 0);
}
static void
-rbt_insert(enum AOFF_Flavor flavor, AOFF_RBTree_t** root, AOFF_RBTree_t* blk)
+rbt_insert(enum AOFFSortOrder order, AOFF_RBTree_t** root, AOFF_RBTree_t* blk)
{
Uint blk_sz = AOFF_BLK_SZ(blk);
#ifdef DEBUG
- blk->flags = (flavor == AOFF_BF) ? IS_BF_FLG : 0;
+ blk->flags = (order == FF_BF) ? IS_BF_FLG : 0;
#else
blk->flags = 0;
#endif
@@ -760,7 +799,7 @@ rbt_insert(enum AOFF_Flavor flavor, AOFF_RBTree_t** root, AOFF_RBTree_t* blk)
if (x->max_sz < blk_sz) {
x->max_sz = blk_sz;
}
- diff = cmp_blocks(flavor, blk, x);
+ diff = cmp_blocks(order, blk, x);
if (diff < 0) {
if (!x->left) {
blk->parent = x;
@@ -778,7 +817,7 @@ rbt_insert(enum AOFF_Flavor flavor, AOFF_RBTree_t** root, AOFF_RBTree_t* blk)
x = x->right;
}
else {
- ASSERT(flavor == AOFF_BF);
+ ASSERT(order == FF_BF);
ASSERT(blk->flags & IS_BF_FLG);
ASSERT(x->flags & IS_BF_FLG);
SET_LIST_ELEM(blk);
@@ -798,7 +837,7 @@ rbt_insert(enum AOFF_Flavor flavor, AOFF_RBTree_t** root, AOFF_RBTree_t* blk)
if (IS_RED(blk->parent))
tree_insert_fixup(root, blk);
}
- if (flavor == AOFF_BF) {
+ if (order == FF_BF) {
SET_TREE_NODE(blk);
LIST_NEXT(blk) = NULL;
}
@@ -826,6 +865,16 @@ rbt_search(AOFF_RBTree_t* root, Uint size)
}
}
+Carrier_t* aoff_lookup_pooled_mbc(Allctr_t* allctr, Uint size)
+{
+ AOFF_RBTree_t* node;
+
+ if (!allctr->cpool.pooled_tree)
+ return NULL;
+ node = rbt_search(allctr->cpool.pooled_tree, size);
+ return node ? ErtsContainerStruct(node, Carrier_t, cpool.pooled) : NULL;
+}
+
static Block_t *
aoff_get_free_block(Allctr_t *allctr, Uint size,
Block_t *cand_blk, Uint cand_size)
@@ -850,7 +899,7 @@ aoff_get_free_block(Allctr_t *allctr, Uint size,
/* Get block within carrier tree
*/
#ifdef HARD_DEBUG
- dbg_blk = HARD_CHECK_TREE(&crr->crr, alc->flavor, crr->root, size);
+ dbg_blk = HARD_CHECK_TREE(&crr->crr, alc->blk_order, crr->root, size);
#endif
blk = rbt_search(crr->root, size);
@@ -863,7 +912,7 @@ aoff_get_free_block(Allctr_t *allctr, Uint size,
if (!blk)
return NULL;
- if (cand_blk && cmp_cand_blk(alc->flavor, cand_blk, blk) < 0) {
+ if (cand_blk && cmp_cand_blk(alc->blk_order, cand_blk, blk) < 0) {
return NULL; /* cand_blk was better */
}
@@ -872,23 +921,32 @@ aoff_get_free_block(Allctr_t *allctr, Uint size,
return (Block_t *) blk;
}
+static ERTS_INLINE Sint64 get_birth_time(void)
+{
+#ifdef ERTS_HAVE_OS_MONOTONIC_TIME_SUPPORT
+ return (Sint64) erts_os_monotonic_time();
+#else
+ return (Sint64) erts_atomic64_inc_read_nob(&birth_time_counter);
+#endif
+}
+
static void aoff_creating_mbc(Allctr_t *allctr, Carrier_t *carrier)
{
AOFFAllctr_t *alc = (AOFFAllctr_t *) allctr;
AOFF_Carrier_t *crr = (AOFF_Carrier_t*) carrier;
AOFF_RBTree_t **root = &alc->mbc_root;
- HARD_CHECK_TREE(NULL, 0, *root, 0);
+ HARD_CHECK_TREE(NULL, alc->crr_order, *root, 0);
- /* Link carrier in address order tree
- */
crr->rbt_node.hdr.bhdr = 0;
- rbt_insert(AOFF_AOFF, root, &crr->rbt_node);
+ if (alc->crr_order == FF_AGEFF || IS_DEBUG)
+ crr->birth_time = get_birth_time();
+ rbt_insert(alc->crr_order, root, &crr->rbt_node);
/* aoff_link_free_block will add free block later */
crr->root = NULL;
- HARD_CHECK_TREE(NULL, 0, *root, 0);
+ HARD_CHECK_TREE(NULL, alc->crr_order, *root, 0);
}
#define IS_CRR_IN_TREE(CRR,ROOT) \
@@ -911,27 +969,38 @@ static void aoff_add_mbc(Allctr_t *allctr, Carrier_t *carrier)
AOFF_RBTree_t **root = &alc->mbc_root;
ASSERT(!IS_CRR_IN_TREE(crr, *root));
- HARD_CHECK_TREE(NULL, 0, *root, 0);
+ HARD_CHECK_TREE(NULL, alc->crr_order, *root, 0);
+
+ rbt_insert(alc->crr_order, root, &crr->rbt_node);
+
+ HARD_CHECK_TREE(NULL, alc->crr_order, *root, 0);
+}
+
+void aoff_add_pooled_mbc(Allctr_t *allctr, Carrier_t *crr)
+{
+ AOFF_RBTree_t **root = &allctr->cpool.pooled_tree;
+
+ ASSERT(allctr == crr->cpool.orig_allctr);
+ HARD_CHECK_TREE(NULL, 0, *root, 0);
/* Link carrier in address order tree
*/
- rbt_insert(AOFF_AOFF, root, &crr->rbt_node);
+ rbt_insert(FF_AOFF, root, &crr->cpool.pooled);
HARD_CHECK_TREE(NULL, 0, *root, 0);
}
static void aoff_remove_mbc(Allctr_t *allctr, Carrier_t *carrier)
{
- AOFFAllctr_t *alc = (AOFFAllctr_t *) allctr;
- AOFF_Carrier_t *crr = (AOFF_Carrier_t*) carrier;
- AOFF_RBTree_t **root = &alc->mbc_root;
+ AOFF_RBTree_t **root = &((AOFFAllctr_t*)allctr)->mbc_root;
+ AOFF_Carrier_t *crr = (AOFF_Carrier_t*)carrier;
ASSERT(allctr == ERTS_ALC_CARRIER_TO_ALLCTR(carrier));
if (!IS_CRR_IN_TREE(crr,*root))
- return;
+ return;
- HARD_CHECK_TREE(NULL, 0, *root, 0);
+ HARD_CHECK_TREE(NULL, alc->crr_order, *root, 0);
rbt_delete(root, &crr->rbt_node);
crr->rbt_node.parent = NULL;
@@ -939,9 +1008,27 @@ static void aoff_remove_mbc(Allctr_t *allctr, Carrier_t *carrier)
crr->rbt_node.right = NULL;
crr->rbt_node.max_sz = crr->rbt_node.hdr.bhdr;
- HARD_CHECK_TREE(NULL, 0, *root, 0);
+ HARD_CHECK_TREE(NULL, alc->crr_order, *root, 0);
+}
+
+void aoff_remove_pooled_mbc(Allctr_t *allctr, Carrier_t *crr)
+{
+ ASSERT(allctr == crr->cpool.orig_allctr);
+
+ HARD_CHECK_TREE(NULL, 0, allctr->cpool.pooled_tree, 0);
+
+ rbt_delete(&allctr->cpool.pooled_tree, &crr->cpool.pooled);
+#ifdef DEBUG
+ crr->cpool.pooled.parent = NULL;
+ crr->cpool.pooled.left = NULL;
+ crr->cpool.pooled.right = NULL;
+ crr->cpool.pooled.max_sz = 0;
+#endif
+ HARD_CHECK_TREE(NULL, 0, allctr->cpool.pooled_tree, 0);
+
}
+
static UWord aoff_largest_fblk_in_mbc(Allctr_t* allctr, Carrier_t* carrier)
{
AOFF_Carrier_t *crr = (AOFF_Carrier_t*) carrier;
@@ -955,47 +1042,35 @@ static UWord aoff_largest_fblk_in_mbc(Allctr_t* allctr, Carrier_t* carrier)
* info_options()
*/
+static const char* flavor_str[2][3] = {
+ {"ageffcaoff", "ageffcaobf", "ageffcbf"},
+ { "aoff", "aoffcaobf", "aoffcbf"}
+};
+static Eterm flavor_atoms[2][3];
+
static struct {
Eterm as;
- Eterm aoff;
- Eterm aoffcaobf;
- Eterm aoffcbf;
-#ifdef DEBUG
- Eterm end_of_atoms;
-#endif
} am;
-static void ERTS_INLINE atom_init(Eterm *atom, char *name)
+static void ERTS_INLINE atom_init(Eterm *atom, const char *name)
{
- *atom = am_atom_put(name, strlen(name));
+ *atom = am_atom_put(name, sys_strlen(name));
}
#define AM_INIT(AM) atom_init(&am.AM, #AM)
static void
init_atoms(void)
{
-#ifdef DEBUG
- Eterm *atom;
-#endif
+ int i, j;
if (atoms_initialized)
return;
-#ifdef DEBUG
- for (atom = (Eterm *) &am; atom <= &am.end_of_atoms; atom++) {
- *atom = THE_NON_VALUE;
- }
-#endif
AM_INIT(as);
- AM_INIT(aoff);
- AM_INIT(aoffcaobf);
- AM_INIT(aoffcbf);
-#ifdef DEBUG
- for (atom = (Eterm *) &am; atom < &am.end_of_atoms; atom++) {
- ASSERT(*atom != THE_NON_VALUE);
- }
-#endif
+ for (i = 0; i < 2; i++)
+ for (j = 0; j < 3; j++)
+ atom_init(&flavor_atoms[i][j], flavor_str[i][j]);
atoms_initialized = 1;
}
@@ -1021,15 +1096,16 @@ info_options(Allctr_t *allctr,
{
AOFFAllctr_t* alc = (AOFFAllctr_t*) allctr;
Eterm res = THE_NON_VALUE;
- const char* flavor_str[3] = {"aoff", "aoffcaobf", "aoffcbf"};
- Eterm flavor_atom[3] = {am.aoff, am.aoffcaobf, am.aoffcbf};
+
+ ASSERT(alc->crr_order >= 0 && alc->crr_order <= 1);
+ ASSERT(alc->blk_order >= 1 && alc->blk_order <= 3);
if (print_to_p) {
erts_print(*print_to_p,
print_to_arg,
"%sas: %s\n",
prefix,
- flavor_str[alc->flavor]);
+ flavor_str[alc->crr_order][alc->blk_order-1]);
}
if (hpp || szp) {
@@ -1039,7 +1115,8 @@ info_options(Allctr_t *allctr,
__FILE__, __LINE__);;
res = NIL;
- add_2tup(hpp, szp, &res, am.as, flavor_atom[alc->flavor]);
+ add_2tup(hpp, szp, &res, am.as,
+ flavor_atoms[alc->crr_order][alc->blk_order-1]);
}
return res;
@@ -1057,7 +1134,7 @@ UWord
erts_aoffalc_test(UWord op, UWord a1, UWord a2)
{
switch (op) {
- case 0x500: return (UWord) ((AOFFAllctr_t *) a1)->flavor == AOFF_AOBF;
+ case 0x500: return (UWord) ((AOFFAllctr_t *) a1)->blk_order == FF_AOBF;
case 0x501: {
AOFF_RBTree_t *node = ((AOFFAllctr_t *) a1)->mbc_root;
Uint size = (Uint) a2;
@@ -1072,7 +1149,7 @@ erts_aoffalc_test(UWord op, UWord a1, UWord a2)
case 0x507: return (UWord) IS_TREE_NODE((AOFF_RBTree_t *) a1);
case 0x508: return (UWord) 0; /* IS_BF_ALGO */
case 0x509: return (UWord) ((AOFF_RBTree_t *) a1)->max_sz;
- case 0x50a: return (UWord) ((AOFFAllctr_t *) a1)->flavor == AOFF_BF;
+ case 0x50a: return (UWord) ((AOFFAllctr_t *) a1)->blk_order == FF_BF;
case 0x50b: return (UWord) LIST_PREV(a1);
default: ASSERT(0); return ~((UWord) 0);
}
@@ -1085,12 +1162,13 @@ erts_aoffalc_test(UWord op, UWord a1, UWord a2)
#ifdef HARD_DEBUG
-
-static int rbt_assert_is_member(AOFF_RBTree_t* root, AOFF_RBTree_t* node)
+static int rbt_is_member(AOFF_RBTree_t* root, AOFF_RBTree_t* node)
{
while (node != root) {
- ASSERT(node->parent);
- ASSERT(node->parent->left == node || node->parent->right == node);
+ if (!node->parent || (node->parent->left != node &&
+ node->parent->right != node)) {
+ return 0;
+ }
node = node->parent;
}
return 1;
@@ -1132,7 +1210,7 @@ static void print_tree(AOFF_RBTree_t*);
*/
static AOFF_RBTree_t *
-check_tree(Carrier_t* within_crr, enum AOFF_Flavor flavor, AOFF_RBTree_t* root, Uint size)
+check_tree(Carrier_t* within_crr, enum AOFFSortOrder order, AOFF_RBTree_t* root, Uint size)
{
AOFF_RBTree_t *res = NULL;
Sint blacks;
@@ -1144,7 +1222,8 @@ check_tree(Carrier_t* within_crr, enum AOFF_Flavor flavor, AOFF_RBTree_t* root,
#ifdef PRINT_TREE
print_tree(root);
#endif
- ASSERT(within_crr || flavor == AOFF_AOFF);
+ ASSERT((within_crr && order >= FF_AOFF) ||
+ (!within_crr && order <= FF_AOFF));
if (!root)
return res;
@@ -1202,7 +1281,7 @@ check_tree(Carrier_t* within_crr, enum AOFF_Flavor flavor, AOFF_RBTree_t* root,
ASSERT(((char*)x + AOFF_BLK_SZ(x)) <= ((char*)crr + CARRIER_SZ(crr)));
}
- if (flavor == AOFF_BF) {
+ if (order == FF_BF) {
AOFF_RBTree_t* y = x;
AOFF_RBTree_t* nxt = LIST_NEXT(y);
ASSERT(IS_TREE_NODE(x));
@@ -1225,13 +1304,13 @@ check_tree(Carrier_t* within_crr, enum AOFF_Flavor flavor, AOFF_RBTree_t* root,
if (x->left) {
ASSERT(x->left->parent == x);
- ASSERT(cmp_blocks(flavor, x->left, x) < 0);
+ ASSERT(cmp_blocks(order, x->left, x) < 0);
ASSERT(x->left->max_sz <= x->max_sz);
}
if (x->right) {
ASSERT(x->right->parent == x);
- ASSERT(cmp_blocks(flavor, x->right, x) > 0);
+ ASSERT(cmp_blocks(order, x->right, x) > 0);
ASSERT(x->right->max_sz <= x->max_sz);
}
ASSERT(x->max_sz >= AOFF_BLK_SZ(x));
@@ -1240,7 +1319,7 @@ check_tree(Carrier_t* within_crr, enum AOFF_Flavor flavor, AOFF_RBTree_t* root,
|| x->max_sz == (x->right ? x->right->max_sz : 0));
if (size && AOFF_BLK_SZ(x) >= size) {
- if (!res || cmp_blocks(flavor, x, res) < 0) {
+ if (!res || cmp_blocks(order, x, res) < 0) {
res = x;
}
}
diff --git a/erts/emulator/beam/erl_ao_firstfit_alloc.h b/erts/emulator/beam/erl_ao_firstfit_alloc.h
index 7349c6ab19..9cf4fc81a8 100644
--- a/erts/emulator/beam/erl_ao_firstfit_alloc.h
+++ b/erts/emulator/beam/erl_ao_firstfit_alloc.h
@@ -28,14 +28,16 @@
typedef struct AOFFAllctr_t_ AOFFAllctr_t;
-enum AOFF_Flavor {
- AOFF_AOFF = 0,
- AOFF_AOBF = 1,
- AOFF_BF = 2
+enum AOFFSortOrder {
+ FF_AGEFF = 0,
+ FF_AOFF = 1,
+ FF_AOBF = 2,
+ FF_BF = 3
};
typedef struct {
- enum AOFF_Flavor flavor;
+ enum AOFFSortOrder blk_order;
+ enum AOFFSortOrder crr_order;
} AOFFAllctrInit_t;
#define ERTS_DEFAULT_AOFF_ALLCTR_INIT {0/*dummy*/}
@@ -53,12 +55,12 @@ Allctr_t *erts_aoffalc_start(AOFFAllctr_t *, AOFFAllctrInit_t*, AllctrInit_t *);
#define GET_ERL_ALLOC_UTIL_IMPL
#include "erl_alloc_util.h"
-
struct AOFFAllctr_t_ {
Allctr_t allctr; /* Has to be first! */
struct AOFF_RBTree_t_* mbc_root;
- enum AOFF_Flavor flavor;
+ enum AOFFSortOrder blk_order;
+ enum AOFFSortOrder crr_order;
};
UWord erts_aoffalc_test(UWord, UWord, UWord);
diff --git a/erts/emulator/beam/erl_arith.c b/erts/emulator/beam/erl_arith.c
index f2a3e411ec..b6625db0d3 100644
--- a/erts/emulator/beam/erl_arith.c
+++ b/erts/emulator/beam/erl_arith.c
@@ -114,7 +114,7 @@ BIF_RETTYPE intdiv_2(BIF_ALIST_2)
}
if (is_both_small(BIF_ARG_1,BIF_ARG_2)){
Sint ires = signed_val(BIF_ARG_1) / signed_val(BIF_ARG_2);
- if (MY_IS_SSMALL(ires))
+ if (IS_SSMALL(ires))
BIF_RET(make_small(ires));
}
BIF_RET(erts_int_div(BIF_P, BIF_ARG_1, BIF_ARG_2));
@@ -340,8 +340,7 @@ erts_mixed_plus(Process* p, Eterm arg1, Eterm arg2)
switch ((arg2 & _TAG_IMMED1_MASK) >> _TAG_PRIMARY_SIZE) {
case (_TAG_IMMED1_SMALL >> _TAG_PRIMARY_SIZE):
ires = signed_val(arg1) + signed_val(arg2);
- ASSERT(MY_IS_SSMALL(ires) == IS_SSMALL(ires));
- if (MY_IS_SSMALL(ires)) {
+ if (IS_SSMALL(ires)) {
return make_small(ires);
} else {
hp = HAlloc(p, 2);
@@ -486,8 +485,7 @@ erts_mixed_minus(Process* p, Eterm arg1, Eterm arg2)
switch ((arg2 & _TAG_IMMED1_MASK) >> _TAG_PRIMARY_SIZE) {
case (_TAG_IMMED1_SMALL >> _TAG_PRIMARY_SIZE):
ires = signed_val(arg1) - signed_val(arg2);
- ASSERT(MY_IS_SSMALL(ires) == IS_SSMALL(ires));
- if (MY_IS_SSMALL(ires)) {
+ if (IS_SSMALL(ires)) {
return make_small(ires);
} else {
hp = HAlloc(p, 2);
@@ -1181,8 +1179,7 @@ erts_gc_mixed_plus(Process* p, Eterm* reg, Uint live)
switch ((arg2 & _TAG_IMMED1_MASK) >> _TAG_PRIMARY_SIZE) {
case (_TAG_IMMED1_SMALL >> _TAG_PRIMARY_SIZE):
ires = signed_val(arg1) + signed_val(arg2);
- ASSERT(MY_IS_SSMALL(ires) == IS_SSMALL(ires));
- if (MY_IS_SSMALL(ires)) {
+ if (IS_SSMALL(ires)) {
return make_small(ires);
} else {
if (ERTS_NEED_GC(p, 2)) {
@@ -1349,8 +1346,7 @@ erts_gc_mixed_minus(Process* p, Eterm* reg, Uint live)
switch ((arg2 & _TAG_IMMED1_MASK) >> _TAG_PRIMARY_SIZE) {
case (_TAG_IMMED1_SMALL >> _TAG_PRIMARY_SIZE):
ires = signed_val(arg1) - signed_val(arg2);
- ASSERT(MY_IS_SSMALL(ires) == IS_SSMALL(ires));
- if (MY_IS_SSMALL(ires)) {
+ if (IS_SSMALL(ires)) {
return make_small(ires);
} else {
if (ERTS_NEED_GC(p, 2)) {
diff --git a/erts/emulator/beam/erl_bestfit_alloc.c b/erts/emulator/beam/erl_bestfit_alloc.c
index 6173c408e1..85fc4c3a85 100644
--- a/erts/emulator/beam/erl_bestfit_alloc.c
+++ b/erts/emulator/beam/erl_bestfit_alloc.c
@@ -875,7 +875,7 @@ static struct {
static void ERTS_INLINE atom_init(Eterm *atom, char *name)
{
- *atom = am_atom_put(name, strlen(name));
+ *atom = am_atom_put(name, sys_strlen(name));
}
#define AM_INIT(AM) atom_init(&am.AM, #AM)
diff --git a/erts/emulator/beam/erl_bif_binary.c b/erts/emulator/beam/erl_bif_binary.c
index 4cafa499a9..469f6a1ea8 100644
--- a/erts/emulator/beam/erl_bif_binary.c
+++ b/erts/emulator/beam/erl_bif_binary.c
@@ -61,8 +61,6 @@ static Export binary_longest_prefix_trap_export;
static BIF_RETTYPE binary_longest_prefix_trap(BIF_ALIST_3);
static Export binary_longest_suffix_trap_export;
static BIF_RETTYPE binary_longest_suffix_trap(BIF_ALIST_3);
-static Export binary_bin_to_list_trap_export;
-static BIF_RETTYPE binary_bin_to_list_trap(BIF_ALIST_3);
static Export binary_copy_trap_export;
static BIF_RETTYPE binary_copy_trap(BIF_ALIST_2);
static Uint max_loop_limit;
@@ -86,10 +84,6 @@ void erts_init_bif_binary(void)
am_erlang, am_binary_longest_suffix_trap, 3,
&binary_longest_suffix_trap);
- erts_init_trap_export(&binary_bin_to_list_trap_export,
- am_erlang, am_binary_bin_to_list_trap, 3,
- &binary_bin_to_list_trap);
-
erts_init_trap_export(&binary_copy_trap_export,
am_erlang, am_binary_copy_trap, 2,
&binary_copy_trap);
@@ -371,7 +365,7 @@ static ACTrie *create_acdata(MyAllocator *my, Uint len,
acn->d = 0;
acn->final = 0;
acn->h = NULL;
- memset(acn->g, 0, sizeof(ACNode *) * ALPHABET_SIZE);
+ sys_memset(acn->g, 0, sizeof(ACNode *) * ALPHABET_SIZE);
#ifdef HARDDEBUG
act->idc = 0;
acn->id = 0;
@@ -394,7 +388,7 @@ static BMData *create_bmdata(MyAllocator *my, byte *x, Uint len,
init_my_allocator(my, datasize, data);
bmd = my_alloc(my, sizeof(BMData));
bmd->x = my_alloc(my,len);
- memcpy(bmd->x,x,len);
+ sys_memcpy(bmd->x,x,len);
bmd->len = len;
bmd->goodshift = my_alloc(my,sizeof(Uint) * len);
*the_bin = mb;
@@ -439,7 +433,7 @@ static void ac_add_one_pattern(MyAllocator *my, ACTrie *act, byte *x, Uint len)
nn->d = i+1;
nn->h = act->root;
nn->final = 0;
- memset(nn->g, 0, sizeof(ACNode *) * ALPHABET_SIZE);
+ sys_memset(nn->g, 0, sizeof(ACNode *) * ALPHABET_SIZE);
acn->g[x[i]] = nn;
++i;
acn = nn;
@@ -2270,7 +2264,7 @@ static BIF_RETTYPE do_longest_common(Process *p, Eterm list, int direction)
if (cd[i].type == CL_TYPE_HEAP_NOALLOC) {
unsigned char *tmp = cd[i].buff;
cd[i].buff = erts_alloc(ERTS_ALC_T_BINARY_BUFFER, cd[i].bufflen);
- memcpy(cd[i].buff,tmp,cd[i].bufflen);
+ sys_memcpy(cd[i].buff,tmp,cd[i].bufflen);
cd[i].type = CL_TYPE_HEAP;
}
}
@@ -2440,191 +2434,6 @@ BIF_RETTYPE binary_at_2(BIF_ALIST_2)
BIF_ERROR(BIF_P,BADARG);
}
-#define BIN_TO_LIST_OK 0
-#define BIN_TO_LIST_TRAP 1
-/* No badarg, checked before call */
-
-#define BIN_TO_LIST_LOOP_FACTOR 10
-
-static int do_bin_to_list(Process *p, byte *bytes, Uint bit_offs,
- Uint start, Sint *lenp, Eterm *termp)
-{
- Uint reds = get_reds(p, BIN_TO_LIST_LOOP_FACTOR); /* reds can never be 0 */
- Uint len = *lenp;
- Uint loops;
- Eterm *hp;
- Eterm term = *termp;
- Uint n;
-
- ASSERT(reds > 0);
-
- loops = MIN(reds,len);
-
- BUMP_REDS(p, loops / BIN_TO_LIST_LOOP_FACTOR);
-
- hp = HAlloc(p,2*loops);
- while (loops--) {
- --len;
- if (bit_offs) {
- n = ((((Uint) bytes[start+len]) << bit_offs) |
- (((Uint) bytes[start+len+1]) >> (8-bit_offs))) & 0xFF;
- } else {
- n = bytes[start+len];
- }
-
- term = CONS(hp,make_small(n),term);
- hp +=2;
- }
- *termp = term;
- *lenp = len;
- if (len) {
- BUMP_ALL_REDS(p);
- return BIN_TO_LIST_TRAP;
- }
- return BIN_TO_LIST_OK;
-}
-
-
-static BIF_RETTYPE do_trap_bin_to_list(Process *p, Eterm binary,
- Uint start, Sint len, Eterm sofar)
-{
- Eterm *hp;
- Eterm blob;
-
- hp = HAlloc(p,3);
- hp[0] = make_pos_bignum_header(2);
- hp[1] = start;
- hp[2] = (Uint) len;
- blob = make_big(hp);
- BIF_TRAP3(&binary_bin_to_list_trap_export, p, binary, blob, sofar);
-}
-
-static BIF_RETTYPE binary_bin_to_list_trap(BIF_ALIST_3)
-{
- Eterm *ptr;
- Uint start;
- Sint len;
- byte *bytes;
- Uint bit_offs;
- Uint bit_size;
- Eterm res = BIF_ARG_3;
-
- ptr = big_val(BIF_ARG_2);
- start = ptr[1];
- len = (Sint) ptr[2];
-
- ERTS_GET_BINARY_BYTES(BIF_ARG_1,bytes,bit_offs,bit_size);
- if (do_bin_to_list(BIF_P, bytes, bit_offs, start, &len, &res) ==
- BIN_TO_LIST_OK) {
- BIF_RET(res);
- }
- return do_trap_bin_to_list(BIF_P,BIF_ARG_1,start,len,res);
-}
-
-static BIF_RETTYPE binary_bin_to_list_common(Process *p,
- Eterm bin,
- Eterm epos,
- Eterm elen)
-{
- Uint pos;
- Sint len;
- size_t sz;
- byte *bytes;
- Uint bit_offs;
- Uint bit_size;
- Eterm res = NIL;
-
- if (is_not_binary(bin)) {
- goto badarg;
- }
- if (!term_to_Uint(epos, &pos)) {
- goto badarg;
- }
- if (!term_to_Sint(elen, &len)) {
- goto badarg;
- }
- if (len < 0) {
- Uint lentmp = -(Uint)len;
- /* overflow */
- if ((Sint)lentmp < 0) {
- goto badarg;
- }
- len = lentmp;
- if (len > pos) {
- goto badarg;
- }
- pos -= len;
- }
- /* overflow */
- if ((pos + len) < pos || (len > 0 && (pos + len) == pos)) {
- goto badarg;
- }
- sz = binary_size(bin);
-
- if (pos+len > sz) {
- goto badarg;
- }
- ERTS_GET_BINARY_BYTES(bin,bytes,bit_offs,bit_size);
- if (bit_size != 0) {
- goto badarg;
- }
- if(do_bin_to_list(p, bytes, bit_offs, pos, &len, &res) ==
- BIN_TO_LIST_OK) {
- BIF_RET(res);
- }
- return do_trap_bin_to_list(p,bin,pos,len,res);
-
- badarg:
- BIF_ERROR(p,BADARG);
-}
-
-BIF_RETTYPE binary_bin_to_list_3(BIF_ALIST_3)
-{
- return binary_bin_to_list_common(BIF_P,BIF_ARG_1,BIF_ARG_2,BIF_ARG_3);
-}
-
-BIF_RETTYPE binary_bin_to_list_2(BIF_ALIST_2)
-{
- Eterm *tp;
-
- if (is_not_tuple(BIF_ARG_2)) {
- goto badarg;
- }
- tp = tuple_val(BIF_ARG_2);
- if (arityval(*tp) != 2) {
- goto badarg;
- }
- return binary_bin_to_list_common(BIF_P,BIF_ARG_1,tp[1],tp[2]);
- badarg:
- BIF_ERROR(BIF_P,BADARG);
-}
-
-BIF_RETTYPE binary_bin_to_list_1(BIF_ALIST_1)
-{
- Uint pos = 0;
- Sint len;
- byte *bytes;
- Uint bit_offs;
- Uint bit_size;
- Eterm res = NIL;
-
- if (is_not_binary(BIF_ARG_1)) {
- goto badarg;
- }
- len = binary_size(BIF_ARG_1);
- ERTS_GET_BINARY_BYTES(BIF_ARG_1,bytes,bit_offs,bit_size);
- if (bit_size != 0) {
- goto badarg;
- }
- if(do_bin_to_list(BIF_P, bytes, bit_offs, pos, &len, &res) ==
- BIN_TO_LIST_OK) {
- BIF_RET(res);
- }
- return do_trap_bin_to_list(BIF_P,BIF_ARG_1,pos,len,res);
- badarg:
- BIF_ERROR(BIF_P,BADARG);
-}
-
HIPE_WRAPPER_BIF_DISABLE_GC(binary_list_to_bin, 1)
BIF_RETTYPE binary_list_to_bin_1(BIF_ALIST_1)
@@ -2747,7 +2556,7 @@ static BIF_RETTYPE do_binary_copy(Process *p, Eterm bin, Eterm en)
0);
if (!(cbs->temp_alloc)) { /* alignment not needed, need to copy */
byte *tmp = erts_alloc(ERTS_ALC_T_BINARY_BUFFER,size);
- memcpy(tmp,cbs->source,size);
+ sys_memcpy(tmp,cbs->source,size);
cbs->source = tmp;
cbs->source_type = BC_TYPE_HEAP;
} else {
@@ -2760,7 +2569,7 @@ static BIF_RETTYPE do_binary_copy(Process *p, Eterm bin, Eterm en)
pos = 0;
i = 0;
while (pos < reds) {
- memcpy(t+pos,cbs->source, size);
+ sys_memcpy(t+pos,cbs->source, size);
pos += size;
++i;
}
@@ -2785,7 +2594,7 @@ static BIF_RETTYPE do_binary_copy(Process *p, Eterm bin, Eterm en)
}
pos = 0;
while (pos < target_size) {
- memcpy(t+pos,source, size);
+ sys_memcpy(t+pos,source, size);
pos += size;
}
erts_free_aligned_binary_bytes(temp_alloc);
@@ -2815,7 +2624,7 @@ BIF_RETTYPE binary_copy_trap(BIF_ALIST_2)
if ((n-1) * size >= reds) {
Uint i = 0;
while ((pos - opos) < reds) {
- memcpy(t+pos,cbs->source, size);
+ sys_memcpy(t+pos,cbs->source, size);
pos += size;
++i;
}
@@ -2825,28 +2634,21 @@ BIF_RETTYPE binary_copy_trap(BIF_ALIST_2)
BIF_TRAP2(&binary_copy_trap_export, BIF_P, BIF_ARG_1, BIF_ARG_2);
} else {
Binary *save;
- ProcBin* pb;
+ Eterm resbin;
Uint target_size = cbs->result->orig_size;
while (pos < target_size) {
- memcpy(t+pos,cbs->source, size);
+ sys_memcpy(t+pos,cbs->source, size);
pos += size;
}
- save = cbs->result;
+ save = cbs->result;
cbs->result = NULL;
cleanup_copy_bin_state(mb); /* now cbs is dead */
- pb = (ProcBin *) HAlloc(BIF_P, PROC_BIN_SIZE);
- pb->thing_word = HEADER_PROC_BIN;
- pb->size = target_size;
- pb->next = MSO(BIF_P).first;
- MSO(BIF_P).first = (struct erl_off_heap_header*) pb;
- pb->val = save;
- pb->bytes = t;
- pb->flags = 0;
-
- OH_OVERHEAD(&(MSO(BIF_P)), target_size / sizeof(Eterm));
- BUMP_REDS(BIF_P,(pos - opos) / BINARY_COPY_LOOP_FACTOR);
- BIF_RET(make_binary(pb));
+ resbin = erts_build_proc_bin(&MSO(BIF_P),
+ HAlloc(BIF_P, PROC_BIN_SIZE),
+ save);
+ BUMP_REDS(BIF_P,(pos - opos) / BINARY_COPY_LOOP_FACTOR);
+ BIF_RET(resbin);
}
}
@@ -3227,7 +3029,7 @@ static void dump_bm_data(BMData *bm)
static void dump_ac_node(ACNode *node, int indent, int ch) {
int i;
char *spaces = erts_alloc(ERTS_ALC_T_TMP, 10 * indent + 1);
- memset(spaces,' ',10*indent);
+ sys_memset(spaces,' ',10*indent);
spaces[10*indent] = '\0';
erts_printf("%s-> %c\n",spaces,ch);
erts_printf("%sId: %u\n",spaces,(unsigned) node->id);
diff --git a/erts/emulator/beam/erl_bif_chksum.c b/erts/emulator/beam/erl_bif_chksum.c
index 9417803e14..9095bcd380 100644
--- a/erts/emulator/beam/erl_bif_chksum.c
+++ b/erts/emulator/beam/erl_bif_chksum.c
@@ -516,7 +516,7 @@ md5_2(BIF_ALIST_2)
/* No need to check context, this function cannot be called with unaligned
or badly sized context as it's always trapped to. */
bytes = binary_bytes(BIF_ARG_1);
- memcpy(&context,bytes,sizeof(MD5_CTX));
+ sys_memcpy(&context,bytes,sizeof(MD5_CTX));
rest = do_chksum(&md5_wrap,BIF_P,BIF_ARG_2,100,(void *) &context,&res,
&err);
if (err != 0) {
@@ -564,7 +564,7 @@ md5_update_2(BIF_ALIST_2)
erts_free_aligned_binary_bytes(temp_alloc);
BIF_ERROR(BIF_P, BADARG);
}
- memcpy(&context,bytes,sizeof(MD5_CTX));
+ sys_memcpy(&context,bytes,sizeof(MD5_CTX));
erts_free_aligned_binary_bytes(temp_alloc);
rest = do_chksum(&md5_wrap,BIF_P,BIF_ARG_2,100,(void *) &context,&res,
&err);
@@ -599,7 +599,7 @@ md5_final_1(BIF_ALIST_1)
goto error;
}
bin = erts_new_heap_binary(BIF_P, (byte *)NULL, 16, &result);
- memcpy(&ctx_copy, context, sizeof(MD5_CTX));
+ sys_memcpy(&ctx_copy, context, sizeof(MD5_CTX));
erts_free_aligned_binary_bytes(temp_alloc);
MD5Final(result, &ctx_copy);
BIF_RET(bin);
diff --git a/erts/emulator/beam/erl_bif_ddll.c b/erts/emulator/beam/erl_bif_ddll.c
index f673ef3194..294bce115f 100644
--- a/erts/emulator/beam/erl_bif_ddll.c
+++ b/erts/emulator/beam/erl_bif_ddll.c
@@ -1412,7 +1412,7 @@ static Eterm copy_ref(Eterm ref, Eterm *hp)
ErtsORefThing *ptr;
ASSERT(is_internal_ordinary_ref(ref));
ptr = ordinary_ref_thing_ptr(ref);
- memcpy(hp, ptr, sizeof(ErtsORefThing));
+ sys_memcpy(hp, ptr, sizeof(ErtsORefThing));
return (make_internal_ref(hp));
}
@@ -1454,9 +1454,9 @@ static void set_driver_reloading(DE_Handle *dh, Process *proc, char *path, char
dh->procs = p;
dh->status = ERL_DE_RELOAD;
dh->reload_full_path = erts_alloc(ERTS_ALC_T_DDLL_HANDLE, sys_strlen(path) + 1);
- strcpy(dh->reload_full_path,path);
+ sys_strcpy(dh->reload_full_path,path);
dh->reload_driver_name = erts_alloc(ERTS_ALC_T_DDLL_HANDLE, sys_strlen(name) + 1);
- strcpy(dh->reload_driver_name,name);
+ sys_strcpy(dh->reload_driver_name,name);
dh->reload_flags = flags;
}
@@ -1501,10 +1501,11 @@ static int do_load_driver_entry(DE_Handle *dh, char *path, char *name)
goto error;
}
- if (strcmp(name, dp->driver_name) != 0) {
+ if (sys_strcmp(name, dp->driver_name) != 0) {
res = ERL_DE_LOAD_ERROR_BAD_NAME;
goto error;
}
+
erts_atomic_init_nob(&(dh->refc), (erts_aint_t) 0);
erts_atomic32_init_nob(&dh->port_count, 0);
dh->full_path = erts_alloc(ERTS_ALC_T_DDLL_HANDLE, sys_strlen(path) + 1);
@@ -1512,7 +1513,7 @@ static int do_load_driver_entry(DE_Handle *dh, char *path, char *name)
dh->flags = 0;
dh->status = ERL_DE_OK;
- if (erts_add_driver_entry(dp, dh, 1) != 0 /* io.c */) {
+ if (erts_add_driver_entry(dp, dh, 1, 1) != 0 /* io.c */) {
/*
* The init in the driver struct did not return 0
*/
@@ -1799,7 +1800,7 @@ static char *pick_list_or_atom(Eterm name_term)
goto error;
}
name = erts_alloc(ERTS_ALC_T_DDLL_TMP_BUF, ap->len + 1);
- memcpy(name,ap->name,ap->len);
+ sys_memcpy(name,ap->name,ap->len);
name[ap->len] = '\0';
} else {
if (erts_iolist_size(name_term, &name_len)) {
@@ -1870,7 +1871,7 @@ static erts_driver_t *lookup_driver(char *name)
{
erts_driver_t *drv;
assert_drv_list_locked();
- for (drv = driver_list; drv != NULL && strcmp(drv->name, name); drv = drv->next)
+ for (drv = driver_list; drv != NULL && sys_strcmp(drv->name, name); drv = drv->next)
;
return drv;
}
diff --git a/erts/emulator/beam/erl_bif_info.c b/erts/emulator/beam/erl_bif_info.c
index 17c936f041..8687aefd78 100644
--- a/erts/emulator/beam/erl_bif_info.c
+++ b/erts/emulator/beam/erl_bif_info.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1999-2017. All Rights Reserved.
+ * Copyright Ericsson AB 1999-2018. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -38,7 +38,7 @@
#include "erl_message.h"
#include "erl_binary.h"
#include "erl_db.h"
-#include "erl_instrument.h"
+#include "erl_mtrace.h"
#include "dist.h"
#include "erl_gc.h"
#include "erl_cpu_topology.h"
@@ -50,6 +50,8 @@
#define ERTS_PTAB_WANT_DEBUG_FUNCS__
#include "erl_ptab.h"
#include "erl_time.h"
+#include "erl_proc_sig_queue.h"
+#include "erl_alloc_util.h"
#ifdef HIPE
#include "hipe_arch.h"
#endif
@@ -72,6 +74,9 @@ static Export *gather_msacc_res_trap;
static Export *gather_gc_info_res_trap;
static Export *gather_system_check_res_trap;
+static Export *is_process_alive_trap;
+
+
#define DECL_AM(S) Eterm AM_ ## S = am_atom_put(#S, sizeof(#S) - 1)
static char otp_version[] = ERLANG_OTP_VERSION;
@@ -151,8 +156,10 @@ static Eterm os_type_tuple;
static Eterm os_version_tuple;
static Eterm
-current_function(Process* p, Process* rp, Eterm** hpp, int full_info);
-static Eterm current_stacktrace(Process* p, Process* rp, Eterm** hpp);
+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
bld_bin_list(Uint **hpp, Uint *szp, ErlOffHeap* oh)
@@ -211,21 +218,27 @@ bld_magic_ref_bin_list(Uint **hpp, Uint *szp, ErlOffHeap* oh)
make_monitor_list:
returns a list of records..
-record(erl_monitor, {
- type, % MON_ORIGIN or MON_TARGET (1 or 3)
- ref,
+ type, % process | port | time_offset | dist_process | resource
+ % | node | nodes | suspend
+ dir, % origin | target
+ ref, % reference or []
pid, % Process or nodename
- name % registered name or []
+ extra % registered name, integer or []
}).
*/
static void do_calc_mon_size(ErtsMonitor *mon, void *vpsz)
{
+ ErtsMonitorData *mdp = erts_monitor_to_data(mon);
Uint *psz = vpsz;
- *psz += NC_HEAP_SIZE(mon->ref);
- *psz += (mon->type == MON_NIF_TARGET ?
- erts_resource_ref_size(mon->u.resource) :
- (is_immed(mon->u.pid) ? 0 : NC_HEAP_SIZE(mon->u.pid)));
- *psz += 8; /* CONS + 5-tuple */
+ *psz += is_immed(mdp->ref) ? 0 : NC_HEAP_SIZE(mdp->ref);
+
+ if (mon->type == ERTS_MON_TYPE_RESOURCE && erts_monitor_is_target(mon))
+ *psz += erts_resource_ref_size(mon->other.ptr);
+ else
+ *psz += is_immed(mon->other.item) ? 0 : NC_HEAP_SIZE(mon->other.item);
+
+ *psz += 9; /* CONS + 6-tuple */
}
typedef struct {
@@ -237,35 +250,97 @@ typedef struct {
static void do_make_one_mon_element(ErtsMonitor *mon, void * vpmlc)
{
+ ErtsMonitorData *mdp = erts_monitor_to_data(mon);
MonListContext *pmlc = vpmlc;
- Eterm tup;
- Eterm r = STORE_NC(&(pmlc->hp), &MSO(pmlc->p), mon->ref);
- Eterm p = (mon->type == MON_NIF_TARGET ?
- erts_bld_resource_ref(&(pmlc->hp), &MSO(pmlc->p), mon->u.resource)
- : (is_immed(mon->u.pid) ? mon->u.pid
- : STORE_NC(&(pmlc->hp), &MSO(pmlc->p), mon->u.pid)));
- tup = TUPLE5(pmlc->hp, pmlc->tag, make_small(mon->type), r, p, mon->name);
- pmlc->hp += 6;
+ Eterm tup, t, d, r, p, x;
+
+ r = is_immed(mdp->ref) ? mdp->ref : STORE_NC(&(pmlc->hp), &MSO(pmlc->p), mdp->ref);
+ if (mon->type == ERTS_MON_TYPE_RESOURCE && erts_monitor_is_target(mon))
+ p = erts_bld_resource_ref(&(pmlc->hp), &MSO(pmlc->p), mon->other.ptr);
+ else
+ p = (is_immed(mon->other.item)
+ ? mon->other.item
+ : STORE_NC(&(pmlc->hp), &MSO(pmlc->p), mon->other.item));
+
+ if (mon->flags & ERTS_ML_FLG_NAME)
+ x = ((ErtsMonitorDataExtended *) mdp)->u.name;
+ else if (erts_monitor_is_target(mon))
+ x = NIL;
+ else if (mon->type == ERTS_MON_TYPE_NODE || mon->type == ERTS_MON_TYPE_NODES)
+ x = make_small(((ErtsMonitorDataExtended *) mdp)->u.refc);
+ else
+ x = NIL;
+
+ switch (mon->type) {
+ case ERTS_MON_TYPE_PROC:
+ t = am_process;
+ break;
+ case ERTS_MON_TYPE_PORT:
+ t = am_port;
+ break;
+ case ERTS_MON_TYPE_TIME_OFFSET:
+ t = am_time_offset;
+ break;
+ case ERTS_MON_TYPE_DIST_PROC: {
+ ERTS_DECL_AM(dist_process);
+ t = AM_dist_process;
+ break;
+ }
+ case ERTS_MON_TYPE_RESOURCE: {
+ ERTS_DECL_AM(resource);
+ t = AM_resource;
+ break;
+ }
+ case ERTS_MON_TYPE_NODE:
+ t = am_node;
+ break;
+ case ERTS_MON_TYPE_NODES: {
+ ERTS_DECL_AM(nodes);
+ t = AM_nodes;
+ break;
+ }
+ case ERTS_MON_TYPE_SUSPEND:
+ t = am_suspend;
+ break;
+ default:
+ ERTS_INTERNAL_ERROR("Unknown monitor type");
+ t = am_error;
+ break;
+ }
+ if (erts_monitor_is_target(mon)) {
+ ERTS_DECL_AM(target);
+ d = AM_target;
+ }
+ else {
+ ERTS_DECL_AM(origin);
+ d = AM_origin;
+ }
+ tup = TUPLE6(pmlc->hp, pmlc->tag, t, d, r, p, x);
+ pmlc->hp += 7;
pmlc->res = CONS(pmlc->hp, tup, pmlc->res);
pmlc->hp += 2;
}
static Eterm
-make_monitor_list(Process *p, ErtsMonitor *root)
+make_monitor_list(Process *p, int tree, ErtsMonitor *root, Eterm tail)
{
DECL_AM(erl_monitor);
Uint sz = 0;
MonListContext mlc;
+ void (*foreach)(ErtsMonitor *,
+ void (*)(ErtsMonitor *, void *),
+ void *);
- erts_doforall_monitors(root, &do_calc_mon_size, &sz);
- if (sz == 0) {
- return NIL;
- }
+ foreach = tree ? erts_monitor_tree_foreach : erts_monitor_list_foreach;
+
+ (*foreach)(root, do_calc_mon_size, &sz);
+ if (sz == 0)
+ return tail;
mlc.p = p;
mlc.hp = HAlloc(p,sz);
- mlc.res = NIL;
+ mlc.res = tail;
mlc.tag = AM_erl_monitor;
- erts_doforall_monitors(root, &do_make_one_mon_element, &mlc);
+ (*foreach)(root, do_make_one_mon_element, &mlc);
return mlc.res;
}
@@ -273,20 +348,22 @@ make_monitor_list(Process *p, ErtsMonitor *root)
make_link_list:
returns a list of records..
-record(erl_link, {
- type, % LINK_NODE or LINK_PID (1 or 3)
- pid, % Process or nodename
- targets % List of erl_link's or nil
+ type, % process | port | dist_process
+ pid, % Process or port
+ id % (address)
}).
*/
-static void do_calc_lnk_size(ErtsLink *lnk, void *vpsz)
+static void calc_lnk_size(ErtsLink *lnk, void *vpsz)
{
Uint *psz = vpsz;
- *psz += is_immed(lnk->pid) ? 0 : NC_HEAP_SIZE(lnk->pid);
- if (lnk->type != LINK_NODE && ERTS_LINK_ROOT(lnk) != NULL) {
- /* Node links use this pointer as ref counter... */
- erts_doforall_links(ERTS_LINK_ROOT(lnk),&do_calc_lnk_size,vpsz);
- }
+ Uint sz = 0;
+ ErtsLinkData *ldp = erts_link_to_data(lnk);
+
+ (void) erts_bld_uword(NULL, &sz, (UWord) ldp);
+
+ *psz += sz;
+ *psz += is_immed(lnk->other.item) ? 0 : size_object(lnk->other.item);
*psz += 7; /* CONS + 4-tuple */
}
@@ -297,37 +374,58 @@ typedef struct {
Eterm tag;
} LnkListContext;
-static void do_make_one_lnk_element(ErtsLink *lnk, void * vpllc)
+static void make_one_lnk_element(ErtsLink *lnk, void * vpllc)
{
LnkListContext *pllc = vpllc;
- Eterm tup;
- Eterm old_res, targets = NIL;
- Eterm p = (is_immed(lnk->pid)
- ? lnk->pid
- : STORE_NC(&(pllc->hp), &MSO(pllc->p), lnk->pid));
- if (lnk->type == LINK_NODE) {
- targets = make_small(ERTS_LINK_REFC(lnk));
- } else if (ERTS_LINK_ROOT(lnk) != NULL) {
- old_res = pllc->res;
- pllc->res = NIL;
- erts_doforall_links(ERTS_LINK_ROOT(lnk),&do_make_one_lnk_element, vpllc);
- targets = pllc->res;
- pllc->res = old_res;
- }
- tup = TUPLE4(pllc->hp, pllc->tag, make_small(lnk->type), p, targets);
+ Eterm tup, t, pid, id;
+ ErtsLinkData *ldp = erts_link_to_data(lnk);
+
+ id = erts_bld_uword(&pllc->hp, NULL, (UWord) ldp);
+
+ if (is_immed(lnk->other.item))
+ pid = lnk->other.item;
+ else {
+ Uint sz = size_object(lnk->other.item);
+ pid = copy_struct(lnk->other.item, sz, &(pllc->hp), &MSO(pllc->p));
+ }
+
+ switch (lnk->type) {
+ case ERTS_LNK_TYPE_PROC:
+ t = am_process;
+ break;
+ case ERTS_LNK_TYPE_PORT:
+ t = am_port;
+ break;
+ case ERTS_LNK_TYPE_DIST_PROC: {
+ ERTS_DECL_AM(dist_process);
+ t = AM_dist_process;
+ break;
+ }
+ default:
+ ERTS_INTERNAL_ERROR("Unkown link type");
+ t = am_undefined;
+ break;
+ }
+
+ tup = TUPLE4(pllc->hp, pllc->tag, t, pid, id);
pllc->hp += 5;
pllc->res = CONS(pllc->hp, tup, pllc->res);
pllc->hp += 2;
}
static Eterm
-make_link_list(Process *p, ErtsLink *root, Eterm tail)
+make_link_list(Process *p, int tree, ErtsLink *root, Eterm tail)
{
DECL_AM(erl_link);
Uint sz = 0;
LnkListContext llc;
+ void (*foreach)(ErtsLink *,
+ void (*)(ErtsLink *, void *),
+ void *);
- erts_doforall_links(root, &do_calc_lnk_size, &sz);
+ foreach = tree ? erts_link_tree_foreach : erts_link_list_foreach;
+
+ (*foreach)(root, calc_lnk_size, (void *) &sz);
if (sz == 0) {
return tail;
}
@@ -335,7 +433,7 @@ make_link_list(Process *p, ErtsLink *root, Eterm tail)
llc.hp = HAlloc(p,sz);
llc.res = tail;
llc.tag = AM_erl_link;
- erts_doforall_links(root, &do_make_one_lnk_element, &llc);
+ (*foreach)(root, make_one_lnk_element, (void *) &llc);
return llc.res;
}
@@ -381,6 +479,8 @@ typedef struct {
Eterm term;
ErtsResource* resource;
}entity;
+ int named;
+ Uint16 type;
Eterm node;
/* pid is actual target being monitored, no matter pid/port or name */
Eterm pid;
@@ -423,78 +523,103 @@ static void collect_one_link(ErtsLink *lnk, void *vmicp)
{
MonitorInfoCollection *micp = vmicp;
EXTEND_MONITOR_INFOS(micp);
- if (!(lnk->type == LINK_PID)) {
- return;
- }
- micp->mi[micp->mi_i].entity.term = lnk->pid;
- micp->sz += 2 + NC_HEAP_SIZE(lnk->pid);
+ micp->mi[micp->mi_i].entity.term = lnk->other.item;
+ micp->sz += 2 + NC_HEAP_SIZE(lnk->other.item);
micp->mi_i++;
}
static void collect_one_origin_monitor(ErtsMonitor *mon, void *vmicp)
{
- MonitorInfoCollection *micp = vmicp;
+ if (erts_monitor_is_origin(mon)) {
+ MonitorInfoCollection *micp = vmicp;
- if (mon->type != MON_ORIGIN) {
- return;
- }
- EXTEND_MONITOR_INFOS(micp);
- if (is_atom(mon->u.pid)) { /* external by name */
- micp->mi[micp->mi_i].entity.term = mon->name;
- micp->mi[micp->mi_i].node = mon->u.pid;
- micp->sz += 3; /* need one 2-tuple */
- } else if (is_external_pid(mon->u.pid)) { /* external by pid */
- micp->mi[micp->mi_i].entity.term = mon->u.pid;
- micp->mi[micp->mi_i].node = NIL;
- micp->sz += NC_HEAP_SIZE(mon->u.pid);
- } else if (!is_nil(mon->name)) { /* internal by name */
- micp->mi[micp->mi_i].entity.term = mon->name;
- micp->mi[micp->mi_i].node = erts_this_dist_entry->sysname;
- micp->sz += 3; /* need one 2-tuple */
- } else { /* internal by pid */
- micp->mi[micp->mi_i].entity.term = mon->u.pid;
- micp->mi[micp->mi_i].node = NIL;
- /* no additional heap space needed */
- }
-
- /* have always pid at hand, to assist with figuring out if its a port or
- * a process, when we monitored by name and process_info is requested.
- * See: erl_bif_info.c:process_info_aux section for am_monitors */
- micp->mi[micp->mi_i].pid = mon->u.pid;
+ EXTEND_MONITOR_INFOS(micp);
+
+ micp->mi[micp->mi_i].type = mon->type;
+
+ switch (mon->type) {
+ case ERTS_MON_TYPE_PROC:
+ case ERTS_MON_TYPE_PORT:
+ case ERTS_MON_TYPE_DIST_PROC:
+ case ERTS_MON_TYPE_TIME_OFFSET:
+ if (!(mon->flags & ERTS_ML_FLG_NAME)) {
+ micp->mi[micp->mi_i].named = 0;
+ micp->mi[micp->mi_i].entity.term = mon->other.item;
+ micp->mi[micp->mi_i].node = NIL;
+ if (is_not_atom(mon->other.item))
+ micp->sz += NC_HEAP_SIZE(mon->other.item);
+ }
+ else {
+ ErtsMonitorDataExtended *mdep;
+ micp->mi[micp->mi_i].named = !0;
+ mdep = (ErtsMonitorDataExtended *) erts_monitor_to_data(mon);
+ micp->mi[micp->mi_i].entity.term = mdep->u.name;
+ if (mdep->dist)
+ micp->mi[micp->mi_i].node = mdep->dist->nodename;
+ else
+ micp->mi[micp->mi_i].node = erts_this_dist_entry->sysname;
+ micp->sz += 3; /* need one 2-tuple */
+ }
- micp->mi_i++;
- micp->sz += 2 + 3; /* For a cons cell and a 2-tuple */
+ /* have always pid at hand, to assist with figuring out if its a port or
+ * a process, when we monitored by name and process_info is requested.
+ * See: erl_bif_info.c:process_info_aux section for am_monitors */
+ micp->mi[micp->mi_i].pid = mon->other.item;
+
+ micp->mi_i++;
+ micp->sz += 2 + 3; /* For a cons cell and a 2-tuple */
+ break;
+ default:
+ break;
+ }
+ }
}
static void collect_one_target_monitor(ErtsMonitor *mon, void *vmicp)
{
MonitorInfoCollection *micp = vmicp;
- if (mon->type != MON_TARGET && mon->type != MON_NIF_TARGET) {
- return;
- }
+ if (erts_monitor_is_target(mon)) {
- EXTEND_MONITOR_INFOS(micp);
+ EXTEND_MONITOR_INFOS(micp);
+ micp->mi[micp->mi_i].type = mon->type;
+ micp->mi[micp->mi_i].named = !!(mon->flags & ERTS_ML_FLG_NAME);
+ switch (mon->type) {
+
+ case ERTS_MON_TYPE_PROC:
+ case ERTS_MON_TYPE_PORT:
+ case ERTS_MON_TYPE_DIST_PROC:
+
+ micp->mi[micp->mi_i].entity.term = mon->other.item;
+ micp->mi[micp->mi_i].node = NIL;
+ micp->sz += NC_HEAP_SIZE(mon->other.item);
+
+ micp->sz += 2; /* cons */;
+ micp->mi_i++;
+ break;
+
+ case ERTS_MON_TYPE_RESOURCE:
+
+ micp->mi[micp->mi_i].entity.resource = mon->other.ptr;
+ micp->mi[micp->mi_i].node = NIL;
+ micp->sz += erts_resource_ref_size(mon->other.ptr);
+
+ micp->sz += 2; /* cons */;
+ micp->mi_i++;
+ break;
+
+ default:
+ break;
+ }
- if (mon->type == MON_NIF_TARGET) {
- micp->mi[micp->mi_i].entity.resource = mon->u.resource;
- micp->mi[micp->mi_i].node = make_small(MON_NIF_TARGET);
- micp->sz += erts_resource_ref_size(mon->u.resource);
- }
- else {
- micp->mi[micp->mi_i].entity.term = mon->u.pid;
- micp->mi[micp->mi_i].node = NIL;
- micp->sz += NC_HEAP_SIZE(mon->u.pid);
}
- micp->sz += 2; /* cons */;
- micp->mi_i++;
}
typedef struct {
Process *c_p;
ErtsProcLocks c_p_locks;
- ErtsSuspendMonitor **smi;
+ ErtsMonitorSuspend **smi;
Uint smi_i;
Uint smi_max;
int sz;
@@ -517,10 +642,10 @@ do { \
(SMICP)->smi, \
((SMICP)->smi_max \
+ ERTS_SMI_INC) \
- * sizeof(ErtsSuspendMonitor *)) \
+ * sizeof(ErtsMonitorSuspend *)) \
: erts_alloc(ERTS_ALC_T_TMP, \
ERTS_SMI_INC \
- * sizeof(ErtsSuspendMonitor *))); \
+ * sizeof(ErtsMonitorSuspend *))); \
(SMICP)->smi_max += ERTS_SMI_INC; \
} \
} while (0)
@@ -533,12 +658,13 @@ do { \
} while (0)
static void
-collect_one_suspend_monitor(ErtsSuspendMonitor *smon, void *vsmicp)
+collect_one_suspend_monitor(ErtsMonitor *mon, void *vsmicp)
{
+ ErtsMonitorSuspend *smon = erts_monitor_suspend(mon);
ErtsSuspendMonitorInfoCollection *smicp = vsmicp;
Process *suspendee = erts_pid2proc(smicp->c_p,
smicp->c_p_locks,
- smon->pid,
+ mon->other.item,
0);
if (suspendee) { /* suspendee is alive */
Sint a, p;
@@ -570,125 +696,222 @@ collect_one_suspend_monitor(ErtsSuspendMonitor *smon, void *vsmicp)
* process_info/[1,2]
*/
-#define ERTS_PI_FAIL_TYPE_BADARG 0
-#define ERTS_PI_FAIL_TYPE_YIELD 1
-#define ERTS_PI_FAIL_TYPE_AWAIT_EXIT 2
-
-static ERTS_INLINE ErtsProcLocks
-pi_locks(Eterm info)
-{
- switch (info) {
- case am_status:
- case am_priority:
- case am_trap_exit:
- return ERTS_PROC_LOCK_STATUS;
- case am_links:
- case am_monitors:
- case am_monitored_by:
- case am_suspending:
- return ERTS_PROC_LOCK_LINK;
- case am_messages:
- case am_message_queue_len:
- case am_total_heap_size:
- return ERTS_PROC_LOCK_MAIN|ERTS_PROC_LOCK_MSGQ;
- case am_memory:
- return ERTS_PROC_LOCK_MAIN|ERTS_PROC_LOCK_LINK|ERTS_PROC_LOCK_MSGQ;
- default:
- return ERTS_PROC_LOCK_MAIN;
- }
-}
-
/*
* All valid process_info arguments.
*/
-static Eterm pi_args[] = {
- am_registered_name,
- am_current_function,
- am_initial_call,
- am_status,
- am_messages,
- am_message_queue_len,
- am_links,
- am_monitors,
- am_monitored_by,
- am_dictionary,
- am_trap_exit,
- am_error_handler,
- am_heap_size,
- am_stack_size,
- am_memory,
- am_garbage_collection,
- am_group_leader,
- am_reductions,
- am_priority,
- am_trace,
- am_binary,
- am_sequential_trace_token,
- am_catchlevel,
- am_backtrace,
- am_last_calls,
- am_total_heap_size,
- am_suspending,
- am_min_heap_size,
- am_min_bin_vheap_size,
- am_max_heap_size,
- am_current_location,
- am_current_stacktrace,
- am_message_queue_data,
- am_garbage_collection_info,
- am_magic_ref
+
+#define ERTS_PI_IX_REGISTERED_NAME 0
+#define ERTS_PI_IX_CURRENT_FUNCTION 1
+#define ERTS_PI_IX_INITIAL_CALL 2
+#define ERTS_PI_IX_STATUS 3
+#define ERTS_PI_IX_MESSAGES 4
+#define ERTS_PI_IX_MESSAGE_QUEUE_LEN 5
+#define ERTS_PI_IX_LINKS 6
+#define ERTS_PI_IX_MONITORS 7
+#define ERTS_PI_IX_MONITORED_BY 8
+#define ERTS_PI_IX_DICTIONARY 9
+#define ERTS_PI_IX_TRAP_EXIT 10
+#define ERTS_PI_IX_ERROR_HANDLER 11
+#define ERTS_PI_IX_HEAP_SIZE 12
+#define ERTS_PI_IX_STACK_SIZE 13
+#define ERTS_PI_IX_MEMORY 14
+#define ERTS_PI_IX_GARBAGE_COLLECTION 15
+#define ERTS_PI_IX_GROUP_LEADER 16
+#define ERTS_PI_IX_REDUCTIONS 17
+#define ERTS_PI_IX_PRIORITY 18
+#define ERTS_PI_IX_TRACE 19
+#define ERTS_PI_IX_BINARY 20
+#define ERTS_PI_IX_SEQUENTIAL_TRACE_TOKEN 21
+#define ERTS_PI_IX_CATCHLEVEL 22
+#define ERTS_PI_IX_BACKTRACE 23
+#define ERTS_PI_IX_LAST_CALLS 24
+#define ERTS_PI_IX_TOTAL_HEAP_SIZE 25
+#define ERTS_PI_IX_SUSPENDING 26
+#define ERTS_PI_IX_MIN_HEAP_SIZE 27
+#define ERTS_PI_IX_MIN_BIN_VHEAP_SIZE 28
+#define ERTS_PI_IX_MAX_HEAP_SIZE 29
+#define ERTS_PI_IX_CURRENT_LOCATION 30
+#define ERTS_PI_IX_CURRENT_STACKTRACE 31
+#define ERTS_PI_IX_MESSAGE_QUEUE_DATA 32
+#define ERTS_PI_IX_GARBAGE_COLLECTION_INFO 33
+#define ERTS_PI_IX_MAGIC_REF 34
+#define ERTS_PI_IX_FULLSWEEP_AFTER 35
+
+#define ERTS_PI_FLAG_SINGELTON (1 << 0)
+#define ERTS_PI_FLAG_ALWAYS_WRAP (1 << 1)
+#define ERTS_PI_FLAG_WANT_MSGS (1 << 2)
+#define ERTS_PI_FLAG_NEED_MSGQ_LEN (1 << 3)
+#define ERTS_PI_FLAG_FORCE_SIG_SEND (1 << 4)
+#define ERTS_PI_FLAG_REQUEST_FOR_OTHER (1 << 5)
+
+#define ERTS_PI_UNRESERVE(RS, SZ) \
+ (ASSERT((RS) >= (SZ)), (RS) -= (SZ))
+
+
+typedef struct {
+ Eterm name;
+ Uint reserve_size;
+ int flags;
+ ErtsProcLocks locks;
+} ErtsProcessInfoArgs;
+
+static ErtsProcessInfoArgs pi_args[] = {
+ {am_registered_name, 0, 0, ERTS_PROC_LOCK_MAIN},
+ {am_current_function, 4, 0, ERTS_PROC_LOCK_MAIN},
+ {am_initial_call, 4, 0, ERTS_PROC_LOCK_MAIN},
+ {am_status, 0, 0, 0},
+ {am_messages, 0, ERTS_PI_FLAG_WANT_MSGS|ERTS_PI_FLAG_NEED_MSGQ_LEN|ERTS_PI_FLAG_FORCE_SIG_SEND, ERTS_PROC_LOCK_MAIN},
+ {am_message_queue_len, 0, ERTS_PI_FLAG_NEED_MSGQ_LEN, ERTS_PROC_LOCK_MAIN},
+ {am_links, 0, ERTS_PI_FLAG_FORCE_SIG_SEND, ERTS_PROC_LOCK_MAIN},
+ {am_monitors, 0, ERTS_PI_FLAG_FORCE_SIG_SEND, ERTS_PROC_LOCK_MAIN},
+ {am_monitored_by, 0, ERTS_PI_FLAG_FORCE_SIG_SEND, ERTS_PROC_LOCK_MAIN},
+ {am_dictionary, 0, ERTS_PI_FLAG_FORCE_SIG_SEND, ERTS_PROC_LOCK_MAIN},
+ {am_trap_exit, 0, 0, ERTS_PROC_LOCK_MAIN},
+ {am_error_handler, 0, 0, ERTS_PROC_LOCK_MAIN},
+ {am_heap_size, 0, 0, ERTS_PROC_LOCK_MAIN},
+ {am_stack_size, 0, 0, ERTS_PROC_LOCK_MAIN},
+ {am_memory, 0, ERTS_PI_FLAG_NEED_MSGQ_LEN|ERTS_PI_FLAG_FORCE_SIG_SEND, ERTS_PROC_LOCK_MAIN},
+ {am_garbage_collection, 3+2 + 3+2 + 3+2 + 3+2 + 3+2 + ERTS_MAX_HEAP_SIZE_MAP_SZ, 0, ERTS_PROC_LOCK_MAIN},
+ {am_group_leader, 0, 0, ERTS_PROC_LOCK_MAIN},
+ {am_reductions, 0, 0, ERTS_PROC_LOCK_MAIN},
+ {am_priority, 0, 0, 0},
+ {am_trace, 0, 0, ERTS_PROC_LOCK_MAIN},
+ {am_binary, 0, ERTS_PI_FLAG_FORCE_SIG_SEND, ERTS_PROC_LOCK_MAIN},
+ {am_sequential_trace_token, 0, 0, ERTS_PROC_LOCK_MAIN},
+ {am_catchlevel, 0, 0, ERTS_PROC_LOCK_MAIN},
+ {am_backtrace, 0, 0, ERTS_PROC_LOCK_MAIN},
+ {am_last_calls, 0, 0, ERTS_PROC_LOCK_MAIN},
+ {am_total_heap_size, 0, ERTS_PI_FLAG_NEED_MSGQ_LEN|ERTS_PI_FLAG_FORCE_SIG_SEND, ERTS_PROC_LOCK_MAIN},
+ {am_suspending, 0, ERTS_PI_FLAG_FORCE_SIG_SEND, 0},
+ {am_min_heap_size, 0, 0, ERTS_PROC_LOCK_MAIN},
+ {am_min_bin_vheap_size, 0, 0, ERTS_PROC_LOCK_MAIN},
+ {am_max_heap_size, 0, 0, ERTS_PROC_LOCK_MAIN},
+ {am_current_location, 0, 0, ERTS_PROC_LOCK_MAIN},
+ {am_current_stacktrace, 0, 0, ERTS_PROC_LOCK_MAIN},
+ {am_message_queue_data, 0, 0, ERTS_PROC_LOCK_MAIN},
+ {am_garbage_collection_info, ERTS_PROCESS_GC_INFO_MAX_SIZE, 0, ERTS_PROC_LOCK_MAIN},
+ {am_magic_ref, 0, ERTS_PI_FLAG_FORCE_SIG_SEND, ERTS_PROC_LOCK_MAIN},
+ {am_fullsweep_after, 0, 0, ERTS_PROC_LOCK_MAIN}
};
-#define ERTS_PI_ARGS ((int) (sizeof(pi_args)/sizeof(Eterm)))
+#define ERTS_PI_ARGS ((int) (sizeof(pi_args)/sizeof(pi_args[0])))
+
+#ifdef DEBUG
+# define ERTS_PI_DEF_ARR_SZ 2
+#else
+# define ERTS_PI_DEF_ARR_SZ ERTS_PI_ARGS
+#endif
static ERTS_INLINE Eterm
pi_ix2arg(int ix)
{
if (ix < 0 || ERTS_PI_ARGS <= ix)
return am_undefined;
- return pi_args[ix];
+ return pi_args[ix].name;
+}
+
+static ERTS_INLINE int
+pi_ix2flags(int ix)
+{
+ if (ix < 0 || ERTS_PI_ARGS <= ix)
+ return 0;
+ return pi_args[ix].flags;
+}
+
+static ERTS_INLINE Uint
+pi_ix2rsz(int ix)
+{
+ if (ix < 0 || ERTS_PI_ARGS <= ix)
+ return 0;
+ return pi_args[ix].reserve_size;
+}
+
+static ERTS_INLINE ErtsProcLocks
+pi_ix2locks(int ix)
+{
+ if (ix < 0 || ERTS_PI_ARGS <= ix)
+ return 0;
+ return pi_args[ix].locks;
}
static ERTS_INLINE int
pi_arg2ix(Eterm arg)
{
switch (arg) {
- case am_registered_name: return 0;
- case am_current_function: return 1;
- case am_initial_call: return 2;
- case am_status: return 3;
- case am_messages: return 4;
- case am_message_queue_len: return 5;
- case am_links: return 6;
- case am_monitors: return 7;
- case am_monitored_by: return 8;
- case am_dictionary: return 9;
- case am_trap_exit: return 10;
- case am_error_handler: return 11;
- case am_heap_size: return 12;
- case am_stack_size: return 13;
- case am_memory: return 14;
- case am_garbage_collection: return 15;
- case am_group_leader: return 16;
- case am_reductions: return 17;
- case am_priority: return 18;
- case am_trace: return 19;
- case am_binary: return 20;
- case am_sequential_trace_token: return 21;
- case am_catchlevel: return 22;
- case am_backtrace: return 23;
- case am_last_calls: return 24;
- case am_total_heap_size: return 25;
- case am_suspending: return 26;
- case am_min_heap_size: return 27;
- case am_min_bin_vheap_size: return 28;
- case am_max_heap_size: return 29;
- case am_current_location: return 30;
- case am_current_stacktrace: return 31;
- case am_message_queue_data: return 32;
- case am_garbage_collection_info: return 33;
- case am_magic_ref: return 34;
- default: return -1;
+ case am_registered_name:
+ return ERTS_PI_IX_REGISTERED_NAME;
+ case am_current_function:
+ return ERTS_PI_IX_CURRENT_FUNCTION;
+ case am_initial_call:
+ return ERTS_PI_IX_INITIAL_CALL;
+ case am_status:
+ return ERTS_PI_IX_STATUS;
+ case am_messages:
+ return ERTS_PI_IX_MESSAGES;
+ case am_message_queue_len:
+ return ERTS_PI_IX_MESSAGE_QUEUE_LEN;
+ case am_links:
+ return ERTS_PI_IX_LINKS;
+ case am_monitors:
+ return ERTS_PI_IX_MONITORS;
+ case am_monitored_by:
+ return ERTS_PI_IX_MONITORED_BY;
+ case am_dictionary:
+ return ERTS_PI_IX_DICTIONARY;
+ case am_trap_exit:
+ return ERTS_PI_IX_TRAP_EXIT;
+ case am_error_handler:
+ return ERTS_PI_IX_ERROR_HANDLER;
+ case am_heap_size:
+ return ERTS_PI_IX_HEAP_SIZE;
+ case am_stack_size:
+ return ERTS_PI_IX_STACK_SIZE;
+ case am_memory:
+ return ERTS_PI_IX_MEMORY;
+ case am_garbage_collection:
+ return ERTS_PI_IX_GARBAGE_COLLECTION;
+ case am_group_leader:
+ return ERTS_PI_IX_GROUP_LEADER;
+ case am_reductions:
+ return ERTS_PI_IX_REDUCTIONS;
+ case am_priority:
+ return ERTS_PI_IX_PRIORITY;
+ case am_trace:
+ return ERTS_PI_IX_TRACE;
+ case am_binary:
+ return ERTS_PI_IX_BINARY;
+ case am_sequential_trace_token:
+ return ERTS_PI_IX_SEQUENTIAL_TRACE_TOKEN;
+ case am_catchlevel:
+ return ERTS_PI_IX_CATCHLEVEL;
+ case am_backtrace:
+ return ERTS_PI_IX_BACKTRACE;
+ case am_last_calls:
+ return ERTS_PI_IX_LAST_CALLS;
+ case am_total_heap_size:
+ return ERTS_PI_IX_TOTAL_HEAP_SIZE;
+ case am_suspending:
+ return ERTS_PI_IX_SUSPENDING;
+ case am_min_heap_size:
+ return ERTS_PI_IX_MIN_HEAP_SIZE;
+ case am_min_bin_vheap_size:
+ return ERTS_PI_IX_MIN_BIN_VHEAP_SIZE;
+ case am_max_heap_size:
+ return ERTS_PI_IX_MAX_HEAP_SIZE;
+ case am_current_location:
+ return ERTS_PI_IX_CURRENT_LOCATION;
+ case am_current_stacktrace:
+ return ERTS_PI_IX_CURRENT_STACKTRACE;
+ case am_message_queue_data:
+ return ERTS_PI_IX_MESSAGE_QUEUE_DATA;
+ case am_garbage_collection_info:
+ return ERTS_PI_IX_GARBAGE_COLLECTION_INFO;
+ case am_magic_ref:
+ return ERTS_PI_IX_MAGIC_REF;
+ case am_fullsweep_after:
+ return ERTS_PI_IX_FULLSWEEP_AFTER;
+ default:
+ return -1;
}
}
@@ -698,7 +921,6 @@ static Eterm pi_1_keys[] = {
am_initial_call,
am_status,
am_message_queue_len,
- am_messages,
am_links,
am_dictionary,
am_trap_exit,
@@ -742,153 +964,41 @@ process_info_init(void)
}
-static ERTS_INLINE Process *
-pi_pid2proc(Process *c_p, Eterm pid, ErtsProcLocks info_locks)
-{
- /*
- * If the main lock is needed, we use erts_pid2proc_not_running()
- * instead of erts_pid2proc() for two reasons:
- * * Current function of pid and possibly other information will
- * have been updated so that process_info() is consistent with an
- * info-request/info-response signal model.
- * * We avoid blocking the whole scheduler executing the
- * process that is calling process_info() for a long time
- * which will happen if pid is currently running.
- * The caller of process_info() may have to yield if pid
- * is currently running.
- */
-
- if (info_locks & ERTS_PROC_LOCK_MAIN)
- return erts_pid2proc_not_running(c_p, ERTS_PROC_LOCK_MAIN,
- pid, info_locks);
- else
- return erts_pid2proc(c_p, ERTS_PROC_LOCK_MAIN,
- pid, info_locks);
-}
-
-
-
static BIF_RETTYPE
-process_info_aux(Process *BIF_P,
+process_info_aux(Process *c_p,
+ ErtsHeapFactory *hfact,
Process *rp,
ErtsProcLocks rp_locks,
- Eterm rpid,
- Eterm item,
- int always_wrap);
+ int item_ix,
+ int flags,
+ Uint *reserve_sizep,
+ Uint *reds);
-#define ERTS_PI_RES_ELEM_IX_BUF_INC 1024
-#define ERTS_PI_DEF_RES_ELEM_IX_BUF_SZ ERTS_PI_ARGS
-
-static Eterm
-process_info_list(Process *c_p, Eterm pid, Eterm list, int always_wrap,
- int *fail_type)
+Eterm
+erts_process_info(Process *c_p,
+ ErtsHeapFactory *hfact,
+ Process *rp,
+ ErtsProcLocks rp_locks,
+ int *item_ix,
+ int item_ix_len,
+ int flags,
+ Uint reserve_size,
+ Uint *reds)
{
- int want_messages = 0;
- int def_res_elem_ix_buf[ERTS_PI_DEF_RES_ELEM_IX_BUF_SZ];
- int *res_elem_ix = &def_res_elem_ix_buf[0];
- int res_elem_ix_ix = -1;
- int res_elem_ix_sz = ERTS_PI_DEF_RES_ELEM_IX_BUF_SZ;
+ Eterm res;
Eterm part_res[ERTS_PI_ARGS];
- Eterm res, arg;
- Uint *hp, *hp_end;
- ErtsProcLocks locks = (ErtsProcLocks) 0;
- int res_len, ix;
- Process *rp = NULL;
+ int item_ix_ix, ix;
- *fail_type = ERTS_PI_FAIL_TYPE_BADARG;
+ if (ERTS_PI_FLAG_SINGELTON & flags) {
+ ASSERT(item_ix_len == 1);
+ res = process_info_aux(c_p, hfact, rp, rp_locks, item_ix[0],
+ flags, &reserve_size, reds);
+ return res;
+ }
for (ix = 0; ix < ERTS_PI_ARGS; ix++)
part_res[ix] = THE_NON_VALUE;
- ASSERT(is_list(list));
-
- while (is_list(list)) {
- Eterm* consp = list_val(list);
-
- arg = CAR(consp);
- ix = pi_arg2ix(arg);
- if (ix < 0) {
- res = THE_NON_VALUE;
- goto done;
- }
- if (arg == am_messages)
- want_messages = 1;
- locks |= pi_locks(arg);
- res_elem_ix_ix++;
- if (res_elem_ix_ix >= res_elem_ix_sz) {
- if (res_elem_ix != &def_res_elem_ix_buf[0])
- res_elem_ix =
- erts_realloc(ERTS_ALC_T_TMP,
- res_elem_ix,
- sizeof(int)*(res_elem_ix_sz
- += ERTS_PI_RES_ELEM_IX_BUF_INC));
- else {
- int new_res_elem_ix_sz = ERTS_PI_RES_ELEM_IX_BUF_INC;
- int *new_res_elem_ix = erts_alloc(ERTS_ALC_T_TMP,
- sizeof(int)*new_res_elem_ix_sz);
- sys_memcpy((void *) new_res_elem_ix,
- (void *) res_elem_ix,
- sizeof(int)*res_elem_ix_sz);
- res_elem_ix = new_res_elem_ix;
- res_elem_ix_sz = new_res_elem_ix_sz;
- }
- }
- res_elem_ix[res_elem_ix_ix] = ix;
- list = CDR(consp);
- }
- if (is_not_nil(list)) {
- res = THE_NON_VALUE;
- goto done;
- }
-
- res_len = res_elem_ix_ix+1;
-
- ASSERT(res_len > 0);
-
- rp = pi_pid2proc(c_p, pid, locks|ERTS_PROC_LOCK_STATUS);
- if (!rp) {
- res = am_undefined;
- goto done;
- }
- else if (rp == ERTS_PROC_LOCK_BUSY) {
- rp = NULL;
- res = THE_NON_VALUE;
- *fail_type = ERTS_PI_FAIL_TYPE_YIELD;
- goto done;
- }
- else if (c_p != rp && ERTS_PROC_PENDING_EXIT(rp)) {
- locks |= ERTS_PROC_LOCK_STATUS;
- res = THE_NON_VALUE;
- *fail_type = ERTS_PI_FAIL_TYPE_AWAIT_EXIT;
- goto done;
- }
- else {
- ErtsProcLocks unlock_locks = 0;
-
- if (c_p == rp)
- locks |= ERTS_PROC_LOCK_MAIN;
-
- if (!(locks & ERTS_PROC_LOCK_STATUS))
- unlock_locks |= ERTS_PROC_LOCK_STATUS;
-
- if (locks & ERTS_PROC_LOCK_MSGQ) {
- /*
- * Move in queue into private queue and
- * release msgq lock, enabling others to
- * send messages to the process while it
- * is being inspected...
- */
- ASSERT(locks & ERTS_PROC_LOCK_MAIN);
- ERTS_MSGQ_MV_INQ2PRIVQ(rp);
- locks &= ~ERTS_PROC_LOCK_MSGQ;
- unlock_locks |= ERTS_PROC_LOCK_MSGQ;
- }
-
- if (unlock_locks)
- erts_proc_unlock(rp, unlock_locks);
-
- }
-
/*
* We always handle 'messages' first if it should be part
* of the result. This since if both 'messages' and
@@ -896,28 +1006,31 @@ process_info_list(Process *c_p, Eterm pid, Eterm list, int always_wrap,
* change the result of 'message_queue_len' (in case
* the queue contain bad distribution messages).
*/
- if (want_messages) {
+ if (flags & ERTS_PI_FLAG_WANT_MSGS) {
ix = pi_arg2ix(am_messages);
ASSERT(part_res[ix] == THE_NON_VALUE);
- part_res[ix] = process_info_aux(c_p, rp, locks, pid, am_messages, always_wrap);
- ASSERT(part_res[ix] != THE_NON_VALUE);
+ res = process_info_aux(c_p, hfact, rp, rp_locks, ix,
+ flags, &reserve_size, reds);
+ ASSERT(res != am_undefined);
+ ASSERT(res != THE_NON_VALUE);
+ part_res[ix] = res;
}
- for (; res_elem_ix_ix >= 0; res_elem_ix_ix--) {
- ix = res_elem_ix[res_elem_ix_ix];
+ for (item_ix_ix = item_ix_len - 1; item_ix_ix >= 0; item_ix_ix--) {
+ ix = item_ix[item_ix_ix];
if (part_res[ix] == THE_NON_VALUE) {
- arg = pi_ix2arg(ix);
- part_res[ix] = process_info_aux(c_p, rp, locks, pid, arg, always_wrap);
- ASSERT(part_res[ix] != THE_NON_VALUE);
+ res = process_info_aux(c_p, hfact, rp, rp_locks, ix,
+ flags, &reserve_size, reds);
+ ASSERT(res != am_undefined);
+ ASSERT(res != THE_NON_VALUE);
+ part_res[ix] = res;
}
}
- hp = HAlloc(c_p, res_len*2);
- hp_end = hp + res_len*2;
res = NIL;
- for (res_elem_ix_ix = res_len - 1; res_elem_ix_ix >= 0; res_elem_ix_ix--) {
- ix = res_elem_ix[res_elem_ix_ix];
+ for (item_ix_ix = item_ix_len - 1; item_ix_ix >= 0; item_ix_ix--) {
+ ix = item_ix[item_ix_ix];
ASSERT(part_res[ix] != THE_NON_VALUE);
/*
* If we should ignore the value of registered_name,
@@ -925,171 +1038,310 @@ process_info_list(Process *c_p, Eterm pid, Eterm list, int always_wrap,
* beginning of process_info_aux().
*/
if (is_nil(part_res[ix])) {
- ASSERT(!always_wrap);
+ ASSERT(!(flags & ERTS_PI_FLAG_ALWAYS_WRAP));
ASSERT(pi_ix2arg(ix) == am_registered_name);
}
else {
+ Eterm *hp;
+ ERTS_PI_UNRESERVE(reserve_size, 2);
+ hp = erts_produce_heap(hfact, 2, reserve_size);
res = CONS(hp, part_res[ix], res);
- hp += 2;
}
}
- if (!always_wrap) {
- HRelease(c_p, hp_end, hp);
- }
-
- done:
-
- if (c_p == rp)
- locks &= ~ERTS_PROC_LOCK_MAIN;
- if (locks && rp)
- erts_proc_unlock(rp, locks);
-
- if (res_elem_ix != &def_res_elem_ix_buf[0])
- erts_free(ERTS_ALC_T_TMP, res_elem_ix);
-
return res;
}
-BIF_RETTYPE process_info_1(BIF_ALIST_1)
+static void
+pi_setup_grow(int **arr, int *def_arr, Uint *sz, int ix);
+
+static BIF_RETTYPE
+process_info_bif(Process *c_p, Eterm pid, Eterm opt, int always_wrap, int pi2)
{
+ ErtsHeapFactory hfact;
+ int def_arr[ERTS_PI_DEF_ARR_SZ];
+ int *item_ix = &def_arr[0];
+ Process *rp = NULL;
+ erts_aint32_t state;
+ BIF_RETTYPE ret;
+ Uint reds = 0;
+ ErtsProcLocks locks = 0;
+ int flags;
+ Uint reserve_size;
+ int len;
Eterm res;
- int fail_type;
- if (is_external_pid(BIF_ARG_1)
- && external_pid_dist_entry(BIF_ARG_1) == erts_this_dist_entry)
- BIF_RET(am_undefined);
-
- if (is_not_internal_pid(BIF_ARG_1)) {
- BIF_ERROR(BIF_P, BADARG);
- }
-
- res = process_info_list(BIF_P, BIF_ARG_1, pi_1_keys_list, 0, &fail_type);
- if (is_non_value(res)) {
- switch (fail_type) {
- case ERTS_PI_FAIL_TYPE_BADARG:
- BIF_ERROR(BIF_P, BADARG);
- case ERTS_PI_FAIL_TYPE_YIELD:
- ERTS_BIF_YIELD1(bif_export[BIF_process_info_1], BIF_P, BIF_ARG_1);
- case ERTS_PI_FAIL_TYPE_AWAIT_EXIT:
- ERTS_BIF_AWAIT_X_DATA_TRAP(BIF_P, BIF_ARG_1, am_undefined);
- default:
- erts_exit(ERTS_ABORT_EXIT, "%s:%d: Internal error", __FILE__, __LINE__);
- }
+ ERTS_CT_ASSERT(ERTS_PI_DEF_ARR_SZ > 0);
+
+ if (c_p->common.id == pid) {
+ int local_only = c_p->flags & F_LOCAL_SIGS_ONLY;
+ int sreds = ERTS_BIF_REDS_LEFT(c_p);
+ int sres;
+
+ if (!local_only) {
+ erts_proc_lock(c_p, ERTS_PROC_LOCK_MSGQ);
+ erts_proc_sig_fetch(c_p);
+ erts_proc_unlock(c_p, ERTS_PROC_LOCK_MSGQ);
+ }
+
+ sres = erts_proc_sig_handle_incoming(c_p, &state, &sreds, sreds, !0);
+ if (state & ERTS_PSFLG_EXITING) {
+ c_p->flags &= ~F_LOCAL_SIGS_ONLY;
+ goto exited;
+ }
+ if (!sres) {
+ /*
+ * More signals to handle; need to yield and continue.
+ * Prevent fetching of more signals by setting
+ * local-sigs-only flag.
+ */
+ c_p->flags |= F_LOCAL_SIGS_ONLY;
+ goto yield;
+ }
+
+ c_p->flags &= ~F_LOCAL_SIGS_ONLY;
}
- ASSERT(!(BIF_P->flags & F_P2PNR_RESCHED));
- BIF_RET(res);
-}
+ if (is_atom(opt)) {
+ int ix = pi_arg2ix(opt);
+ item_ix[0] = ix;
+ len = 1;
+ locks = pi_ix2locks(ix);
+ reserve_size = 3 + pi_ix2rsz(ix);
+ flags = ERTS_PI_FLAG_SINGELTON;
+ flags |= pi_ix2flags(ix);
+ if (ix < 0)
+ goto badarg;
+ }
+ else {
+ Eterm list = opt;
+ Uint size = ERTS_PI_DEF_ARR_SZ;
+ len = 0;
+ reserve_size = 0;
+ locks = 0;
+ flags = 0;
-BIF_RETTYPE process_info_2(BIF_ALIST_2)
-{
- Eterm res;
- Process *rp;
- Eterm pid = BIF_ARG_1;
- ErtsProcLocks info_locks;
- int fail_type;
+ while (is_list(list)) {
+ Eterm *consp = list_val(list);
+ Eterm arg = CAR(consp);
+ int ix = pi_arg2ix(arg);
+ if (ix < 0)
+ goto badarg;
+
+ if (len >= size)
+ pi_setup_grow(&item_ix, def_arr, &size, len);
+
+ item_ix[len++] = ix;
+
+ locks |= pi_ix2locks(ix);
+ flags |= pi_ix2flags(ix);
+ reserve_size += pi_ix2rsz(ix);
+ reserve_size += 3; /* 2-tuple */
+ reserve_size += 2; /* cons */
+
+ list = CDR(consp);
+ }
+
+ if (is_not_nil(list))
+ goto badarg;
+ }
- if (is_external_pid(pid)
- && external_pid_dist_entry(pid) == erts_this_dist_entry)
- BIF_RET(am_undefined);
-
if (is_not_internal_pid(pid)) {
- BIF_ERROR(BIF_P, BADARG);
+ if (is_external_pid(pid)
+ && external_pid_dist_entry(pid) == erts_this_dist_entry)
+ goto undefined;
+ goto badarg;
}
- if (is_nil(BIF_ARG_2))
- BIF_RET(NIL);
+ if (always_wrap)
+ flags |= ERTS_PI_FLAG_ALWAYS_WRAP;
- if (is_list(BIF_ARG_2)) {
- res = process_info_list(BIF_P, BIF_ARG_1, BIF_ARG_2, 1, &fail_type);
- if (is_non_value(res)) {
- switch (fail_type) {
- case ERTS_PI_FAIL_TYPE_BADARG:
- BIF_ERROR(BIF_P, BADARG);
- case ERTS_PI_FAIL_TYPE_YIELD:
- ERTS_BIF_YIELD2(bif_export[BIF_process_info_2], BIF_P,
- BIF_ARG_1, BIF_ARG_2);
- case ERTS_PI_FAIL_TYPE_AWAIT_EXIT:
- ERTS_BIF_AWAIT_X_DATA_TRAP(BIF_P, BIF_ARG_1, am_undefined);
- default:
- erts_exit(ERTS_ABORT_EXIT, "%s:%d: Internal error",
- __FILE__, __LINE__);
- }
- }
- ASSERT(!(BIF_P->flags & F_P2PNR_RESCHED));
- BIF_RET(res);
+ if (c_p->common.id == pid) {
+ rp = c_p;
+ if (locks & ~ERTS_PROC_LOCK_MAIN)
+ erts_proc_lock(c_p, locks & ~ERTS_PROC_LOCK_MAIN);
+ locks |= ERTS_PROC_LOCK_MAIN;
+ }
+ else {
+ if (flags & ERTS_PI_FLAG_FORCE_SIG_SEND)
+ goto send_signal;
+ rp = erts_try_lock_sig_free_proc(pid, locks, &state);
+ if (!rp)
+ goto undefined;
+ if (rp == ERTS_PROC_LOCK_BUSY) {
+ rp = NULL;
+ goto send_signal;
+ }
+ if (state & ERTS_PSFLG_EXITING) {
+ if (locks)
+ erts_proc_unlock(rp, locks);
+ locks = 0;
+ /* wait for it to terminate properly... */
+ goto send_signal;
+ }
+ if (flags & ERTS_PI_FLAG_NEED_MSGQ_LEN) {
+ ASSERT(locks & ERTS_PROC_LOCK_MAIN);
+ erts_proc_lock(rp, ERTS_PROC_LOCK_MSGQ);
+ erts_proc_sig_fetch(rp);
+ if (c_p->sig_qs.cont) {
+ erts_proc_unlock(rp, locks|ERTS_PROC_LOCK_MSGQ);
+ locks = 0;
+ goto send_signal;
+ }
+ erts_proc_unlock(rp, ERTS_PROC_LOCK_MSGQ);
+ }
}
- if (pi_arg2ix(BIF_ARG_2) < 0)
- BIF_ERROR(BIF_P, BADARG);
+ erts_factory_proc_init(&hfact, c_p);
- info_locks = pi_locks(BIF_ARG_2);
+ res = erts_process_info(c_p, &hfact, rp, locks, item_ix, len,
+ flags, reserve_size, &reds);
- rp = pi_pid2proc(BIF_P, pid, info_locks|ERTS_PROC_LOCK_STATUS);
- if (!rp)
- res = am_undefined;
- else if (rp == ERTS_PROC_LOCK_BUSY)
- ERTS_BIF_YIELD2(bif_export[BIF_process_info_2], BIF_P,
- BIF_ARG_1, BIF_ARG_2);
- else if (rp != BIF_P && ERTS_PROC_PENDING_EXIT(rp)) {
- erts_proc_unlock(rp, info_locks|ERTS_PROC_LOCK_STATUS);
- ERTS_BIF_AWAIT_X_DATA_TRAP(BIF_P, BIF_ARG_1, am_undefined);
+ erts_factory_close(&hfact);
+
+ if (reds > INT_MAX/2)
+ reds = INT_MAX/2;
+ BUMP_REDS(c_p, (int) reds);
+
+ state = erts_atomic32_read_acqb(&rp->state);
+ if (state & (ERTS_PSFLG_EXITING|ERTS_PSFLG_FREE)) {
+ if (state & ERTS_PSFLG_FREE) {
+ ASSERT(!locks);
+ goto undefined;
+ }
+ if (locks)
+ erts_proc_unlock(rp, locks);
+ locks = 0;
+ /* wait for it to terminate properly... */
+ goto send_signal;
}
- else {
- ErtsProcLocks unlock_locks = 0;
- if (BIF_P == rp)
- info_locks |= ERTS_PROC_LOCK_MAIN;
+ ERTS_BIF_PREP_RET(ret, res);
- if (!(info_locks & ERTS_PROC_LOCK_STATUS))
- unlock_locks |= ERTS_PROC_LOCK_STATUS;
+done:
- if (info_locks & ERTS_PROC_LOCK_MSGQ) {
- /*
- * Move in queue into private queue and
- * release msgq lock, enabling others to
- * send messages to the process while it
- * is being inspected...
- */
- ASSERT(info_locks & ERTS_PROC_LOCK_MAIN);
- ERTS_MSGQ_MV_INQ2PRIVQ(rp);
- info_locks &= ~ERTS_PROC_LOCK_MSGQ;
- unlock_locks |= ERTS_PROC_LOCK_MSGQ;
- }
+ if (c_p == rp)
+ locks &= ~ERTS_PROC_LOCK_MAIN;
+
+ if (locks && rp)
+ erts_proc_unlock(rp, locks);
+
+ if (item_ix != def_arr)
+ erts_free(ERTS_ALC_T_TMP, item_ix);
+
+ return ret;
+
+badarg:
+ ERTS_BIF_PREP_ERROR(ret, c_p, BADARG);
+ goto done;
+
+undefined:
+ ERTS_BIF_PREP_RET(ret, am_undefined);
+ goto done;
- if (unlock_locks)
- erts_proc_unlock(rp, unlock_locks);
+exited:
+ ERTS_BIF_PREP_EXITED(ret, c_p);
+ goto done;
- res = process_info_aux(BIF_P, rp, info_locks, pid, BIF_ARG_2, 0);
+yield:
+ if (pi2)
+ ERTS_BIF_PREP_YIELD2(ret, bif_export[BIF_process_info_2], c_p, pid, opt);
+ else
+ ERTS_BIF_PREP_YIELD1(ret, bif_export[BIF_process_info_1], c_p, pid);
+ goto done;
+
+send_signal: {
+ Eterm ref = erts_make_ref(c_p);
+ int enqueued, need_msgq_len;
+ flags |= ERTS_PI_FLAG_REQUEST_FOR_OTHER;
+ need_msgq_len = (flags & ERTS_PI_FLAG_NEED_MSGQ_LEN);
+ /*
+ * Set receive mark so we wont have to scan the whole
+ * message queue for the result. Note caller unconditionally
+ * has to enter a receive only matching messages containing
+ * 'ref', or restore save pointer.
+ */
+ ERTS_RECV_MARK_SAVE(c_p);
+ ERTS_RECV_MARK_SET(c_p);
+ enqueued = erts_proc_sig_send_process_info_request(c_p, pid, item_ix,
+ len, need_msgq_len,
+ flags, reserve_size,
+ ref);
+ if (!enqueued) {
+ /* Restore save pointer... */
+ JOIN_MESSAGE(c_p);
+ goto undefined;
+ }
+ ERTS_BIF_PREP_TRAP1(ret, erts_await_result, c_p, ref);
+ goto done;
}
- ASSERT(is_value(res));
+}
- if (BIF_P == rp)
- info_locks &= ~ERTS_PROC_LOCK_MAIN;
- if (rp && info_locks)
- erts_proc_unlock(rp, info_locks);
+static void
+pi_setup_grow(int **arr, int *def_arr, Uint *sz, int ix)
+{
+ *sz = (ix+1) + ERTS_PI_DEF_ARR_SZ;
+ if (*arr != def_arr)
+ *arr = erts_realloc(ERTS_ALC_T_TMP, *arr, (*sz)*sizeof(int));
+ else {
+ int *new_arr = erts_alloc(ERTS_ALC_T_TMP, (*sz)*sizeof(int));
+ sys_memcpy((void *) new_arr, (void *) def_arr,
+ sizeof(int)*ERTS_PI_DEF_ARR_SZ);
+ *arr = new_arr;
+ }
+}
+
+
+BIF_RETTYPE process_info_2(BIF_ALIST_2)
+{
+ return process_info_bif(BIF_P, BIF_ARG_1, BIF_ARG_2, !is_atom(BIF_ARG_2), !0);
+}
- ASSERT(!(BIF_P->flags & F_P2PNR_RESCHED));
- BIF_RET(res);
+BIF_RETTYPE process_info_1(BIF_ALIST_1)
+{
+ return process_info_bif(BIF_P, BIF_ARG_1, pi_1_keys_list, 0, 0);
}
Eterm
-process_info_aux(Process *BIF_P,
+process_info_aux(Process *c_p,
+ ErtsHeapFactory *hfact,
Process *rp,
ErtsProcLocks rp_locks,
- Eterm rpid,
- Eterm item,
- int always_wrap)
+ int item_ix,
+ int flags,
+ Uint *reserve_sizep,
+ Uint *reds)
{
Eterm *hp;
Eterm res = NIL;
+ Uint reserved;
+ Uint reserve_size = *reserve_sizep;
+
+#ifdef ERTS_ENABLE_LOCK_CHECK
+ ErtsProcLocks locks = erts_proc_lc_my_proc_locks(rp);
+
+ switch (item_ix) {
+ case ERTS_PI_IX_STATUS:
+ case ERTS_PI_IX_PRIORITY:
+ case ERTS_PI_IX_SUSPENDING:
+ ERTS_LC_ASSERT((locks & ~ERTS_PROC_LOCK_MAIN) == 0);
+ break;
+ default:
+ ERTS_LC_ASSERT(locks == ERTS_PROC_LOCK_MAIN);
+ break;
+ }
+#endif
+
+ reserved = pi_ix2rsz(item_ix);
+ ERTS_PI_UNRESERVE(reserve_size, reserved);
+
+ (*reds)++;
ASSERT(rp);
/*
- * Q: Why this always_wrap argument?
+ * Q: Why this ERTS_PI_FLAG_ALWAYS_WRAP flag?
*
* A: registered_name is strange. If process has no registered name,
* process_info(Pid, registered_name) returns [], and
@@ -1101,41 +1353,39 @@ process_info_aux(Process *BIF_P,
* registered_name behaves as it should, i.e. a
* {registered_name, []} will appear in the resulting list.
*
- * If always_wrap != 0, process_info_aux() always wrap the result
- * in a key two tuple.
+ * If ERTS_PI_FLAG_ALWAYS_WRAP is set, process_info_aux() always
+ * wrap the result in a key two tuple.
*/
- switch (item) {
+ switch (item_ix) {
- case am_registered_name:
- if (rp->common.u.alive.reg) {
- hp = HAlloc(BIF_P, 3);
+ case ERTS_PI_IX_REGISTERED_NAME:
+ if (rp->common.u.alive.reg)
res = rp->common.u.alive.reg->name;
- } else {
- if (always_wrap) {
- hp = HAlloc(BIF_P, 3);
+ else {
+ if (flags & ERTS_PI_FLAG_ALWAYS_WRAP)
res = NIL;
- }
- else {
+ else
return NIL;
- }
}
break;
- case am_current_function:
- res = current_function(BIF_P, rp, &hp, 0);
+ case ERTS_PI_IX_CURRENT_FUNCTION:
+ res = current_function(c_p, hfact, rp, 0,
+ reserve_size, flags);
break;
- case am_current_location:
- res = current_function(BIF_P, rp, &hp, 1);
+ case ERTS_PI_IX_CURRENT_LOCATION:
+ res = current_function(c_p, hfact, rp, 1,
+ reserve_size, flags);
break;
- case am_current_stacktrace:
- res = current_stacktrace(BIF_P, rp, &hp);
+ case ERTS_PI_IX_CURRENT_STACKTRACE:
+ res = current_stacktrace(hfact, rp, reserve_size);
break;
- case am_initial_call:
- hp = HAlloc(BIF_P, 3+4);
+ case ERTS_PI_IX_INITIAL_CALL:
+ hp = erts_produce_heap(hfact, 4, reserve_size);
res = TUPLE3(hp,
rp->u.initial.module,
rp->u.initial.function,
@@ -1143,96 +1393,130 @@ process_info_aux(Process *BIF_P,
hp += 4;
break;
- case am_status:
- res = erts_process_status(rp, rpid);
- ASSERT(res != am_undefined);
- hp = HAlloc(BIF_P, 3);
+ case ERTS_PI_IX_STATUS: {
+ erts_aint32_t state = erts_atomic32_read_nob(&rp->state);
+ res = erts_process_state2status(state);
+ if (res == am_running && (state & ERTS_PSFLG_RUNNING_SYS)) {
+ ASSERT(c_p == rp);
+ ASSERT(flags & ERTS_PI_FLAG_REQUEST_FOR_OTHER);
+ if (!(state & (ERTS_PSFLG_SYS_TASKS
+ | ERTS_PSFLG_ACTIVE
+ | ERTS_PSFLG_SIG_Q
+ | ERTS_PSFLG_SIG_IN_Q))) {
+ /*
+ * We are servicing a process-info request from
+ * another process. If that other process could
+ * have inspected our state itself, we would have
+ * been in the 'waiting' state.
+ */
+ res = am_waiting;
+ }
+ }
break;
+ }
- case am_messages: {
-
- if (rp->msg.len == 0 || ERTS_TRACE_FLAGS(rp) & F_SENSITIVE) {
- hp = HAlloc(BIF_P, 3);
- } else {
+ case ERTS_PI_IX_MESSAGES: {
+ ASSERT(flags & ERTS_PI_FLAG_NEED_MSGQ_LEN);
+ if (rp->sig_qs.len == 0 || (ERTS_TRACE_FLAGS(rp) & F_SENSITIVE))
+ res = NIL;
+ else {
+ int info_on_self = !(flags & ERTS_PI_FLAG_REQUEST_FOR_OTHER);
ErtsMessageInfo *mip;
- Sint i;
+ Sint i, len;
Uint heap_need;
-#ifdef DEBUG
- Eterm *hp_end;
-#endif
mip = erts_alloc(ERTS_ALC_T_TMP,
- rp->msg.len*sizeof(ErtsMessageInfo));
+ rp->sig_qs.len*sizeof(ErtsMessageInfo));
/*
* Note that message queue may shrink when calling
- * erts_prep_msgq_for_inspection() since it removes
+ * erts_proc_sig_prep_msgq_for_inspection() since it removes
* corrupt distribution messages.
*/
- heap_need = erts_prep_msgq_for_inspection(BIF_P, rp, rp_locks, mip);
- heap_need += 3; /* top 2-tuple */
- heap_need += rp->msg.len*2; /* Cons cells */
+ heap_need = erts_proc_sig_prep_msgq_for_inspection(c_p, rp,
+ rp_locks,
+ info_on_self,
+ mip);
+ len = rp->sig_qs.len;
- hp = HAlloc(BIF_P, heap_need); /* heap_need is exact */
-#ifdef DEBUG
- hp_end = hp + heap_need;
-#endif
+ heap_need += len*2; /* Cons cells */
+
+ reserve_size += heap_need;
/* Build list of messages... */
- for (i = rp->msg.len - 1, res = NIL; i >= 0; i--) {
+ for (i = len - 1, res = NIL; i >= 0; i--) {
Eterm msg = ERL_MESSAGE_TERM(mip[i].msgp);
Uint sz = mip[i].size;
+ ERTS_PI_UNRESERVE(reserve_size, sz+2);
+ hp = erts_produce_heap(hfact, sz+2, reserve_size);
+
if (sz != 0)
- msg = copy_struct(msg, sz, &hp, &BIF_P->off_heap);
+ msg = copy_struct(msg, sz, &hp, hfact->off_heap);
res = CONS(hp, msg, res);
hp += 2;
}
- ASSERT(hp_end == hp + 3);
+ *reds += (Uint) len / 4;
erts_free(ERTS_ALC_T_TMP, mip);
}
break;
}
- case am_message_queue_len:
- hp = HAlloc(BIF_P, 3);
- res = make_small(rp->msg.len);
+ case ERTS_PI_IX_MESSAGE_QUEUE_LEN: {
+ Sint len = rp->sig_qs.len;
+ ASSERT(flags & ERTS_PI_FLAG_NEED_MSGQ_LEN);
+ ASSERT(len >= 0);
+ if (len <= MAX_SMALL)
+ res = make_small(len);
+ else {
+ hp = erts_produce_heap(hfact, BIG_UINT_HEAP_SIZE, reserve_size);
+ res = uint_to_big((Uint) len, hp);
+ }
break;
+ }
- case am_links: {
+ case ERTS_PI_IX_LINKS: {
MonitorInfoCollection mic;
int i;
Eterm item;
INIT_MONITOR_INFOS(mic);
- erts_doforall_links(ERTS_P_LINKS(rp),&collect_one_link,&mic);
+ erts_link_tree_foreach(ERTS_P_LINKS(rp), collect_one_link, (void *) &mic);
- hp = HAlloc(BIF_P, 3 + mic.sz);
+ reserve_size += mic.sz;
res = NIL;
for (i = 0; i < mic.mi_i; i++) {
- item = STORE_NC(&hp, &MSO(BIF_P), mic.mi[i].entity.term);
+ Eterm item_src = mic.mi[i].entity.term;
+ Uint sz = NC_HEAP_SIZE(item_src) + 2;
+ ERTS_PI_UNRESERVE(reserve_size, sz);
+ hp = erts_produce_heap(hfact, sz, reserve_size);
+ item = STORE_NC(&hp, hfact->off_heap, item_src);
res = CONS(hp, item, res);
- hp += 2;
}
+
+ *reds += (Uint) mic.mi_i / 4;
+
DESTROY_MONITOR_INFOS(mic);
break;
}
- case am_monitors: {
+ case ERTS_PI_IX_MONITORS: {
MonitorInfoCollection mic;
int i;
INIT_MONITOR_INFOS(mic);
- erts_doforall_monitors(ERTS_P_MONITORS(rp),
- &collect_one_origin_monitor, &mic);
- hp = HAlloc(BIF_P, 3 + mic.sz);
+ erts_monitor_tree_foreach(ERTS_P_MONITORS(rp),
+ collect_one_origin_monitor,
+ (void *) &mic);
+
+ reserve_size += mic.sz;
res = NIL;
for (i = 0; i < mic.mi_i; i++) {
- if (is_atom(mic.mi[i].entity.term)) {
+ if (mic.mi[i].named) {
/* Monitor by name.
* Build {process|port, {Name, Node}} and cons it.
*/
@@ -1244,84 +1528,136 @@ process_info_aux(Process *BIF_P,
|| is_port(mic.mi[i].pid)
|| is_atom(mic.mi[i].pid));
+ ERTS_PI_UNRESERVE(reserve_size, 3+3+2);
+ hp = erts_produce_heap(hfact, 3+3+2, reserve_size);
+
t1 = TUPLE2(hp, mic.mi[i].entity.term, mic.mi[i].node);
hp += 3;
t2 = TUPLE2(hp, m_type, t1);
hp += 3;
res = CONS(hp, t2, res);
- hp += 2;
}
else {
- /* Monitor by pid. Build {process|port, Pid} and cons it. */
+ /* Build {process|port|time_offset, Pid|clock_service} and cons it. */
Eterm t;
- Eterm pid = STORE_NC(&hp, &MSO(BIF_P), mic.mi[i].entity.term);
+ Eterm pid;
+ Eterm m_type;
+ Eterm pid_src = mic.mi[i].entity.term;
+ Uint sz = is_atom(pid_src) ? 0 : NC_HEAP_SIZE(pid_src);
+ sz += 3 + 2;
+
+ ERTS_PI_UNRESERVE(reserve_size, sz);
+ hp = erts_produce_heap(hfact, sz, reserve_size);
+
+ pid = (is_atom(pid_src)
+ ? pid_src
+ : STORE_NC(&hp, hfact->off_heap, pid_src));
+
+ switch (mic.mi[i].type) {
+ case ERTS_MON_TYPE_PORT:
+ m_type = am_port;
+ break;
+ case ERTS_MON_TYPE_TIME_OFFSET:
+ m_type = am_time_offset;
+ break;
+ default:
+ m_type = am_process;
+ break;
+ }
- Eterm m_type = is_port(mic.mi[i].pid) ? am_port : am_process;
ASSERT(is_pid(mic.mi[i].pid)
|| is_port(mic.mi[i].pid));
t = TUPLE2(hp, m_type, pid);
hp += 3;
res = CONS(hp, t, res);
- hp += 2;
}
}
+
+ *reds += (Uint) mic.mi_i / 4;
+
DESTROY_MONITOR_INFOS(mic);
break;
}
- case am_monitored_by: {
+ case ERTS_PI_IX_MONITORED_BY: {
MonitorInfoCollection mic;
int i;
Eterm item;
INIT_MONITOR_INFOS(mic);
- erts_doforall_monitors(ERTS_P_MONITORS(rp),&collect_one_target_monitor,&mic);
- hp = HAlloc(BIF_P, 3 + mic.sz);
+ erts_monitor_list_foreach(ERTS_P_LT_MONITORS(rp),
+ collect_one_target_monitor,
+ (void *) &mic);
+ erts_monitor_tree_foreach(ERTS_P_MONITORS(rp),
+ collect_one_target_monitor,
+ (void *) &mic);
+
+ reserve_size += mic.sz;
res = NIL;
for (i = 0; i < mic.mi_i; ++i) {
- if (mic.mi[i].node == make_small(MON_NIF_TARGET)) {
- item = erts_bld_resource_ref(&hp, &MSO(BIF_P), mic.mi[i].entity.resource);
- }
- else {
- item = STORE_NC(&hp, &MSO(BIF_P), mic.mi[i].entity.term);
- }
+ Uint sz = 2;
+
+ if (mic.mi[i].type == ERTS_MON_TYPE_RESOURCE)
+ sz += erts_resource_ref_size(mic.mi[i].entity.resource);
+ else
+ sz += NC_HEAP_SIZE(mic.mi[i].entity.term);
+
+ ERTS_PI_UNRESERVE(reserve_size, sz);
+ hp = erts_produce_heap(hfact, sz, reserve_size);
+
+ if (mic.mi[i].type == ERTS_MON_TYPE_RESOURCE)
+ item = erts_bld_resource_ref(&hp,
+ hfact->off_heap,
+ mic.mi[i].entity.resource);
+ else
+ item = STORE_NC(&hp,
+ hfact->off_heap,
+ mic.mi[i].entity.term);
res = CONS(hp, item, res);
- hp += 2;
}
+
+ *reds += (Uint) mic.mi_i / 4;
+
DESTROY_MONITOR_INFOS(mic);
break;
}
- case am_suspending: {
+ case ERTS_PI_IX_SUSPENDING: {
ErtsSuspendMonitorInfoCollection smic;
int i;
Eterm item;
-#ifdef DEBUG
- Eterm *hp_end;
-#endif
+
+ erts_proc_lock(rp, ERTS_PROC_LOCK_STATUS);
ERTS_INIT_SUSPEND_MONITOR_INFOS(smic,
- BIF_P,
- (BIF_P == rp
+ c_p,
+ (c_p == rp
? ERTS_PROC_LOCK_MAIN
- : 0) | ERTS_PROC_LOCK_LINK);
+ : 0) | ERTS_PROC_LOCK_STATUS);
+
+ erts_monitor_tree_foreach(rp->suspend_monitors,
+ &collect_one_suspend_monitor,
+ &smic);
+
+ reserve_size += smic.sz;
- erts_doforall_suspend_monitors(rp->suspend_monitors,
- &collect_one_suspend_monitor,
- &smic);
- hp = HAlloc(BIF_P, 3 + smic.sz);
-#ifdef DEBUG
- hp_end = hp + smic.sz;
-#endif
-
res = NIL;
for (i = 0; i < smic.smi_i; i++) {
Sint a = (Sint) smic.smi[i]->active; /* quiet compiler warnings */
Sint p = (Sint) smic.smi[i]->pending; /* on 64-bit machines... */
Eterm active;
Eterm pending;
+ Uint sz = 4 + 2;
+ if (!IS_SSMALL(a))
+ sz += BIG_UINT_HEAP_SIZE;
+ if (!IS_SSMALL(p))
+ sz += BIG_UINT_HEAP_SIZE;
+
+ ERTS_PI_UNRESERVE(reserve_size, sz);
+ hp = erts_produce_heap(hfact, sz, reserve_size);
+
if (IS_SSMALL(a))
active = make_small(a);
else {
@@ -1334,90 +1670,86 @@ process_info_aux(Process *BIF_P,
pending = small_to_big(p, hp);
hp += BIG_UINT_HEAP_SIZE;
}
- item = TUPLE3(hp, smic.smi[i]->pid, active, pending);
+ item = TUPLE3(hp, smic.smi[i]->mon.other.item, active, pending);
hp += 4;
res = CONS(hp, item, res);
- hp += 2;
}
+ erts_proc_unlock(rp, ERTS_PROC_LOCK_STATUS);
+
+ *reds += (Uint) smic.smi_i / 4;
+
ERTS_DESTROY_SUSPEND_MONITOR_INFOS(smic);
- ASSERT(hp == hp_end);
break;
}
- case am_dictionary:
- if (ERTS_TRACE_FLAGS(rp) & F_SENSITIVE) {
+ case ERTS_PI_IX_DICTIONARY:
+ if (!rp->dictionary || (ERTS_TRACE_FLAGS(rp) & F_SENSITIVE)) {
res = NIL;
} else {
- res = erts_dictionary_copy(BIF_P, rp->dictionary);
+ Uint num = rp->dictionary->numElements;
+ res = erts_dictionary_copy(hfact, rp->dictionary, reserve_size);
+ *reds += (Uint) num / 4;
}
- hp = HAlloc(BIF_P, 3);
+
break;
- case am_trap_exit: {
- erts_aint32_t state = erts_atomic32_read_nob(&rp->state);
- hp = HAlloc(BIF_P, 3);
- if (state & ERTS_PSFLG_TRAP_EXIT)
- res = am_true;
- else
- res = am_false;
+ case ERTS_PI_IX_TRAP_EXIT:
+ res = (rp->flags & F_TRAP_EXIT) ? am_true : am_false;
break;
- }
- case am_error_handler:
- hp = HAlloc(BIF_P, 3);
- res = erts_proc_get_error_handler(BIF_P);
+ case ERTS_PI_IX_ERROR_HANDLER:
+ res = erts_proc_get_error_handler(rp);
break;
- case am_heap_size: {
- Uint hsz = 3;
+ case ERTS_PI_IX_HEAP_SIZE: {
+ Uint hsz = 0;
(void) erts_bld_uint(NULL, &hsz, HEAP_SIZE(rp));
- hp = HAlloc(BIF_P, hsz);
+ hp = erts_produce_heap(hfact, hsz, reserve_size);
res = erts_bld_uint(&hp, NULL, HEAP_SIZE(rp));
break;
}
- case am_fullsweep_after: {
- Uint hsz = 3;
+ case ERTS_PI_IX_FULLSWEEP_AFTER: {
+ Uint hsz = 0;
(void) erts_bld_uint(NULL, &hsz, MAX_GEN_GCS(rp));
- hp = HAlloc(BIF_P, hsz);
+ hp = erts_produce_heap(hfact, hsz, reserve_size);
res = erts_bld_uint(&hp, NULL, MAX_GEN_GCS(rp));
break;
}
- case am_min_heap_size: {
- Uint hsz = 3;
+ case ERTS_PI_IX_MIN_HEAP_SIZE: {
+ Uint hsz = 0;
(void) erts_bld_uint(NULL, &hsz, MIN_HEAP_SIZE(rp));
- hp = HAlloc(BIF_P, hsz);
+ hp = erts_produce_heap(hfact, hsz, reserve_size);
res = erts_bld_uint(&hp, NULL, MIN_HEAP_SIZE(rp));
break;
}
- case am_min_bin_vheap_size: {
- Uint hsz = 3;
+ case ERTS_PI_IX_MIN_BIN_VHEAP_SIZE: {
+ Uint hsz = 0;
(void) erts_bld_uint(NULL, &hsz, MIN_VHEAP_SIZE(rp));
- hp = HAlloc(BIF_P, hsz);
+ hp = erts_produce_heap(hfact, hsz, reserve_size);
res = erts_bld_uint(&hp, NULL, MIN_VHEAP_SIZE(rp));
break;
}
- case am_max_heap_size: {
- Uint hsz = 3;
+ case ERTS_PI_IX_MAX_HEAP_SIZE: {
+ Uint hsz = 0;
(void) erts_max_heap_size_map(MAX_HEAP_SIZE_GET(rp),
MAX_HEAP_SIZE_FLAGS_GET(rp),
NULL, &hsz);
- hp = HAlloc(BIF_P, hsz);
+ hp = erts_produce_heap(hfact, hsz, reserve_size);
res = erts_max_heap_size_map(MAX_HEAP_SIZE_GET(rp),
MAX_HEAP_SIZE_FLAGS_GET(rp),
&hp, NULL);
break;
}
- case am_total_heap_size: {
- ErtsMessage *mp;
+ case ERTS_PI_IX_TOTAL_HEAP_SIZE: {
Uint total_heap_size;
- Uint hsz = 3;
+ Uint hsz = 0;
total_heap_size = rp->heap_sz;
if (rp->old_hend && rp->old_heap)
@@ -1425,44 +1757,53 @@ process_info_aux(Process *BIF_P,
total_heap_size += rp->mbuf_sz;
- if (rp->flags & F_ON_HEAP_MSGQ)
- for (mp = rp->msg.first; mp; mp = mp->next)
+ if (rp->flags & F_ON_HEAP_MSGQ) {
+ ErtsMessage *mp;
+ ASSERT(flags & ERTS_PI_FLAG_NEED_MSGQ_LEN);
+ for (mp = rp->sig_qs.first; mp; mp = mp->next) {
+ ASSERT(ERTS_SIG_IS_MSG(mp));
if (mp->data.attached)
total_heap_size += erts_msg_attached_data_size(mp);
+ }
+ *reds += (Uint) rp->sig_qs.len / 4;
+ }
(void) erts_bld_uint(NULL, &hsz, total_heap_size);
- hp = HAlloc(BIF_P, hsz);
+ hp = erts_produce_heap(hfact, hsz, reserve_size);
res = erts_bld_uint(&hp, NULL, total_heap_size);
break;
}
- case am_stack_size: {
+ case ERTS_PI_IX_STACK_SIZE: {
Uint stack_size = STACK_START(rp) - rp->stop;
- Uint hsz = 3;
+ Uint hsz = 0;
(void) erts_bld_uint(NULL, &hsz, stack_size);
- hp = HAlloc(BIF_P, hsz);
+ hp = erts_produce_heap(hfact, hsz, reserve_size);
res = erts_bld_uint(&hp, NULL, stack_size);
break;
}
- case am_memory: { /* Memory consumed in bytes */
- Uint hsz = 3;
+ case ERTS_PI_IX_MEMORY: { /* Memory consumed in bytes */
+ Uint hsz = 0;
Uint size = erts_process_memory(rp, 0);
(void) erts_bld_uint(NULL, &hsz, size);
- hp = HAlloc(BIF_P, hsz);
+ hp = erts_produce_heap(hfact, hsz, reserve_size);
res = erts_bld_uint(&hp, NULL, size);
+
+ ASSERT(flags & ERTS_PI_FLAG_NEED_MSGQ_LEN);
+ *reds += (Uint) rp->sig_qs.len / 4;
+
break;
}
- case am_garbage_collection: {
+ case ERTS_PI_IX_GARBAGE_COLLECTION: {
DECL_AM(minor_gcs);
Eterm t;
Uint map_sz = 0;
erts_max_heap_size_map(MAX_HEAP_SIZE_GET(rp), MAX_HEAP_SIZE_FLAGS_GET(rp), NULL, &map_sz);
- hp = HAlloc(BIF_P, 3+2 + 3+2 + 3+2 + 3+2 + 3+2 + map_sz + 3);
- /* last "3" is for outside tuple */
+ hp = erts_produce_heap(hfact, 3+2 + 3+2 + 3+2 + 3+2 + 3+2 + map_sz, reserve_size);
t = TUPLE2(hp, AM_minor_gcs, make_small(GEN_GCS(rp))); hp += 3;
res = CONS(hp, t, NIL); hp += 2;
@@ -1481,89 +1822,76 @@ process_info_aux(Process *BIF_P,
break;
}
- case am_garbage_collection_info: {
+ case ERTS_PI_IX_GARBAGE_COLLECTION_INFO: {
Uint sz = 0, actual_sz = 0;
- if (rp == BIF_P) {
- sz += ERTS_PROCESS_GC_INFO_MAX_SIZE;
- } else {
- erts_process_gc_info(rp, &sz, NULL, 0, 0);
- sz += 3;
- }
+ erts_process_gc_info(rp, &sz, NULL, 0, 0);
- hp = HAlloc(BIF_P, sz);
+ hp = erts_produce_heap(hfact, sz, reserve_size);
res = erts_process_gc_info(rp, &actual_sz, &hp, 0, 0);
- /* We may have some extra space, fill with 0 tuples */
- if (actual_sz <= sz - 3) {
- for (; actual_sz < sz - 3; hp++, actual_sz++)
- hp[0] = make_arityval(0);
- } else {
- for (; actual_sz < sz; hp++, actual_sz++)
- hp[0] = make_arityval(0);
- hp = HAlloc(BIF_P, 3);
- }
-
break;
}
- case am_group_leader: {
+ case ERTS_PI_IX_GROUP_LEADER: {
int sz = NC_HEAP_SIZE(rp->group_leader);
- hp = HAlloc(BIF_P, 3 + sz);
- res = STORE_NC(&hp, &MSO(BIF_P), rp->group_leader);
+ hp = erts_produce_heap(hfact, sz, reserve_size);
+ res = STORE_NC(&hp, hfact->off_heap, rp->group_leader);
break;
}
- case am_reductions: {
- Uint reds = rp->reds + erts_current_reductions(BIF_P, rp);
- Uint hsz = 3;
+ case ERTS_PI_IX_REDUCTIONS: {
+ Uint reds = rp->reds + erts_current_reductions(c_p, rp);
+ Uint hsz = 0;
(void) erts_bld_uint(NULL, &hsz, reds);
- hp = HAlloc(BIF_P, hsz);
+ hp = erts_produce_heap(hfact, hsz, reserve_size);
res = erts_bld_uint(&hp, NULL, reds);
break;
}
- case am_priority:
- hp = HAlloc(BIF_P, 3);
- res = erts_get_process_priority(rp);
+ case ERTS_PI_IX_PRIORITY: {
+ erts_aint32_t state = erts_atomic32_read_nob(&rp->state);
+ if (ERTS_PSFLG_EXITING & state)
+ return am_undefined;
+ res = erts_get_process_priority(state);
break;
+ }
- case am_trace:
- hp = HAlloc(BIF_P, 3);
+ case ERTS_PI_IX_TRACE:
res = make_small(ERTS_TRACE_FLAGS(rp) & TRACEE_FLAGS);
break;
- case am_binary: {
- Uint sz = 3;
+ case ERTS_PI_IX_BINARY: {
+ Uint sz = 0;
(void) bld_bin_list(NULL, &sz, &MSO(rp));
- hp = HAlloc(BIF_P, sz);
+ hp = erts_produce_heap(hfact, sz, reserve_size);
res = bld_bin_list(&hp, NULL, &MSO(rp));
break;
}
- case am_sequential_trace_token:
- res = copy_object(rp->seq_trace_token, BIF_P);
- hp = HAlloc(BIF_P, 3);
+ case ERTS_PI_IX_SEQUENTIAL_TRACE_TOKEN: {
+ Uint sz = size_object(rp->seq_trace_token);
+ hp = erts_produce_heap(hfact, sz, reserve_size);
+ res = copy_struct(rp->seq_trace_token, sz, &hp, hfact->off_heap);
break;
+ }
- case am_catchlevel:
- hp = HAlloc(BIF_P, 3);
- res = make_small(catchlevel(BIF_P));
+ case ERTS_PI_IX_CATCHLEVEL:
+ res = make_small(catchlevel(rp));
break;
- case am_backtrace: {
+ case ERTS_PI_IX_BACKTRACE: {
erts_dsprintf_buf_t *dsbufp = erts_create_tmp_dsbuf(0);
erts_stack_dump(ERTS_PRINT_DSBUF, (void *) dsbufp, rp);
- res = new_binary(BIF_P, (byte *) dsbufp->str, dsbufp->str_len);
+ res = erts_heap_factory_new_binary(hfact, (byte *) dsbufp->str,
+ dsbufp->str_len, reserve_size);
erts_destroy_tmp_dsbuf(dsbufp);
- hp = HAlloc(BIF_P, 3);
break;
}
- case am_last_calls: {
+ case ERTS_PI_IX_LAST_CALLS: {
struct saved_calls *scb = ERTS_PROC_GET_SAVED_CALLS_BUF(rp);
if (!scb) {
- hp = HAlloc(BIF_P, 3);
res = am_false;
} else {
/*
@@ -1571,23 +1899,34 @@ process_info_aux(Process *BIF_P,
* Might be less than that, if there are sends, receives or timeouts,
* so we must do a HRelease() to avoid creating holes.
*/
- Uint needed = scb->n*(2+4) + 3;
- Eterm* limit;
+ Sint needed = scb->n*(2+4);
Eterm term, list;
int i, j;
+ Export *exp;
+
+ reserve_size += needed;
- hp = HAlloc(BIF_P, needed);
- limit = hp + needed;
list = NIL;
for (i = 0; i < scb->n; i++) {
+ Uint sz;
j = scb->cur - i - 1;
if (j < 0)
j += scb->len;
- if (scb->ct[j] == &exp_send)
+
+ sz = 2;
+ exp = scb->ct[j];
+ if (exp != &exp_send && exp != &exp_receive && exp != &exp_timeout)
+ sz += 4;
+
+ needed -= sz;
+ ERTS_PI_UNRESERVE(reserve_size, sz);
+ hp = erts_produce_heap(hfact, sz, reserve_size);
+
+ if (exp == &exp_send)
term = am_send;
- else if (scb->ct[j] == &exp_receive)
+ else if (exp == &exp_receive)
term = am_receive;
- else if (scb->ct[j] == &exp_timeout)
+ else if (exp == &exp_timeout)
term = am_timeout;
else {
term = TUPLE3(hp,
@@ -1597,18 +1936,18 @@ process_info_aux(Process *BIF_P,
hp += 4;
}
list = CONS(hp, term, list);
- hp += 2;
}
+
+ ASSERT(needed >= 0);
+ if (needed > 0)
+ reserve_size -= needed;
+
res = list;
- res = TUPLE2(hp, item, res);
- hp += 3;
- HRelease(BIF_P,limit,hp);
- return res;
}
break;
}
- case am_message_queue_data:
+ case ERTS_PI_IX_MESSAGE_QUEUE_DATA:
switch (rp->flags & (F_OFF_HEAP_MSGQ|F_ON_HEAP_MSGQ)) {
case F_OFF_HEAP_MSGQ:
res = am_off_heap;
@@ -1621,14 +1960,15 @@ process_info_aux(Process *BIF_P,
ERTS_INTERNAL_ERROR("Inconsistent message queue management state");
break;
}
- hp = HAlloc(BIF_P, 3);
break;
- case am_magic_ref: {
- Uint sz = 3;
+ case ERTS_PI_IX_MAGIC_REF: {
+ Uint sz = 0;
(void) bld_magic_ref_bin_list(NULL, &sz, &MSO(rp));
- hp = HAlloc(BIF_P, sz);
+ hp = erts_produce_heap(hfact, sz, 0);
res = bld_magic_ref_bin_list(&hp, NULL, &MSO(rp));
+
+ *reds += (Uint) 10;
break;
}
@@ -1637,12 +1977,17 @@ process_info_aux(Process *BIF_P,
}
- return TUPLE2(hp, item, res);
+ ERTS_PI_UNRESERVE(reserve_size, 3);
+ *reserve_sizep = reserve_size;
+ hp = erts_produce_heap(hfact, 3, reserve_size);
+
+ return TUPLE2(hp, pi_ix2arg(item_ix), res);
}
#undef MI_INC
static Eterm
-current_function(Process* BIF_P, Process* rp, Eterm** hpp, int full_info)
+current_function(Process *c_p, ErtsHeapFactory *hfact, Process* rp,
+ int full_info, Uint reserve_size, int flags)
{
Eterm* hp;
Eterm res;
@@ -1659,7 +2004,7 @@ current_function(Process* BIF_P, Process* rp, Eterm** hpp, int full_info)
}
}
- if (BIF_P == rp) {
+ if (c_p == rp && !(flags & ERTS_PI_FLAG_REQUEST_FOR_OTHER)) {
FunctionInfo fi2;
/*
@@ -1679,24 +2024,22 @@ current_function(Process* BIF_P, Process* rp, Eterm** hpp, int full_info)
* Return the result.
*/
if (rp->current == NULL) {
- hp = HAlloc(BIF_P, 3);
res = am_undefined;
} else if (full_info) {
- hp = HAlloc(BIF_P, 3+fi.needed);
- hp = erts_build_mfa_item(&fi, hp, am_true, &res);
+ hp = erts_produce_heap(hfact, fi.needed, reserve_size);
+ erts_build_mfa_item(&fi, hp, am_true, &res);
} else {
- hp = HAlloc(BIF_P, 3+4);
+ hp = erts_produce_heap(hfact, 4, reserve_size);
res = TUPLE3(hp, rp->current->module,
rp->current->function,
make_small(rp->current->arity));
- hp += 4;
}
- *hpp = hp;
return res;
}
static Eterm
-current_stacktrace(Process* p, Process* rp, Eterm** hpp)
+current_stacktrace(ErtsHeapFactory *hfact, Process* rp,
+ Uint reserve_size)
{
Uint sz;
struct StackTrace* s;
@@ -1705,7 +2048,7 @@ current_stacktrace(Process* p, Process* rp, Eterm** hpp)
FunctionInfo* stkp;
Uint heap_size;
int i;
- Eterm* hp = *hpp;
+ Eterm* hp;
Eterm mfa;
Eterm res = NIL;
@@ -1735,17 +2078,23 @@ current_stacktrace(Process* p, Process* rp, Eterm** hpp)
}
}
- hp = HAlloc(p, heap_size);
+ reserve_size += heap_size;
+
+ /*
+ * We intentionally produce heap in small chunks
+ * (for more info see process_info_aux()).
+ */
while (stkp > stk) {
stkp--;
+ sz = stkp->needed + 2;
+ ERTS_PI_UNRESERVE(reserve_size, sz);
+ hp = erts_produce_heap(hfact, sz, reserve_size);
hp = erts_build_mfa_item(stkp, hp, am_true, &mfa);
res = CONS(hp, mfa, res);
- hp += 2;
}
erts_free(ERTS_ALC_T_TMP, stk);
erts_free(ERTS_ALC_T_TMP, s);
- *hpp = hp;
return res;
}
@@ -1754,7 +2103,7 @@ static int check_if_xml(void)
{
char buf[1];
size_t bufsz = sizeof(buf);
- return erts_sys_getenv_raw("VALGRIND_LOG_XML", buf, &bufsz) >= 0;
+ return erts_sys_explicit_8bit_getenv("VALGRIND_LOG_XML", buf, &bufsz) >= 0;
}
#else
#define check_if_xml() 0
@@ -1803,45 +2152,6 @@ info_1_tuple(Process* BIF_P, /* Pointer to current process. */
return make_small(sizeof(UWord));
}
goto badarg;
- } else if (sel == am_allocated) {
- if (arity == 2) {
- Eterm res = THE_NON_VALUE;
- char *buf;
- Sint len = is_string(*tp);
- if (len <= 0)
- return res;
- buf = (char *) erts_alloc(ERTS_ALC_T_TMP, len+1);
- if (intlist_to_buf(*tp, buf, len) != len)
- erts_exit(ERTS_ERROR_EXIT, "%s:%d: Internal error\n", __FILE__, __LINE__);
- buf[len] = '\0';
- res = erts_instr_dump_memory_map(buf) ? am_true : am_false;
- erts_free(ERTS_ALC_T_TMP, (void *) buf);
- if (is_non_value(res))
- goto badarg;
- return res;
- }
- else if (arity == 3 && tp[0] == am_status) {
- if (is_atom(tp[1]))
- return erts_instr_get_stat(BIF_P, tp[1], 1);
- else {
- Eterm res = THE_NON_VALUE;
- char *buf;
- Sint len = is_string(tp[1]);
- if (len <= 0)
- return res;
- buf = (char *) erts_alloc(ERTS_ALC_T_TMP, len+1);
- if (intlist_to_buf(tp[1], buf, len) != len)
- erts_exit(ERTS_ERROR_EXIT, "%s:%d: Internal error\n", __FILE__, __LINE__);
- buf[len] = '\0';
- res = erts_instr_dump_stat(buf, 1) ? am_true : am_false;
- erts_free(ERTS_ALC_T_TMP, (void *) buf);
- if (is_non_value(res))
- goto badarg;
- return res;
- }
- }
- else
- goto badarg;
} else if (sel == am_allocator) {
switch (arity) {
case 2:
@@ -2209,8 +2519,6 @@ BIF_RETTYPE system_info_1(BIF_ALIST_1)
} else if (BIF_ARG_1 == am_allocated_areas) {
res = erts_allocated_areas(NULL, NULL, BIF_P);
BIF_RET(res);
- } else if (BIF_ARG_1 == am_allocated) {
- BIF_RET(erts_instr_get_memory_map(BIF_P));
} else if (BIF_ARG_1 == am_hipe_architecture) {
#if defined(HIPE)
BIF_RET(hipe_arch_name);
@@ -2351,9 +2659,6 @@ BIF_RETTYPE system_info_1(BIF_ALIST_1)
sizeof(ERLANG_ARCHITECTURE)-1,
NIL));
}
- else if (BIF_ARG_1 == am_memory_types) {
- return erts_instr_get_type_info(BIF_P);
- }
else if (BIF_ARG_1 == am_os_type) {
BIF_RET(os_type_tuple);
}
@@ -2378,12 +2683,12 @@ BIF_RETTYPE system_info_1(BIF_ALIST_1)
BIF_RET(os_version_tuple);
}
else if (BIF_ARG_1 == am_version) {
- int n = strlen(ERLANG_VERSION);
+ int n = sys_strlen(ERLANG_VERSION);
hp = HAlloc(BIF_P, ((sizeof ERLANG_VERSION)-1) * 2);
BIF_RET(buf_to_intlist(&hp, ERLANG_VERSION, n, NIL));
}
else if (BIF_ARG_1 == am_machine) {
- int n = strlen(EMULATOR);
+ int n = sys_strlen(EMULATOR);
hp = HAlloc(BIF_P, n*2);
BIF_RET(buf_to_intlist(&hp, EMULATOR, n, NIL));
}
@@ -2409,7 +2714,7 @@ BIF_RETTYPE system_info_1(BIF_ALIST_1)
res = erts_bld_cons(hpp, hszp,
erts_bld_tuple(hpp, hszp, 2,
erts_atom_put((byte *)opc[i].name,
- strlen(opc[i].name),
+ sys_strlen(opc[i].name),
ERTS_ATOM_ENC_LATIN1,
1),
erts_bld_uint(hpp, hszp,
@@ -2805,21 +3110,21 @@ BIF_RETTYPE system_info_1(BIF_ALIST_1)
Uint sz;
Eterm res = NIL, tup, text;
Eterm *hp = HAlloc(BIF_P, 3*(2 + 3) + /* three 2-tuples and three cons */
- 2*(strlen(erts_build_flags_CONFIG_H) +
- strlen(erts_build_flags_CFLAGS) +
- strlen(erts_build_flags_LDFLAGS)));
+ 2*(sys_strlen(erts_build_flags_CONFIG_H) +
+ sys_strlen(erts_build_flags_CFLAGS) +
+ sys_strlen(erts_build_flags_LDFLAGS)));
- sz = strlen(erts_build_flags_CONFIG_H);
+ sz = sys_strlen(erts_build_flags_CONFIG_H);
text = buf_to_intlist(&hp, erts_build_flags_CONFIG_H, sz, NIL);
tup = TUPLE2(hp, am_config_h, text); hp += 3;
res = CONS(hp, tup, res); hp += 2;
- sz = strlen(erts_build_flags_CFLAGS);
+ sz = sys_strlen(erts_build_flags_CFLAGS);
text = buf_to_intlist(&hp, erts_build_flags_CFLAGS, sz, NIL);
tup = TUPLE2(hp, am_cflags, text); hp += 3;
res = CONS(hp, tup, res); hp += 2;
- sz = strlen(erts_build_flags_LDFLAGS);
+ sz = sys_strlen(erts_build_flags_LDFLAGS);
text = buf_to_intlist(&hp, erts_build_flags_LDFLAGS, sz, NIL);
tup = TUPLE2(hp, am_ldflags, text); hp += 3;
res = CONS(hp, tup, res); hp += 2;
@@ -2863,6 +3168,16 @@ BIF_RETTYPE system_info_1(BIF_ALIST_1)
BIF_ERROR(BIF_P, BADARG);
}
+static void monitor_size(ErtsMonitor *mon, void *vsz)
+{
+ *((Uint *) vsz) = erts_monitor_size(mon);
+}
+
+static void link_size(ErtsMonitor *lnk, void *vsz)
+{
+ *((Uint *) vsz) = erts_link_size(lnk);
+}
+
/**********************************************************************/
/* Return information on ports */
/* Info:
@@ -2898,7 +3213,7 @@ erts_bld_port_info(Eterm **hpp, ErlOffHeap *ohp, Uint *szp, Port *prt,
INIT_MONITOR_INFOS(mic);
- erts_doforall_links(ERTS_P_LINKS(prt), &collect_one_link, &mic);
+ erts_link_tree_foreach(ERTS_P_LINKS(prt), collect_one_link, (void *) &mic);
if (szp)
*szp += mic.sz;
@@ -2922,11 +3237,11 @@ erts_bld_port_info(Eterm **hpp, ErlOffHeap *ohp, Uint *szp, Port *prt,
else if (item == am_monitors) {
MonitorInfoCollection mic;
int i;
- Eterm item;
INIT_MONITOR_INFOS(mic);
- erts_doforall_monitors(ERTS_P_MONITORS(prt),
- &collect_one_origin_monitor, &mic);
+ erts_monitor_tree_foreach(ERTS_P_MONITORS(prt),
+ collect_one_origin_monitor,
+ (void *) &mic);
if (szp)
*szp += mic.sz;
@@ -2935,11 +3250,10 @@ erts_bld_port_info(Eterm **hpp, ErlOffHeap *ohp, Uint *szp, Port *prt,
res = NIL;
for (i = 0; i < mic.mi_i; i++) {
Eterm t;
- Eterm m_type;
- item = STORE_NC(hpp, ohp, mic.mi[i].entity.term);
- m_type = is_port(item) ? am_port : am_process;
- t = TUPLE2(*hpp, m_type, item);
+ ASSERT(mic.mi[i].type == ERTS_MON_TYPE_PORT);
+ ASSERT(is_internal_pid(mic.mi[i].entity.term));
+ t = TUPLE2(*hpp, am_process, mic.mi[i].entity.term);
*hpp += 3;
res = CONS(*hpp, t, res);
*hpp += 2;
@@ -2958,15 +3272,19 @@ erts_bld_port_info(Eterm **hpp, ErlOffHeap *ohp, Uint *szp, Port *prt,
Eterm item;
INIT_MONITOR_INFOS(mic);
- erts_doforall_monitors(ERTS_P_MONITORS(prt),
- &collect_one_target_monitor, &mic);
+ erts_monitor_list_foreach(ERTS_P_LT_MONITORS(prt),
+ collect_one_target_monitor,
+ (void *) &mic);
+ erts_monitor_tree_foreach(ERTS_P_MONITORS(prt),
+ collect_one_target_monitor,
+ (void *) &mic);
if (szp)
*szp += mic.sz;
if (hpp) {
res = NIL;
for (i = 0; i < mic.mi_i; ++i) {
- ASSERT(mic.mi[i].node == NIL);
+ ASSERT(mic.mi[i].type != ERTS_MON_TYPE_RESOURCE);
item = STORE_NC(hpp, ohp, mic.mi[i].entity.term);
res = CONS(*hpp, item, res);
*hpp += 2;
@@ -3043,7 +3361,12 @@ erts_bld_port_info(Eterm **hpp, ErlOffHeap *ohp, Uint *szp, Port *prt,
*/
Uint size = 0;
- erts_doforall_links(ERTS_P_LINKS(prt), &erts_one_link_size, &size);
+ erts_link_tree_foreach(ERTS_P_LINKS(prt),
+ link_size, (void *) &size);
+ erts_monitor_tree_foreach(ERTS_P_MONITORS(prt),
+ monitor_size, (void *) &size);
+ erts_monitor_list_foreach(ERTS_P_LT_MONITORS(prt),
+ monitor_size, (void *) &size);
size += erts_port_data_size(prt);
@@ -3265,35 +3588,53 @@ fun_info_mfa_1(BIF_ALIST_1)
BIF_ERROR(p, BADARG);
}
+BIF_RETTYPE erts_internal_is_process_alive_2(BIF_ALIST_2)
+{
+ if (!is_internal_pid(BIF_ARG_1) || !is_internal_ordinary_ref(BIF_ARG_2))
+ BIF_ERROR(BIF_P, BADARG);
+ erts_proc_sig_send_is_alive_request(BIF_P, BIF_ARG_1, BIF_ARG_2);
+ BIF_RET(am_ok);
+}
+
BIF_RETTYPE is_process_alive_1(BIF_ALIST_1)
{
- if(is_internal_pid(BIF_ARG_1)) {
- Process *rp;
-
- if (BIF_ARG_1 == BIF_P->common.id)
- BIF_RET(am_true);
-
- rp = erts_proc_lookup_raw(BIF_ARG_1);
- if (!rp) {
- BIF_RET(am_false);
- }
- else {
- if (erts_atomic32_read_acqb(&rp->state)
- & (ERTS_PSFLG_PENDING_EXIT|ERTS_PSFLG_EXITING))
- ERTS_BIF_AWAIT_X_DATA_TRAP(BIF_P, BIF_ARG_1, am_false);
- else
- BIF_RET(am_true);
- }
- }
- else if(is_external_pid(BIF_ARG_1)) {
- if(external_pid_dist_entry(BIF_ARG_1) == erts_this_dist_entry)
+ if (is_internal_pid(BIF_ARG_1)) {
+ erts_aint32_t state;
+ Process *rp;
+
+ if (BIF_ARG_1 == BIF_P->common.id)
+ BIF_RET(am_true);
+
+ rp = erts_proc_lookup_raw(BIF_ARG_1);
+ if (!rp)
+ BIF_RET(am_false);
+
+ state = erts_atomic32_read_acqb(&rp->state);
+ if (state & (ERTS_PSFLG_EXITING
+ | ERTS_PSFLG_SIG_Q
+ | ERTS_PSFLG_SIG_IN_Q)) {
+ /*
+ * If in exiting state, trap out and send 'is alive'
+ * request and wait for it to complete termination.
+ *
+ * If process has signals enqueued, we need to
+ * send it an 'is alive' request via its signal
+ * queue in order to ensure that signal order is
+ * preserved (we may earlier have sent it an
+ * exit signal that has not been processed yet).
+ */
+ BIF_TRAP1(is_process_alive_trap, BIF_P, BIF_ARG_1);
+ }
+
+ BIF_RET(am_true);
+ }
+
+ if (is_external_pid(BIF_ARG_1)) {
+ if (external_pid_dist_entry(BIF_ARG_1) == erts_this_dist_entry)
BIF_RET(am_false); /* A pid from an old incarnation of this node */
- else
- BIF_ERROR(BIF_P, BADARG);
- }
- else {
- BIF_ERROR(BIF_P, BADARG);
}
+
+ BIF_ERROR(BIF_P, BADARG);
}
BIF_RETTYPE process_display_2(BIF_ALIST_2)
@@ -3311,16 +3652,6 @@ BIF_RETTYPE process_display_2(BIF_ALIST_2)
if (rp == ERTS_PROC_LOCK_BUSY)
ERTS_BIF_YIELD2(bif_export[BIF_process_display_2], BIF_P,
BIF_ARG_1, BIF_ARG_2);
- if (rp != BIF_P && ERTS_PROC_PENDING_EXIT(rp)) {
- Eterm args[2] = {BIF_ARG_1, BIF_ARG_2};
- erts_proc_unlock(rp, ERTS_PROC_LOCKS_ALL);
- ERTS_BIF_AWAIT_X_APPLY_TRAP(BIF_P,
- BIF_ARG_1,
- am_erlang,
- am_process_display,
- args,
- 2);
- }
erts_stack_dump(ERTS_PRINT_STDERR, NULL, rp);
erts_proc_unlock(rp, (BIF_P == rp
? ERTS_PROC_LOCKS_ALL_MINOR
@@ -3591,7 +3922,7 @@ BIF_RETTYPE erts_debug_get_internal_state_1(BIF_ALIST_1)
Eterm res = NIL;
Uint *hp = HAlloc(BIF_P, 2*ERTS_PI_ARGS);
for (i = ERTS_PI_ARGS-1; i >= 0; i--) {
- res = CONS(hp, pi_args[i], res);
+ res = CONS(hp, pi_args[i].name, res);
hp += 2;
}
BIF_RET(res);
@@ -3674,7 +4005,22 @@ BIF_RETTYPE erts_debug_get_internal_state_1(BIF_ALIST_1)
Eterm *hp = HAlloc(BIF_P, hsz);
BIF_RET(uword_to_big(size, hp));
}
+ } else if (ERTS_IS_ATOM_STR("scheduler_dump", BIF_ARG_1)) {
+#if defined(ERTS_HAVE_TRY_CATCH) && defined(ERTS_SYS_SUSPEND_SIGNAL)
+ BIF_RET(am_true);
+#else
+ BIF_RET(am_false);
+#endif
+ }
+ else if (ERTS_IS_ATOM_STR("lc_graph", BIF_ARG_1)) {
+#ifdef ERTS_ENABLE_LOCK_CHECK
+ Eterm res = erts_lc_dump_graph();
+ BIF_RET(res);
+#else
+ BIF_RET(am_notsup);
+#endif
}
+
}
else if (is_tuple(BIF_ARG_1)) {
Eterm* tp = tuple_val(BIF_ARG_1);
@@ -3687,22 +4033,59 @@ BIF_RETTYPE erts_debug_get_internal_state_1(BIF_ALIST_1)
BIF_RET(erts_process_status(NULL, tp[2]));
}
}
+ else if (ERTS_IS_ATOM_STR("connection_id", tp[1])) {
+ DistEntry *dep;
+ Eterm *hp, res;
+ Uint con_id, hsz = 0;
+ if (!is_atom(tp[2]))
+ BIF_ERROR(BIF_P, BADARG);
+ dep = erts_sysname_to_connected_dist_entry(tp[2]);
+ if (!dep)
+ BIF_ERROR(BIF_P, BADARG);
+ erts_de_rlock(dep);
+ con_id = (Uint) dep->connection_id;
+ erts_de_runlock(dep);
+ (void) erts_bld_uint(NULL, &hsz, con_id);
+ hp = hsz ? HAlloc(BIF_P, hsz) : NULL;
+ res = erts_bld_uint(&hp, NULL, con_id);
+ BIF_RET(res);
+ }
else if (ERTS_IS_ATOM_STR("link_list", tp[1])) {
/* Used by erl_link_SUITE (emulator) */
if(is_internal_pid(tp[2])) {
+ erts_aint32_t state;
Eterm res;
Process *p;
+ int sigs_done, local_only;
p = erts_pid2proc(BIF_P,
ERTS_PROC_LOCK_MAIN,
tp[2],
- ERTS_PROC_LOCK_LINK);
+ ERTS_PROC_LOCK_MAIN);
if (!p) {
ERTS_ASSERT_IS_NOT_EXITING(BIF_P);
BIF_RET(am_undefined);
}
- res = make_link_list(BIF_P, ERTS_P_LINKS(p), NIL);
- erts_proc_unlock(p, ERTS_PROC_LOCK_LINK);
+
+ local_only = 0;
+ do {
+ int reds = CONTEXT_REDS;
+ sigs_done = erts_proc_sig_handle_incoming(p,
+ &state,
+ &reds,
+ CONTEXT_REDS,
+ local_only);
+ local_only = !0;
+ } while (!sigs_done && !(state & ERTS_PSFLG_EXITING));
+
+ if (!(state & ERTS_PSFLG_EXITING))
+ res = make_link_list(BIF_P, 1, ERTS_P_LINKS(p), NIL);
+ else if (BIF_P == p)
+ ERTS_BIF_EXITED(BIF_P);
+ else
+ res = am_undefined;
+ if (BIF_P != p)
+ erts_proc_unlock(p, ERTS_PROC_LOCK_MAIN);
BIF_RET(res);
}
else if(is_internal_port(tp[2])) {
@@ -3713,19 +4096,20 @@ BIF_RETTYPE erts_debug_get_internal_state_1(BIF_ALIST_1)
ERTS_PORT_SFLGS_INVALID_LOOKUP);
if(!p)
BIF_RET(am_undefined);
- res = make_link_list(BIF_P, ERTS_P_LINKS(p), NIL);
+ res = make_link_list(BIF_P, 1, ERTS_P_LINKS(p), NIL);
erts_port_release(p);
BIF_RET(res);
}
else if(is_node_name_atom(tp[2])) {
DistEntry *dep = erts_find_dist_entry(tp[2]);
if(dep) {
- Eterm subres;
- erts_de_links_lock(dep);
- subres = make_link_list(BIF_P, dep->nlinks, NIL);
- subres = make_link_list(BIF_P, dep->node_links, subres);
- erts_de_links_unlock(dep);
- BIF_RET(subres);
+ Eterm res = NIL;
+ if (dep->mld) {
+ erts_mtx_lock(&dep->mld->mtx);
+ res = make_link_list(BIF_P, 0, dep->mld->links, NIL);
+ erts_mtx_unlock(&dep->mld->mtx);
+ }
+ BIF_RET(res);
} else {
BIF_RET(am_undefined);
}
@@ -3734,27 +4118,54 @@ BIF_RETTYPE erts_debug_get_internal_state_1(BIF_ALIST_1)
else if (ERTS_IS_ATOM_STR("monitor_list", tp[1])) {
/* Used by erl_link_SUITE (emulator) */
if(is_internal_pid(tp[2])) {
+ erts_aint32_t state;
Process *p;
Eterm res;
+ int sigs_done, local_only;
p = erts_pid2proc(BIF_P,
ERTS_PROC_LOCK_MAIN,
tp[2],
- ERTS_PROC_LOCK_LINK);
+ ERTS_PROC_LOCK_MAIN);
if (!p) {
ERTS_ASSERT_IS_NOT_EXITING(BIF_P);
BIF_RET(am_undefined);
}
- res = make_monitor_list(BIF_P, ERTS_P_MONITORS(p));
- erts_proc_unlock(p, ERTS_PROC_LOCK_LINK);
+
+ local_only = 0;
+ do {
+ int reds = CONTEXT_REDS;
+ sigs_done = erts_proc_sig_handle_incoming(p,
+ &state,
+ &reds,
+ CONTEXT_REDS,
+ local_only);
+ local_only = !0;
+ } while (!sigs_done && !(state & ERTS_PSFLG_EXITING));
+
+ if (!(state & ERTS_PSFLG_EXITING)) {
+ res = make_monitor_list(BIF_P, 1, ERTS_P_MONITORS(p), NIL);
+ res = make_monitor_list(BIF_P, 0, ERTS_P_LT_MONITORS(p), res);
+ }
+ else {
+ if (BIF_P == p)
+ ERTS_BIF_EXITED(BIF_P);
+ else
+ res = am_undefined;
+ }
+ if (BIF_P != p)
+ erts_proc_unlock(p, ERTS_PROC_LOCK_MAIN);
BIF_RET(res);
} else if(is_node_name_atom(tp[2])) {
DistEntry *dep = erts_find_dist_entry(tp[2]);
if(dep) {
- Eterm ml;
- erts_de_links_lock(dep);
- ml = make_monitor_list(BIF_P, dep->monitors);
- erts_de_links_unlock(dep);
+ Eterm ml = NIL;
+ if (dep->mld) {
+ erts_mtx_lock(&dep->mld->mtx);
+ ml = make_monitor_list(BIF_P, 1, dep->mld->orig_name_monitors, NIL);
+ ml = make_monitor_list(BIF_P, 0, dep->mld->monitors, ml);
+ erts_mtx_unlock(&dep->mld->mtx);
+ }
BIF_RET(ml);
} else {
BIF_RET(am_undefined);
@@ -3772,18 +4183,6 @@ BIF_RETTYPE erts_debug_get_internal_state_1(BIF_ALIST_1)
}
BIF_RET(res);
}
- else if (ERTS_IS_ATOM_STR("have_pending_exit", tp[1])) {
- Process *rp = erts_pid2proc(BIF_P, ERTS_PROC_LOCK_MAIN,
- tp[2], ERTS_PROC_LOCK_STATUS);
- if (!rp) {
- BIF_RET(am_undefined);
- }
- else {
- Eterm res = ERTS_PROC_PENDING_EXIT(rp) ? am_true : am_false;
- erts_proc_unlock(rp, ERTS_PROC_LOCK_STATUS);
- BIF_RET(res);
- }
- }
else if (ERTS_IS_ATOM_STR("binary_info", tp[1])) {
Eterm bin = tp[2];
if (is_binary(bin)) {
@@ -3824,10 +4223,10 @@ BIF_RETTYPE erts_debug_get_internal_state_1(BIF_ALIST_1)
BIF_RET(res);
}
}
- else if (ERTS_IS_ATOM_STR("term_to_binary_no_funs", tp[1])) {
- Uint dflags = (DFLAG_EXTENDED_REFERENCES |
- DFLAG_EXTENDED_PIDS_PORTS |
- DFLAG_BIT_BINARIES);
+ 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);
BIF_RET(erts_term_to_binary(BIF_P, tp[2], 0, dflags));
}
else if (ERTS_IS_ATOM_STR("dist_ctrl", tp[1])) {
@@ -3877,7 +4276,7 @@ BIF_RETTYPE erts_debug_get_internal_state_1(BIF_ALIST_1)
while (ix >= atom_table_size()) {
char tmp[20];
erts_snprintf(tmp, sizeof(tmp), "am%x", atom_table_size());
- erts_atom_put((byte *) tmp, strlen(tmp), ERTS_ATOM_ENC_LATIN1, 1);
+ erts_atom_put((byte *) tmp, sys_strlen(tmp), ERTS_ATOM_ENC_LATIN1, 1);
}
return make_atom(ix);
}
@@ -4110,57 +4509,6 @@ BIF_RETTYPE erts_debug_set_internal_state_2(BIF_ALIST_2)
erts_set_gc_state(BIF_P, enable);
BIF_RET(res);
}
- else if (ERTS_IS_ATOM_STR("send_fake_exit_signal", BIF_ARG_1)) {
- /* Used by signal_SUITE (emulator) */
-
- /* Testcases depend on the exit being received via
- a pending exit when the receiver is the same as
- the caller. */
- if (is_tuple(BIF_ARG_2)) {
- Eterm* tp = tuple_val(BIF_ARG_2);
- if (arityval(tp[0]) == 3
- && (is_pid(tp[1]) || is_port(tp[1]))
- && is_internal_pid(tp[2])) {
- int xres;
- ErtsProcLocks rp_locks = ERTS_PROC_LOCKS_XSIG_SEND;
- Process *rp = erts_pid2proc(BIF_P, ERTS_PROC_LOCK_MAIN,
- tp[2], rp_locks);
- if (!rp) {
- DECL_AM(dead);
- BIF_RET(AM_dead);
- }
-
- if (BIF_P == rp)
- rp_locks |= ERTS_PROC_LOCK_MAIN;
- xres = erts_send_exit_signal(NULL, /* NULL in order to
- force a pending exit
- when we send to our
- selves. */
- tp[1],
- rp,
- &rp_locks,
- tp[3],
- NIL,
- NULL,
- 0);
- if (BIF_P == rp)
- rp_locks &= ~ERTS_PROC_LOCK_MAIN;
- erts_proc_unlock(rp, rp_locks);
- if (xres > 1) {
- DECL_AM(message);
- BIF_RET(AM_message);
- }
- else if (xres == 0) {
- DECL_AM(unaffected);
- BIF_RET(AM_unaffected);
- }
- else {
- DECL_AM(exit);
- BIF_RET(AM_exit);
- }
- }
- }
- }
else if (ERTS_IS_ATOM_STR("colliding_names", BIF_ARG_1)) {
/* Used by ets_SUITE (stdlib) */
if (is_tuple(BIF_ARG_2)) {
@@ -4328,11 +4676,73 @@ BIF_RETTYPE erts_debug_set_internal_state_2(BIF_ALIST_2)
BIF_RET(res);
}
}
+ else if (ERTS_IS_ATOM_STR("binary", BIF_ARG_1)) {
+ Sint64 size;
+ if (term_to_Sint64(BIF_ARG_2, &size)) {
+ Binary* refbin = erts_bin_drv_alloc_fnf(size);
+ if (!refbin)
+ BIF_RET(am_false);
+ sys_memset(refbin->orig_bytes, 0, size);
+ BIF_RET(erts_build_proc_bin(&MSO(BIF_P),
+ HAlloc(BIF_P, PROC_BIN_SIZE),
+ refbin));
+ }
+ }
+
}
BIF_ERROR(BIF_P, BADARG);
}
+static BIF_RETTYPE
+gather_histograms_helper(Process * c_p, Eterm arg_tuple,
+ int gather(Process *, int, int, int, UWord, Eterm))
+{
+ SWord hist_start, hist_width, sched_id;
+ int msg_count, alloc_num;
+ Eterm *args;
+
+ /* This is an internal BIF, so the error checking is mostly left to erlang
+ * code. */
+
+ ASSERT(is_tuple_arity(arg_tuple, 5));
+ args = tuple_val(arg_tuple);
+
+ for (alloc_num = ERTS_ALC_A_MIN; alloc_num <= ERTS_ALC_A_MAX; alloc_num++) {
+ if(erts_is_atom_str(ERTS_ALC_A2AD(alloc_num), args[1], 0)) {
+ break;
+ }
+ }
+
+ if (alloc_num > ERTS_ALC_A_MAX) {
+ BIF_ERROR(c_p, BADARG);
+ }
+
+ sched_id = signed_val(args[2]);
+ hist_width = signed_val(args[3]);
+ hist_start = signed_val(args[4]);
+
+ if (sched_id < 0 || sched_id > erts_no_schedulers) {
+ BIF_ERROR(c_p, BADARG);
+ }
+
+ msg_count = gather(c_p, alloc_num, sched_id, hist_width, hist_start, args[5]);
+
+ BIF_RET(make_small(msg_count));
+}
+
+BIF_RETTYPE erts_internal_gather_alloc_histograms_1(BIF_ALIST_1)
+{
+ return gather_histograms_helper(BIF_P, BIF_ARG_1,
+ erts_alcu_gather_alloc_histograms);
+}
+
+BIF_RETTYPE erts_internal_gather_carrier_info_1(BIF_ALIST_1)
+{
+ return gather_histograms_helper(BIF_P, BIF_ARG_1,
+ erts_alcu_gather_carrier_info);
+}
+
#ifdef ERTS_ENABLE_LOCK_COUNT
typedef struct {
@@ -4430,7 +4840,7 @@ static Eterm lcnt_build_lock_stats_term(Eterm **hpp, Uint *szp, erts_lcnt_lock_s
file = stats->file ? stats->file : "undefined";
- af = erts_atom_put((byte *)file, strlen(file), ERTS_ATOM_ENC_LATIN1, 1);
+ af = erts_atom_put((byte *)file, sys_strlen(file), ERTS_ATOM_ENC_LATIN1, 1);
uil = erts_bld_uint( hpp, szp, stats->line);
tloc = erts_bld_tuple(hpp, szp, 2, af, uil);
@@ -4459,7 +4869,7 @@ static Eterm lcnt_build_lock_stats_term(Eterm **hpp, Uint *szp, erts_lcnt_lock_s
static Eterm lcnt_pretty_print_lock_id(erts_lcnt_lock_info_t *info) {
Eterm id = info->id;
- if((info->flags & ERTS_LOCK_FLAGS_MASK_TYPE) == ERTS_LOCK_TYPE_PROCLOCK) {
+ if((info->flags & ERTS_LOCK_FLAGS_MASK_TYPE) == ERTS_LOCK_FLAGS_TYPE_PROCLOCK) {
/* Use registered names as id's for process locks if available. Thread
* progress is delayed since we may be running on a dirty scheduler. */
ErtsThrPrgrDelayHandle delay_handle;
@@ -4476,7 +4886,7 @@ static Eterm lcnt_pretty_print_lock_id(erts_lcnt_lock_info_t *info) {
} else if(info->flags & ERTS_LOCK_FLAGS_CATEGORY_ALLOCATOR) {
if(is_small(id) && !sys_strcmp(info->name, "alcu_allocator")) {
const char *name = (const char*)ERTS_ALC_A2AD(signed_val(id));
- id = erts_atom_put((byte*)name, strlen(name), ERTS_ATOM_ENC_LATIN1, 1);
+ id = erts_atom_put((byte*)name, sys_strlen(name), ERTS_ATOM_ENC_LATIN1, 1);
}
}
@@ -4496,8 +4906,8 @@ static Eterm lcnt_build_lock_term(Eterm **hpp, Uint *szp, lcnt_sample_t *sample,
lock_desc = erts_lock_flags_get_type_name(info->flags);
- type = erts_atom_put((byte*)lock_desc, strlen(lock_desc), ERTS_ATOM_ENC_LATIN1, 1);
- name = erts_atom_put((byte*)info->name, strlen(info->name), ERTS_ATOM_ENC_LATIN1, 1);
+ type = erts_atom_put((byte*)lock_desc, sys_strlen(lock_desc), ERTS_ATOM_ENC_LATIN1, 1);
+ name = erts_atom_put((byte*)info->name, sys_strlen(info->name), ERTS_ATOM_ENC_LATIN1, 1);
/* Only attempt to resolve ids when actually emitting the term. This ought
* to be safe since all immediates are the same size. */
@@ -4533,11 +4943,11 @@ static Eterm lcnt_build_result_term(Eterm **hpp, Uint *szp, erts_lcnt_time_t *du
dtns = bld_unstable_uint64(hpp, szp, duration->ns);
tdt = erts_bld_tuple(hpp, szp, 2, dts, dtns);
- adur = erts_atom_put((byte *)str_duration, strlen(str_duration), ERTS_ATOM_ENC_LATIN1, 1);
+ adur = erts_atom_put((byte *)str_duration, sys_strlen(str_duration), ERTS_ATOM_ENC_LATIN1, 1);
tdur = erts_bld_tuple(hpp, szp, 2, adur, tdt);
/* lock tuple */
- aloc = erts_atom_put((byte *)str_locks, strlen(str_locks), ERTS_ATOM_ENC_LATIN1, 1);
+ aloc = erts_atom_put((byte *)str_locks, sys_strlen(str_locks), ERTS_ATOM_ENC_LATIN1, 1);
for(i = 0; i < current_locks->size; i++) {
lloc = lcnt_build_lock_term(hpp, szp, &current_locks->elements[i], lloc);
@@ -4591,7 +5001,7 @@ static Eterm lcnt_build_category_list(Eterm **hpp, Uint *szp, erts_lock_flags_t
for(i = 0; lcnt_category_map[i].name != NULL; i++) {
if(mask & lcnt_category_map[i].flag) {
Eterm category = erts_atom_put((byte*)lcnt_category_map[i].name,
- strlen(lcnt_category_map[i].name),
+ sys_strlen(lcnt_category_map[i].name),
ERTS_ATOM_ENC_UTF8, 0);
res = erts_bld_cons(hpp, szp, category, res);
@@ -4721,14 +5131,14 @@ BIF_RETTYPE erts_debug_lcnt_control_2(BIF_ALIST_2)
static void os_info_init(void)
{
- Eterm type = erts_atom_put((byte *) os_type, strlen(os_type), ERTS_ATOM_ENC_LATIN1, 1);
+ Eterm type = erts_atom_put((byte *) os_type, sys_strlen(os_type), ERTS_ATOM_ENC_LATIN1, 1);
Eterm flav;
int major, minor, build;
char* buf = erts_alloc(ERTS_ALC_T_TMP, 1024); /* More than enough */
Eterm* hp;
os_flavor(buf, 1024);
- flav = erts_atom_put((byte *) buf, strlen(buf), ERTS_ATOM_ENC_LATIN1, 1);
+ flav = erts_atom_put((byte *) buf, sys_strlen(buf), ERTS_ATOM_ENC_LATIN1, 1);
erts_free(ERTS_ALC_T_TMP, (void *) buf);
hp = erts_alloc(ERTS_ALC_T_LITERAL, (3+4)*sizeof(Eterm));
os_type_tuple = TUPLE2(hp, type, flav);
@@ -4752,7 +5162,7 @@ erts_bif_info_init(void)
alloc_info_trap = erts_export_put(am_erlang, am_alloc_info, 1);
alloc_sizes_trap = erts_export_put(am_erlang, am_alloc_sizes, 1);
gather_sched_wall_time_res_trap
- = erts_export_put(am_erlang, am_gather_sched_wall_time_result, 1);
+ = erts_export_put(am_erts_internal, am_gather_sched_wall_time_result, 1);
gather_gc_info_res_trap
= erts_export_put(am_erlang, am_gather_gc_info_result, 1);
gather_io_bytes_trap
@@ -4761,6 +5171,10 @@ erts_bif_info_init(void)
= erts_export_put(am_erts_internal, am_gather_microstate_accounting_result, 2);
gather_system_check_res_trap
= erts_export_put(am_erts_internal, am_gather_system_check_result, 1);
+
+ is_process_alive_trap = erts_export_put(am_erts_internal, am_is_process_alive, 1);
+
+
process_info_init();
os_info_init();
}
diff --git a/erts/emulator/beam/erl_bif_os.c b/erts/emulator/beam/erl_bif_os.c
index 5cd172c26f..ce2b27409b 100644
--- a/erts/emulator/beam/erl_bif_os.c
+++ b/erts/emulator/beam/erl_bif_os.c
@@ -36,6 +36,7 @@
#include "big.h"
#include "dist.h"
#include "erl_version.h"
+#include "erl_osenv.h"
/*
* Return the pid for the Erlang process in the host OS.
@@ -65,142 +66,77 @@ BIF_RETTYPE os_getpid_0(BIF_ALIST_0)
BIF_RET(buf_to_intlist(&hp, pid_string, n, NIL));
}
-BIF_RETTYPE os_getenv_0(BIF_ALIST_0)
+static void os_getenv_foreach(Process *process, Eterm *result, Eterm key, Eterm value)
{
- GETENV_STATE state;
- char *cp;
- Eterm* hp;
- Eterm ret;
- Eterm str;
+ Eterm kvp_term, *hp;
- init_getenv_state(&state);
+ hp = HAlloc(process, 5);
+ kvp_term = TUPLE2(hp, key, value);
+ hp += 3;
- ret = NIL;
- while ((cp = getenv_string(&state)) != NULL) {
- str = erts_convert_native_to_filename(BIF_P,(byte *)cp);
- hp = HAlloc(BIF_P, 2);
- ret = CONS(hp, str, ret);
- }
+ (*result) = CONS(hp, kvp_term, (*result));
+}
+
+BIF_RETTYPE os_list_env_vars_0(BIF_ALIST_0)
+{
+ const erts_osenv_t *global_env;
+ Eterm result = NIL;
- fini_getenv_state(&state);
+ global_env = erts_sys_rlock_global_osenv();
+ erts_osenv_foreach_term(global_env, BIF_P, &result, (void*)&os_getenv_foreach);
+ erts_sys_runlock_global_osenv();
- return ret;
+ return result;
}
-#define STATIC_BUF_SIZE 1024
-BIF_RETTYPE os_getenv_1(BIF_ALIST_1)
+BIF_RETTYPE os_get_env_var_1(BIF_ALIST_1)
{
- Process* p = BIF_P;
- Eterm str;
- Sint len;
- int res;
- char *key_str, *val;
- char buf[STATIC_BUF_SIZE];
- size_t val_size = sizeof(buf);
-
- key_str = erts_convert_filename_to_native(BIF_ARG_1,buf,STATIC_BUF_SIZE,
- ERTS_ALC_T_TMP,1,0,&len);
-
- if (!key_str) {
- BIF_ERROR(p, BADARG);
- }
+ const erts_osenv_t *global_env;
+ Eterm out_term;
+ int error;
- if (key_str != &buf[0])
- val = &buf[0];
- else {
- /* len includes zero byte */
- val_size -= len;
- val = &buf[len];
- }
- res = erts_sys_getenv(key_str, val, &val_size);
-
- if (res < 0) {
- no_var:
- str = am_false;
- } else {
- if (res > 0) {
- val = erts_alloc(ERTS_ALC_T_TMP, val_size);
- while (1) {
- res = erts_sys_getenv(key_str, val, &val_size);
- if (res == 0)
- break;
- else if (res < 0)
- goto no_var;
- else
- val = erts_realloc(ERTS_ALC_T_TMP, val, val_size);
- }
- }
- str = erts_convert_native_to_filename(p,(byte *)val);
- }
- if (key_str != &buf[0])
- erts_free(ERTS_ALC_T_TMP, key_str);
- if (val < &buf[0] || &buf[sizeof(buf)-1] < val)
- erts_free(ERTS_ALC_T_TMP, val);
- BIF_RET(str);
+ global_env = erts_sys_rlock_global_osenv();
+ error = erts_osenv_get_term(global_env, BIF_P, BIF_ARG_1, &out_term);
+ erts_sys_runlock_global_osenv();
+
+ if (error == 0) {
+ return am_false;
+ } else if (error < 0) {
+ BIF_ERROR(BIF_P, BADARG);
+ }
+
+ return out_term;
}
-BIF_RETTYPE os_putenv_2(BIF_ALIST_2)
+BIF_RETTYPE os_set_env_var_2(BIF_ALIST_2)
{
- char def_buf_key[STATIC_BUF_SIZE];
- char def_buf_value[STATIC_BUF_SIZE];
- char *key_buf, *value_buf;
-
- key_buf = erts_convert_filename_to_native(BIF_ARG_1,def_buf_key,
- STATIC_BUF_SIZE,
- ERTS_ALC_T_TMP,0,0,NULL);
- if (!key_buf) {
- BIF_ERROR(BIF_P, BADARG);
- }
- value_buf = erts_convert_filename_to_native(BIF_ARG_2,def_buf_value,
- STATIC_BUF_SIZE,
- ERTS_ALC_T_TMP,1,0,
- NULL);
- if (!value_buf) {
- if (key_buf != def_buf_key) {
- erts_free(ERTS_ALC_T_TMP, key_buf);
- }
- BIF_ERROR(BIF_P, BADARG);
- }
-
-
- if (erts_sys_putenv(key_buf, value_buf)) {
- if (key_buf != def_buf_key) {
- erts_free(ERTS_ALC_T_TMP, key_buf);
- }
- if (value_buf != def_buf_value) {
- erts_free(ERTS_ALC_T_TMP, value_buf);
- }
- BIF_ERROR(BIF_P, BADARG);
- }
- if (key_buf != def_buf_key) {
- erts_free(ERTS_ALC_T_TMP, key_buf);
- }
- if (value_buf != def_buf_value) {
- erts_free(ERTS_ALC_T_TMP, value_buf);
+ erts_osenv_t *global_env;
+ int error;
+
+ global_env = erts_sys_rwlock_global_osenv();
+ error = erts_osenv_put_term(global_env, BIF_ARG_1, BIF_ARG_2);
+ erts_sys_rwunlock_global_osenv();
+
+ if (error < 0) {
+ BIF_ERROR(BIF_P, BADARG);
}
+
BIF_RET(am_true);
}
-BIF_RETTYPE os_unsetenv_1(BIF_ALIST_1)
+BIF_RETTYPE os_unset_env_var_1(BIF_ALIST_1)
{
- char *key_buf;
- char buf[STATIC_BUF_SIZE];
+ erts_osenv_t *global_env;
+ int error;
- key_buf = erts_convert_filename_to_native(BIF_ARG_1,buf,STATIC_BUF_SIZE,
- ERTS_ALC_T_TMP,0,0,NULL);
- if (!key_buf) {
- BIF_ERROR(BIF_P, BADARG);
- }
+ global_env = erts_sys_rwlock_global_osenv();
+ error = erts_osenv_unset_term(global_env, BIF_ARG_1);
+ erts_sys_rwunlock_global_osenv();
- if (erts_sys_unsetenv(key_buf)) {
- if (key_buf != buf) {
- erts_free(ERTS_ALC_T_TMP, key_buf);
- }
- BIF_ERROR(BIF_P, BADARG);
- }
- if (key_buf != buf) {
- erts_free(ERTS_ALC_T_TMP, key_buf);
+ if (error < 0) {
+ BIF_ERROR(BIF_P, BADARG);
}
+
BIF_RET(am_true);
}
diff --git a/erts/emulator/beam/erl_bif_port.c b/erts/emulator/beam/erl_bif_port.c
index 4b73be55c6..7fe4e02782 100644
--- a/erts/emulator/beam/erl_bif_port.c
+++ b/erts/emulator/beam/erl_bif_port.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2001-2017. All Rights Reserved.
+ * Copyright Ericsson AB 2001-2018. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -43,9 +43,10 @@
#include "erl_bits.h"
#include "erl_bif_unique.h"
#include "dtrace-wrapper.h"
+#include "erl_proc_sig_queue.h"
static Port *open_port(Process* p, Eterm name, Eterm settings, int *err_typep, int *err_nump);
-static byte* convert_environment(Process* p, Eterm env);
+static int merge_global_environment(erts_osenv_t *env, Eterm key_value_pairs);
static char **convert_args(Eterm);
static void free_args(char **);
@@ -53,10 +54,13 @@ char *erts_default_arg0 = "default";
BIF_RETTYPE erts_internal_open_port_2(BIF_ALIST_2)
{
+ BIF_RETTYPE ret;
Port *port;
Eterm res;
char *str;
int err_type, err_num;
+ ErtsLinkData *ldp;
+ ErtsLink *lnk;
port = open_port(BIF_P, BIF_ARG_1, BIF_ARG_2, &err_type, &err_num);
if (!port) {
@@ -71,13 +75,23 @@ BIF_RETTYPE erts_internal_open_port_2(BIF_ALIST_2)
BIF_ERROR(BIF_P, EXC_INTERNAL_ERROR);
} else if (err_type == -2) {
str = erl_errno_id(err_num);
- res = erts_atom_put((byte *) str, strlen(str), ERTS_ATOM_ENC_LATIN1, 1);
+ res = erts_atom_put((byte *) str, sys_strlen(str), ERTS_ATOM_ENC_LATIN1, 1);
} else {
res = am_einval;
}
BIF_RET(res);
}
+ ldp = erts_link_create(ERTS_LNK_TYPE_PORT, BIF_P->common.id, port->common.id);
+ ASSERT(ldp->a.other.item == port->common.id);
+ ASSERT(ldp->b.other.item == BIF_P->common.id);
+ /*
+ * This link should not already be present, but can potentially
+ * due to id wrapping...
+ */
+ lnk = erts_link_tree_lookup_insert(&ERTS_P_LINKS(BIF_P), &ldp->a);
+ erts_link_tree_insert(&ERTS_P_LINKS(port), &ldp->b);
+
if (port->drv_ptr->flags & ERL_DRV_FLAG_USE_INIT_ACK) {
/* Copied from erl_port_task.c */
@@ -86,39 +100,30 @@ BIF_RETTYPE erts_internal_open_port_2(BIF_ALIST_2)
erts_make_ref_in_array(port->async_open_port->ref);
port->async_open_port->to = BIF_P->common.id;
- erts_proc_lock(BIF_P, ERTS_PROC_LOCKS_MSG_RECEIVE | ERTS_PROC_LOCK_LINK);
- if (ERTS_PROC_PENDING_EXIT(BIF_P)) {
- /* need to exit caller instead */
- erts_proc_unlock(BIF_P, ERTS_PROC_LOCKS_MSG_RECEIVE | ERTS_PROC_LOCK_LINK);
- KILL_CATCHES(BIF_P);
- BIF_P->freason = EXC_EXIT;
- erts_port_release(port);
- BIF_RET(am_badarg);
- }
-
- ERTS_MSGQ_MV_INQ2PRIVQ(BIF_P);
- BIF_P->msg.save = BIF_P->msg.last;
-
- erts_proc_unlock(BIF_P, ERTS_PROC_LOCKS_MSG_RECEIVE);
+ /*
+ * We unconditionaly *must* do a receive on a message
+ * containing the reference after this...
+ */
+ ERTS_RECV_MARK_SAVE(BIF_P);
+ ERTS_RECV_MARK_SET(BIF_P);
res = erts_proc_store_ref(BIF_P, port->async_open_port->ref);
} else {
res = port->common.id;
- erts_proc_lock(BIF_P, ERTS_PROC_LOCK_LINK);
}
- erts_add_link(&ERTS_P_LINKS(port), LINK_PID, BIF_P->common.id);
- erts_add_link(&ERTS_P_LINKS(BIF_P), LINK_PID, port->common.id);
-
if (IS_TRACED_FL(BIF_P, F_TRACE_PROCS))
- trace_proc(BIF_P, ERTS_PROC_LOCK_MAIN|ERTS_PROC_LOCK_LINK, BIF_P,
+ trace_proc(BIF_P, ERTS_PROC_LOCK_MAIN, BIF_P,
am_link, port->common.id);
- erts_proc_unlock(BIF_P, ERTS_PROC_LOCK_LINK);
+ ERTS_BIF_PREP_RET(ret, res);
erts_port_release(port);
- BIF_RET(res);
+ if (lnk)
+ erts_link_release(lnk);
+
+ return ret;
}
static ERTS_INLINE Port *
@@ -195,7 +200,6 @@ BIF_RETTYPE erts_internal_port_command_3(BIF_ALIST_3)
#endif
switch (erts_port_output(BIF_P, flags, prt, prt->common.id, BIF_ARG_2, &ref)) {
- case ERTS_PORT_OP_CALLER_EXIT:
case ERTS_PORT_OP_BADARG:
case ERTS_PORT_OP_DROPPED:
ERTS_BIF_PREP_RET(res, am_badarg);
@@ -254,7 +258,6 @@ BIF_RETTYPE erts_internal_port_call_3(BIF_ALIST_3)
op = (unsigned int) uint_op;
switch (erts_port_call(BIF_P, prt, op, BIF_ARG_3, &retval)) {
- case ERTS_PORT_OP_CALLER_EXIT:
case ERTS_PORT_OP_DROPPED:
case ERTS_PORT_OP_BADARG:
retval = am_badarg;
@@ -272,11 +275,8 @@ BIF_RETTYPE erts_internal_port_call_3(BIF_ALIST_3)
}
state = erts_atomic32_read_acqb(&BIF_P->state);
- if (state & (ERTS_PSFLG_EXITING|ERTS_PSFLG_PENDING_EXIT)) {
- if (state & ERTS_PSFLG_PENDING_EXIT)
- erts_handle_pending_exit(BIF_P, ERTS_PROC_LOCK_MAIN);
+ if (state & ERTS_PSFLG_EXITING)
ERTS_BIF_EXITED(BIF_P);
- }
BIF_RET(retval);
}
@@ -302,7 +302,6 @@ BIF_RETTYPE erts_internal_port_control_3(BIF_ALIST_3)
op = (unsigned int) uint_op;
switch (erts_port_control(BIF_P, prt, op, BIF_ARG_3, &retval)) {
- case ERTS_PORT_OP_CALLER_EXIT:
case ERTS_PORT_OP_BADARG:
case ERTS_PORT_OP_DROPPED:
retval = am_badarg;
@@ -320,11 +319,8 @@ BIF_RETTYPE erts_internal_port_control_3(BIF_ALIST_3)
}
state = erts_atomic32_read_acqb(&BIF_P->state);
- if (state & (ERTS_PSFLG_EXITING|ERTS_PSFLG_PENDING_EXIT)) {
- if (state & ERTS_PSFLG_PENDING_EXIT)
- erts_handle_pending_exit(BIF_P, ERTS_PROC_LOCK_MAIN);
+ if (state & ERTS_PSFLG_EXITING)
ERTS_BIF_EXITED(BIF_P);
- }
BIF_RET(retval);
}
@@ -347,7 +343,6 @@ BIF_RETTYPE erts_internal_port_close_1(BIF_ALIST_1)
BIF_RET(am_badarg);
switch (erts_port_exit(BIF_P, 0, prt, BIF_P->common.id, am_normal, &ref)) {
- case ERTS_PORT_OP_CALLER_EXIT:
case ERTS_PORT_OP_BADARG:
case ERTS_PORT_OP_DROPPED:
BIF_RET(am_badarg);
@@ -380,7 +375,6 @@ BIF_RETTYPE erts_internal_port_connect_2(BIF_ALIST_2)
#endif
switch (erts_port_connect(BIF_P, 0, prt, BIF_P->common.id, BIF_ARG_2, &ref)) {
- case ERTS_PORT_OP_CALLER_EXIT:
case ERTS_PORT_OP_BADARG:
case ERTS_PORT_OP_DROPPED:
BIF_RET(am_badarg);
@@ -418,7 +412,6 @@ BIF_RETTYPE erts_internal_port_info_1(BIF_ALIST_1)
}
switch (erts_port_info(BIF_P, prt, THE_NON_VALUE, &retval)) {
- case ERTS_PORT_OP_CALLER_EXIT:
case ERTS_PORT_OP_BADARG:
BIF_RET(am_badarg);
case ERTS_PORT_OP_DROPPED:
@@ -457,7 +450,6 @@ BIF_RETTYPE erts_internal_port_info_2(BIF_ALIST_2)
}
switch (erts_port_info(BIF_P, prt, BIF_ARG_2, &retval)) {
- case ERTS_PORT_OP_CALLER_EXIT:
case ERTS_PORT_OP_BADARG:
BIF_RET(am_badarg);
case ERTS_PORT_OP_DROPPED:
@@ -639,6 +631,27 @@ BIF_RETTYPE port_get_data_1(BIF_ALIST_1)
BIF_RET(res);
}
+Eterm erts_port_data_read(Port* prt)
+{
+ Eterm res;
+ erts_aint_t data;
+
+ data = erts_atomic_read_ddrb(&prt->data);
+ if (data == (erts_aint_t)NULL)
+ return am_undefined; /* Port terminated by racing thread */
+
+ if ((data & 0x3) != 0) {
+ res = (Eterm) (UWord) data;
+ ASSERT(is_immed(res));
+ }
+ else {
+ ErtsPortDataHeap *pdhp = (ErtsPortDataHeap *) data;
+ res = pdhp->data;
+ }
+ return res;
+}
+
+
/*
* Open a port. Most of the work is not done here but rather in
* the file io.c.
@@ -651,6 +664,7 @@ BIF_RETTYPE port_get_data_1(BIF_ALIST_1)
static Port *
open_port(Process* p, Eterm name, Eterm settings, int *err_typep, int *err_nump)
{
+ int merged_environment = 0;
Sint i;
Eterm option;
Uint arity;
@@ -672,12 +686,13 @@ open_port(Process* p, Eterm name, Eterm settings, int *err_typep, int *err_nump)
opts.read_write = 0;
opts.hide_window = 0;
opts.wd = NULL;
- opts.envir = NULL;
opts.exit_status = 0;
opts.overlapped_io = 0;
opts.spawn_type = ERTS_SPAWN_ANY;
opts.argv = NULL;
opts.parallelism = erts_port_parallelism;
+ erts_osenv_init(&opts.envir);
+
linebuf = 0;
*err_nump = 0;
@@ -718,11 +733,16 @@ open_port(Process* p, Eterm name, Eterm settings, int *err_typep, int *err_nump)
goto badarg;
}
} else if (option == am_env) {
- byte* bytes;
- if ((bytes = convert_environment(p, *tp)) == NULL) {
- goto badarg;
+ if (merged_environment) {
+ /* Ignore previous env option */
+ erts_osenv_clear(&opts.envir);
+ }
+
+ merged_environment = 1;
+
+ if (merge_global_environment(&opts.envir, *tp)) {
+ goto badarg;
}
- opts.envir = (char *) bytes;
} else if (option == am_args) {
char **av;
char **oav = opts.argv;
@@ -807,6 +827,12 @@ open_port(Process* p, Eterm name, Eterm settings, int *err_typep, int *err_nump)
if((linebuf && opts.packet_bytes) ||
(opts.redir_stderr && !opts.use_stdio)) {
goto badarg;
+}
+
+ /* If we lacked an env option, fill in the global environment without
+ * changes. */
+ if (!merged_environment) {
+ merge_global_environment(&opts.envir, NIL);
}
/*
@@ -956,6 +982,7 @@ open_port(Process* p, Eterm name, Eterm settings, int *err_typep, int *err_nump)
erts_atomic32_read_bor_relb(&port->state, sflgs);
do_return:
+ erts_osenv_clear(&opts.envir);
if (name_buf)
erts_free(ERTS_ALC_T_TMP, (void *) name_buf);
if (opts.argv) {
@@ -975,6 +1002,45 @@ open_port(Process* p, Eterm name, Eterm settings, int *err_typep, int *err_nump)
goto do_return;
}
+/* Merges the the global environment and the given {Key, Value} list into env,
+ * unsetting all keys whose value is either 'false' or NIL. The behavior on
+ * NIL is undocumented and perhaps surprising, but the previous implementation
+ * worked in this manner. */
+static int merge_global_environment(erts_osenv_t *env, Eterm key_value_pairs) {
+ const erts_osenv_t *global_env = erts_sys_rlock_global_osenv();
+ erts_osenv_merge(env, global_env, 0);
+ erts_sys_runlock_global_osenv();
+
+ while (is_list(key_value_pairs)) {
+ Eterm *cell, *tuple;
+
+ cell = list_val(key_value_pairs);
+
+ if(!is_tuple_arity(CAR(cell), 2)) {
+ return -1;
+ }
+
+ tuple = tuple_val(CAR(cell));
+ key_value_pairs = CDR(cell);
+
+ if(is_nil(tuple[2]) || tuple[2] == am_false) {
+ if(erts_osenv_unset_term(env, tuple[1]) < 0) {
+ return -1;
+ }
+ } else {
+ if(erts_osenv_put_term(env, tuple[1], tuple[2]) < 0) {
+ return -1;
+ }
+ }
+ }
+
+ if(!is_nil(key_value_pairs)) {
+ return -1;
+ }
+
+ return 0;
+}
+
/* Arguments can be given i unicode and as raw binaries, convert filename is used to convert */
static char **convert_args(Eterm l)
{
@@ -1021,75 +1087,6 @@ static void free_args(char **av)
}
erts_free(ERTS_ALC_T_TMP, av);
}
-
-
-static byte* convert_environment(Process* p, Eterm env)
-{
- Eterm all;
- Eterm* temp_heap;
- Eterm* hp;
- Uint heap_size;
- Sint n;
- Sint size;
- byte* bytes;
- int encoding = erts_get_native_filename_encoding();
-
- if ((n = erts_list_length(env)) < 0) {
- return NULL;
- }
- heap_size = 2*(5*n+1);
- temp_heap = hp = (Eterm *) erts_alloc(ERTS_ALC_T_TMP, heap_size*sizeof(Eterm));
- bytes = NULL; /* Indicating error */
-
- /*
- * All errors below are handled by jumping to 'done', to ensure that the memory
- * gets deallocated. Do NOT return directly from this function.
- */
-
- all = CONS(hp, make_small(0), NIL);
- hp += 2;
-
- while(is_list(env)) {
- Eterm tmp;
- Eterm* tp;
-
- tmp = CAR(list_val(env));
- if (is_not_tuple_arity(tmp, 2)) {
- goto done;
- }
- tp = tuple_val(tmp);
- tmp = CONS(hp, make_small(0), NIL);
- hp += 2;
- if (tp[2] != am_false) {
- tmp = CONS(hp, tp[2], tmp);
- hp += 2;
- }
- tmp = CONS(hp, make_small('='), tmp);
- hp += 2;
- tmp = CONS(hp, tp[1], tmp);
- hp += 2;
- all = CONS(hp, tmp, all);
- hp += 2;
- env = CDR(list_val(env));
- }
- if (is_not_nil(env)) {
- goto done;
- }
-
- if ((size = erts_native_filename_need(all,encoding)) < 0) {
- goto done;
- }
-
- /*
- * Put the result in a binary (no risk for a memory leak that way).
- */
- (void) erts_new_heap_binary(p, NULL, size, &bytes);
- erts_native_filename_put(all,encoding,bytes);
-
- done:
- erts_free(ERTS_ALC_T_TMP, temp_heap);
- return bytes;
-}
/* ------------ decode_packet() and friends: */
@@ -1140,7 +1137,7 @@ http_bld_string(struct packet_callback_args* pca, Uint **hpp, Uint *szp,
ErlHeapBin* bin = (ErlHeapBin*) *hpp;
bin->thing_word = header_heap_bin(len);
bin->size = len;
- memcpy(bin->data, str, len);
+ sys_memcpy(bin->data, str, len);
*hpp += size;
}
}
@@ -1318,9 +1315,9 @@ int ssl_tls_erl(void* arg, unsigned type, unsigned major, unsigned minor,
Eterm bin = new_binary(pca->p, NULL, plen+len);
byte* bin_ptr = binary_bytes(bin);
- memcpy(bin_ptr+plen, buf, len);
+ sys_memcpy(bin_ptr+plen, buf, len);
if (plen) {
- memcpy(bin_ptr, prefix, plen);
+ sys_memcpy(bin_ptr, prefix, plen);
}
/* {ssl_tls,NIL,ContentType,{Major,Minor},Bin} */
diff --git a/erts/emulator/beam/erl_bif_re.c b/erts/emulator/beam/erl_bif_re.c
index bc819505e7..4d769c2d46 100644
--- a/erts/emulator/beam/erl_bif_re.c
+++ b/erts/emulator/beam/erl_bif_re.c
@@ -514,7 +514,7 @@ re_version_0(BIF_ALIST_0)
Eterm ret;
size_t version_size = 0;
byte *version = (byte *) erts_pcre_version();
- version_size = strlen((const char *) version);
+ version_size = sys_strlen((const char *) version);
ret = new_binary(BIF_P, version, version_size);
BIF_RET(ret);
}
@@ -998,7 +998,7 @@ build_capture(Eterm capture_spec[CAPSPEC_SIZE], const pcre *code)
has_dupnames = ((options & PCRE_DUPNAMES) != 0);
for(i=0;i<top;++i) {
- if (last == NULL || !has_dupnames || strcmp((char *) last+2,(char *) nametable+2)) {
+ if (last == NULL || !has_dupnames || sys_strcmp((char *) last+2,(char *) nametable+2)) {
ASSERT(ri->num_spec >= 0);
++(ri->num_spec);
if(ri->num_spec > sallocated) {
@@ -1048,7 +1048,7 @@ build_capture(Eterm capture_spec[CAPSPEC_SIZE], const pcre *code)
(tmpbsiz = ap->len + 1));
}
}
- memcpy(tmpb,ap->name,ap->len);
+ sys_memcpy(tmpb,ap->name,ap->len);
tmpb[ap->len] = '\0';
} else {
ErlDrvSizeT slen;
@@ -1210,7 +1210,7 @@ re_run(Process *p, Eterm arg1, Eterm arg2, Eterm arg3)
erts_pcre_fullinfo(result, NULL, PCRE_INFO_CAPTURECOUNT, &capture_count);
ovsize = 3*(capture_count+1);
restart.code = erts_alloc(ERTS_ALC_T_RE_SUBJECT, code_size);
- memcpy(restart.code, result, code_size);
+ sys_memcpy(restart.code, result, code_size);
erts_pcre_free(result);
erts_free(ERTS_ALC_T_RE_TMP_BUF, expr);
/*unicode = (pflags & PARSE_FLAG_UNICODE) ? 1 : 0;*/
@@ -1252,7 +1252,7 @@ re_run(Process *p, Eterm arg1, Eterm arg2, Eterm arg3)
BIF_ERROR(p, BADARG);
}
restart.code = erts_alloc(ERTS_ALC_T_RE_SUBJECT, code_size);
- memcpy(restart.code, code_tmp, code_size);
+ sys_memcpy(restart.code, code_tmp, code_size);
erts_free_aligned_binary_bytes(temp_alloc);
}
@@ -1364,7 +1364,7 @@ handle_iolist:
RestartContext *restartp = ERTS_MAGIC_BIN_DATA(mbp);
Eterm magic_ref;
Eterm *hp;
- memcpy(restartp,&restart,sizeof(RestartContext));
+ sys_memcpy(restartp,&restart,sizeof(RestartContext));
BUMP_ALL_REDS(p);
hp = HAlloc(p, ERTS_MAGIC_REF_THING_SIZE);
magic_ref = erts_mk_magic_ref(&hp, &MSO(p), mbp);
@@ -1517,7 +1517,7 @@ re_inspect_2(BIF_ALIST_2)
last = NULL;
name = nametable;
for(i=0;i<top;++i) {
- if (last == NULL || !has_dupnames || strcmp((char *) last+2,
+ if (last == NULL || !has_dupnames || sys_strcmp((char *) last+2,
(char *) name+2)) {
++num_names;
}
@@ -1531,9 +1531,9 @@ re_inspect_2(BIF_ALIST_2)
name = nametable;
j = 0;
for(i=0;i<top;++i) {
- if (last == NULL || !has_dupnames || strcmp((char *) last+2,
+ if (last == NULL || !has_dupnames || sys_strcmp((char *) last+2,
(char *) name+2)) {
- tmp_vec[j++] = new_binary(BIF_P, (byte *) name+2, strlen((char *) name+2));
+ tmp_vec[j++] = new_binary(BIF_P, (byte *) name+2, sys_strlen((char *) name+2));
}
last = name;
name += entrysize;
diff --git a/erts/emulator/beam/erl_bif_trace.c b/erts/emulator/beam/erl_bif_trace.c
index 3fe089a00e..1953f79d79 100644
--- a/erts/emulator/beam/erl_bif_trace.c
+++ b/erts/emulator/beam/erl_bif_trace.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1999-2017. All Rights Reserved.
+ * Copyright Ericsson AB 1999-2018. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -40,6 +40,7 @@
#include "erl_binary.h"
#include "erl_thr_progress.h"
#include "erl_bif_unique.h"
+#include "erl_proc_sig_queue.h"
#define DECL_AM(S) Eterm AM_ ## S = am_atom_put(#S, sizeof(#S) - 1)
@@ -966,12 +967,12 @@ static int function_is_traced(Process *p,
if ((ep = export_get(&e)) != NULL) {
pc = ep->beam;
if (ep->addressv[erts_active_code_ix()] == pc &&
- *pc != (BeamInstr) em_call_error_handler) {
+ ! BeamIsOpCode(*pc, op_call_error_handler)) {
int r = 0;
- ASSERT(*pc == (BeamInstr) em_apply_bif ||
- *pc == (BeamInstr) BeamOp(op_i_generic_breakpoint));
+ ASSERT(BeamIsOpCode(*pc, op_apply_bif) ||
+ BeamIsOpCode(*pc, op_i_generic_breakpoint));
if (erts_is_trace_break(&ep->info, ms, 0)) {
return FUNC_TRACE_GLOBAL_TRACE;
@@ -1361,14 +1362,14 @@ erts_set_trace_pattern(Process*p, ErtsCodeMFA *mfa, int specified,
if (ep->addressv[code_ix] != pc) {
fp[i].mod->curr.num_traced_exports++;
#ifdef DEBUG
- ep->info.op = (BeamInstr) BeamOp(op_i_func_info_IaaI);
+ ep->info.op = BeamOpCodeAddr(op_i_func_info_IaaI);
#endif
- ep->beam[0] = (BeamInstr) BeamOp(op_trace_jump_W);
+ ep->beam[0] = BeamOpCodeAddr(op_trace_jump_W);
ep->beam[1] = (BeamInstr) ep->addressv[code_ix];
}
erts_set_call_trace_bif(ci, match_prog_set, 0);
if (ep->addressv[code_ix] != pc) {
- ep->beam[0] = (BeamInstr) BeamOp(op_i_generic_breakpoint);
+ ep->beam[0] = BeamOpCodeAddr(op_i_generic_breakpoint);
}
} else if (!on && flags.breakpoint) {
/* Turn off breakpoint tracing -- nothing to do here. */
@@ -1378,8 +1379,8 @@ erts_set_trace_pattern(Process*p, ErtsCodeMFA *mfa, int specified,
* before turning on breakpoint tracing.
*/
erts_clear_call_trace_bif(ci, 0);
- if (ep->beam[0] == (BeamInstr) BeamOp(op_i_generic_breakpoint)) {
- ep->beam[0] = (BeamInstr) BeamOp(op_trace_jump_W);
+ if (BeamIsOpCode(ep->beam[0], op_i_generic_breakpoint)) {
+ ep->beam[0] = BeamOpCodeAddr(op_trace_jump_W);
}
}
}
@@ -1671,7 +1672,7 @@ uninstall_exp_breakpoints(BpFunctions* f)
if (ep->addressv[code_ix] != ep->beam) {
continue;
}
- ASSERT(ep->beam[0] == (BeamInstr) BeamOp(op_trace_jump_W));
+ ASSERT(BeamIsOpCode(ep->beam[0], op_trace_jump_W));
ep->addressv[code_ix] = (BeamInstr *) ep->beam[1];
}
}
@@ -1690,7 +1691,7 @@ clean_export_entries(BpFunctions* f)
if (ep->addressv[code_ix] == ep->beam) {
continue;
}
- if (ep->beam[0] == (BeamInstr) BeamOp(op_trace_jump_W)) {
+ if (BeamIsOpCode(ep->beam[0], op_trace_jump_W)) {
ep->beam[0] = (BeamInstr) 0;
ep->beam[1] = (BeamInstr) 0;
}
@@ -1806,9 +1807,6 @@ Eterm erts_seq_trace(Process *p, Eterm arg1, Eterm arg2,
return old_value;
}
else if (arg1 == am_label) {
- if (! is_small(arg2)) {
- return THE_NON_VALUE;
- }
new_seq_trace_token(p);
if (build_result) {
old_value = SEQ_TRACE_TOKEN_LABEL(p);
diff --git a/erts/emulator/beam/erl_bif_unique.h b/erts/emulator/beam/erl_bif_unique.h
index 9aa631fde9..6af9d4ac8c 100644
--- a/erts/emulator/beam/erl_bif_unique.h
+++ b/erts/emulator/beam/erl_bif_unique.h
@@ -307,7 +307,7 @@ erts_iref_storage_clean(ErtsIRefStorage *iref)
if (iref->is_magic && erts_refc_dectest(&iref->u.mb->intern.refc, 0) == 0)
erts_ref_bin_free(iref->u.mb);
#ifdef DEBUG
- memset((void *) iref, 0xf, sizeof(ErtsIRefStorage));
+ sys_memset((void *) iref, 0xf, sizeof(ErtsIRefStorage));
#endif
}
@@ -342,7 +342,7 @@ erts_iref_storage_make_ref(ErtsIRefStorage *iref,
#ifdef DEBUG
if (clean_storage)
- memset((void *) iref, 0xf, sizeof(ErtsIRefStorage));
+ sys_memset((void *) iref, 0xf, sizeof(ErtsIRefStorage));
#endif
return make_internal_ref(hp);
diff --git a/erts/emulator/beam/erl_binary.h b/erts/emulator/beam/erl_binary.h
index 05007e864e..46653a8580 100644
--- a/erts/emulator/beam/erl_binary.h
+++ b/erts/emulator/beam/erl_binary.h
@@ -363,8 +363,6 @@ erts_free_aligned_binary_bytes(byte* buf)
# define CHICKEN_PAD (sizeof(void*) - 1)
#endif
-/* Caller must initialize 'refc'
-*/
ERTS_GLB_INLINE Binary *
erts_bin_drv_alloc_fnf(Uint size)
{
@@ -383,8 +381,6 @@ erts_bin_drv_alloc_fnf(Uint size)
return res;
}
-/* Caller must initialize 'refc'
-*/
ERTS_GLB_INLINE Binary *
erts_bin_drv_alloc(Uint size)
{
@@ -401,9 +397,6 @@ erts_bin_drv_alloc(Uint size)
return res;
}
-
-/* Caller must initialize 'refc'
-*/
ERTS_GLB_INLINE Binary *
erts_bin_nrml_alloc(Uint size)
{
diff --git a/erts/emulator/beam/erl_bits.h b/erts/emulator/beam/erl_bits.h
index b9d141d585..a3816fa820 100644
--- a/erts/emulator/beam/erl_bits.h
+++ b/erts/emulator/beam/erl_bits.h
@@ -111,7 +111,7 @@ typedef struct erl_bin_match_struct{
#define copy_binary_to_buffer(DstBuffer, DstBufOffset, SrcBuffer, SrcBufferOffset, NumBits) \
do { \
if (BIT_OFFSET(DstBufOffset) == 0 && (SrcBufferOffset == 0) && \
- (BIT_OFFSET(NumBits)==0)) { \
+ (BIT_OFFSET(NumBits)==0) && (NumBits != 0)) { \
sys_memcpy(DstBuffer+BYTE_OFFSET(DstBufOffset), \
SrcBuffer, NBYTES(NumBits)); \
} else { \
diff --git a/erts/emulator/beam/erl_cpu_topology.c b/erts/emulator/beam/erl_cpu_topology.c
index 49f9beb19f..6f8d2f8c35 100644
--- a/erts/emulator/beam/erl_cpu_topology.c
+++ b/erts/emulator/beam/erl_cpu_topology.c
@@ -602,7 +602,7 @@ write_schedulers_bind_change(erts_cpu_topology_t *cpudata, int size)
cpu_bind_order_sort(cpudata, size, cpu_bind_order, 1);
- for (cpu_ix = 0; cpu_ix < size && cpu_ix < erts_no_schedulers; cpu_ix++)
+ for (cpu_ix = 0; cpu_ix < size && s_ix <= erts_no_schedulers; cpu_ix++)
if (erts_is_cpu_available(cpuinfo, cpudata[cpu_ix].logical))
scheduler2cpu_map[s_ix++].bind_id = cpudata[cpu_ix].logical;
}
diff --git a/erts/emulator/beam/erl_db.c b/erts/emulator/beam/erl_db.c
index 3ba0886464..f7ee408991 100644
--- a/erts/emulator/beam/erl_db.c
+++ b/erts/emulator/beam/erl_db.c
@@ -283,6 +283,13 @@ make_tid(Process *c_p, DbTable *tb)
return erts_mk_magic_ref(&hp, &c_p->off_heap, tb->common.btid);
}
+Eterm
+erts_db_make_tid(Process *c_p, DbTableCommon *tb)
+{
+ return make_tid(c_p, (DbTable*)tb);
+}
+
+
/*
** The meta hash table of all NAMED ets tables
@@ -1753,6 +1760,28 @@ BIF_RETTYPE ets_new_2(BIF_ALIST_2)
BIF_RET(ret);
}
+/*
+** Retrieves the tid() of a named ets table.
+*/
+BIF_RETTYPE ets_whereis_1(BIF_ALIST_1)
+{
+ DbTable* tb;
+ Eterm res;
+
+ if (is_not_atom(BIF_ARG_1)) {
+ BIF_ERROR(BIF_P, BADARG);
+ }
+
+ if ((tb = db_get_table(BIF_P, BIF_ARG_1, DB_INFO, LCK_READ)) == NULL) {
+ BIF_RET(am_undefined);
+ }
+
+ res = make_tid(BIF_P, tb);
+ db_unlock(tb, LCK_READ);
+
+ BIF_RET(res);
+}
+
/*
** The lookup BIF
*/
@@ -3126,7 +3155,8 @@ BIF_RETTYPE ets_info_1(BIF_ALIST_1)
static Eterm fields[] = {am_protection, am_keypos, am_type, am_named_table,
am_node, am_size, am_name, am_heir, am_owner, am_memory, am_compressed,
am_write_concurrency,
- am_read_concurrency};
+ am_read_concurrency,
+ am_id};
Eterm results[sizeof(fields)/sizeof(Eterm)];
DbTable* tb;
Eterm res;
@@ -4016,7 +4046,10 @@ static Eterm table_info(Process* p, DbTable* tb, Eterm What)
ret = is_table_named(tb) ? am_true : am_false;
} else if (What == am_compressed) {
ret = tb->common.compress ? am_true : am_false;
+ } else if (What == am_id) {
+ ret = make_tid(p, tb);
}
+
/*
* For debugging purposes
*/
@@ -4235,7 +4268,7 @@ erts_ets_colliding_names(Process* p, Eterm name, Uint cnt)
while (index >= atom_table_size()) {
char tmp[20];
erts_snprintf(tmp, sizeof(tmp), "am%x", atom_table_size());
- erts_atom_put((byte *) tmp, strlen(tmp), ERTS_ATOM_ENC_LATIN1, 1);
+ erts_atom_put((byte *) tmp, sys_strlen(tmp), ERTS_ATOM_ENC_LATIN1, 1);
}
list = CONS(hp, make_atom(index), list);
hp += 2;
diff --git a/erts/emulator/beam/erl_db.h b/erts/emulator/beam/erl_db.h
index 318e90cb28..eb6da2c9fb 100644
--- a/erts/emulator/beam/erl_db.h
+++ b/erts/emulator/beam/erl_db.h
@@ -128,6 +128,7 @@ extern erts_atomic_t erts_ets_misc_mem_size;
Eterm erts_ets_colliding_names(Process*, Eterm name, Uint cnt);
Uint erts_db_get_max_tabs(void);
+Eterm erts_db_make_tid(Process *c_p, DbTableCommon *tb);
#ifdef ERTS_ENABLE_LOCK_COUNT
void erts_lcnt_enable_db_lock_count(DbTable *tb, int enable);
diff --git a/erts/emulator/beam/erl_db_hash.c b/erts/emulator/beam/erl_db_hash.c
index 5d49b2ea14..cb5c496e90 100644
--- a/erts/emulator/beam/erl_db_hash.c
+++ b/erts/emulator/beam/erl_db_hash.c
@@ -340,8 +340,8 @@ 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 HashDbTerm* next(DbTableHash *tb, Uint *iptr, erts_rwmtx_t** lck_ptr,
- HashDbTerm *list);
+static HashDbTerm* next_live(DbTableHash *tb, Uint *iptr, erts_rwmtx_t** lck_ptr,
+ HashDbTerm *list);
static HashDbTerm* search_list(DbTableHash* tb, Eterm key,
HashValue hval, HashDbTerm *list);
static void shrink(DbTableHash* tb, int nitems);
@@ -646,9 +646,9 @@ int db_create_hash(Process *p, DbTable *tbl)
rwmtx_opt.type = ERTS_RWMTX_TYPE_FREQUENT_READ;
if (erts_ets_rwmtx_spin_count >= 0)
rwmtx_opt.main_spincount = erts_ets_rwmtx_spin_count;
- tb->locks = (DbTableHashFineLocks*) erts_db_alloc_fnf(ERTS_ALC_T_DB_SEG, /* Other type maybe? */
- (DbTable *) tb,
- sizeof(DbTableHashFineLocks));
+ tb->locks = (DbTableHashFineLocks*) erts_db_alloc(ERTS_ALC_T_DB_SEG, /* Other type maybe? */
+ (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,
"db_hash_slot", tb->common.the_name, ERTS_LOCK_FLAGS_CATEGORY_DB);
@@ -672,19 +672,9 @@ static int db_first_hash(Process *p, DbTable *tbl, Eterm *ret)
erts_rwmtx_t* lck = RLOCK_HASH(tb,ix);
HashDbTerm* list;
- for (;;) {
- list = BUCKET(tb,ix);
- if (list != NULL) {
- if (list->hvalue == INVALID_HASH) {
- list = next(tb,&ix,&lck,list);
- }
- break;
- }
- if ((ix=next_slot(tb,ix,&lck)) == 0) {
- list = NULL;
- break;
- }
- }
+ list = BUCKET(tb,ix);
+ list = next_live(tb, &ix, &lck, list);
+
if (list != NULL) {
*ret = db_copy_key(p, tbl, &list->dbterm);
RUNLOCK_HASH(lck);
@@ -721,13 +711,13 @@ static int db_next_hash(Process *p, DbTable *tbl, Eterm key, Eterm *ret)
}
/* Key found */
- b = next(tb, &ix, &lck, b);
+ b = next_live(tb, &ix, &lck, b->next);
if (tb->common.status & (DB_BAG | DB_DUPLICATE_BAG)) {
while (b != 0) {
if (!has_live_key(tb, b, key, hval)) {
break;
}
- b = next(tb, &ix, &lck, b);
+ b = next_live(tb, &ix, &lck, b->next);
}
}
if (b == NULL) {
@@ -1463,20 +1453,24 @@ static ERTS_INLINE int on_mtraversal_simple_trap(Export* trap_function,
BUMP_ALL_REDS(p);
if (IS_USMALL(0, got)) {
- hp = HAlloc(p, base_halloc_sz + 5);
+ hp = HAllocX(p, base_halloc_sz + 5, ERTS_MAGIC_REF_THING_SIZE);
egot = make_small(got);
}
else {
- hp = HAlloc(p, base_halloc_sz + BIG_UINT_HEAP_SIZE + 5);
+ hp = HAllocX(p, base_halloc_sz + BIG_UINT_HEAP_SIZE + 5,
+ ERTS_MAGIC_REF_THING_SIZE);
egot = uint_to_big(got, hp);
hp += BIG_UINT_HEAP_SIZE;
}
if (is_first_trap) {
+ if (is_atom(tid))
+ tid = erts_db_make_tid(p, &tb->common);
mpb = erts_db_make_match_prog_ref(p, *mpp, &hp);
*mpp = NULL; /* otherwise the caller will destroy it */
}
else {
+ ASSERT(!is_atom(tid));
mpb = prev_continuation_tptr[3];
}
@@ -1590,11 +1584,17 @@ static int mtraversal_select_chunk_on_loop_ended(void* context_ptr, Sint slot_ix
been in 'user space' */
}
if (rest != NIL || slot_ix >= 0) { /* Need more calls */
- sc_context_ptr->hp = HAlloc(sc_context_ptr->p, 3 + 7 + ERTS_MAGIC_REF_THING_SIZE);
+ Eterm tid = sc_context_ptr->tid;
+ sc_context_ptr->hp = HAllocX(sc_context_ptr->p,
+ 3 + 7 + ERTS_MAGIC_REF_THING_SIZE,
+ ERTS_MAGIC_REF_THING_SIZE);
mpb = erts_db_make_match_prog_ref(sc_context_ptr->p, *mpp, &sc_context_ptr->hp);
+ if (is_atom(tid))
+ tid = erts_db_make_tid(sc_context_ptr->p,
+ &sc_context_ptr->tb->common);
continuation = TUPLE6(
sc_context_ptr->hp,
- sc_context_ptr->tid,
+ tid,
make_small(slot_ix),
make_small(sc_context_ptr->chunk_size),
mpb, rest,
@@ -1631,12 +1631,16 @@ static int mtraversal_select_chunk_on_trap(void* context_ptr, Sint slot_ix, Sint
BUMP_ALL_REDS(sc_context_ptr->p);
if (sc_context_ptr->prev_continuation_tptr == NULL) {
+ Eterm tid = sc_context_ptr->tid;
/* First time we're trapping */
- hp = HAlloc(sc_context_ptr->p, 7 + ERTS_MAGIC_REF_THING_SIZE);
+ hp = HAllocX(sc_context_ptr->p, 7 + ERTS_MAGIC_REF_THING_SIZE,
+ ERTS_MAGIC_REF_THING_SIZE);
+ if (is_atom(tid))
+ tid = erts_db_make_tid(sc_context_ptr->p, &sc_context_ptr->tb->common);
mpb = erts_db_make_match_prog_ref(sc_context_ptr->p, *mpp, &hp);
continuation = TUPLE6(
hp,
- sc_context_ptr->tid,
+ tid,
make_small(slot_ix),
make_small(sc_context_ptr->chunk_size),
mpb,
@@ -2905,14 +2909,14 @@ static HashDbTerm* search_list(DbTableHash* tb, Eterm key,
/* It return the next live object in a table, NULL if no more */
/* In-bucket: RLOCKED */
/* Out-bucket: RLOCKED unless NULL */
-static HashDbTerm* next(DbTableHash *tb, Uint *iptr, erts_rwmtx_t** lck_ptr,
- HashDbTerm *list)
+static HashDbTerm* next_live(DbTableHash *tb, Uint *iptr, erts_rwmtx_t** lck_ptr,
+ HashDbTerm *list)
{
int i;
ERTS_LC_ASSERT(IS_HASH_RLOCKED(tb,*iptr));
- for (list = list->next; list != NULL; list = list->next) {
+ for ( ; list != NULL; list = list->next) {
if (list->hvalue != INVALID_HASH)
return list;
}
diff --git a/erts/emulator/beam/erl_db_util.c b/erts/emulator/beam/erl_db_util.c
index e017b9552b..3836f28aa4 100644
--- a/erts/emulator/beam/erl_db_util.c
+++ b/erts/emulator/beam/erl_db_util.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1998-2017. All Rights Reserved.
+ * Copyright Ericsson AB 1998-2018. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -38,6 +38,7 @@
#include "erl_binary.h"
#include "erl_map.h"
#include "erl_thr_progress.h"
+#include "erl_proc_sig_queue.h"
#include "erl_db_util.h"
@@ -74,12 +75,35 @@ DBIF_TABLE_GUARD | DBIF_TABLE_BODY | DBIF_TRACE_GUARD | DBIF_TRACE_BODY
typedef struct DMC_STACK_TYPE(Type) { \
int pos; \
int siz; \
- Type def[DMC_DEFAULT_SIZE]; \
+ int bytes; \
Type *data; \
+ Type def[DMC_DEFAULT_SIZE]; \
} DMC_STACK_TYPE(Type)
+
+
+typedef int Dummy;
+DMC_DECLARE_STACK_TYPE(Dummy);
+
+static void dmc_stack_grow(DMC_Dummy_stack* s)
+{
+ int was_bytes = s->bytes;
+ s->siz *= 2;
+ s->bytes *= 2;
+ if (s->data == s->def) {
+ s->data = erts_alloc(ERTS_ALC_T_DB_MC_STK, s->bytes);
+ sys_memcpy(s->data, s->def, was_bytes);
+ }
+ else {
+ s->data = erts_realloc(ERTS_ALC_T_DB_MC_STK, s->data, s->bytes);
+ }
+}
-#define DMC_INIT_STACK(Name) \
- (Name).pos = 0; (Name).siz = DMC_DEFAULT_SIZE; (Name).data = (Name).def
+#define DMC_INIT_STACK(Name) do { \
+ (Name).pos = 0; \
+ (Name).siz = DMC_DEFAULT_SIZE; \
+ (Name).bytes = sizeof((Name).def); \
+ (Name).data = (Name).def; \
+} while (0)
#define DMC_STACK_DATA(Name) (Name).data
@@ -87,21 +111,19 @@ typedef struct DMC_STACK_TYPE(Type) { \
#define DMC_PUSH(On, What) \
do { \
- if ((On).pos >= (On).siz) { \
- (On).siz *= 2; \
- (On).data \
- = (((On).def == (On).data) \
- ? memcpy(erts_alloc(ERTS_ALC_T_DB_MC_STK, \
- (On).siz*sizeof(*((On).data))), \
- (On).def, \
- DMC_DEFAULT_SIZE*sizeof(*((On).data))) \
- : erts_realloc(ERTS_ALC_T_DB_MC_STK, \
- (void *) (On).data, \
- (On).siz*sizeof(*((On).data)))); \
- } \
+ if ((On).pos >= (On).siz) \
+ dmc_stack_grow((DMC_Dummy_stack*)&(On)); \
(On).data[(On).pos++] = What; \
} while (0)
+#define DMC_PUSH2(On, A, B) \
+do { \
+ if ((On).pos+1 >= (On).siz) \
+ dmc_stack_grow((DMC_Dummy_stack*)&(On)); \
+ (On).data[(On).pos++] = A; \
+ (On).data[(On).pos++] = B; \
+} while (0)
+
#define DMC_POP(From) (From).data[--(From).pos]
#define DMC_TOP(From) (From).data[(From).pos - 1]
@@ -155,6 +177,7 @@ set_tracee_flags(Process *tracee_p, ErtsTracer tracer,
: am_false);
erts_tracer_replace(&tracee_p->common, tracer);
ERTS_TRACE_FLAGS(tracee_p) = flags;
+
return ret;
}
/*
@@ -1518,7 +1541,7 @@ restart:
context.current_match < num_progs;
++context.current_match) { /* This loop is long,
too long */
- memset(heap.vars, 0, heap.size * sizeof(*heap.vars));
+ sys_memset(heap.vars, 0, heap.size * sizeof(*heap.vars));
t = context.matchexpr[context.current_match];
context.stack_used = 0;
structure_checked = 0;
@@ -1537,8 +1560,7 @@ restart:
if (is_flatmap(t)) {
num_iters = flatmap_get_size(flatmap_val(t));
if (!structure_checked) {
- DMC_PUSH(text, matchMap);
- DMC_PUSH(text, num_iters);
+ DMC_PUSH2(text, matchMap, num_iters);
}
structure_checked = 0;
for (i = 0; i < num_iters; ++i) {
@@ -1558,8 +1580,7 @@ restart:
}
goto error;
}
- DMC_PUSH(text, matchKey);
- DMC_PUSH(text, dmc_private_copy(&context, key));
+ DMC_PUSH2(text, matchKey, dmc_private_copy(&context, key));
{
int old_stack = ++(context.stack_used);
Eterm value = flatmap_get_values(flatmap_val(t))[i];
@@ -1587,8 +1608,7 @@ restart:
Eterm *kv;
num_iters = hashmap_size(t);
if (!structure_checked) {
- DMC_PUSH(text, matchMap);
- DMC_PUSH(text, num_iters);
+ DMC_PUSH2(text, matchMap, num_iters);
}
structure_checked = 0;
@@ -1614,8 +1634,7 @@ restart:
DESTROY_WSTACK(wstack);
goto error;
}
- DMC_PUSH(text, matchKey);
- DMC_PUSH(text, dmc_private_copy(&context, key));
+ DMC_PUSH2(text, matchKey, dmc_private_copy(&context, key));
{
int old_stack = ++(context.stack_used);
res = dmc_one_term(&context, &heap, &stack, &text,
@@ -1645,8 +1664,7 @@ restart:
num_iters = arityval(*tuple_val(t));
if (!structure_checked) { /* i.e. we did not
pop it */
- DMC_PUSH(text,matchTuple);
- DMC_PUSH(text,num_iters);
+ DMC_PUSH2(text, matchTuple, num_iters);
}
structure_checked = 0;
for (i = 1; i <= num_iters; ++i) {
@@ -2140,7 +2158,7 @@ restart:
case matchEqFloat:
if (!is_float(*ep))
FAIL();
- if (memcmp(float_val(*ep) + 1, pc, sizeof(double)))
+ if (sys_memcmp(float_val(*ep) + 1, pc, sizeof(double)))
FAIL();
pc += TermWords(2);
++ep;
@@ -2490,25 +2508,20 @@ restart:
if (have_no_seqtrace(SEQ_TRACE_TOKEN(c_p)))
*esp++ = NIL;
else {
- Eterm sender = SEQ_TRACE_TOKEN_SENDER(c_p);
- Uint sender_sz = is_immed(sender) ? 0 : size_object(sender);
- ehp = HAllocX(build_proc, 6 + sender_sz, HEAP_XTRA);
- if (sender_sz) {
- sender = copy_struct(sender, sender_sz, &ehp, &MSO(build_proc));
- }
- *esp++ = make_tuple(ehp);
- ehp[0] = make_arityval(5);
- ehp[1] = SEQ_TRACE_TOKEN_FLAGS(c_p);
- ehp[2] = SEQ_TRACE_TOKEN_LABEL(c_p);
- ehp[3] = SEQ_TRACE_TOKEN_SERIAL(c_p);
- ehp[4] = sender;
- ehp[5] = SEQ_TRACE_TOKEN_LASTCNT(c_p);
- ASSERT(SEQ_TRACE_TOKEN_ARITY(c_p) == 5);
- ASSERT(is_immed(ehp[1]));
- ASSERT(is_immed(ehp[2]));
- ASSERT(is_immed(ehp[3]));
- ASSERT(is_immed(ehp[5]));
- }
+ Eterm token;
+ Uint token_sz;
+
+ ASSERT(SEQ_TRACE_TOKEN_ARITY(c_p) == 5);
+ ASSERT(is_immed(SEQ_TRACE_TOKEN_FLAGS(c_p)));
+ ASSERT(is_immed(SEQ_TRACE_TOKEN_SERIAL(c_p)));
+ ASSERT(is_immed(SEQ_TRACE_TOKEN_LASTCNT(c_p)));
+
+ token = SEQ_TRACE_TOKEN(c_p);
+ token_sz = size_object(token);
+
+ ehp = HAllocX(build_proc, token_sz, HEAP_XTRA);
+ *esp++ = copy_struct(token, token_sz, &ehp, &MSO(build_proc));
+ }
break;
case matchEnableTrace:
ASSERT(c_p == self);
@@ -2742,8 +2755,8 @@ Eterm db_format_dmc_err_info(Process *p, DMCErrInfo *ei)
if (vnum >= 0)
erts_snprintf(buff,sizeof(buff)+20,tmp->error_string, vnum);
else
- strcpy(buff,tmp->error_string);
- sl = strlen(buff);
+ sys_strcpy(buff,tmp->error_string);
+ sl = sys_strlen(buff);
shp = HAlloc(p, sl * 2 + 5);
sev = (tmp->severity == dmcWarning) ?
am_atom_put("warning",7) :
@@ -3535,20 +3548,17 @@ static DMCRet dmc_one_term(DMCContext *context,
return retRestart;
}
if (heap->vars[n].is_bound) {
- DMC_PUSH(*text,matchCmp);
- DMC_PUSH(*text,n);
+ DMC_PUSH2(*text, matchCmp, n);
} else { /* Not bound, bind! */
if (n >= heap->vars_used)
heap->vars_used = n + 1;
- DMC_PUSH(*text,matchBind);
- DMC_PUSH(*text,n);
+ DMC_PUSH2(*text, matchBind, n);
heap->vars[n].is_bound = 1;
}
} else if (c == am_Underscore) {
DMC_PUSH(*text, matchSkip);
} else { /* Any immediate value */
- DMC_PUSH(*text, matchEq);
- DMC_PUSH(*text, (Uint) c);
+ DMC_PUSH2(*text, matchEq, (Uint) c);
}
break;
case TAG_PRIMARY_LIST:
@@ -3561,9 +3571,8 @@ static DMCRet dmc_one_term(DMCContext *context,
switch ((hdr & _TAG_HEADER_MASK) >> _TAG_PRIMARY_SIZE) {
case (_TAG_HEADER_ARITYVAL >> _TAG_PRIMARY_SIZE):
n = arityval(*tuple_val(c));
- DMC_PUSH(*text, matchPushT);
+ DMC_PUSH2(*text, matchPushT, n);
++(context->stack_used);
- DMC_PUSH(*text, n);
DMC_PUSH(*stack, c);
break;
case (_TAG_HEADER_MAP >> _TAG_PRIMARY_SIZE):
@@ -3571,9 +3580,8 @@ static DMCRet dmc_one_term(DMCContext *context,
n = flatmap_get_size(flatmap_val(c));
else
n = hashmap_size(c);
- DMC_PUSH(*text, matchPushM);
+ DMC_PUSH2(*text, matchPushM, n);
++(context->stack_used);
- DMC_PUSH(*text, n);
DMC_PUSH(*stack, c);
break;
case (_TAG_HEADER_REF >> _TAG_PRIMARY_SIZE):
@@ -3598,8 +3606,7 @@ static DMCRet dmc_one_term(DMCContext *context,
break;
}
case (_TAG_HEADER_FLOAT >> _TAG_PRIMARY_SIZE):
- DMC_PUSH(*text,matchEqFloat);
- DMC_PUSH(*text, (Uint) float_val(c)[1]);
+ DMC_PUSH2(*text, matchEqFloat, (Uint) float_val(c)[1]);
#ifdef ARCH_64
DMC_PUSH(*text, (Uint) 0);
#else
@@ -3607,8 +3614,7 @@ static DMCRet dmc_one_term(DMCContext *context,
#endif
break;
default: /* BINARY, FUN, VECTOR, or EXTERNAL */
- DMC_PUSH(*text, matchEqBin);
- DMC_PUSH(*text, dmc_private_copy(context, c));
+ DMC_PUSH2(*text, matchEqBin, dmc_private_copy(context, c));
break;
}
break;
@@ -3658,8 +3664,7 @@ static void do_emit_constant(DMCContext *context, DMC_STACK_TYPE(UWord) *text,
emb->next = context->save;
context->save = emb;
}
- DMC_PUSH(*text,matchPushC);
- DMC_PUSH(*text,(Uint) tmp);
+ DMC_PUSH2(*text, matchPushC, (Uint)tmp);
if (++context->stack_used > context->stack_need)
context->stack_need = context->stack_used;
}
@@ -3797,8 +3802,7 @@ dmc_tuple(DMCContext *context, DMCHeap *heap, DMC_STACK_TYPE(UWord) *text,
*constant = 1;
return retOk;
}
- DMC_PUSH(*text, matchMkTuple);
- DMC_PUSH(*text, nelems);
+ DMC_PUSH2(*text, matchMkTuple, nelems);
context->stack_used -= (nelems - 1);
*constant = 0;
return retOk;
@@ -3825,13 +3829,11 @@ dmc_map(DMCContext *context, DMCHeap *heap, DMC_STACK_TYPE(UWord) *text,
*constant = 1;
return retOk;
}
- DMC_PUSH(*text, matchPushC);
- DMC_PUSH(*text, dmc_private_copy(context, m->keys));
+ DMC_PUSH2(*text, matchPushC, dmc_private_copy(context, m->keys));
if (++context->stack_used > context->stack_need) {
context->stack_need = context->stack_used;
}
- DMC_PUSH(*text, matchMkFlatMap);
- DMC_PUSH(*text, nelems);
+ DMC_PUSH2(*text, matchMkFlatMap, nelems);
context->stack_used -= nelems;
*constant = 0;
return retOk;
@@ -3883,8 +3885,7 @@ dmc_map(DMCContext *context, DMCHeap *heap, DMC_STACK_TYPE(UWord) *text,
do_emit_constant(context, text, CDR(kv));
}
}
- DMC_PUSH(*text, matchMkHashMap);
- DMC_PUSH(*text, nelems);
+ DMC_PUSH2(*text, matchMkHashMap, nelems);
context->stack_used -= nelems;
DESTROY_WSTACK(wstack);
return retOk;
@@ -5044,7 +5045,7 @@ static int match_compact(ErlHeapFragment *expr, DMCErrInfo *err_info)
ASSERT(j < x);
erts_snprintf(buff+1, sizeof(buff) - 1, "%u", (unsigned) j);
/* Yes, writing directly into terms, they ARE off heap */
- *p = erts_atom_put((byte *) buff, strlen(buff),
+ *p = erts_atom_put((byte *) buff, sys_strlen(buff),
ERTS_ATOM_ENC_LATIN1, 1);
}
++p;
@@ -5505,7 +5506,7 @@ void db_match_dis(Binary *bp)
++t;
{
double num;
- memcpy(&num,t,sizeof(double));
+ sys_memcpy(&num,t,sizeof(double));
t += TermWords(2);
erts_printf("EqFloat\t%f\n", num);
}
diff --git a/erts/emulator/beam/erl_debug.c b/erts/emulator/beam/erl_debug.c
index bf8244564a..db78378257 100644
--- a/erts/emulator/beam/erl_debug.c
+++ b/erts/emulator/beam/erl_debug.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1998-2017. All Rights Reserved.
+ * Copyright Ericsson AB 1998-2018. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -404,15 +404,16 @@ void verify_process(Process *p)
erts_exit(ERTS_ERROR_EXIT,"Wild pointer found in " name " of %T!\n",p->common.id); }
- ErtsMessage* mp = p->msg.first;
-
VERBOSE(DEBUG_MEMORY,("Verify process: %T...\n",p->common.id));
- while (mp != NULL) {
- VERIFY_ETERM("message term",ERL_MESSAGE_TERM(mp));
- VERIFY_ETERM("message token",ERL_MESSAGE_TOKEN(mp));
- mp = mp->next;
- }
+ ERTS_FOREACH_SIG_PRIVQS(
+ p, mp,
+ {
+ if (ERTS_SIG_IS_MSG(mp)) {
+ VERIFY_ETERM("message term",ERL_MESSAGE_TERM(mp));
+ VERIFY_ETERM("message token",ERL_MESSAGE_TOKEN(mp));
+ }
+ });
erts_check_stack(p);
erts_check_heap(p);
@@ -532,16 +533,15 @@ static void print_process_memory(Process *p)
erts_printf("-- %-*s ---%s-%s-%s-%s--\n",
PTR_SIZE, "PCB", dashes, dashes, dashes, dashes);
- if (p->msg.first != NULL) {
- ErtsMessage* mp;
- erts_printf(" Message Queue:\n");
- mp = p->msg.first;
- while (mp != NULL) {
- erts_printf("| 0x%0*lx | 0x%0*lx |\n",PTR_SIZE,
- ERL_MESSAGE_TERM(mp),PTR_SIZE,ERL_MESSAGE_TOKEN(mp));
- mp = mp->next;
- }
- }
+
+ erts_printf(" Message Queue:\n");
+ ERTS_FOREACH_SIG_PRIVQS(
+ p, mp,
+ {
+ if (ERTS_SIG_IS_MSG(mp))
+ erts_printf("| 0x%0*lx | 0x%0*lx |\n",PTR_SIZE,
+ ERL_MESSAGE_TERM(mp),PTR_SIZE,ERL_MESSAGE_TOKEN(mp));
+ });
if (p->dictionary != NULL) {
int n = ERTS_PD_SIZE(p->dictionary);
diff --git a/erts/emulator/beam/erl_drv_thread.c b/erts/emulator/beam/erl_drv_thread.c
index 71d4534ef9..4cf42fce57 100644
--- a/erts/emulator/beam/erl_drv_thread.c
+++ b/erts/emulator/beam/erl_drv_thread.c
@@ -439,7 +439,7 @@ erl_drv_tsd_key_create(char *name, ErlDrvTSDKey *key)
name_copy = no_name;
else {
name_copy = erts_alloc_fnf(ERTS_ALC_T_DRV_TSD,
- sizeof(char)*(strlen(name) + 1));
+ sizeof(char)*(sys_strlen(name) + 1));
if (!name_copy) {
*key = -1;
return ENOMEM;
@@ -750,7 +750,7 @@ erl_drv_steal_main_thread(char *name,
+ (name ? sys_strlen(name) + 1 : 0)));
if (!dtid)
return ENOMEM;
- memset(dtid,0,sizeof(ErlDrvTid_));
+ sys_memset(dtid,0,sizeof(ErlDrvTid_));
dtid->tid = (void * ) -1;
dtid->drv_thr = 1;
dtid->func = func;
@@ -763,8 +763,8 @@ erl_drv_steal_main_thread(char *name,
*tid = NULL;
/* Ignore options and name... */
- memcpy(buff,&func,sizeof(void* (*)(void*)));
- memcpy(buff + sizeof(void* (*)(void*)),&arg,sizeof(void *));
+ sys_memcpy(buff,&func,sizeof(void* (*)(void*)));
+ sys_memcpy(buff + sizeof(void* (*)(void*)),&arg,sizeof(void *));
write(erts_darwin_main_thread_pipe[1],buff,buff_sz);
return 0;
}
diff --git a/erts/emulator/beam/erl_gc.c b/erts/emulator/beam/erl_gc.c
index 8344c164fa..b498fd9cf9 100644
--- a/erts/emulator/beam/erl_gc.c
+++ b/erts/emulator/beam/erl_gc.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2002-2017. All Rights Reserved.
+ * Copyright Ericsson AB 2002-2018. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -43,6 +43,7 @@
#include "erl_bif_unique.h"
#include "dist.h"
#include "erl_nfunc_sched.h"
+#include "erl_proc_sig_queue.h"
#define ERTS_INACT_WR_PB_LEAVE_MUCH_LIMIT 1
#define ERTS_INACT_WR_PB_LEAVE_MUCH_PERCENTAGE 20
@@ -60,7 +61,7 @@
# define ERTS_GC_ASSERT(B) ((void) 1)
#endif
-#if defined(DEBUG) && 0
+#if defined(DEBUG) && 1
# define HARDDEBUG 1
#endif
@@ -116,6 +117,7 @@ typedef struct {
static Uint setup_rootset(Process*, Eterm*, int, Rootset*);
static void cleanup_rootset(Rootset *rootset);
static Eterm *full_sweep_heaps(Process *p,
+ ErlHeapFragment *live_hf_end,
int hibernate,
Eterm *n_heap, Eterm* n_htop,
char *oh, Uint oh_size,
@@ -142,7 +144,7 @@ static Eterm* sweep_literal_area(Eterm* n_hp, Eterm* n_htop,
static Eterm* sweep_literals_to_old_heap(Eterm* heap_ptr, Eterm* heap_end, Eterm* htop,
char* src, Uint src_size);
static Eterm* collect_live_heap_frags(Process* p, ErlHeapFragment *live_hf_end,
- Eterm* heap, Eterm* htop, Eterm* objv, int nobj);
+ Eterm* htop);
static int adjust_after_fullsweep(Process *p, int need, Eterm *objv, int nobj);
static void shrink_new_heap(Process *p, Uint new_sz, Eterm *objv, int nobj);
static void grow_new_heap(Process *p, Uint new_sz, Eterm* objv, int nobj);
@@ -153,7 +155,6 @@ static void offset_rootset(Process *p, Sint offs, char* area, Uint area_size,
Eterm* objv, int nobj);
static void offset_off_heap(Process* p, Sint offs, char* area, Uint area_size);
static void offset_mqueue(Process *p, Sint offs, char* area, Uint area_size);
-static void move_msgq_to_heap(Process *p);
static int reached_max_heap_size(Process *p, Uint total_heap_size,
Uint extra_heap_size, Uint extra_old_heap_size);
static void init_gc_info(ErtsGCInfo *gcip);
@@ -188,6 +189,21 @@ static struct {
ErtsGCInfo info;
} dirty_gc;
+static void move_msgs_to_heap(Process *c_p)
+{
+ Uint64 pre_oh, post_oh;
+
+ pre_oh = c_p->off_heap.overhead;
+ erts_proc_sig_move_msgs_to_heap(c_p);
+ post_oh = c_p->off_heap.overhead;
+
+ if (pre_oh != post_oh) {
+ /* Got new binaries; update bin vheap size... */
+ c_p->bin_vheap_sz = next_vheap_size(c_p, post_oh,
+ c_p->bin_vheap_sz);
+ }
+}
+
static ERTS_INLINE int
gc_cost(Uint gc_moved_live_words, Uint resize_moved_words)
{
@@ -206,6 +222,24 @@ ERTS_SCHED_PREF_QUICK_ALLOC_IMPL(gcireq,
ErtsGCInfoReq,
5,
ERTS_ALC_T_GC_INFO_REQ)
+
+static ERTS_INLINE void
+ensure_sigq_roots_available(Process *p)
+{
+ ERTS_LC_ASSERT(ERTS_PROC_LOCK_MAIN == erts_proc_lc_my_proc_locks(p));
+ switch (p->flags & (F_OFF_HEAP_MSGQ|F_OFF_HEAP_MSGQ_CHNG)) {
+ case F_OFF_HEAP_MSGQ_CHNG:
+ case 0:
+ erts_proc_lock(p, ERTS_PROC_LOCK_MSGQ);
+ erts_proc_sig_fetch(p);
+ erts_proc_unlock(p, ERTS_PROC_LOCK_MSGQ);
+ break;
+ default:
+ break;
+ }
+}
+
+
/*
* Initialize GC global data.
*/
@@ -337,7 +371,7 @@ erts_heap_sizes(Process* p)
for (i = num_heap_sizes-1; i >= 0; i--) {
n += 2;
- if (!MY_IS_SSMALL(heap_sizes[i])) {
+ if (!IS_SSMALL(heap_sizes[i])) {
big += BIG_UINT_HEAP_SIZE;
}
}
@@ -352,7 +386,7 @@ erts_heap_sizes(Process* p)
Eterm num;
Sint sz = heap_sizes[i];
- if (MY_IS_SSMALL(sz)) {
+ if (IS_SSMALL(sz)) {
num = make_small(sz);
} else {
num = uint_to_big(sz, bigp);
@@ -404,11 +438,20 @@ erts_gc_after_bif_call_lhf(Process* p, ErlHeapFragment *live_hf_end,
return result;
}
+#ifdef HIPE
+ if (p->hipe_smp.have_receive_locks) {
+ /* Do not want to GC with message queue locked... */
+ return result;
+ }
+#endif
+
if (!p->mbuf) {
/* Must have GC:d in BIF call... invalidate live_hf_end */
live_hf_end = ERTS_INVALID_HFRAG_PTR;
}
+ ensure_sigq_roots_available(p);
+
if (is_non_value(result)) {
if (p->freason == TRAP) {
#ifdef HIPE
@@ -564,20 +607,26 @@ young_gen_usage(Process *p)
hsz = p->mbuf_sz;
if (p->flags & F_ON_HEAP_MSGQ) {
- ErtsMessage *mp;
- for (mp = p->msg.first; mp; mp = mp->next) {
- /*
- * We leave not yet decoded distribution messages
- * as they are in the queue since it is not
- * possible to determine a maximum size until
- * actual decoding. However, we use their estimated
- * size when calculating need, and by this making
- * it more likely that they will fit on the heap
- * when actually decoded.
- */
- if (mp->data.attached)
- hsz += erts_msg_attached_data_size(mp);
- }
+ ERTS_FOREACH_SIG_PRIVQS(
+ p, mp,
+ {
+ /*
+ * We leave not yet decoded distribution messages
+ * as they are in the queue since it is not
+ * possible to determine a maximum size until
+ * actual decoding. However, we use their estimated
+ * size when calculating need, and by this making
+ * it more likely that they will fit on the heap
+ * when actually decoded.
+ *
+ * We however ignore off heap messages...
+ */
+ if (ERTS_SIG_IS_MSG(mp)
+ && mp->data.attached
+ && mp->data.attached != ERTS_MSG_COMBINED_HFRAG) {
+ hsz += erts_msg_attached_data_size(mp);
+ }
+ });
}
hsz += p->htop - p->heap;
@@ -620,7 +669,7 @@ check_for_possibly_long_gc(Process *p, Uint ygen_usage)
sz = ygen_usage;
sz += p->hend - p->stop;
if (p->flags & F_ON_HEAP_MSGQ)
- sz += p->msg.len;
+ sz += erts_proc_sig_privqs_len(p);
if (major)
sz += p->old_htop - p->old_heap;
@@ -760,13 +809,9 @@ do_major_collection:
long_gc/large_gc triggers when this happens as process was
killed before a GC could be done. */
if (reds == -2) {
- ErtsProcLocks locks = ERTS_PROC_LOCKS_ALL;
int res;
- erts_proc_lock(p, ERTS_PROC_LOCKS_ALL_MINOR);
- erts_send_exit_signal(p, p->common.id, p, &locks,
- am_kill, NIL, NULL, 0);
- erts_proc_unlock(p, locks & ERTS_PROC_LOCKS_ALL_MINOR);
+ erts_set_self_exiting(p, am_killed);
delay_gc_after_start:
/* erts_send_exit_signal looks for ERTS_PSFLG_GC, so
@@ -850,8 +895,11 @@ do_major_collection:
int
erts_garbage_collect_nobump(Process* p, int need, Eterm* objv, int nobj, int fcalls)
{
- int reds = garbage_collect(p, ERTS_INVALID_HFRAG_PTR, need, objv, nobj, fcalls, 0);
- int reds_left = ERTS_REDS_LEFT(p, fcalls);
+ int reds;
+ int reds_left;
+ ensure_sigq_roots_available(p);
+ reds = garbage_collect(p, ERTS_INVALID_HFRAG_PTR, need, objv, nobj, fcalls, 0);
+ reds_left = ERTS_REDS_LEFT(p, fcalls);
if (reds > reds_left)
reds = reds_left;
ASSERT(CONTEXT_REDS - (reds_left - reds) >= erts_proc_sched_data(p)->virtual_reds);
@@ -861,7 +909,9 @@ erts_garbage_collect_nobump(Process* p, int need, Eterm* objv, int nobj, int fca
void
erts_garbage_collect(Process* p, int need, Eterm* objv, int nobj)
{
- int reds = garbage_collect(p, ERTS_INVALID_HFRAG_PTR, need, objv, nobj, p->fcalls, 0);
+ int reds;
+ ensure_sigq_roots_available(p);
+ reds = garbage_collect(p, ERTS_INVALID_HFRAG_PTR, need, objv, nobj, p->fcalls, 0);
BUMP_REDS(p, reds);
ASSERT(CONTEXT_REDS - ERTS_BIF_REDS_LEFT(p)
>= erts_proc_sched_data(p)->virtual_reds);
@@ -917,6 +967,7 @@ garbage_collect_hibernate(Process* p, int check_long_gc)
htop = heap;
htop = full_sweep_heaps(p,
+ ERTS_INVALID_HFRAG_PTR,
1,
heap,
htop,
@@ -1086,6 +1137,8 @@ erts_garbage_collect_literals(Process* p, Eterm* literals,
* First an ordinary major collection...
*/
+ ensure_sigq_roots_available(p);
+
p->flags |= F_NEED_FULLSWEEP;
if (ERTS_SCHEDULER_IS_DIRTY(erts_proc_sched_data(p)))
@@ -1161,7 +1214,7 @@ erts_garbage_collect_literals(Process* p, Eterm* literals,
roots++;
- while (g_sz--) {
+ for ( ; g_sz--; g_ptr++) {
Eterm gval = *g_ptr;
switch (primary_tag(gval)) {
@@ -1170,26 +1223,21 @@ erts_garbage_collect_literals(Process* p, Eterm* literals,
val = *ptr;
if (IS_MOVED_BOXED(val)) {
ASSERT(is_boxed(val));
- *g_ptr++ = val;
+ *g_ptr = val;
} else if (ErtsInArea(ptr, area, area_size)) {
- move_boxed(&ptr,val,&old_htop,g_ptr++);
- } else {
- g_ptr++;
+ move_boxed(ptr,val,&old_htop,g_ptr);
}
break;
case TAG_PRIMARY_LIST:
ptr = list_val(gval);
val = *ptr;
if (IS_MOVED_CONS(val)) { /* Moved */
- *g_ptr++ = ptr[1];
+ *g_ptr = ptr[1];
} else if (ErtsInArea(ptr, area, area_size)) {
- move_cons(&ptr,val,&old_htop,g_ptr++);
- } else {
- g_ptr++;
- }
+ move_cons(ptr,val,&old_htop,g_ptr);
+ }
break;
default:
- g_ptr++;
break;
}
}
@@ -1210,8 +1258,8 @@ erts_garbage_collect_literals(Process* p, Eterm* literals,
p->old_htop = old_htop;
/*
- * Prepare to sweep binaries. Since all MSOs on the new heap
- * must be come before MSOs on the old heap, find the end of
+ * Prepare to sweep off-heap objects. Since all MSOs on the new
+ * heap must be come before MSOs on the old heap, find the end of
* current MSO list and use that as a starting point.
*/
@@ -1223,25 +1271,50 @@ erts_garbage_collect_literals(Process* p, Eterm* literals,
}
/*
- * Sweep through all binaries in the temporary literal area.
+ * Sweep through all off-heap objects in the temporary literal area.
*/
while (oh) {
if (IS_MOVED_BOXED(oh->thing_word)) {
- Binary* bptr;
struct erl_off_heap_header* ptr;
- ptr = (struct erl_off_heap_header*) boxed_val(oh->thing_word);
- ASSERT(thing_subtag(ptr->thing_word) == REFC_BINARY_SUBTAG);
- bptr = ((ProcBin*)ptr)->val;
-
- /*
- * This binary has been copied to the heap.
+ /*
+ * This off-heap object has been copied to the heap.
* We must increment its reference count and
* link it into the MSO list for the process.
*/
- erts_refc_inc(&bptr->intern.refc, 1);
+ ptr = (struct erl_off_heap_header*) boxed_val(oh->thing_word);
+ switch (thing_subtag(ptr->thing_word)) {
+ case REFC_BINARY_SUBTAG:
+ {
+ Binary* bptr = ((ProcBin*)ptr)->val;
+ erts_refc_inc(&bptr->intern.refc, 1);
+ break;
+ }
+ case FUN_SUBTAG:
+ {
+ ErlFunEntry* fe = ((ErlFunThing*)ptr)->fe;
+ erts_refc_inc(&fe->refc, 1);
+ break;
+ }
+ case REF_SUBTAG:
+ {
+ ErtsMagicBinary *bptr;
+ ASSERT(is_magic_ref_thing(ptr));
+ bptr = ((ErtsMRefThing *) ptr)->mb;
+ erts_refc_inc(&bptr->intern.refc, 1);
+ break;
+ }
+ default:
+ {
+ ExternalThing *etp;
+ ASSERT(is_external_header(ptr->thing_word));
+ etp = (ExternalThing *) ptr;
+ erts_refc_inc(&etp->node->refc, 1);
+ break;
+ }
+ }
*prev = ptr;
prev = &ptr->next;
}
@@ -1353,7 +1426,7 @@ minor_collection(Process* p, ErlHeapFragment *live_hf_end,
new_sz, objv, nobj);
if (p->flags & F_ON_HEAP_MSGQ)
- move_msgq_to_heap(p);
+ move_msgs_to_heap(p);
new_mature = p->old_htop - prev_old_htop;
@@ -1454,25 +1527,29 @@ do_minor(Process *p, ErlHeapFragment *live_hf_end,
n_htop = n_heap = (Eterm*) ERTS_HEAP_ALLOC(ERTS_ALC_T_HEAP,
sizeof(Eterm)*new_sz);
+ n = setup_rootset(p, objv, nobj, &rootset);
+ roots = rootset.roots;
+
+ /*
+ * All allocations done. Start defile heap with move markers.
+ * A crash dump due to allocation failure above will see a healthy heap.
+ */
+
if (live_hf_end != ERTS_INVALID_HFRAG_PTR) {
/*
* Move heap frags that we know are completely live
* directly into the new young heap generation.
*/
- n_htop = collect_live_heap_frags(p, live_hf_end, n_heap, n_htop,
- objv, nobj);
+ n_htop = collect_live_heap_frags(p, live_hf_end, n_htop);
}
- n = setup_rootset(p, objv, nobj, &rootset);
- roots = rootset.roots;
-
GENSWEEP_NSTACK(p, old_htop, n_htop);
while (n--) {
Eterm* g_ptr = roots->v;
Uint g_sz = roots->sz;
roots++;
- while (g_sz--) {
+ for ( ; g_sz--; g_ptr++) {
gval = *g_ptr;
switch (primary_tag(gval)) {
@@ -1482,14 +1559,12 @@ do_minor(Process *p, ErlHeapFragment *live_hf_end,
val = *ptr;
if (IS_MOVED_BOXED(val)) {
ASSERT(is_boxed(val));
- *g_ptr++ = val;
+ *g_ptr = val;
} else if (ErtsInArea(ptr, mature, mature_size)) {
- move_boxed(&ptr,val,&old_htop,g_ptr++);
+ move_boxed(ptr,val,&old_htop,g_ptr);
} else if (ErtsInYoungGen(gval, ptr, oh, oh_size)) {
- move_boxed(&ptr,val,&n_htop,g_ptr++);
- } else {
- g_ptr++;
- }
+ move_boxed(ptr,val,&n_htop,g_ptr);
+ }
break;
}
@@ -1497,19 +1572,15 @@ do_minor(Process *p, ErlHeapFragment *live_hf_end,
ptr = list_val(gval);
val = *ptr;
if (IS_MOVED_CONS(val)) { /* Moved */
- *g_ptr++ = ptr[1];
+ *g_ptr = ptr[1];
} else if (ErtsInArea(ptr, mature, mature_size)) {
- move_cons(&ptr,val,&old_htop,g_ptr++);
+ move_cons(ptr,val,&old_htop,g_ptr);
} else if (ErtsInYoungGen(gval, ptr, oh, oh_size)) {
- move_cons(&ptr,val,&n_htop,g_ptr++);
- } else {
- g_ptr++;
- }
+ move_cons(ptr,val,&n_htop,g_ptr);
+ }
break;
}
-
default:
- g_ptr++;
break;
}
}
@@ -1543,9 +1614,9 @@ do_minor(Process *p, ErlHeapFragment *live_hf_end,
ASSERT(is_boxed(val));
*n_hp++ = val;
} else if (ErtsInArea(ptr, mature, mature_size)) {
- move_boxed(&ptr,val,&old_htop,n_hp++);
+ move_boxed(ptr,val,&old_htop,n_hp++);
} else if (ErtsInYoungGen(gval, ptr, oh, oh_size)) {
- move_boxed(&ptr,val,&n_htop,n_hp++);
+ move_boxed(ptr,val,&n_htop,n_hp++);
} else {
n_hp++;
}
@@ -1557,9 +1628,9 @@ do_minor(Process *p, ErlHeapFragment *live_hf_end,
if (IS_MOVED_CONS(val)) {
*n_hp++ = ptr[1];
} else if (ErtsInArea(ptr, mature, mature_size)) {
- move_cons(&ptr,val,&old_htop,n_hp++);
+ move_cons(ptr,val,&old_htop,n_hp++);
} else if (ErtsInYoungGen(gval, ptr, oh, oh_size)) {
- move_cons(&ptr,val,&n_htop,n_hp++);
+ move_cons(ptr,val,&n_htop,n_hp++);
} else {
n_hp++;
}
@@ -1579,10 +1650,10 @@ do_minor(Process *p, ErlHeapFragment *live_hf_end,
*origptr = val;
mb->base = binary_bytes(val);
} else if (ErtsInArea(ptr, mature, mature_size)) {
- move_boxed(&ptr,val,&old_htop,origptr);
+ move_boxed(ptr,val,&old_htop,origptr);
mb->base = binary_bytes(mb->orig);
} else if (ErtsInYoungGen(*origptr, ptr, oh, oh_size)) {
- move_boxed(&ptr,val,&n_htop,origptr);
+ move_boxed(ptr,val,&n_htop,origptr);
mb->base = binary_bytes(mb->orig);
}
}
@@ -1708,16 +1779,8 @@ major_collection(Process* p, ErlHeapFragment *live_hf_end,
n_htop = n_heap = (Eterm *) ERTS_HEAP_ALLOC(ERTS_ALC_T_HEAP,
sizeof(Eterm)*new_sz);
- if (live_hf_end != ERTS_INVALID_HFRAG_PTR) {
- /*
- * Move heap frags that we know are completely live
- * directly into the heap.
- */
- n_htop = collect_live_heap_frags(p, live_hf_end, n_heap, n_htop,
- objv, nobj);
- }
-
- n_htop = full_sweep_heaps(p, 0, n_heap, n_htop, oh, oh_size, objv, nobj);
+ n_htop = full_sweep_heaps(p, live_hf_end, 0, n_heap, n_htop, oh, oh_size,
+ objv, nobj);
/* Move the stack to the end of the heap */
stk_sz = HEAP_END(p) - p->stop;
@@ -1748,7 +1811,7 @@ major_collection(Process* p, ErlHeapFragment *live_hf_end,
HIGH_WATER(p) = HEAP_TOP(p);
if (p->flags & F_ON_HEAP_MSGQ)
- move_msgq_to_heap(p);
+ move_msgs_to_heap(p);
ErtsGcQuickSanityCheck(p);
@@ -1764,6 +1827,7 @@ major_collection(Process* p, ErlHeapFragment *live_hf_end,
static Eterm *
full_sweep_heaps(Process *p,
+ ErlHeapFragment *live_hf_end,
int hibernate,
Eterm *n_heap, Eterm* n_htop,
char *oh, Uint oh_size,
@@ -1780,6 +1844,19 @@ full_sweep_heaps(Process *p,
n = setup_rootset(p, objv, nobj, &rootset);
+ /*
+ * All allocations done. Start defile heap with move markers.
+ * A crash dump due to allocation failure above will see a healthy heap.
+ */
+
+ if (live_hf_end != ERTS_INVALID_HFRAG_PTR) {
+ /*
+ * Move heap frags that we know are completely live
+ * directly into the heap.
+ */
+ n_htop = collect_live_heap_frags(p, live_hf_end, n_htop);
+ }
+
#ifdef HIPE
if (hibernate)
hipe_empty_nstack(p);
@@ -1793,7 +1870,7 @@ full_sweep_heaps(Process *p,
Eterm g_sz = roots->sz;
roots++;
- while (g_sz--) {
+ for ( ; g_sz--; g_ptr++) {
Eterm* ptr;
Eterm val;
Eterm gval = *g_ptr;
@@ -1805,32 +1882,26 @@ full_sweep_heaps(Process *p,
val = *ptr;
if (IS_MOVED_BOXED(val)) {
ASSERT(is_boxed(val));
- *g_ptr++ = val;
+ *g_ptr = val;
} else if (!erts_is_literal(gval, ptr)) {
- move_boxed(&ptr,val,&n_htop,g_ptr++);
- } else {
- g_ptr++;
+ move_boxed(ptr,val,&n_htop,g_ptr);
}
- continue;
+ break;
}
case TAG_PRIMARY_LIST: {
ptr = list_val(gval);
val = *ptr;
if (IS_MOVED_CONS(val)) {
- *g_ptr++ = ptr[1];
+ *g_ptr = ptr[1];
} else if (!erts_is_literal(gval, ptr)) {
- move_cons(&ptr,val,&n_htop,g_ptr++);
- } else {
- g_ptr++;
+ move_cons(ptr,val,&n_htop,g_ptr);
}
- continue;
+ break;
}
- default: {
- g_ptr++;
- continue;
- }
+ default:
+ break;
}
}
}
@@ -2109,7 +2180,7 @@ sweep(Eterm *n_hp, Eterm *n_htop,
ASSERT(is_boxed(val));
*n_hp++ = val;
} else if (ERTS_IS_IN_SWEEP_AREA(gval, ptr)) {
- move_boxed(&ptr,val,&n_htop,n_hp++);
+ move_boxed(ptr,val,&n_htop,n_hp++);
} else {
n_hp++;
}
@@ -2121,7 +2192,7 @@ sweep(Eterm *n_hp, Eterm *n_htop,
if (IS_MOVED_CONS(val)) {
*n_hp++ = ptr[1];
} else if (ERTS_IS_IN_SWEEP_AREA(gval, ptr)) {
- move_cons(&ptr,val,&n_htop,n_hp++);
+ move_cons(ptr,val,&n_htop,n_hp++);
} else {
n_hp++;
}
@@ -2142,7 +2213,7 @@ sweep(Eterm *n_hp, Eterm *n_htop,
*origptr = val;
mb->base = binary_bytes(*origptr);
} else if (ERTS_IS_IN_SWEEP_AREA(*origptr, ptr)) {
- move_boxed(&ptr,val,&n_htop,origptr);
+ move_boxed(ptr,val,&n_htop,origptr);
mb->base = binary_bytes(*origptr);
}
}
@@ -2205,7 +2276,7 @@ sweep_literals_to_old_heap(Eterm* heap_ptr, Eterm* heap_end, Eterm* htop,
ASSERT(is_boxed(val));
*heap_ptr++ = val;
} else if (ErtsInArea(ptr, src, src_size)) {
- move_boxed(&ptr,val,&htop,heap_ptr++);
+ move_boxed(ptr,val,&htop,heap_ptr++);
} else {
heap_ptr++;
}
@@ -2217,7 +2288,7 @@ sweep_literals_to_old_heap(Eterm* heap_ptr, Eterm* heap_end, Eterm* htop,
if (IS_MOVED_CONS(val)) {
*heap_ptr++ = ptr[1];
} else if (ErtsInArea(ptr, src, src_size)) {
- move_cons(&ptr,val,&htop,heap_ptr++);
+ move_cons(ptr,val,&htop,heap_ptr++);
} else {
heap_ptr++;
}
@@ -2238,7 +2309,7 @@ sweep_literals_to_old_heap(Eterm* heap_ptr, Eterm* heap_end, Eterm* htop,
*origptr = val;
mb->base = binary_bytes(*origptr);
} else if (ErtsInArea(ptr, src, src_size)) {
- move_boxed(&ptr,val,&htop,origptr);
+ move_boxed(ptr,val,&htop,origptr);
mb->base = binary_bytes(*origptr);
}
}
@@ -2271,11 +2342,11 @@ move_one_area(Eterm* n_htop, char* src, Uint src_size)
ASSERT(val != ERTS_HOLE_MARKER);
if (is_header(val)) {
ASSERT(ptr + header_arity(val) < end);
- move_boxed(&ptr, val, &n_htop, &dummy_ref);
+ ptr = move_boxed(ptr, val, &n_htop, &dummy_ref);
}
else { /* must be a cons cell */
ASSERT(ptr+1 < end);
- move_cons(&ptr, val, &n_htop, &dummy_ref);
+ move_cons(ptr, val, &n_htop, &dummy_ref);
ptr += 2;
}
}
@@ -2288,9 +2359,7 @@ move_one_area(Eterm* n_htop, char* src, Uint src_size)
*/
static Eterm*
-collect_live_heap_frags(Process* p, ErlHeapFragment *live_hf_end,
- Eterm* n_hstart, Eterm* n_htop,
- Eterm* objv, int nobj)
+collect_live_heap_frags(Process* p, ErlHeapFragment *live_hf_end, Eterm* n_htop)
{
ErlHeapFragment* qb;
char* frag_begin;
@@ -2314,9 +2383,9 @@ collect_live_heap_frags(Process* p, ErlHeapFragment *live_hf_end,
return n_htop;
}
-static ERTS_INLINE void
-copy_one_frag(Eterm** hpp, ErlOffHeap* off_heap,
- ErlHeapFragment *bp, Eterm *refs, int nrefs)
+void
+erts_copy_one_frag(Eterm** hpp, ErlOffHeap* off_heap,
+ ErlHeapFragment *bp, Eterm *refs, int nrefs)
{
Uint sz;
int i;
@@ -2422,61 +2491,6 @@ copy_one_frag(Eterm** hpp, ErlOffHeap* off_heap,
bp->off_heap.first = NULL;
}
-static void
-move_msgq_to_heap(Process *p)
-{
-
- ErtsMessage **mpp = &p->msg.first;
- Uint64 pre_oh = MSO(p).overhead;
-
- while (*mpp) {
- ErtsMessage *mp = *mpp;
-
- if (mp->data.attached) {
- ErlHeapFragment *bp;
-
- /*
- * We leave not yet decoded distribution messages
- * as they are in the queue since it is not
- * possible to determine a maximum size until
- * actual decoding...
- */
- if (is_value(ERL_MESSAGE_TERM(mp))) {
-
- bp = erts_message_to_heap_frag(mp);
-
- if (bp->next)
- erts_move_multi_frags(&p->htop, &p->off_heap, bp,
- mp->m, ERL_MESSAGE_REF_ARRAY_SZ, 0);
- else
- copy_one_frag(&p->htop, &p->off_heap, bp,
- mp->m, ERL_MESSAGE_REF_ARRAY_SZ);
-
- if (mp->data.attached != ERTS_MSG_COMBINED_HFRAG) {
- mp->data.heap_frag = NULL;
- free_message_buffer(bp);
- }
- else {
- ErtsMessage *new_mp = erts_alloc_message(0, NULL);
- sys_memcpy((void *) new_mp->m, (void *) mp->m,
- sizeof(Eterm)*ERL_MESSAGE_REF_ARRAY_SZ);
- erts_msgq_replace_msg_ref(&p->msg, new_mp, mpp);
- mp->next = NULL;
- erts_cleanup_messages(mp);
- mp = new_mp;
- }
- }
- }
-
- mpp = &(*mpp)->next;
- }
-
- if (pre_oh != MSO(p).overhead) {
- /* Got new binaries; update vheap size... */
- BIN_VHEAP_SZ(p) = next_vheap_size(p, MSO(p).overhead, BIN_VHEAP_SZ(p));
- }
-}
-
static Uint
setup_rootset(Process *p, Eterm *objv, int nobj, Rootset *rootset)
{
@@ -2561,15 +2575,17 @@ setup_rootset(Process *p, Eterm *objv, int nobj, Rootset *rootset)
break;
case F_OFF_HEAP_MSGQ_CHNG:
case 0: {
+ Sint len;
/*
* We do not have off heap message queue enabled, i.e. we
- * need to add message queue to rootset...
+ * need to add signal queues to rootset...
*/
- ErtsMessage *mp;
+
+ len = erts_proc_sig_privqs_len(p);
/* Ensure large enough rootset... */
- if (n + p->msg.len > rootset->size) {
- Uint new_size = n + p->msg.len;
+ if (n + len > rootset->size) {
+ Uint new_size = n + len;
ERTS_GC_ASSERT(roots == rootset->def);
roots = erts_alloc(ERTS_ALC_T_ROOTSET,
new_size*sizeof(Roots));
@@ -2577,18 +2593,19 @@ setup_rootset(Process *p, Eterm *objv, int nobj, Rootset *rootset)
rootset->size = new_size;
}
- for (mp = p->msg.first; mp; mp = mp->next) {
-
- if (!mp->data.attached) {
- /*
- * Message may refer data on heap;
- * add it to rootset...
- */
- roots[n].v = mp->m;
- roots[n].sz = ERL_MESSAGE_REF_ARRAY_SZ;
- n++;
- }
- }
+ ERTS_FOREACH_SIG_PRIVQS(
+ p, mp,
+ {
+ if (ERTS_SIG_IS_INTERNAL_MSG(mp) && !mp->data.attached) {
+ /*
+ * Message may refer data on heap;
+ * add it to rootset...
+ */
+ roots[n].v = mp->m;
+ roots[n].sz = ERL_MESSAGE_REF_ARRAY_SZ;
+ n++;
+ }
+ });
break;
}
}
@@ -3099,51 +3116,59 @@ offset_off_heap(Process* p, Sint offs, char* area, Uint area_size)
}
}
+#ifndef USE_VM_PROBES
+#define ERTS_OFFSET_DT_UTAG(MP, A, ASZ, O)
+#else
+#define ERTS_OFFSET_DT_UTAG(MP, A, ASZ, O) \
+ do { \
+ Eterm utag__ = ERL_MESSAGE_DT_UTAG((MP)); \
+ if (is_boxed(utag__) && ErtsInArea(ptr_val(utag__), (A), (ASZ))) { \
+ ERL_MESSAGE_DT_UTAG((MP)) = offset_ptr(utag__, (O)); \
+ } \
+ } while (0)
+#endif
+
+static ERTS_INLINE void
+offset_message(ErtsMessage *mp, Sint offs, char* area, Uint area_size)
+{
+ Eterm mesg = ERL_MESSAGE_TERM(mp);
+ if (ERTS_SIG_IS_MSG_TAG(mesg)) {
+ if (ERTS_SIG_IS_INTERNAL_MSG_TAG(mesg)) {
+ switch (primary_tag(mesg)) {
+ case TAG_PRIMARY_LIST:
+ case TAG_PRIMARY_BOXED:
+ if (ErtsInArea(ptr_val(mesg), area, area_size)) {
+ ERL_MESSAGE_TERM(mp) = offset_ptr(mesg, offs);
+ }
+ break;
+ }
+ }
+ mesg = ERL_MESSAGE_TOKEN(mp);
+ if (is_boxed(mesg) && ErtsInArea(ptr_val(mesg), area, area_size)) {
+ ERL_MESSAGE_TOKEN(mp) = offset_ptr(mesg, offs);
+ }
+
+ ERTS_OFFSET_DT_UTAG(mp, area, area_size, offs);
+
+ ASSERT((is_nil(ERL_MESSAGE_TOKEN(mp)) ||
+ is_tuple(ERL_MESSAGE_TOKEN(mp)) ||
+ is_atom(ERL_MESSAGE_TOKEN(mp))));
+ }
+}
+
/*
* Offset pointers in message queue.
*/
static void
offset_mqueue(Process *p, Sint offs, char* area, Uint area_size)
{
- ErtsMessage* mp = p->msg.first;
-
- if ((p->flags & (F_OFF_HEAP_MSGQ|F_OFF_HEAP_MSGQ_CHNG)) != F_OFF_HEAP_MSGQ) {
-
- while (mp != NULL) {
- Eterm mesg = ERL_MESSAGE_TERM(mp);
- if (is_value(mesg)) {
- switch (primary_tag(mesg)) {
- case TAG_PRIMARY_LIST:
- case TAG_PRIMARY_BOXED:
- if (ErtsInArea(ptr_val(mesg), area, area_size)) {
- ERL_MESSAGE_TERM(mp) = offset_ptr(mesg, offs);
- }
- break;
- }
- }
- mesg = ERL_MESSAGE_TOKEN(mp);
- if (is_boxed(mesg) && ErtsInArea(ptr_val(mesg), area, area_size)) {
- ERL_MESSAGE_TOKEN(mp) = offset_ptr(mesg, offs);
- }
-#ifdef USE_VM_PROBES
- mesg = ERL_MESSAGE_DT_UTAG(mp);
- if (is_boxed(mesg) && ErtsInArea(ptr_val(mesg), area, area_size)) {
- ERL_MESSAGE_DT_UTAG(mp) = offset_ptr(mesg, offs);
- }
-#endif
-
- ASSERT((is_nil(ERL_MESSAGE_TOKEN(mp)) ||
- is_tuple(ERL_MESSAGE_TOKEN(mp)) ||
- is_atom(ERL_MESSAGE_TOKEN(mp))));
- mp = mp->next;
- }
-
- }
+ if ((p->flags & (F_OFF_HEAP_MSGQ|F_OFF_HEAP_MSGQ_CHNG)) != F_OFF_HEAP_MSGQ)
+ ERTS_FOREACH_SIG_PRIVQS(p, mp, offset_message(mp, offs, area, area_size));
}
static void ERTS_INLINE
offset_one_rootset(Process *p, Sint offs, char* area, Uint area_size,
- Eterm* objv, int nobj)
+ Eterm* objv, int nobj)
{
Eterm *v;
Uint sz;
@@ -3256,8 +3281,7 @@ reply_gc_info(void *vgcirp)
gcireq_free(vgcirp);
}
-void erts_sub_binary_to_heap_binary(Eterm **pp, Eterm **hpp, Eterm *orig) {
- Eterm *ptr = *pp;
+Eterm* erts_sub_binary_to_heap_binary(Eterm *ptr, Eterm **hpp, Eterm *orig) {
Eterm *htop = *hpp;
Eterm gval;
ErlSubBin *sb = (ErlSubBin *)ptr;
@@ -3285,7 +3309,7 @@ void erts_sub_binary_to_heap_binary(Eterm **pp, Eterm **hpp, Eterm *orig) {
htop += heap_bin_size(sb->size);
*hpp = htop;
- *pp = ptr;
+ return ptr;
}
@@ -3361,7 +3385,6 @@ erts_process_gc_info(Process *p, Uint *sizep, Eterm **hpp,
};
Eterm res = THE_NON_VALUE;
- ErtsMessage *mp;
ERTS_CT_ASSERT(sizeof(values)/sizeof(*values) == sizeof(tags)/sizeof(*tags));
ERTS_CT_ASSERT(sizeof(values)/sizeof(*values) == ERTS_PROCESS_GC_INFO_MAX_TERMS);
@@ -3380,9 +3403,15 @@ erts_process_gc_info(Process *p, Uint *sizep, Eterm **hpp,
be the same as adding old_heap_block_size + heap_block_size
+ mbuf_size.
*/
- for (mp = p->msg.first; mp; mp = mp->next)
- if (mp->data.attached)
- values[2] += erts_msg_attached_data_size(mp);
+ ERTS_FOREACH_SIG_PRIVQS(
+ p, mp,
+ {
+ if (ERTS_SIG_IS_MSG(mp)
+ && mp->data.attached
+ && mp->data.attached != ERTS_MSG_COMBINED_HFRAG) {
+ values[2] += erts_msg_attached_data_size(mp);
+ }
+ });
}
res = erts_bld_atom_uword_2tup_list(hpp,
@@ -3456,7 +3485,7 @@ erts_max_heap_size_map(Sint max_heap_size, Uint max_heap_flags,
Eterm **hpp, Uint *sz)
{
if (!hpp) {
- *sz += (2*3 + 1 + MAP_HEADER_FLATMAP_SZ);
+ *sz += ERTS_MAX_HEAP_SIZE_MAP_SZ;
return THE_NON_VALUE;
} else {
Eterm *hp = *hpp;
diff --git a/erts/emulator/beam/erl_gc.h b/erts/emulator/beam/erl_gc.h
index 6a529b8443..b9b1ed728c 100644
--- a/erts/emulator/beam/erl_gc.h
+++ b/erts/emulator/beam/erl_gc.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2007-2017. All Rights Reserved.
+ * Copyright Ericsson AB 2007-2018. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -33,14 +33,15 @@
#define IS_MOVED_BOXED(x) (!is_header((x)))
#define IS_MOVED_CONS(x) (is_non_value((x)))
-void erts_sub_binary_to_heap_binary(Eterm **pp, Eterm **hpp, Eterm *orig);
+Eterm* erts_sub_binary_to_heap_binary(Eterm *ptr, Eterm **hpp, Eterm *orig);
-ERTS_GLB_INLINE void move_cons(Eterm **pp, Eterm car, Eterm **hpp, Eterm *orig);
+ERTS_GLB_INLINE void move_cons(Eterm *ERTS_RESTRICT ptr, Eterm car, Eterm **hpp,
+ Eterm *orig);
#if ERTS_GLB_INLINE_INCL_FUNC_DEF
-ERTS_GLB_INLINE void move_cons(Eterm **pp, Eterm car, Eterm **hpp, Eterm *orig)
+ERTS_GLB_INLINE void move_cons(Eterm *ERTS_RESTRICT ptr, Eterm car, Eterm **hpp,
+ Eterm *orig)
{
- Eterm *ptr = *pp;
- Eterm *htop = *hpp;
+ Eterm *ERTS_RESTRICT htop = *hpp;
Eterm gval;
htop[0] = car; /* copy car */
@@ -53,14 +54,15 @@ ERTS_GLB_INLINE void move_cons(Eterm **pp, Eterm car, Eterm **hpp, Eterm *orig)
}
#endif
-ERTS_GLB_INLINE void move_boxed(Eterm **pp, Eterm hdr, Eterm **hpp, Eterm *orig);
+ERTS_GLB_INLINE Eterm* move_boxed(Eterm *ERTS_RESTRICT ptr, Eterm hdr, Eterm **hpp,
+ Eterm *orig);
#if ERTS_GLB_INLINE_INCL_FUNC_DEF
-ERTS_GLB_INLINE void move_boxed(Eterm **pp, Eterm hdr, Eterm **hpp, Eterm *orig)
+ERTS_GLB_INLINE Eterm* move_boxed(Eterm *ERTS_RESTRICT ptr, Eterm hdr, Eterm **hpp,
+ Eterm *orig)
{
Eterm gval;
Sint nelts;
- Eterm *ptr = *pp;
- Eterm *htop = *hpp;
+ Eterm *ERTS_RESTRICT htop = *hpp;
ASSERT(is_header(hdr));
nelts = header_arity(hdr);
@@ -71,8 +73,7 @@ ERTS_GLB_INLINE void move_boxed(Eterm **pp, Eterm hdr, Eterm **hpp, Eterm *orig)
/* convert sub-binary to heap-binary if applicable */
if (sb->bitsize == 0 && sb->bitoffs == 0 &&
sb->is_writable == 0 && sb->size <= sizeof(Eterm) * 3) {
- erts_sub_binary_to_heap_binary(pp, hpp, orig);
- return;
+ return erts_sub_binary_to_heap_binary(ptr, hpp, orig);
}
}
nelts++;
@@ -90,7 +91,7 @@ ERTS_GLB_INLINE void move_boxed(Eterm **pp, Eterm hdr, Eterm **hpp, Eterm *orig)
while (nelts--) *htop++ = *ptr++;
*hpp = htop;
- *pp = ptr;
+ return ptr;
}
#endif
@@ -153,6 +154,8 @@ typedef struct {
Uint64 garbage_cols;
} ErtsGCInfo;
+#define ERTS_MAX_HEAP_SIZE_MAP_SZ (2*3 + 1 + MAP_HEADER_FLATMAP_SZ)
+
#define ERTS_PROCESS_GC_INFO_MAX_TERMS (11) /* number of elements in process_gc_info*/
#define ERTS_PROCESS_GC_INFO_MAX_SIZE \
(ERTS_PROCESS_GC_INFO_MAX_TERMS * (2/*cons*/ + 3/*2-tuple*/ + BIG_UINT_HEAP_SIZE))
@@ -180,6 +183,8 @@ void erts_free_heap_frags(struct process* p);
Eterm erts_max_heap_size_map(Sint, Uint, Eterm **, Uint *);
int erts_max_heap_size(Eterm, Uint *, Uint *);
void erts_deallocate_young_generation(Process *c_p);
+void erts_copy_one_frag(Eterm** hpp, ErlOffHeap* off_heap,
+ ErlHeapFragment *bp, Eterm *refs, int nrefs);
#if defined(DEBUG) || defined(ERTS_OFFHEAP_DEBUG)
int erts_dbg_within_proc(Eterm *ptr, Process *p, Eterm* real_htop);
#endif
diff --git a/erts/emulator/beam/erl_goodfit_alloc.c b/erts/emulator/beam/erl_goodfit_alloc.c
index 50aa41b4d2..e3ba67f0af 100644
--- a/erts/emulator/beam/erl_goodfit_alloc.c
+++ b/erts/emulator/beam/erl_goodfit_alloc.c
@@ -170,6 +170,7 @@ static void unlink_free_block (Allctr_t *, Block_t *);
static void update_last_aux_mbc (Allctr_t *, Carrier_t *);
static Eterm info_options (Allctr_t *, char *, fmtfn_t *,
void *, Uint **, Uint *);
+static int gfalc_try_set_dyn_param(Allctr_t*, Eterm param, Uint value);
static void init_atoms (void);
#ifdef ERTS_ALLOC_UTIL_HARD_DEBUG
@@ -250,6 +251,8 @@ erts_gfalc_start(GFAllctr_t *gfallctr,
if (!erts_alcu_start(allctr, init))
return NULL;
+ allctr->try_set_dyn_param = gfalc_try_set_dyn_param;
+
if (allctr->min_block_size != MIN_BLK_SZ)
return NULL;
@@ -505,7 +508,7 @@ static struct {
static void ERTS_INLINE atom_init(Eterm *atom, char *name)
{
- *atom = am_atom_put(name, strlen(name));
+ *atom = am_atom_put(name, sys_strlen(name));
}
#define AM_INIT(AM) atom_init(&am.AM, #AM)
@@ -584,6 +587,15 @@ info_options(Allctr_t *allctr,
return res;
}
+static int gfalc_try_set_dyn_param(Allctr_t* allctr, Eterm param, Uint value)
+{
+ if (param == am_sbct) {
+ /* Cannot change 'sbct' without rearranging buckets */
+ return 0;
+ }
+ return erts_alcu_try_set_dyn_param(allctr, param, value);
+}
+
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\
* NOTE: erts_gfalc_test() is only supposed to be used for testing. *
* *
diff --git a/erts/emulator/beam/erl_hl_timer.c b/erts/emulator/beam/erl_hl_timer.c
index bda2c9b94d..6ec6f8065e 100644
--- a/erts/emulator/beam/erl_hl_timer.c
+++ b/erts/emulator/beam/erl_hl_timer.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2015-2017. All Rights Reserved.
+ * Copyright Ericsson AB 2015-2018. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -38,6 +38,7 @@
#define ERTS_WANT_TIMER_WHEEL_API
#include "erl_time.h"
#include "erl_hl_timer.h"
+#include "erl_proc_sig_queue.h"
#ifdef ERTS_MAGIC_REF_BIF_TIMERS
#include "erl_binary.h"
#endif
@@ -2470,28 +2471,21 @@ access_bif_timer(Process *c_p, Eterm tref, int cancel, int async, int info)
req->rrefn[1] = rrefn[1];
req->rrefn[2] = rrefn[2];
- erts_proc_lock(c_p, ERTS_PROC_LOCKS_MSG_RECEIVE);
-
- if (ERTS_PROC_PENDING_EXIT(c_p))
- ERTS_VBUMP_ALL_REDS(c_p);
- else {
- /*
- * Caller needs to wait for a message containing
- * the ref that we just created. No such message
- * can exist in callers message queue at this time.
- * We therefore move the save pointer of the
- * callers message queue to the end of the queue.
- *
- * NOTE: It is of vital importance that the caller
- * immediately do a receive unconditionaly
- * waiting for the message with the reference;
- * otherwise, next receive will *not* work
- * as expected!
- */
- ERTS_MSGQ_MV_INQ2PRIVQ(c_p);
- c_p->msg.save = c_p->msg.last;
- }
- erts_proc_unlock(c_p, ERTS_PROC_LOCKS_MSG_RECEIVE);
+ /*
+ * Caller needs to wait for a message containing
+ * the ref that we just created. No such message
+ * can exist in callers message queue at this time.
+ * We therefore move the save pointer of the
+ * callers message queue to the end of the queue.
+ *
+ * NOTE: It is of vital importance that the caller
+ * immediately do a receive unconditionaly
+ * waiting for the message with the reference;
+ * otherwise, next receive will *not* work
+ * as expected!
+ */
+ ERTS_RECV_MARK_SAVE(c_p);
+ ERTS_RECV_MARK_SET(c_p);
ERTS_BIF_PREP_TRAP1(ret, erts_await_result, c_p, rref);
}
diff --git a/erts/emulator/beam/erl_init.c b/erts/emulator/beam/erl_init.c
index 6cef9bd0e3..57c6c10c7f 100644
--- a/erts/emulator/beam/erl_init.c
+++ b/erts/emulator/beam/erl_init.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1997-2017. All Rights Reserved.
+ * Copyright Ericsson AB 1997-2018. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -37,7 +37,7 @@
#include "erl_mseg.h"
#include "erl_threads.h"
#include "erl_hl_timer.h"
-#include "erl_instrument.h"
+#include "erl_mtrace.h"
#include "erl_printf_term.h"
#include "erl_misc_utils.h"
#include "packet_parser.h"
@@ -50,6 +50,8 @@
#define ERTS_WANT_TIMER_WHEEL_API
#include "erl_time.h"
#include "erl_check_io.h"
+#include "erl_osenv.h"
+#include "erl_proc_sig_queue.h"
#ifdef HIPE
#include "hipe_mode_switch.h" /* for hipe_mode_switch_init() */
@@ -60,7 +62,7 @@
# include <sys/resource.h>
#endif
-#define ERTS_DEFAULT_NO_ASYNC_THREADS 10
+#define ERTS_DEFAULT_NO_ASYNC_THREADS 1
#define ERTS_DEFAULT_SCHED_STACK_SIZE 128
#define ERTS_DEFAULT_DCPU_SCHED_STACK_SIZE 40
@@ -126,6 +128,8 @@ const Eterm etp_hole_marker = 0;
static int modified_sched_thread_suggested_stack_size = 0;
+Eterm erts_init_process_id;
+
/*
* Note about VxWorks: All variables must be initialized by executable code,
* not by an initializer. Otherwise a new instance of the emulator will
@@ -155,9 +159,6 @@ erts_atomic32_t erts_writing_erl_crash_dump;
erts_tsd_key_t erts_is_crash_dumping_key;
int erts_initialized = 0;
-
-int erts_use_sender_punish;
-
/*
* Configurable parameters.
*/
@@ -241,7 +242,7 @@ progname(char *fullname)
{
int i;
- i = strlen(fullname);
+ i = sys_strlen(fullname);
while (i >= 0) {
if ((fullname[i] != '/') && (fullname[i] != '\\'))
i--;
@@ -307,8 +308,9 @@ erl_init(int ncpu,
int node_tab_delete_delay,
ErtsDbSpinCount db_spin_count)
{
+ erts_monitor_link_init();
+ erts_proc_sig_queue_init();
erts_bif_unique_init();
- erts_init_monitors();
erts_init_time(time_correction, time_warp_mode);
erts_init_sys_common_misc();
erts_init_process(ncpu, proc_tab_sz, legacy_proc_tab);
@@ -416,7 +418,7 @@ erl_first_process_otp(char* modname, void* code, unsigned size, int argc, char**
}
static Eterm
-erl_system_process_otp(Eterm parent_pid, char* modname, int off_heap_msgq)
+erl_system_process_otp(Eterm parent_pid, char* modname, int off_heap_msgq, int prio)
{
Eterm start_mod;
Process* parent;
@@ -431,9 +433,16 @@ erl_system_process_otp(Eterm parent_pid, char* modname, int off_heap_msgq)
parent = erts_pid2proc(NULL, 0, parent_pid, ERTS_PROC_LOCK_MAIN);
- so.flags = erts_default_spo_flags|SPO_SYSTEM_PROC;
+ so.flags = erts_default_spo_flags|SPO_SYSTEM_PROC|SPO_USE_ARGS;
if (off_heap_msgq)
so.flags |= SPO_OFF_HEAP_MSGQ;
+ so.min_heap_size = H_MIN_SIZE;
+ so.min_vheap_size = BIN_VH_MIN_SIZE;
+ so.max_heap_size = H_MAX_SIZE;
+ so.max_heap_flags = H_MAX_FLAGS;
+ so.priority = prio;
+ so.max_gen_gcs = (Uint16) erts_atomic32_read_nob(&erts_max_gen_gcs);
+ so.scheduler = 0;
res = erl_create_process(parent, start_mod, am_start, NIL, &so);
erts_proc_unlock(parent, ERTS_PROC_LOCK_MAIN);
return res;
@@ -596,6 +605,10 @@ void erts_usage(void)
erts_fprintf(stderr, "-stbt type u|ns|ts|ps|s|nnts|nnps|tnnps|db\n");
erts_fprintf(stderr, "-sbwt val set scheduler busy wait threshold, valid values are:\n");
erts_fprintf(stderr, " none|very_short|short|medium|long|very_long.\n");
+ erts_fprintf(stderr, "-sbwtdcpu val set dirty CPU scheduler busy wait threshold, valid values are:\n");
+ erts_fprintf(stderr, " none|very_short|short|medium|long|very_long.\n");
+ erts_fprintf(stderr, "-sbwtdio val set dirty IO scheduler busy wait threshold, valid values are:\n");
+ erts_fprintf(stderr, " none|very_short|short|medium|long|very_long.\n");
erts_fprintf(stderr, "-scl bool enable/disable compaction of scheduler load,\n");
erts_fprintf(stderr, " see the erl(1) documentation for more info.\n");
erts_fprintf(stderr, "-sct cput set cpu topology,\n");
@@ -614,6 +627,10 @@ void erts_usage(void)
erts_fprintf(stderr, " very_lazy|lazy|medium|eager|very_eager.\n");
erts_fprintf(stderr, "-swt val set scheduler wakeup threshold, valid values are:\n");
erts_fprintf(stderr, " very_low|low|medium|high|very_high.\n");
+ erts_fprintf(stderr, "-swtdcpu val set dirty CPU scheduler wakeup threshold, valid values are:\n");
+ erts_fprintf(stderr, " very_low|low|medium|high|very_high.\n");
+ erts_fprintf(stderr, "-swtdio val set dirty IO scheduler wakeup threshold, valid values are:\n");
+ erts_fprintf(stderr, " very_low|low|medium|high|very_high.\n");
erts_fprintf(stderr, "-sss size suggested stack size in kilo words for scheduler threads,\n");
erts_fprintf(stderr, " valid range is [%d-%d] (default %d)\n",
ERTS_SCHED_THREAD_MIN_STACK_SIZE,
@@ -758,8 +775,6 @@ early_init(int *argc, char **argv) /*
erts_initialized = 0;
- erts_use_sender_punish = 1;
-
erts_pre_early_init_cpu_topology(&max_reader_groups,
&ncpu,
&ncpuonln,
@@ -803,8 +818,9 @@ early_init(int *argc, char **argv) /*
envbufsz = sizeof(envbuf);
- /* erts_sys_getenv(_raw)() not initialized yet; need erts_sys_getenv__() */
- if (erts_sys_getenv__("ERL_THREAD_POOL_SIZE", envbuf, &envbufsz) == 0)
+ /* erts_osenv hasn't been initialized yet, so we need to fall back to
+ * erts_sys_explicit_host_getenv() */
+ if (erts_sys_explicit_host_getenv("ERL_THREAD_POOL_SIZE", envbuf, &envbufsz) == 1)
erts_async_max_threads = atoi(envbuf);
else
erts_async_max_threads = ERTS_DEFAULT_NO_ASYNC_THREADS;
@@ -814,7 +830,7 @@ early_init(int *argc, char **argv) /*
if (argc && argv) {
int i = 1;
while (i < *argc) {
- if (strcmp(argv[i], "--") == 0) { /* end of emulator options */
+ if (sys_strcmp(argv[i], "--") == 0) { /* end of emulator options */
i++;
break;
}
@@ -896,7 +912,7 @@ early_init(int *argc, char **argv) /*
else if (argv[i][2] == 'D') {
char *arg;
char *type = argv[i]+3;
- if (strncmp(type, "Pcpu", 4) == 0) {
+ if (sys_strncmp(type, "Pcpu", 4) == 0) {
int ptot, ponln;
arg = get_arg(argv[i]+7, argv[i+1], &i);
switch (sscanf(arg, "%d:%d", &ptot, &ponln)) {
@@ -933,7 +949,7 @@ early_init(int *argc, char **argv) /*
VERBOSE(DEBUG_SYSTEM,
("using %d:%d dirty CPU scheduler percentages\n",
dirty_cpu_scheds_pctg, dirty_cpu_scheds_onln_pctg));
- } else if (strncmp(type, "cpu", 3) == 0) {
+ } else if (sys_strncmp(type, "cpu", 3) == 0) {
int tot, onln;
arg = get_arg(argv[i]+6, argv[i+1], &i);
switch (sscanf(arg, "%d:%d", &tot, &onln)) {
@@ -984,7 +1000,7 @@ early_init(int *argc, char **argv) /*
}
VERBOSE(DEBUG_SYSTEM,
("using %d:%d dirty CPU scheduler(s)\n", tot, onln));
- } else if (strncmp(type, "io", 2) == 0) {
+ } else if (sys_strncmp(type, "io", 2) == 0) {
arg = get_arg(argv[i]+5, argv[i+1], &i);
dirty_io_scheds = atoi(arg);
if (dirty_io_scheds < 0 ||
@@ -1204,26 +1220,25 @@ erl_start(int argc, char **argv)
ErtsTimeWarpMode time_warp_mode;
int node_tab_delete_delay = ERTS_NODE_TAB_DELAY_GC_DEFAULT;
ErtsDbSpinCount db_spin_count = ERTS_DB_SPNCNT_NORMAL;
- Eterm otp_ring0_pid;
set_default_time_adj(&time_correction,
&time_warp_mode);
envbufsz = sizeof(envbuf);
- if (erts_sys_getenv_raw(ERL_MAX_ETS_TABLES_ENV, envbuf, &envbufsz) == 0)
+ if (erts_sys_explicit_8bit_getenv(ERL_MAX_ETS_TABLES_ENV, envbuf, &envbufsz) == 1)
user_requested_db_max_tabs = atoi(envbuf);
else
user_requested_db_max_tabs = 0;
envbufsz = sizeof(envbuf);
- if (erts_sys_getenv_raw("ERL_FULLSWEEP_AFTER", envbuf, &envbufsz) == 0) {
+ if (erts_sys_explicit_8bit_getenv("ERL_FULLSWEEP_AFTER", envbuf, &envbufsz) == 1) {
Uint16 max_gen_gcs = atoi(envbuf);
erts_atomic32_set_nob(&erts_max_gen_gcs,
(erts_aint32_t) max_gen_gcs);
}
envbufsz = sizeof(envbuf);
- if (erts_sys_getenv_raw("ERL_MAX_PORTS", envbuf, &envbufsz) == 0) {
+ if (erts_sys_explicit_8bit_getenv("ERL_MAX_PORTS", envbuf, &envbufsz) == 1) {
port_tab_sz = atoi(envbuf);
port_tab_sz_ignore_files = 1;
}
@@ -1246,7 +1261,7 @@ erl_start(int argc, char **argv)
if (argv[i][0] != '-') {
erts_usage();
}
- if (strcmp(argv[i], "--") == 0) { /* end of emulator options */
+ if (sys_strcmp(argv[i], "--") == 0) { /* end of emulator options */
i++;
break;
}
@@ -1274,12 +1289,12 @@ erl_start(int argc, char **argv)
("using display items %d\n",display_items));
break;
case 'p':
- if (!strncmp(argv[i],"-pc",3)) {
+ if (!sys_strncmp(argv[i],"-pc",3)) {
int printable_chars = ERL_PRINTABLE_CHARACTERS_LATIN1;
arg = get_arg(argv[i]+3, argv[i+1], &i);
- if (!strcmp(arg,"unicode")) {
+ if (!sys_strcmp(arg,"unicode")) {
printable_chars = ERL_PRINTABLE_CHARACTERS_UNICODE;
- } else if (strcmp(arg,"latin1")) {
+ } else if (sys_strcmp(arg,"latin1")) {
erts_fprintf(stderr, "bad range of printable "
"characters: %s\n", arg);
erts_usage();
@@ -1291,7 +1306,7 @@ erl_start(int argc, char **argv)
erts_usage();
}
case 'f':
- if (!strncmp(argv[i],"-fn",3)) {
+ if (!sys_strncmp(argv[i],"-fn",3)) {
int warning_type = ERL_FILENAME_WARNING_WARNING;
arg = get_arg(argv[i]+3, argv[i+1], &i);
switch (*arg) {
@@ -1474,9 +1489,9 @@ erl_start(int argc, char **argv)
}
} else if (has_prefix("maxk", sub_param)) {
arg = get_arg(sub_param+4, argv[i+1], &i);
- if (strcmp(arg,"true") == 0) {
+ if (sys_strcmp(arg,"true") == 0) {
H_MAX_FLAGS |= MAX_HEAP_SIZE_KILL;
- } else if (strcmp(arg,"false") == 0) {
+ } else if (sys_strcmp(arg,"false") == 0) {
H_MAX_FLAGS &= ~MAX_HEAP_SIZE_KILL;
} else {
erts_fprintf(stderr, "bad max heap kill %s\n", arg);
@@ -1485,9 +1500,9 @@ erl_start(int argc, char **argv)
VERBOSE(DEBUG_SYSTEM, ("using max heap kill %d\n", H_MAX_FLAGS));
} else if (has_prefix("maxel", sub_param)) {
arg = get_arg(sub_param+5, argv[i+1], &i);
- if (strcmp(arg,"true") == 0) {
+ if (sys_strcmp(arg,"true") == 0) {
H_MAX_FLAGS |= MAX_HEAP_SIZE_LOG;
- } else if (strcmp(arg,"false") == 0) {
+ } else if (sys_strcmp(arg,"false") == 0) {
H_MAX_FLAGS &= ~MAX_HEAP_SIZE_LOG;
} else {
erts_fprintf(stderr, "bad max heap error logger %s\n", arg);
@@ -1605,7 +1620,7 @@ erl_start(int argc, char **argv)
case 'P': /* set maximum number of processes */
arg = get_arg(argv[i]+2, argv[i+1], &i);
- if (strcmp(arg, "legacy") == 0)
+ if (sys_strcmp(arg, "legacy") == 0)
legacy_proc_tab = 1;
else {
errno = 0;
@@ -1621,7 +1636,7 @@ erl_start(int argc, char **argv)
case 'Q': /* set maximum number of ports */
arg = get_arg(argv[i]+2, argv[i+1], &i);
- if (strcmp(arg, "legacy") == 0)
+ if (sys_strcmp(arg, "legacy") == 0)
legacy_port_tab = 1;
else {
errno = 0;
@@ -1639,11 +1654,11 @@ erl_start(int argc, char **argv)
case 'S' : /* Was handled in early_init() just read past it */
if (argv[i][2] == 'D') {
char* type = argv[i]+3;
- if (strcmp(type, "Pcpu") == 0)
+ if (sys_strcmp(type, "Pcpu") == 0)
(void) get_arg(argv[i]+7, argv[i+1], &i);
- if (strcmp(type, "cpu") == 0)
+ if (sys_strcmp(type, "cpu") == 0)
(void) get_arg(argv[i]+6, argv[i+1], &i);
- else if (strcmp(type, "io") == 0)
+ else if (sys_strcmp(type, "io") == 0)
(void) get_arg(argv[i]+5, argv[i+1], &i);
} else if (argv[i][2] == 'P')
(void) get_arg(argv[i]+3, argv[i+1], &i);
@@ -1680,15 +1695,41 @@ erl_start(int argc, char **argv)
erts_usage();
}
}
+ else if (has_prefix("bwtdcpu", sub_param)) {
+ arg = get_arg(sub_param + 7, argv[i+1], &i);
+
+ if (erts_sched_set_busy_wait_threshold(ERTS_SCHED_DIRTY_CPU, arg) != 0) {
+ erts_fprintf(stderr, "bad dirty CPU scheduler busy wait threshold: %s\n",
+ arg);
+ erts_usage();
+ }
+
+ VERBOSE(DEBUG_SYSTEM,
+ ("dirty CPU scheduler wakeup threshold: %s\n", arg));
+ }
+ else if (has_prefix("bwtdio", sub_param)) {
+ arg = get_arg(sub_param + 6, argv[i+1], &i);
+
+ if (erts_sched_set_busy_wait_threshold(ERTS_SCHED_DIRTY_IO, arg) != 0) {
+ erts_fprintf(stderr, "bad dirty IO scheduler busy wait threshold: %s\n",
+ arg);
+ erts_usage();
+ }
+
+ VERBOSE(DEBUG_SYSTEM,
+ ("dirty IO scheduler wakeup threshold: %s\n", arg));
+ }
else if (has_prefix("bwt", sub_param)) {
- arg = get_arg(sub_param+3, argv[i+1], &i);
- if (erts_sched_set_busy_wait_threshold(arg) != 0) {
+ arg = get_arg(sub_param + 3, argv[i+1], &i);
+
+ if (erts_sched_set_busy_wait_threshold(ERTS_SCHED_NORMAL, arg) != 0) {
erts_fprintf(stderr, "bad scheduler busy wait threshold: %s\n",
arg);
erts_usage();
}
+
VERBOSE(DEBUG_SYSTEM,
- ("scheduler wakup threshold: %s\n", arg));
+ ("scheduler wakeup threshold: %s\n", arg));
}
else if (has_prefix("cl", sub_param)) {
arg = get_arg(sub_param+2, argv[i+1], &i);
@@ -1750,6 +1791,7 @@ erl_start(int argc, char **argv)
}
else if (has_prefix("ecio", sub_param)) {
/* ignore argument, eager check io no longer used */
+ arg = get_arg(sub_param+4, argv[i+1], &i);
}
else if (has_prefix("pp", sub_param)) {
arg = get_arg(sub_param+2, argv[i+1], &i);
@@ -1764,8 +1806,6 @@ erl_start(int argc, char **argv)
erts_usage();
}
}
- else if (sys_strcmp("nsp", sub_param) == 0)
- erts_use_sender_punish = 0;
else if (has_prefix("tbt", sub_param)) {
arg = get_arg(sub_param+3, argv[i+1], &i);
res = erts_init_scheduler_bind_type_string(arg);
@@ -1806,9 +1846,29 @@ erl_start(int argc, char **argv)
VERBOSE(DEBUG_SYSTEM,
("scheduler wake cleanup threshold: %s\n", arg));
}
+ else if (has_prefix("wtdcpu", sub_param)) {
+ arg = get_arg(sub_param+6, argv[i+1], &i);
+ if (erts_sched_set_wakeup_other_threshold(ERTS_SCHED_DIRTY_CPU, arg) != 0) {
+ erts_fprintf(stderr, "dirty CPU scheduler wakeup threshold: %s\n",
+ arg);
+ erts_usage();
+ }
+ VERBOSE(DEBUG_SYSTEM,
+ ("dirty CPU scheduler wakeup threshold: %s\n", arg));
+ }
+ else if (has_prefix("wtdio", sub_param)) {
+ arg = get_arg(sub_param+5, argv[i+1], &i);
+ if (erts_sched_set_wakeup_other_threshold(ERTS_SCHED_DIRTY_IO, arg) != 0) {
+ erts_fprintf(stderr, "dirty IO scheduler wakeup threshold: %s\n",
+ arg);
+ erts_usage();
+ }
+ VERBOSE(DEBUG_SYSTEM,
+ ("dirty IO scheduler wakeup threshold: %s\n", arg));
+ }
else if (has_prefix("wt", sub_param)) {
arg = get_arg(sub_param+2, argv[i+1], &i);
- if (erts_sched_set_wakeup_other_thresold(arg) != 0) {
+ if (erts_sched_set_wakeup_other_threshold(ERTS_SCHED_NORMAL, arg) != 0) {
erts_fprintf(stderr, "scheduler wakeup threshold: %s\n",
arg);
erts_usage();
@@ -1818,7 +1878,7 @@ erl_start(int argc, char **argv)
}
else if (has_prefix("ws", sub_param)) {
arg = get_arg(sub_param+2, argv[i+1], &i);
- if (erts_sched_set_wakeup_other_type(arg) != 0) {
+ if (erts_sched_set_wakeup_other_type(ERTS_SCHED_NORMAL, arg) != 0) {
erts_fprintf(stderr, "scheduler wakeup strategy: %s\n",
arg);
erts_usage();
@@ -2196,8 +2256,8 @@ erl_start(int argc, char **argv)
erts_initialized = 1;
- otp_ring0_pid = erl_first_process_otp("otp_ring0", NULL, 0,
- boot_argc, boot_argv);
+ erts_init_process_id = erl_first_process_otp("otp_ring0", NULL, 0,
+ boot_argc, boot_argv);
{
/*
@@ -2207,14 +2267,18 @@ erl_start(int argc, char **argv)
*/
Eterm pid;
- pid = erl_system_process_otp(otp_ring0_pid, "erts_code_purger", !0);
+ pid = erl_system_process_otp(erts_init_process_id,
+ "erts_code_purger", !0,
+ PRIORITY_NORMAL);
erts_code_purger
= (Process *) erts_ptab_pix2intptr_ddrb(&erts_proc,
internal_pid_index(pid));
ASSERT(erts_code_purger && erts_code_purger->common.id == pid);
erts_proc_inc_refc(erts_code_purger);
- pid = erl_system_process_otp(otp_ring0_pid, "erts_literal_area_collector", !0);
+ pid = erl_system_process_otp(erts_init_process_id,
+ "erts_literal_area_collector",
+ !0, PRIORITY_NORMAL);
erts_literal_area_collector
= (Process *) erts_ptab_pix2intptr_ddrb(&erts_proc,
internal_pid_index(pid));
@@ -2222,14 +2286,35 @@ erl_start(int argc, char **argv)
&& erts_literal_area_collector->common.id == pid);
erts_proc_inc_refc(erts_literal_area_collector);
- pid = erl_system_process_otp(otp_ring0_pid, "erts_dirty_process_code_checker", !0);
- erts_dirty_process_code_checker
+ pid = erl_system_process_otp(erts_init_process_id,
+ "erts_dirty_process_signal_handler",
+ !0, PRIORITY_NORMAL);
+ erts_dirty_process_signal_handler
= (Process *) erts_ptab_pix2intptr_ddrb(&erts_proc,
internal_pid_index(pid));
- ASSERT(erts_dirty_process_code_checker
- && erts_dirty_process_code_checker->common.id == pid);
- erts_proc_inc_refc(erts_dirty_process_code_checker);
-
+ ASSERT(erts_dirty_process_signal_handler
+ && erts_dirty_process_signal_handler->common.id == pid);
+ erts_proc_inc_refc(erts_dirty_process_signal_handler);
+
+ pid = erl_system_process_otp(erts_init_process_id,
+ "erts_dirty_process_signal_handler",
+ !0, PRIORITY_HIGH);
+ erts_dirty_process_signal_handler_high
+ = (Process *) erts_ptab_pix2intptr_ddrb(&erts_proc,
+ internal_pid_index(pid));
+ ASSERT(erts_dirty_process_signal_handler_high
+ && erts_dirty_process_signal_handler_high->common.id == pid);
+ erts_proc_inc_refc(erts_dirty_process_signal_handler_high);
+
+ pid = erl_system_process_otp(erts_init_process_id,
+ "erts_dirty_process_signal_handler",
+ !0, PRIORITY_MAX);
+ erts_dirty_process_signal_handler_max
+ = (Process *) erts_ptab_pix2intptr_ddrb(&erts_proc,
+ internal_pid_index(pid));
+ ASSERT(erts_dirty_process_signal_handler_max
+ && erts_dirty_process_signal_handler_max->common.id == pid);
+ erts_proc_inc_refc(erts_dirty_process_signal_handler_max);
}
erts_start_schedulers();
diff --git a/erts/emulator/beam/erl_instrument.c b/erts/emulator/beam/erl_instrument.c
deleted file mode 100644
index 634509f880..0000000000
--- a/erts/emulator/beam/erl_instrument.c
+++ /dev/null
@@ -1,1257 +0,0 @@
-/*
- * %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%
- */
-
-#ifdef HAVE_CONFIG_H
-# include "config.h"
-#endif
-
-#include "global.h"
-#include "big.h"
-#include "erl_instrument.h"
-#include "erl_threads.h"
-
-typedef union { long l; double d; } Align_t;
-
-typedef struct {
- Uint size;
-#ifdef VALGRIND
- void* valgrind_leak_suppressor;
-#endif
- Align_t mem[1];
-} StatBlock_t;
-
-#define STAT_BLOCK_HEADER_SIZE (sizeof(StatBlock_t) - sizeof(Align_t))
-
-typedef struct MapStatBlock_t_ MapStatBlock_t;
-struct MapStatBlock_t_ {
- Uint size;
- ErtsAlcType_t type_no;
- Eterm pid;
- MapStatBlock_t *prev;
- MapStatBlock_t *next;
- Align_t mem[1];
-};
-
-#define MAP_STAT_BLOCK_HEADER_SIZE (sizeof(MapStatBlock_t) - sizeof(Align_t))
-
-typedef struct {
- Uint size;
- Uint max_size;
- Uint max_size_ever;
-
- Uint blocks;
- Uint max_blocks;
- Uint max_blocks_ever;
-} Stat_t;
-
-static erts_mtx_t instr_mutex;
-static erts_mtx_t instr_x_mutex;
-
-int erts_instr_memory_map;
-int erts_instr_stat;
-
-static ErtsAllocatorFunctions_t real_allctrs[ERTS_ALC_A_MAX+1];
-
-struct stats_ {
- Stat_t tot;
- Stat_t a[ERTS_ALC_A_MAX+1];
- Stat_t *ap[ERTS_ALC_A_MAX+1];
- Stat_t c[ERTS_ALC_C_MAX+1];
- Stat_t n[ERTS_ALC_N_MAX+1];
-};
-
-static struct stats_ *stats;
-
-static MapStatBlock_t *mem_anchor;
-
-static Eterm *am_tot;
-static Eterm *am_n;
-static Eterm *am_a;
-static Eterm *am_c;
-
-static int atoms_initialized;
-
-static struct {
- Eterm total;
- Eterm allocators;
- Eterm classes;
- Eterm types;
- Eterm sizes;
- Eterm blocks;
- Eterm instr_hdr;
-#ifdef DEBUG
- Eterm end_of_atoms;
-#endif
-} am;
-
-static void ERTS_INLINE atom_init(Eterm *atom, const char *name)
-{
- *atom = am_atom_put((char *) name, strlen(name));
-}
-#define AM_INIT(AM) atom_init(&am.AM, #AM)
-
-static void
-init_atoms(void)
-{
-#ifdef DEBUG
- Eterm *atom;
- for (atom = (Eterm *) &am; atom <= &am.end_of_atoms; atom++) {
- *atom = THE_NON_VALUE;
- }
-#endif
-
- AM_INIT(total);
- AM_INIT(allocators);
- AM_INIT(classes);
- AM_INIT(types);
- AM_INIT(sizes);
- AM_INIT(blocks);
- AM_INIT(instr_hdr);
-
-#ifdef DEBUG
- for (atom = (Eterm *) &am; atom < &am.end_of_atoms; atom++) {
- ASSERT(*atom != THE_NON_VALUE);
- }
-#endif
-
- atoms_initialized = 1;
-}
-
-#undef AM_INIT
-
-static void
-init_am_tot(void)
-{
- am_tot = (Eterm *) erts_alloc(ERTS_ALC_T_INSTR_INFO,
- sizeof(Eterm));
- atom_init(am_tot, "total");
-}
-
-
-static void
-init_am_n(void)
-{
- int i;
- am_n = (Eterm *) erts_alloc(ERTS_ALC_T_INSTR_INFO,
- (ERTS_ALC_N_MAX+1)*sizeof(Eterm));
-
- for (i = ERTS_ALC_N_MIN; i <= ERTS_ALC_N_MAX; i++) {
- atom_init(&am_n[i], ERTS_ALC_N2TD(i));
- }
-
-}
-
-static void
-init_am_c(void)
-{
- int i;
- am_c = (Eterm *) erts_alloc(ERTS_ALC_T_INSTR_INFO,
- (ERTS_ALC_C_MAX+1)*sizeof(Eterm));
-
- for (i = ERTS_ALC_C_MIN; i <= ERTS_ALC_C_MAX; i++) {
- atom_init(&am_c[i], ERTS_ALC_C2CD(i));
- }
-
-}
-
-static void
-init_am_a(void)
-{
- int i;
- am_a = (Eterm *) erts_alloc(ERTS_ALC_T_INSTR_INFO,
- (ERTS_ALC_A_MAX+1)*sizeof(Eterm));
-
- for (i = ERTS_ALC_A_MIN; i <= ERTS_ALC_A_MAX; i++) {
- atom_init(&am_a[i], ERTS_ALC_A2AD(i));
- }
-
-}
-
-static ERTS_INLINE void
-stat_upd_alloc(ErtsAlcType_t n, Uint size)
-{
- ErtsAlcType_t t = ERTS_ALC_N2T(n);
- ErtsAlcType_t a = ERTS_ALC_T2A(t);
- ErtsAlcType_t c = ERTS_ALC_T2C(t);
-
- stats->ap[a]->size += size;
- if (stats->ap[a]->max_size < stats->ap[a]->size)
- stats->ap[a]->max_size = stats->ap[a]->size;
-
- stats->c[c].size += size;
- if (stats->c[c].max_size < stats->c[c].size)
- stats->c[c].max_size = stats->c[c].size;
-
- stats->n[n].size += size;
- if (stats->n[n].max_size < stats->n[n].size)
- stats->n[n].max_size = stats->n[n].size;
-
- stats->tot.size += size;
- if (stats->tot.max_size < stats->tot.size)
- stats->tot.max_size = stats->tot.size;
-
- stats->ap[a]->blocks++;
- if (stats->ap[a]->max_blocks < stats->ap[a]->blocks)
- stats->ap[a]->max_blocks = stats->ap[a]->blocks;
-
- stats->c[c].blocks++;
- if (stats->c[c].max_blocks < stats->c[c].blocks)
- stats->c[c].max_blocks = stats->c[c].blocks;
-
- stats->n[n].blocks++;
- if (stats->n[n].max_blocks < stats->n[n].blocks)
- stats->n[n].max_blocks = stats->n[n].blocks;
-
- stats->tot.blocks++;
- if (stats->tot.max_blocks < stats->tot.blocks)
- stats->tot.max_blocks = stats->tot.blocks;
-
-}
-
-
-static ERTS_INLINE void
-stat_upd_free(ErtsAlcType_t n, Uint size)
-{
- ErtsAlcType_t t = ERTS_ALC_N2T(n);
- ErtsAlcType_t a = ERTS_ALC_T2A(t);
- ErtsAlcType_t c = ERTS_ALC_T2C(t);
-
- ASSERT(stats->ap[a]->size >= size);
- stats->ap[a]->size -= size;
-
- ASSERT(stats->c[c].size >= size);
- stats->c[c].size -= size;
-
- ASSERT(stats->n[n].size >= size);
- stats->n[n].size -= size;
-
- ASSERT(stats->tot.size >= size);
- stats->tot.size -= size;
-
- ASSERT(stats->ap[a]->blocks > 0);
- stats->ap[a]->blocks--;
-
- ASSERT(stats->c[c].blocks > 0);
- stats->c[c].blocks--;
-
- ASSERT(stats->n[n].blocks > 0);
- stats->n[n].blocks--;
-
- ASSERT(stats->tot.blocks > 0);
- stats->tot.blocks--;
-
-}
-
-
-static ERTS_INLINE void
-stat_upd_realloc(ErtsAlcType_t n, Uint size, Uint old_size)
-{
- if (old_size)
- stat_upd_free(n, old_size);
- stat_upd_alloc(n, size);
-}
-
-/*
- * stat instrumentation callback functions
- */
-
-static void stat_pre_lock(void)
-{
- erts_mtx_lock(&instr_mutex);
-}
-
-static void stat_pre_unlock(void)
-{
- erts_mtx_unlock(&instr_mutex);
-}
-
-static ErtsAllocatorWrapper_t instr_wrapper;
-
-static void *
-stat_alloc(ErtsAlcType_t n, void *extra, Uint size)
-{
- ErtsAllocatorFunctions_t *real_af = (ErtsAllocatorFunctions_t *) extra;
- Uint ssize;
- void *res;
-
- if (!erts_is_allctr_wrapper_prelocked()) {
- erts_mtx_lock(&instr_mutex);
- }
-
- ssize = size + STAT_BLOCK_HEADER_SIZE;
- res = (*real_af->alloc)(n, real_af->extra, ssize);
- if (res) {
- stat_upd_alloc(n, size);
- ((StatBlock_t *) res)->size = size;
-#ifdef VALGRIND
- /* Suppress "possibly leaks" by storing an actual dummy pointer
- to the _start_ of the allocated block.*/
- ((StatBlock_t *) res)->valgrind_leak_suppressor = res;
-#endif
- res = (void *) ((StatBlock_t *) res)->mem;
- }
-
- if (!erts_is_allctr_wrapper_prelocked()) {
- erts_mtx_unlock(&instr_mutex);
- }
-
- return res;
-}
-
-static void *
-stat_realloc(ErtsAlcType_t n, void *extra, void *ptr, Uint size)
-{
- ErtsAllocatorFunctions_t *real_af = (ErtsAllocatorFunctions_t *) extra;
- Uint old_size;
- Uint ssize;
- void *sptr;
- void *res;
-
- if (!erts_is_allctr_wrapper_prelocked()) {
- erts_mtx_lock(&instr_mutex);
- }
-
- if (ptr) {
- sptr = (void *) (((char *) ptr) - STAT_BLOCK_HEADER_SIZE);
- old_size = ((StatBlock_t *) sptr)->size;
- }
- else {
- sptr = NULL;
- old_size = 0;
- }
-
- ssize = size + STAT_BLOCK_HEADER_SIZE;
- res = (*real_af->realloc)(n, real_af->extra, sptr, ssize);
- if (res) {
- stat_upd_realloc(n, size, old_size);
- ((StatBlock_t *) res)->size = size;
-#ifdef VALGRIND
- ((StatBlock_t *) res)->valgrind_leak_suppressor = res;
-#endif
- res = (void *) ((StatBlock_t *) res)->mem;
- }
-
- if (!erts_is_allctr_wrapper_prelocked()) {
- erts_mtx_unlock(&instr_mutex);
- }
-
- return res;
-}
-
-static void
-stat_free(ErtsAlcType_t n, void *extra, void *ptr)
-{
- ErtsAllocatorFunctions_t *real_af = (ErtsAllocatorFunctions_t *) extra;
- void *sptr;
-
- if (!erts_is_allctr_wrapper_prelocked()) {
- erts_mtx_lock(&instr_mutex);
- }
-
- if (ptr) {
- sptr = (void *) (((char *) ptr) - STAT_BLOCK_HEADER_SIZE);
- stat_upd_free(n, ((StatBlock_t *) sptr)->size);
- }
- else {
- sptr = NULL;
- }
-
- (*real_af->free)(n, real_af->extra, sptr);
-
- if (!erts_is_allctr_wrapper_prelocked()) {
- erts_mtx_unlock(&instr_mutex);
- }
-
-}
-
-/*
- * map stat instrumentation callback functions
- */
-
-static void map_stat_pre_lock(void)
-{
- erts_mtx_lock(&instr_x_mutex);
- erts_mtx_lock(&instr_mutex);
-}
-
-static void map_stat_pre_unlock(void)
-{
- erts_mtx_unlock(&instr_mutex);
- erts_mtx_unlock(&instr_x_mutex);
-}
-
-static void *
-map_stat_alloc(ErtsAlcType_t n, void *extra, Uint size)
-{
- ErtsAllocatorFunctions_t *real_af = (ErtsAllocatorFunctions_t *) extra;
- Uint msize;
- void *res;
-
- if (!erts_is_allctr_wrapper_prelocked()) {
- erts_mtx_lock(&instr_mutex);
- }
-
- msize = size + MAP_STAT_BLOCK_HEADER_SIZE;
- res = (*real_af->alloc)(n, real_af->extra, msize);
- if (res) {
- MapStatBlock_t *mb = (MapStatBlock_t *) res;
- stat_upd_alloc(n, size);
-
- mb->size = size;
- mb->type_no = n;
- mb->pid = erts_get_current_pid();
-
- mb->prev = NULL;
- mb->next = mem_anchor;
- if (mem_anchor)
- mem_anchor->prev = mb;
- mem_anchor = mb;
-
- res = (void *) mb->mem;
- }
-
- if (!erts_is_allctr_wrapper_prelocked()) {
- erts_mtx_unlock(&instr_mutex);
- }
-
- return res;
-}
-
-static void *
-map_stat_realloc(ErtsAlcType_t n, void *extra, void *ptr, Uint size)
-{
- ErtsAllocatorFunctions_t *real_af = (ErtsAllocatorFunctions_t *) extra;
- Uint old_size;
- Uint msize;
- void *mptr;
- void *res;
-
- if (!erts_is_allctr_wrapper_prelocked()) {
- erts_mtx_lock(&instr_x_mutex);
- erts_mtx_lock(&instr_mutex);
- }
-
- if (ptr) {
- mptr = (void *) (((char *) ptr) - MAP_STAT_BLOCK_HEADER_SIZE);
- old_size = ((MapStatBlock_t *) mptr)->size;
- }
- else {
- mptr = NULL;
- old_size = 0;
- }
-
- msize = size + MAP_STAT_BLOCK_HEADER_SIZE;
- res = (*real_af->realloc)(n, real_af->extra, mptr, msize);
- if (res) {
- MapStatBlock_t *mb = (MapStatBlock_t *) res;
-
- mb->size = size;
- mb->type_no = n;
- mb->pid = erts_get_current_pid();
-
- stat_upd_realloc(n, size, old_size);
-
- if (mptr != res) {
-
- if (mptr) {
- if (mb->prev)
- mb->prev->next = mb;
- else {
- ASSERT(mem_anchor == (MapStatBlock_t *) mptr);
- mem_anchor = mb;
- }
- if (mb->next)
- mb->next->prev = mb;
- }
- else {
- mb->prev = NULL;
- mb->next = mem_anchor;
- if (mem_anchor)
- mem_anchor->prev = mb;
- mem_anchor = mb;
- }
-
- }
-
- res = (void *) mb->mem;
- }
- if (!erts_is_allctr_wrapper_prelocked()) {
- erts_mtx_unlock(&instr_mutex);
- erts_mtx_unlock(&instr_x_mutex);
- }
-
- return res;
-}
-
-static void
-map_stat_free(ErtsAlcType_t n, void *extra, void *ptr)
-{
- ErtsAllocatorFunctions_t *real_af = (ErtsAllocatorFunctions_t *) extra;
- void *mptr;
-
- if (!erts_is_allctr_wrapper_prelocked()) {
- erts_mtx_lock(&instr_x_mutex);
- erts_mtx_lock(&instr_mutex);
- }
-
- if (ptr) {
- MapStatBlock_t *mb;
-
- mptr = (void *) (((char *) ptr) - MAP_STAT_BLOCK_HEADER_SIZE);
- mb = (MapStatBlock_t *) mptr;
-
- stat_upd_free(n, mb->size);
-
- if (mb->prev)
- mb->prev->next = mb->next;
- else
- mem_anchor = mb->next;
- if (mb->next)
- mb->next->prev = mb->prev;
- }
- else {
- mptr = NULL;
- }
-
- (*real_af->free)(n, real_af->extra, mptr);
-
- if (!erts_is_allctr_wrapper_prelocked()) {
- erts_mtx_unlock(&instr_mutex);
- erts_mtx_unlock(&instr_x_mutex);
- }
-
-}
-
-static void dump_memory_map_to_stream(fmtfn_t to, void* to_arg)
-{
- ErtsAlcType_t n;
- MapStatBlock_t *bp;
- int lock = !ERTS_IS_CRASH_DUMPING;
- if (lock) {
- ASSERT(!erts_is_allctr_wrapper_prelocked());
- erts_mtx_lock(&instr_mutex);
- }
-
- /* Write header */
-
- erts_cbprintf(to, to_arg,
- "{instr_hdr,\n"
- " %lu,\n"
- " %lu,\n"
- " {",
- (unsigned long) ERTS_INSTR_VSN,
- (unsigned long) MAP_STAT_BLOCK_HEADER_SIZE);
-
-#if ERTS_ALC_N_MIN != 1
-#error ERTS_ALC_N_MIN is not 1
-#endif
-
- for (n = ERTS_ALC_N_MIN; n <= ERTS_ALC_N_MAX; n++) {
- ErtsAlcType_t t = ERTS_ALC_N2T(n);
- ErtsAlcType_t a = ERTS_ALC_T2A(t);
- ErtsAlcType_t c = ERTS_ALC_T2C(t);
- const char *astr;
-
- if (erts_allctrs_info[a].enabled)
- astr = ERTS_ALC_A2AD(a);
- else
- astr = ERTS_ALC_A2AD(ERTS_ALC_A_SYSTEM);
-
- erts_cbprintf(to, to_arg,
- "%s{%s,%s,%s}%s",
- (n == ERTS_ALC_N_MIN) ? "" : " ",
- ERTS_ALC_N2TD(n),
- astr,
- ERTS_ALC_C2CD(c),
- (n == ERTS_ALC_N_MAX) ? "" : ",\n");
- }
-
- erts_cbprintf(to, to_arg, "}}.\n");
-
- /* Write memory data */
- for (bp = mem_anchor; bp; bp = bp->next) {
- if (is_internal_pid(bp->pid))
- erts_cbprintf(to, to_arg,
- "{%lu, %lu, %lu, {%lu,%lu,%lu}}.\n",
- (UWord) bp->type_no,
- (UWord) bp->mem,
- (UWord) bp->size,
- (UWord) pid_channel_no(bp->pid),
- (UWord) pid_number(bp->pid),
- (UWord) pid_serial(bp->pid));
- else
- erts_cbprintf(to, to_arg,
- "{%lu, %lu, %lu, undefined}.\n",
- (UWord) bp->type_no,
- (UWord) bp->mem,
- (UWord) bp->size);
- }
-
- if (lock)
- erts_mtx_unlock(&instr_mutex);
-}
-
-int erts_instr_dump_memory_map_to(fmtfn_t to, void* to_arg)
-{
- if (!erts_instr_memory_map)
- return 0;
-
- dump_memory_map_to_stream(to, to_arg);
- return 1;
-}
-
-int erts_instr_dump_memory_map(const char *name)
-{
- int fd;
-
- if (!erts_instr_memory_map)
- return 0;
-
- fd = open(name, O_WRONLY | O_CREAT | O_TRUNC, 0640);
- if (fd < 0)
- return 0;
-
- dump_memory_map_to_stream(erts_write_fd, (void*)&fd);
-
- close(fd);
- return 1;
-}
-
-Eterm erts_instr_get_memory_map(Process *proc)
-{
- MapStatBlock_t *org_mem_anchor;
- Eterm hdr_tuple, md_list, res;
- Eterm *hp;
- Uint hsz;
- MapStatBlock_t *bp;
-#ifdef DEBUG
- Eterm *end_hp;
-#endif
-
- if (!erts_instr_memory_map)
- return am_false;
-
- if (!atoms_initialized)
- init_atoms();
- if (!am_n)
- init_am_n();
- if (!am_c)
- init_am_c();
- if (!am_a)
- init_am_a();
-
- erts_mtx_lock(&instr_x_mutex);
- erts_mtx_lock(&instr_mutex);
-
- /* Header size */
- hsz = 5 + 1 + (ERTS_ALC_N_MAX+1-ERTS_ALC_N_MIN)*(1 + 4);
-
- /* Memory data list */
- for (bp = mem_anchor; bp; bp = bp->next) {
- if (is_internal_pid(bp->pid)) {
-#if (_PID_NUM_SIZE - 1 > MAX_SMALL)
- if (internal_pid_number(bp->pid) > MAX_SMALL)
- hsz += BIG_UINT_HEAP_SIZE;
-#endif
-#if (_PID_SER_SIZE - 1 > MAX_SMALL)
- if (internal_pid_serial(bp->pid) > MAX_SMALL)
- hsz += BIG_UINT_HEAP_SIZE;
-#endif
- hsz += 4;
- }
-
- if ((UWord) bp->mem > MAX_SMALL)
- hsz += BIG_UINT_HEAP_SIZE;
- if (bp->size > MAX_SMALL)
- hsz += BIG_UINT_HEAP_SIZE;
-
- hsz += 5 + 2;
- }
-
- hsz += 3; /* Root tuple */
-
- org_mem_anchor = mem_anchor;
- mem_anchor = NULL;
-
- erts_mtx_unlock(&instr_mutex);
-
- hp = HAlloc(proc, hsz); /* May end up calling map_stat_alloc() */
-
- erts_mtx_lock(&instr_mutex);
-
-#ifdef DEBUG
- end_hp = hp + hsz;
-#endif
-
- { /* Build header */
- ErtsAlcType_t n;
- Eterm type_map;
- Uint *hp2 = hp;
-#ifdef DEBUG
- Uint *hp2_end;
-#endif
-
- hp += (ERTS_ALC_N_MAX + 1 - ERTS_ALC_N_MIN)*4;
-
-#ifdef DEBUG
- hp2_end = hp;
-#endif
-
- type_map = make_tuple(hp);
- *(hp++) = make_arityval(ERTS_ALC_N_MAX + 1 - ERTS_ALC_N_MIN);
-
- for (n = ERTS_ALC_N_MIN; n <= ERTS_ALC_N_MAX; n++) {
- ErtsAlcType_t t = ERTS_ALC_N2T(n);
- ErtsAlcType_t a = ERTS_ALC_T2A(t);
- ErtsAlcType_t c = ERTS_ALC_T2C(t);
-
- if (!erts_allctrs_info[a].enabled)
- a = ERTS_ALC_A_SYSTEM;
-
- *(hp++) = TUPLE3(hp2, am_n[n], am_a[a], am_c[c]);
- hp2 += 4;
- }
-
- ASSERT(hp2 == hp2_end);
-
- hdr_tuple = TUPLE4(hp,
- am.instr_hdr,
- make_small(ERTS_INSTR_VSN),
- make_small(MAP_STAT_BLOCK_HEADER_SIZE),
- type_map);
-
- hp += 5;
- }
-
- /* Build memory data list */
-
- for (md_list = NIL, bp = org_mem_anchor; bp; bp = bp->next) {
- Eterm tuple;
- Eterm type;
- Eterm ptr;
- Eterm size;
- Eterm pid;
-
- if (is_not_internal_pid(bp->pid))
- pid = am_undefined;
- else {
- Eterm c;
- Eterm n;
- Eterm s;
-
-#if (ERST_INTERNAL_CHANNEL_NO > MAX_SMALL)
-#error Oversized internal channel number
-#endif
- c = make_small(ERST_INTERNAL_CHANNEL_NO);
-
-#if (_PID_NUM_SIZE - 1 > MAX_SMALL)
- if (internal_pid_number(bp->pid) > MAX_SMALL) {
- n = uint_to_big(internal_pid_number(bp->pid), hp);
- hp += BIG_UINT_HEAP_SIZE;
- }
- else
-#endif
- n = make_small(internal_pid_number(bp->pid));
-
-#if (_PID_SER_SIZE - 1 > MAX_SMALL)
- if (internal_pid_serial(bp->pid) > MAX_SMALL) {
- s = uint_to_big(internal_pid_serial(bp->pid), hp);
- hp += BIG_UINT_HEAP_SIZE;
- }
- else
-#endif
- s = make_small(internal_pid_serial(bp->pid));
- pid = TUPLE3(hp, c, n, s);
- hp += 4;
- }
-
-
-#if ERTS_ALC_N_MAX > MAX_SMALL
-#error Oversized memory type number
-#endif
- type = make_small(bp->type_no);
-
- if ((UWord) bp->mem > MAX_SMALL) {
- ptr = uint_to_big((UWord) bp->mem, hp);
- hp += BIG_UINT_HEAP_SIZE;
- }
- else
- ptr = make_small((UWord) bp->mem);
-
- if (bp->size > MAX_SMALL) {
- size = uint_to_big(bp->size, hp);
- hp += BIG_UINT_HEAP_SIZE;
- }
- else
- size = make_small(bp->size);
-
- tuple = TUPLE4(hp, type, ptr, size, pid);
- hp += 5;
-
- md_list = CONS(hp, tuple, md_list);
- hp += 2;
- }
-
- res = TUPLE2(hp, hdr_tuple, md_list);
-
- ASSERT(hp + 3 == end_hp);
-
- if (mem_anchor) {
- for (bp = mem_anchor; bp->next; bp = bp->next)
- ;
- ASSERT(org_mem_anchor);
- org_mem_anchor->prev = bp;
- bp->next = org_mem_anchor;
- }
- else {
- mem_anchor = org_mem_anchor;
- }
-
- erts_mtx_unlock(&instr_mutex);
- erts_mtx_unlock(&instr_x_mutex);
-
- return res;
-}
-
-static ERTS_INLINE void
-begin_new_max_period(Stat_t *stat, int min, int max)
-{
- int i;
- for (i = min; i <= max; i++) {
- stat[i].max_size = stat[i].size;
- stat[i].max_blocks = stat[i].blocks;
- }
-}
-
-static ERTS_INLINE void
-update_max_ever_values(Stat_t *stat, int min, int max)
-{
- int i;
- for (i = min; i <= max; i++) {
- if (stat[i].max_size_ever < stat[i].max_size)
- stat[i].max_size_ever = stat[i].max_size;
- if (stat[i].max_blocks_ever < stat[i].max_blocks)
- stat[i].max_blocks_ever = stat[i].max_blocks;
- }
-}
-
-#define bld_string erts_bld_string
-#define bld_tuple erts_bld_tuple
-#define bld_tuplev erts_bld_tuplev
-#define bld_list erts_bld_list
-#define bld_2tup_list erts_bld_2tup_list
-#define bld_uint erts_bld_uint
-
-Eterm
-erts_instr_get_stat(Process *proc, Eterm what, int begin_max_period)
-{
- int i, len, max, min, allctr;
- Eterm *names, *values, res;
- Uint arr_size, stat_size, hsz, *hszp, *hp, **hpp;
- Stat_t *stat_src, *stat;
-
- if (!erts_instr_stat)
- return am_false;
-
- if (!atoms_initialized)
- init_atoms();
-
- if (what == am.total) {
- min = 0;
- max = 0;
- allctr = 0;
- stat_size = sizeof(Stat_t);
- stat_src = &stats->tot;
- if (!am_tot)
- init_am_tot();
- names = am_tot;
- }
- else if (what == am.allocators) {
- min = ERTS_ALC_A_MIN;
- max = ERTS_ALC_A_MAX;
- allctr = 1;
- stat_size = sizeof(Stat_t)*(ERTS_ALC_A_MAX+1);
- stat_src = stats->a;
- if (!am_a)
- init_am_a();
- names = am_a;
- }
- else if (what == am.classes) {
- min = ERTS_ALC_C_MIN;
- max = ERTS_ALC_C_MAX;
- allctr = 0;
- stat_size = sizeof(Stat_t)*(ERTS_ALC_C_MAX+1);
- stat_src = stats->c;
- if (!am_c)
- init_am_c();
- names = &am_c[ERTS_ALC_C_MIN];
- }
- else if (what == am.types) {
- min = ERTS_ALC_N_MIN;
- max = ERTS_ALC_N_MAX;
- allctr = 0;
- stat_size = sizeof(Stat_t)*(ERTS_ALC_N_MAX+1);
- stat_src = stats->n;
- if (!am_n)
- init_am_n();
- names = &am_n[ERTS_ALC_N_MIN];
- }
- else {
- return THE_NON_VALUE;
- }
-
- stat = (Stat_t *) erts_alloc(ERTS_ALC_T_TMP, stat_size);
-
- arr_size = (max - min + 1)*sizeof(Eterm);
-
- if (allctr)
- names = (Eterm *) erts_alloc(ERTS_ALC_T_TMP, arr_size);
-
- values = (Eterm *) erts_alloc(ERTS_ALC_T_TMP, arr_size);
-
- erts_mtx_lock(&instr_mutex);
-
- update_max_ever_values(stat_src, min, max);
-
- sys_memcpy((void *) stat, (void *) stat_src, stat_size);
-
- if (begin_max_period)
- begin_new_max_period(stat_src, min, max);
-
- erts_mtx_unlock(&instr_mutex);
-
- hsz = 0;
- hszp = &hsz;
- hpp = NULL;
-
- restart_bld:
-
- len = 0;
- for (i = min; i <= max; i++) {
- if (!allctr || erts_allctrs_info[i].enabled) {
- Eterm s[2];
-
- if (allctr)
- names[len] = am_a[i];
-
- s[0] = bld_tuple(hpp, hszp, 4,
- am.sizes,
- bld_uint(hpp, hszp, stat[i].size),
- bld_uint(hpp, hszp, stat[i].max_size),
- bld_uint(hpp, hszp, stat[i].max_size_ever));
-
- s[1] = bld_tuple(hpp, hszp, 4,
- am.blocks,
- bld_uint(hpp, hszp, stat[i].blocks),
- bld_uint(hpp, hszp, stat[i].max_blocks),
- bld_uint(hpp, hszp, stat[i].max_blocks_ever));
-
- values[len] = bld_list(hpp, hszp, 2, s);
-
- len++;
- }
- }
-
- res = bld_2tup_list(hpp, hszp, len, names, values);
-
- if (!hpp) {
- hp = HAlloc(proc, hsz);
- hszp = NULL;
- hpp = &hp;
- goto restart_bld;
- }
-
- erts_free(ERTS_ALC_T_TMP, (void *) stat);
- erts_free(ERTS_ALC_T_TMP, (void *) values);
- if (allctr)
- erts_free(ERTS_ALC_T_TMP, (void *) names);
-
- return res;
-}
-
-static void
-dump_stat_to_stream(fmtfn_t to, void* to_arg, int begin_max_period)
-{
- ErtsAlcType_t i, a_max, a_min;
-
- erts_mtx_lock(&instr_mutex);
-
- erts_cbprintf(to, to_arg,
- "{instr_vsn,%lu}.\n",
- (unsigned long) ERTS_INSTR_VSN);
-
- update_max_ever_values(&stats->tot, 0, 0);
-
- erts_cbprintf(to, to_arg,
- "{total,[{total,[{sizes,%lu,%lu,%lu},{blocks,%lu,%lu,%lu}]}]}.\n",
- (UWord) stats->tot.size,
- (UWord) stats->tot.max_size,
- (UWord) stats->tot.max_size_ever,
- (UWord) stats->tot.blocks,
- (UWord) stats->tot.max_blocks,
- (UWord) stats->tot.max_blocks_ever);
-
- a_max = 0;
- a_min = ~0;
- for (i = ERTS_ALC_A_MIN; i <= ERTS_ALC_A_MAX; i++) {
- if (erts_allctrs_info[i].enabled) {
- if (a_min > i)
- a_min = i;
- if (a_max < i)
- a_max = i;
- }
- }
-
- ASSERT(ERTS_ALC_A_MIN <= a_min && a_min <= ERTS_ALC_A_MAX);
- ASSERT(ERTS_ALC_A_MIN <= a_max && a_max <= ERTS_ALC_A_MAX);
- ASSERT(a_min <= a_max);
-
- update_max_ever_values(stats->a, a_min, a_max);
-
- for (i = ERTS_ALC_A_MIN; i <= ERTS_ALC_A_MAX; i++) {
- if (erts_allctrs_info[i].enabled) {
- erts_cbprintf(to, to_arg,
- "%s{%s,[{sizes,%lu,%lu,%lu},{blocks,%lu,%lu,%lu}]}%s",
- i == a_min ? "{allocators,\n [" : " ",
- ERTS_ALC_A2AD(i),
- (UWord) stats->a[i].size,
- (UWord) stats->a[i].max_size,
- (UWord) stats->a[i].max_size_ever,
- (UWord) stats->a[i].blocks,
- (UWord) stats->a[i].max_blocks,
- (UWord) stats->a[i].max_blocks_ever,
- i == a_max ? "]}.\n" : ",\n");
- }
- }
-
- update_max_ever_values(stats->c, ERTS_ALC_C_MIN, ERTS_ALC_C_MAX);
-
- for (i = ERTS_ALC_C_MIN; i <= ERTS_ALC_C_MAX; i++) {
- erts_cbprintf(to, to_arg,
- "%s{%s,[{sizes,%lu,%lu,%lu},{blocks,%lu,%lu,%lu}]}%s",
- i == ERTS_ALC_C_MIN ? "{classes,\n [" : " ",
- ERTS_ALC_C2CD(i),
- (UWord) stats->c[i].size,
- (UWord) stats->c[i].max_size,
- (UWord) stats->c[i].max_size_ever,
- (UWord) stats->c[i].blocks,
- (UWord) stats->c[i].max_blocks,
- (UWord) stats->c[i].max_blocks_ever,
- i == ERTS_ALC_C_MAX ? "]}.\n" : ",\n" );
- }
-
- update_max_ever_values(stats->n, ERTS_ALC_N_MIN, ERTS_ALC_N_MAX);
-
- for (i = ERTS_ALC_N_MIN; i <= ERTS_ALC_N_MAX; i++) {
- erts_cbprintf(to, to_arg,
- "%s{%s,[{sizes,%lu,%lu,%lu},{blocks,%lu,%lu,%lu}]}%s",
- i == ERTS_ALC_N_MIN ? "{types,\n [" : " ",
- ERTS_ALC_N2TD(i),
- (UWord) stats->n[i].size,
- (UWord) stats->n[i].max_size,
- (UWord) stats->n[i].max_size_ever,
- (UWord) stats->n[i].blocks,
- (UWord) stats->n[i].max_blocks,
- (UWord) stats->n[i].max_blocks_ever,
- i == ERTS_ALC_N_MAX ? "]}.\n" : ",\n" );
- }
-
- if (begin_max_period) {
- begin_new_max_period(&stats->tot, 0, 0);
- begin_new_max_period(stats->a, a_min, a_max);
- begin_new_max_period(stats->c, ERTS_ALC_C_MIN, ERTS_ALC_C_MAX);
- begin_new_max_period(stats->n, ERTS_ALC_N_MIN, ERTS_ALC_N_MAX);
- }
-
- erts_mtx_unlock(&instr_mutex);
-
-}
-
-int erts_instr_dump_stat_to(fmtfn_t to, void* to_arg, int begin_max_period)
-{
- if (!erts_instr_stat)
- return 0;
-
- dump_stat_to_stream(to, to_arg, begin_max_period);
- return 1;
-}
-
-int erts_instr_dump_stat(const char *name, int begin_max_period)
-{
- int fd;
-
- if (!erts_instr_stat)
- return 0;
-
- fd = open(name, O_WRONLY | O_CREAT | O_TRUNC,0640);
- if (fd < 0)
- return 0;
-
- dump_stat_to_stream(erts_write_fd, (void*)&fd, begin_max_period);
-
- close(fd);
- return 1;
-}
-
-
-Uint
-erts_instr_get_total(void)
-{
- return erts_instr_stat ? stats->tot.size : 0;
-}
-
-Uint
-erts_instr_get_max_total(void)
-{
- if (erts_instr_stat) {
- update_max_ever_values(&stats->tot, 0, 0);
- return stats->tot.max_size_ever;
- }
- return 0;
-}
-
-Eterm
-erts_instr_get_type_info(Process *proc)
-{
- Eterm res, *tpls;
- Uint hsz, *hszp, *hp, **hpp;
- ErtsAlcType_t n;
-
- if (!am_n)
- init_am_n();
- if (!am_a)
- init_am_a();
- if (!am_c)
- init_am_c();
-
- tpls = (Eterm *) erts_alloc(ERTS_ALC_T_TMP,
- (ERTS_ALC_N_MAX-ERTS_ALC_N_MIN+1)
- * sizeof(Eterm));
- hsz = 0;
- hszp = &hsz;
- hpp = NULL;
-
- restart_bld:
-
-#if ERTS_ALC_N_MIN != 1
-#error ERTS_ALC_N_MIN is not 1
-#endif
-
- for (n = ERTS_ALC_N_MIN; n <= ERTS_ALC_N_MAX; n++) {
- ErtsAlcType_t t = ERTS_ALC_N2T(n);
- ErtsAlcType_t a = ERTS_ALC_T2A(t);
- ErtsAlcType_t c = ERTS_ALC_T2C(t);
-
- if (!erts_allctrs_info[a].enabled)
- a = ERTS_ALC_A_SYSTEM;
-
- tpls[n - ERTS_ALC_N_MIN]
- = bld_tuple(hpp, hszp, 3, am_n[n], am_a[a], am_c[c]);
- }
-
- res = bld_tuplev(hpp, hszp, ERTS_ALC_N_MAX-ERTS_ALC_N_MIN+1, tpls);
-
- if (!hpp) {
- hp = HAlloc(proc, hsz);
- hszp = NULL;
- hpp = &hp;
- goto restart_bld;
- }
-
- erts_free(ERTS_ALC_T_TMP, tpls);
-
- return res;
-}
-
-Uint
-erts_instr_init(int stat, int map_stat)
-{
- Uint extra_sz;
- int i;
-
- am_tot = NULL;
- am_n = NULL;
- am_c = NULL;
- am_a = NULL;
-
- erts_instr_memory_map = 0;
- erts_instr_stat = 0;
- atoms_initialized = 0;
-
- if (!stat && !map_stat)
- return 0;
-
- stats = erts_alloc(ERTS_ALC_T_INSTR_INFO, sizeof(struct stats_));
-
- erts_mtx_init(&instr_mutex, "instr", NIL,
- ERTS_LOCK_FLAGS_PROPERTY_STATIC | ERTS_LOCK_FLAGS_CATEGORY_DEBUG);
-
- mem_anchor = NULL;
-
- /* Install instrumentation functions */
- ERTS_CT_ASSERT(sizeof(erts_allctrs) == sizeof(real_allctrs));
-
- sys_memcpy((void *)real_allctrs,(void *)erts_allctrs,sizeof(erts_allctrs));
-
- sys_memzero((void *) &stats->tot, sizeof(Stat_t));
- sys_memzero((void *) stats->a, sizeof(Stat_t)*(ERTS_ALC_A_MAX+1));
- sys_memzero((void *) stats->c, sizeof(Stat_t)*(ERTS_ALC_C_MAX+1));
- sys_memzero((void *) stats->n, sizeof(Stat_t)*(ERTS_ALC_N_MAX+1));
-
- for (i = ERTS_ALC_A_MIN; i <= ERTS_ALC_A_MAX; i++) {
- if (erts_allctrs_info[i].enabled)
- stats->ap[i] = &stats->a[i];
- else
- stats->ap[i] = &stats->a[ERTS_ALC_A_SYSTEM];
- }
-
- if (map_stat) {
-
- erts_mtx_init(&instr_x_mutex, "instr_x", NIL,
- ERTS_LOCK_FLAGS_PROPERTY_STATIC | ERTS_LOCK_FLAGS_CATEGORY_DEBUG);
-
- erts_instr_memory_map = 1;
- erts_instr_stat = 1;
- for (i = ERTS_ALC_A_MIN; i <= ERTS_ALC_A_MAX; i++) {
- erts_allctrs[i].alloc = map_stat_alloc;
- erts_allctrs[i].realloc = map_stat_realloc;
- erts_allctrs[i].free = map_stat_free;
- erts_allctrs[i].extra = (void *) &real_allctrs[i];
- }
- instr_wrapper.lock = map_stat_pre_lock;
- instr_wrapper.unlock = map_stat_pre_unlock;
- extra_sz = MAP_STAT_BLOCK_HEADER_SIZE;
- }
- else {
- erts_instr_stat = 1;
- for (i = ERTS_ALC_A_MIN; i <= ERTS_ALC_A_MAX; i++) {
- erts_allctrs[i].alloc = stat_alloc;
- erts_allctrs[i].realloc = stat_realloc;
- erts_allctrs[i].free = stat_free;
- erts_allctrs[i].extra = (void *) &real_allctrs[i];
- }
- instr_wrapper.lock = stat_pre_lock;
- instr_wrapper.unlock = stat_pre_unlock;
- extra_sz = STAT_BLOCK_HEADER_SIZE;
- }
- erts_allctr_wrapper_prelock_init(&instr_wrapper);
- return extra_sz;
-}
-
diff --git a/erts/emulator/beam/erl_instrument.h b/erts/emulator/beam/erl_instrument.h
deleted file mode 100644
index 351172b2fa..0000000000
--- a/erts/emulator/beam/erl_instrument.h
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * %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%
- */
-
-#ifndef ERL_INSTRUMENT_H__
-#define ERL_INSTRUMENT_H__
-
-#include "erl_mtrace.h"
-
-#define ERTS_INSTR_VSN 2
-
-extern int erts_instr_memory_map;
-extern int erts_instr_stat;
-
-Uint erts_instr_init(int stat, int map_stat);
-int erts_instr_dump_memory_map_to(fmtfn_t to, void* to_arg);
-int erts_instr_dump_memory_map(const char *name);
-Eterm erts_instr_get_memory_map(Process *process);
-int erts_instr_dump_stat_to(fmtfn_t to, void* to_arg, int begin_max_period);
-int erts_instr_dump_stat(const char *name, int begin_max_period);
-Eterm erts_instr_get_stat(Process *proc, Eterm what, int begin_max_period);
-Eterm erts_instr_get_type_info(Process *proc);
-Uint erts_instr_get_total(void);
-Uint erts_instr_get_max_total(void);
-
-#endif
diff --git a/erts/emulator/beam/erl_io_queue.c b/erts/emulator/beam/erl_io_queue.c
index a01b676d39..d779d1031a 100644
--- a/erts/emulator/beam/erl_io_queue.c
+++ b/erts/emulator/beam/erl_io_queue.c
@@ -658,7 +658,7 @@ io_list_vec_count(Eterm obj, Uint *v_size,
int
erts_ioq_iolist_vec_len(Eterm obj, int* vsize, Uint* csize,
Uint* pvsize, Uint* pcsize,
- Uint* total_size, Uint blimit)
+ size_t* total_size, Uint blimit)
{
DECLARE_ESTACK(s);
Eterm* objp;
@@ -669,7 +669,7 @@ erts_ioq_iolist_vec_len(Eterm obj, int* vsize, Uint* csize,
Uint p_v_size = 0;
Uint p_c_size = 0;
Uint p_in_clist = 0;
- Uint total;
+ size_t total;
goto L_jump_start; /* avoid a push */
@@ -801,12 +801,11 @@ static Eterm iol2v_make_sub_bin(iol2v_state_t *state, Eterm bin_term,
ERTS_GET_REAL_BIN(bin_term, orig_pb_term,
byte_offset, bit_offset, bit_size);
- (void)bit_offset;
- (void)bit_size;
+ ASSERT(bit_size == 0);
sb->thing_word = HEADER_SUB_BIN;
+ sb->bitoffs = bit_offset;
sb->bitsize = 0;
- sb->bitoffs = 0;
sb->orig = orig_pb_term;
sb->is_writable = 0;
@@ -817,24 +816,15 @@ static Eterm iol2v_make_sub_bin(iol2v_state_t *state, Eterm bin_term,
}
static Eterm iol2v_promote_acc(iol2v_state_t *state) {
- ProcBin *pb;
-
- state->acc = erts_bin_realloc(state->acc, state->acc_size);
-
- pb = (ProcBin*)HAlloc(state->process, PROC_BIN_SIZE);
- pb->thing_word = HEADER_PROC_BIN;
- pb->size = state->acc_size;
- pb->val = state->acc;
- pb->bytes = (byte*)(state->acc)->orig_bytes;
- pb->flags = 0;
- pb->next = MSO(state->process).first;
- OH_OVERHEAD(&(MSO(state->process)), pb->size / sizeof(Eterm));
- MSO(state->process).first = (struct erl_off_heap_header*)pb;
+ Eterm bin;
+ bin = erts_build_proc_bin(&MSO(state->process),
+ HAlloc(state->process, PROC_BIN_SIZE),
+ erts_bin_realloc(state->acc, state->acc_size));
state->acc_size = 0;
state->acc = NULL;
- return make_binary(pb);
+ return bin;
}
/* Destructively enqueues a term to the result list, saving us the hassle of
@@ -973,9 +963,10 @@ static int iol2v_append_binary(iol2v_state_t *state, Eterm bin_term) {
UWord binary_size;
Uint byte_offset, bit_offset, bit_size;
+ byte *binary_data;
+
Eterm *parent_header;
Eterm parent_binary;
- byte *binary_data;
ASSERT(state->bytereds_available > state->bytereds_spent);
@@ -983,7 +974,7 @@ static int iol2v_append_binary(iol2v_state_t *state, Eterm bin_term) {
parent_header = binary_val(parent_binary);
binary_size = binary_size(bin_term);
- if (bit_offset != 0 || bit_size != 0) {
+ if (bit_size != 0) {
return 0;
} else if (binary_size == 0) {
state->bytereds_spent += 1;
@@ -1001,14 +992,14 @@ static int iol2v_append_binary(iol2v_state_t *state, Eterm bin_term) {
erts_emasculate_writable_binary(pb);
}
- binary_data = pb->bytes;
+ binary_data = &((byte*)pb->bytes)[byte_offset];
} else {
ErlHeapBin *hb = (ErlHeapBin*)parent_header;
ASSERT(thing_subtag(*parent_header) == HEAP_BINARY_SUBTAG);
ASSERT(is_bin_small);
- binary_data = &((unsigned char*)&hb->data)[byte_offset];
+ binary_data = &((byte*)&hb->data)[byte_offset];
}
if (!is_bin_small && (state->acc_size == 0 || !is_acc_small)) {
@@ -1025,8 +1016,16 @@ static int iol2v_append_binary(iol2v_state_t *state, Eterm bin_term) {
* then just copy it into the accumulator. */
iol2v_expand_acc(state, binary_size);
- sys_memcpy(&(state->acc)->orig_bytes[state->acc_size],
- binary_data, binary_size);
+ if (ERTS_LIKELY(bit_offset == 0)) {
+ sys_memcpy(&(state->acc)->orig_bytes[state->acc_size],
+ binary_data, binary_size);
+ } else {
+ ASSERT(binary_size <= ERTS_UWORD_MAX / 8);
+
+ erts_copy_bits(binary_data, bit_offset, 1,
+ (byte*)&(state->acc)->orig_bytes[state->acc_size], 0, 1,
+ binary_size * 8);
+ }
state->acc_size += binary_size;
} else {
@@ -1037,8 +1036,16 @@ static int iol2v_append_binary(iol2v_state_t *state, Eterm bin_term) {
iol2v_expand_acc(state, spill);
- sys_memcpy(&(state->acc)->orig_bytes[state->acc_size],
- binary_data, spill);
+ if (ERTS_LIKELY(bit_offset == 0)) {
+ sys_memcpy(&(state->acc)->orig_bytes[state->acc_size],
+ binary_data, spill);
+ } else {
+ ASSERT(binary_size <= ERTS_UWORD_MAX / 8);
+
+ erts_copy_bits(binary_data, bit_offset, 1,
+ (byte*)&(state->acc)->orig_bytes[state->acc_size], 0, 1,
+ spill * 8);
+ }
state->acc_size += spill;
@@ -1187,7 +1194,10 @@ BIF_RETTYPE iolist_to_iovec_1(BIF_ALIST_1) {
if (is_nil(BIF_ARG_1)) {
BIF_RET(NIL);
} else if (is_binary(BIF_ARG_1)) {
- if (binary_size(BIF_ARG_1) != 0) {
+ if (binary_bitsize(BIF_ARG_1) != 0) {
+ ASSERT(!(BIF_P->flags & F_DISABLE_GC));
+ BIF_ERROR(BIF_P, BADARG);
+ } else if (binary_size(BIF_ARG_1) != 0) {
Eterm *hp = HAlloc(BIF_P, 2);
BIF_RET(CONS(hp, BIF_ARG_1, NIL));
diff --git a/erts/emulator/beam/erl_io_queue.h b/erts/emulator/beam/erl_io_queue.h
index 51abe99510..7d0fe6751c 100644
--- a/erts/emulator/beam/erl_io_queue.h
+++ b/erts/emulator/beam/erl_io_queue.h
@@ -103,7 +103,7 @@ Uint erts_ioq_sizeq(ErtsIOQueue *q);
int erts_ioq_iolist_vec_len(Eterm obj, int* vsize, Uint* csize,
Uint* pvsize, Uint* pcsize,
- Uint* total_size, Uint blimit);
+ size_t* total_size, Uint blimit);
int erts_ioq_iolist_to_vec(Eterm obj, SysIOVec* iov,
ErtsIOQBinary** binv, ErtsIOQBinary* cbin,
Uint bin_limit, int driver_binary);
@@ -111,7 +111,7 @@ int erts_ioq_iolist_to_vec(Eterm obj, SysIOVec* iov,
ERTS_GLB_INLINE
int erts_ioq_iodata_vec_len(Eterm obj, int* vsize, Uint* csize,
Uint* pvsize, Uint* pcsize,
- Uint* total_size, Uint blimit);
+ size_t* total_size, Uint blimit);
ERTS_GLB_INLINE
int erts_ioq_iodata_to_vec(Eterm obj, SysIOVec* iov,
ErtsIOQBinary** binv, ErtsIOQBinary* cbin,
@@ -123,7 +123,7 @@ int erts_ioq_iodata_to_vec(Eterm obj, SysIOVec* iov,
ERTS_GLB_INLINE
int erts_ioq_iodata_vec_len(Eterm obj, int* vsize, Uint* csize,
Uint* pvsize, Uint* pcsize,
- Uint* total_size, Uint blimit) {
+ size_t* total_size, Uint blimit) {
if (is_binary(obj)) {
/* We optimize for when we get a procbin without a bit-offset
* that fits in one iov slot
diff --git a/erts/emulator/beam/erl_lock_check.c b/erts/emulator/beam/erl_lock_check.c
index 4cdef0200f..d66410367b 100644
--- a/erts/emulator/beam/erl_lock_check.c
+++ b/erts/emulator/beam/erl_lock_check.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2005-2017. All Rights Reserved.
+ * Copyright Ericsson AB 2005-2018. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -41,6 +41,7 @@
#include "erl_lock_check.h"
#include "erl_term.h"
#include "erl_threads.h"
+#include "erl_atom_table.h"
typedef struct {
char *name;
@@ -75,10 +76,10 @@ static erts_lc_lock_order_t erts_lock_order[] = {
* if only one lock use
* the lock name)"
*/
+ { "NO LOCK", NULL },
{ "driver_lock", "driver_name" },
{ "port_lock", "port_id" },
{ "port_data_lock", "address" },
- { "bif_timers", NULL },
{ "reg_tab", NULL },
{ "proc_main", "pid" },
{ "old_code", "address" },
@@ -86,34 +87,30 @@ static erts_lc_lock_order_t erts_lock_order[] = {
{ "hipe_mfait_lock", NULL },
#endif
{ "nodes_monitors", NULL },
+ { "meta_name_tab", "address" },
+ { "db_tab", "address" },
+ { "db_tab_fix", "address" },
+ { "db_hash_slot", "address" },
{ "resource_monitors", "address" },
{ "driver_list", NULL },
- { "proc_link", "pid" },
{ "proc_msgq", "pid" },
{ "proc_btm", "pid" },
{ "dist_entry", "address" },
{ "dist_entry_links", "address" },
{ "code_write_permission", NULL },
{ "purge_state", NULL },
- { "meta_name_tab", "address" },
- { "db_tab", "address" },
{ "proc_status", "pid" },
{ "proc_trace", "pid" },
- { "db_tab_fix", "address" },
- { "db_hash_slot", "address" },
{ "node_table", NULL },
{ "dist_table", NULL },
{ "sys_tracers", NULL },
- { "module_tab", NULL },
{ "export_tab", NULL },
{ "fun_tab", NULL },
{ "environ", NULL },
{ "release_literal_areas", NULL },
- { "efile_drv", "address" },
{ "drv_ev_state_grow", NULL, },
{ "drv_ev_state", "address" },
{ "safe_hash", "address" },
- { "removed_fd_pre_alloc_lock", "address" },
{ "state_prealloc", NULL },
{ "schdlr_sspnd", NULL },
{ "migration_info_update", NULL },
@@ -136,10 +133,6 @@ static erts_lc_lock_order_t erts_lock_order[] = {
{ "msacc_list_mutex", NULL },
{ "msacc_unmanaged_mutex", NULL },
{ "atom_tab", NULL },
- { "misc_op_list_pre_alloc_lock", "address" },
- { "message_pre_alloc_lock", "address" },
- { "ptimer_pre_alloc_lock", "address", },
- { "btm_pre_alloc_lock", NULL, },
{ "dist_entry_out_queue", "address" },
{ "port_sched_lock", "port_id" },
{ "sys_msg_q", NULL },
@@ -149,20 +142,12 @@ static erts_lc_lock_order_t erts_lock_order[] = {
{ "mtrace_op", NULL },
{ "instr_x", NULL },
{ "instr", NULL },
- { "pollsets_lock", NULL },
{ "alcu_allocator", "index" },
{ "mseg", NULL },
- { "port_task_pre_alloc_lock", "address" },
- { "proclist_pre_alloc_lock", "address" },
- { "xports_list_pre_alloc_lock", "address" },
- { "inet_buffer_stack_lock", NULL },
- { "system_block", NULL },
{ "get_time", NULL },
{ "get_corrected_time", NULL },
{ "runtime", NULL },
- { "breakpoints", NULL },
{ "pix_lock", "address" },
- { "run_queues_lists", NULL },
{ "sched_stat", NULL },
{ "async_init_mtx", NULL },
#ifdef __WIN32__
@@ -170,9 +155,6 @@ static erts_lc_lock_order_t erts_lock_order[] = {
{ "save_ops_lock", NULL },
#endif
#endif
-#ifdef USE_VM_PROBES
- { "efile_drv dtrace mutex", NULL },
-#endif
{ "mtrace_buf", NULL },
{ "os_monotonic_time", NULL },
{ "erts_alloc_hard_debug", NULL },
@@ -199,10 +181,10 @@ static const char *rw_op_str(erts_lock_options_t options)
return erts_lock_options_get_short_desc(options);
}
-typedef struct erts_lc_locked_lock_t_ erts_lc_locked_lock_t;
-struct erts_lc_locked_lock_t_ {
- erts_lc_locked_lock_t *next;
- erts_lc_locked_lock_t *prev;
+typedef struct lc_locked_lock_t_ lc_locked_lock_t;
+struct lc_locked_lock_t_ {
+ lc_locked_lock_t *next;
+ lc_locked_lock_t *prev;
UWord extra;
Sint16 id;
char *file;
@@ -212,32 +194,47 @@ struct erts_lc_locked_lock_t_ {
};
typedef struct {
- erts_lc_locked_lock_t *first;
- erts_lc_locked_lock_t *last;
-} erts_lc_locked_lock_list_t;
+ lc_locked_lock_t *first;
+ lc_locked_lock_t *last;
+} lc_locked_lock_list_t;
+
+typedef union lc_free_block_t_ lc_free_block_t;
+union lc_free_block_t_ {
+ lc_free_block_t *next;
+ lc_locked_lock_t lock;
+};
+
+typedef struct {
+ /*
+ * m[X][Y] & 1 if we locked X directly after Y was locked.
+ * m[X][Y] & 2 if we locked X indirectly after Y was locked.
+ * m[X][0] = 1 if we locked X when nothing else was locked.
+ * m[0][] is unused as it would represent locking "NO LOCK"
+ */
+ char m[ERTS_LOCK_ORDER_SIZE][ERTS_LOCK_ORDER_SIZE];
+
+} lc_matrix_t;
-typedef struct erts_lc_locked_locks_t_ erts_lc_locked_locks_t;
-struct erts_lc_locked_locks_t_ {
+static lc_matrix_t tot_lc_matrix;
+
+typedef struct lc_thread_t_ lc_thread_t;
+struct lc_thread_t_ {
char *thread_name;
int emu_thread;
erts_tid_t tid;
- erts_lc_locked_locks_t *next;
- erts_lc_locked_locks_t *prev;
- erts_lc_locked_lock_list_t locked;
- erts_lc_locked_lock_list_t required;
-};
-
-typedef union erts_lc_free_block_t_ erts_lc_free_block_t;
-union erts_lc_free_block_t_ {
- erts_lc_free_block_t *next;
- erts_lc_locked_lock_t lock;
+ lc_thread_t *next;
+ lc_thread_t *prev;
+ lc_locked_lock_list_t locked;
+ lc_locked_lock_list_t required;
+ lc_free_block_t *free_blocks;
+ lc_matrix_t matrix;
};
static ethr_tsd_key locks_key;
-static erts_lc_locked_locks_t *erts_locked_locks = NULL;
+static lc_thread_t *lc_threads = NULL;
+static ethr_spinlock_t lc_threads_lock;
-static erts_lc_free_block_t *free_blocks = NULL;
#ifdef ERTS_LC_STATIC_ALLOC
#define ERTS_LC_FB_CHUNK_SIZE 10000
@@ -245,176 +242,165 @@ static erts_lc_free_block_t *free_blocks = NULL;
#define ERTS_LC_FB_CHUNK_SIZE 10
#endif
-static ethr_spinlock_t free_blocks_lock;
static ERTS_INLINE void
-lc_lock(void)
+lc_lock_threads(void)
{
- ethr_spin_lock(&free_blocks_lock);
+ ethr_spin_lock(&lc_threads_lock);
}
static ERTS_INLINE void
-lc_unlock(void)
+lc_unlock_threads(void)
{
- ethr_spin_unlock(&free_blocks_lock);
+ ethr_spin_unlock(&lc_threads_lock);
}
-static ERTS_INLINE void lc_free(void *p)
+static ERTS_INLINE void lc_free(lc_thread_t* thr, lc_locked_lock_t *p)
{
- erts_lc_free_block_t *fb = (erts_lc_free_block_t *) p;
+ lc_free_block_t *fb = (lc_free_block_t *) p;
#ifdef DEBUG
- memset((void *) p, 0xdf, sizeof(erts_lc_free_block_t));
+ sys_memset((void *) p, 0xdf, sizeof(lc_free_block_t));
#endif
- lc_lock();
- fb->next = free_blocks;
- free_blocks = fb;
- lc_unlock();
-}
-
-#ifdef ERTS_LC_STATIC_ALLOC
-
-static void *lc_core_alloc(void)
-{
- lc_unlock();
- ERTS_INTERNAL_ERROR("Lock checker out of memory!\n");
+ fb->next = thr->free_blocks;
+ thr->free_blocks = fb;
}
-#else
-
-static void *lc_core_alloc(void)
+static lc_locked_lock_t *lc_core_alloc(lc_thread_t* thr)
{
int i;
- erts_lc_free_block_t *fbs;
- lc_unlock();
- fbs = (erts_lc_free_block_t *) malloc(sizeof(erts_lc_free_block_t)
+ lc_free_block_t *fbs;
+ fbs = (lc_free_block_t *) malloc(sizeof(lc_free_block_t)
* ERTS_LC_FB_CHUNK_SIZE);
if (!fbs) {
ERTS_INTERNAL_ERROR("Lock checker failed to allocate memory!");
}
for (i = 1; i < ERTS_LC_FB_CHUNK_SIZE - 1; i++) {
#ifdef DEBUG
- memset((void *) &fbs[i], 0xdf, sizeof(erts_lc_free_block_t));
+ sys_memset((void *) &fbs[i], 0xdf, sizeof(lc_free_block_t));
#endif
fbs[i].next = &fbs[i+1];
}
#ifdef DEBUG
- memset((void *) &fbs[ERTS_LC_FB_CHUNK_SIZE-1],
- 0xdf, sizeof(erts_lc_free_block_t));
+ sys_memset((void *) &fbs[ERTS_LC_FB_CHUNK_SIZE-1],
+ 0xdf, sizeof(lc_free_block_t));
#endif
- lc_lock();
- fbs[ERTS_LC_FB_CHUNK_SIZE-1].next = free_blocks;
- free_blocks = &fbs[1];
- return (void *) &fbs[0];
+ fbs[ERTS_LC_FB_CHUNK_SIZE-1].next = thr->free_blocks;
+ thr->free_blocks = &fbs[1];
+ return &fbs[0].lock;
}
-#endif
-
-static ERTS_INLINE void *lc_alloc(void)
+static ERTS_INLINE lc_locked_lock_t *lc_alloc(lc_thread_t* thr)
{
- void *res;
- lc_lock();
- if (!free_blocks)
- res = lc_core_alloc();
+ lc_locked_lock_t *res;
+ if (!thr->free_blocks)
+ res = lc_core_alloc(thr);
else {
- res = (void *) free_blocks;
- free_blocks = free_blocks->next;
+ res = &thr->free_blocks->lock;
+ thr->free_blocks = thr->free_blocks->next;
}
- lc_unlock();
return res;
}
-static erts_lc_locked_locks_t *
-create_locked_locks(char *thread_name)
+static lc_thread_t *
+create_thread_data(char *thread_name)
{
- erts_lc_locked_locks_t *l_lcks = malloc(sizeof(erts_lc_locked_locks_t));
- if (!l_lcks)
+ lc_thread_t *thr = malloc(sizeof(lc_thread_t));
+ if (!thr)
ERTS_INTERNAL_ERROR("Lock checker failed to allocate memory!");
- l_lcks->thread_name = strdup(thread_name ? thread_name : "unknown");
- if (!l_lcks->thread_name)
+ thr->thread_name = strdup(thread_name ? thread_name : "unknown");
+ if (!thr->thread_name)
ERTS_INTERNAL_ERROR("Lock checker failed to allocate memory!");
- l_lcks->emu_thread = 0;
- l_lcks->tid = erts_thr_self();
- l_lcks->required.first = NULL;
- l_lcks->required.last = NULL;
- l_lcks->locked.first = NULL;
- l_lcks->locked.last = NULL;
- l_lcks->prev = NULL;
- lc_lock();
- l_lcks->next = erts_locked_locks;
- if (erts_locked_locks)
- erts_locked_locks->prev = l_lcks;
- erts_locked_locks = l_lcks;
- lc_unlock();
- erts_tsd_set(locks_key, (void *) l_lcks);
- return l_lcks;
+ thr->emu_thread = 0;
+ thr->tid = erts_thr_self();
+ thr->required.first = NULL;
+ thr->required.last = NULL;
+ thr->locked.first = NULL;
+ thr->locked.last = NULL;
+ thr->prev = NULL;
+ thr->free_blocks = NULL;
+ sys_memzero(&thr->matrix, sizeof(thr->matrix));
+
+ lc_lock_threads();
+ thr->next = lc_threads;
+ if (lc_threads)
+ lc_threads->prev = thr;
+ lc_threads = thr;
+ lc_unlock_threads();
+ erts_tsd_set(locks_key, (void *) thr);
+ return thr;
}
+static void collect_matrix(lc_matrix_t*);
+
static void
-destroy_locked_locks(erts_lc_locked_locks_t *l_lcks)
-{
- ASSERT(l_lcks->thread_name);
- free((void *) l_lcks->thread_name);
- ASSERT(l_lcks->required.first == NULL);
- ASSERT(l_lcks->required.last == NULL);
- ASSERT(l_lcks->locked.first == NULL);
- ASSERT(l_lcks->locked.last == NULL);
-
- lc_lock();
- if (l_lcks->prev)
- l_lcks->prev->next = l_lcks->next;
+destroy_locked_locks(lc_thread_t *thr)
+{
+ ASSERT(thr->thread_name);
+ free((void *) thr->thread_name);
+ ASSERT(thr->required.first == NULL);
+ ASSERT(thr->required.last == NULL);
+ ASSERT(thr->locked.first == NULL);
+ ASSERT(thr->locked.last == NULL);
+
+ lc_lock_threads();
+ if (thr->prev)
+ thr->prev->next = thr->next;
else {
- ASSERT(erts_locked_locks == l_lcks);
- erts_locked_locks = l_lcks->next;
+ ASSERT(lc_threads == thr);
+ lc_threads = thr->next;
}
+ if (thr->next)
+ thr->next->prev = thr->prev;
- if (l_lcks->next)
- l_lcks->next->prev = l_lcks->prev;
- lc_unlock();
+ collect_matrix(&thr->matrix);
- free((void *) l_lcks);
+ lc_unlock_threads();
+
+ free((void *) thr);
}
-static ERTS_INLINE erts_lc_locked_locks_t *
+static ERTS_INLINE lc_thread_t *
get_my_locked_locks(void)
{
return erts_tsd_get(locks_key);
}
-static ERTS_INLINE erts_lc_locked_locks_t *
+static ERTS_INLINE lc_thread_t *
make_my_locked_locks(void)
{
- erts_lc_locked_locks_t *l_lcks = get_my_locked_locks();
- if (l_lcks)
- return l_lcks;
+ lc_thread_t *thr = get_my_locked_locks();
+ if (thr)
+ return thr;
else
- return create_locked_locks(NULL);
+ return create_thread_data(NULL);
}
-static ERTS_INLINE erts_lc_locked_lock_t *
-new_locked_lock(erts_lc_lock_t *lck, erts_lock_options_t options,
+static ERTS_INLINE lc_locked_lock_t *
+new_locked_lock(lc_thread_t* thr,
+ erts_lc_lock_t *lck, erts_lock_options_t options,
char *file, unsigned int line)
{
- erts_lc_locked_lock_t *l_lck = (erts_lc_locked_lock_t *) lc_alloc();
- l_lck->next = NULL;
- l_lck->prev = NULL;
- l_lck->id = lck->id;
- l_lck->extra = lck->extra;
- l_lck->file = file;
- l_lck->line = line;
- l_lck->flags = lck->flags;
- l_lck->taken_options = options;
- return l_lck;
+ lc_locked_lock_t *ll = lc_alloc(thr);
+ ll->next = NULL;
+ ll->prev = NULL;
+ ll->id = lck->id;
+ ll->extra = lck->extra;
+ ll->file = file;
+ ll->line = line;
+ ll->flags = lck->flags;
+ ll->taken_options = options;
+ return ll;
}
static void
raw_print_lock(char *prefix, Sint16 id, Wterm extra, erts_lock_flags_t flags,
char* file, unsigned int line, char *suffix)
{
- char *lname = (0 <= id && id < ERTS_LOCK_ORDER_SIZE
+ char *lname = (1 <= id && id < ERTS_LOCK_ORDER_SIZE
? erts_lock_order[id].name
: "unknown");
erts_fprintf(stderr,"%s'%s:",prefix,lname);
@@ -444,20 +430,20 @@ print_lock(char *prefix, erts_lc_lock_t *lck, char *suffix)
}
static void
-print_curr_locks(erts_lc_locked_locks_t *l_lcks)
+print_curr_locks(lc_thread_t *thr)
{
- erts_lc_locked_lock_t *l_lck;
- if (!l_lcks || !l_lcks->locked.first)
+ lc_locked_lock_t *ll;
+ if (!thr || !thr->locked.first)
erts_fprintf(stderr,
"Currently no locks are locked by the %s thread.\n",
- l_lcks->thread_name);
+ thr->thread_name);
else {
erts_fprintf(stderr,
"Currently these locks are locked by the %s thread:\n",
- l_lcks->thread_name);
- for (l_lck = l_lcks->locked.first; l_lck; l_lck = l_lck->next)
- raw_print_lock(" ", l_lck->id, l_lck->extra, l_lck->flags,
- l_lck->file, l_lck->line, "\n");
+ thr->thread_name);
+ for (ll = thr->locked.first; ll; ll = ll->next)
+ raw_print_lock(" ", ll->id, ll->extra, ll->flags,
+ ll->file, ll->line, "\n");
}
}
@@ -486,55 +472,55 @@ uninitialized_lock(void)
}
static void
-lock_twice(char *prefix, erts_lc_locked_locks_t *l_lcks, erts_lc_lock_t *lck,
+lock_twice(char *prefix, lc_thread_t *thr, erts_lc_lock_t *lck,
erts_lock_options_t options)
{
erts_fprintf(stderr, "%s (%s)", prefix, rw_op_str(options));
print_lock(" ", lck, " lock which is already locked by thread!\n");
- print_curr_locks(l_lcks);
+ print_curr_locks(thr);
lc_abort();
}
static void
-unlock_op_mismatch(erts_lc_locked_locks_t *l_lcks, erts_lc_lock_t *lck,
+unlock_op_mismatch(lc_thread_t *thr, erts_lc_lock_t *lck,
erts_lock_options_t options)
{
erts_fprintf(stderr, "Unlocking (%s) ", rw_op_str(options));
print_lock("", lck, " lock which mismatch previous lock operation!\n");
- print_curr_locks(l_lcks);
+ print_curr_locks(thr);
lc_abort();
}
static void
-unlock_of_not_locked(erts_lc_locked_locks_t *l_lcks, erts_lc_lock_t *lck)
+unlock_of_not_locked(lc_thread_t *thr, erts_lc_lock_t *lck)
{
print_lock("Unlocking ", lck, " lock which is not locked by thread!\n");
- print_curr_locks(l_lcks);
+ print_curr_locks(thr);
lc_abort();
}
static void
-lock_order_violation(erts_lc_locked_locks_t *l_lcks, erts_lc_lock_t *lck)
+lock_order_violation(lc_thread_t *thr, erts_lc_lock_t *lck)
{
print_lock("Lock order violation occured when locking ", lck, "!\n");
- print_curr_locks(l_lcks);
+ print_curr_locks(thr);
print_lock_order();
lc_abort();
}
static void
-type_order_violation(char *op, erts_lc_locked_locks_t *l_lcks,
+type_order_violation(char *op, lc_thread_t *thr,
erts_lc_lock_t *lck)
{
erts_fprintf(stderr, "Lock type order violation occured when ");
print_lock(op, lck, "!\n");
- ASSERT(l_lcks);
- print_curr_locks(l_lcks);
+ ASSERT(thr);
+ print_curr_locks(thr);
lc_abort();
}
static void
-lock_mismatch(erts_lc_locked_locks_t *l_lcks, int exact,
+lock_mismatch(lc_thread_t *thr, int exact,
int failed_have, erts_lc_lock_t *have, int have_len,
int failed_have_not, erts_lc_lock_t *have_not, int have_not_len)
{
@@ -581,39 +567,39 @@ lock_mismatch(erts_lc_locked_locks_t *l_lcks, int exact,
print_lock2(" ", have_not[i].id, have_not[i].extra, 0, "\n");
}
}
- print_curr_locks(l_lcks);
+ print_curr_locks(thr);
lc_abort();
}
static void
-unlock_of_required_lock(erts_lc_locked_locks_t *l_lcks, erts_lc_lock_t *lck)
+unlock_of_required_lock(lc_thread_t *thr, erts_lc_lock_t *lck)
{
print_lock("Unlocking required ", lck, " lock!\n");
- print_curr_locks(l_lcks);
+ print_curr_locks(thr);
lc_abort();
}
static void
-unrequire_of_not_required_lock(erts_lc_locked_locks_t *l_lcks, erts_lc_lock_t *lck)
+unrequire_of_not_required_lock(lc_thread_t *thr, erts_lc_lock_t *lck)
{
print_lock("Unrequire on ", lck, " lock not required!\n");
- print_curr_locks(l_lcks);
+ print_curr_locks(thr);
lc_abort();
}
static void
-require_twice(erts_lc_locked_locks_t *l_lcks, erts_lc_lock_t *lck)
+require_twice(lc_thread_t *thr, erts_lc_lock_t *lck)
{
print_lock("Require on ", lck, " lock already required!\n");
- print_curr_locks(l_lcks);
+ print_curr_locks(thr);
lc_abort();
}
static void
-required_not_locked(erts_lc_locked_locks_t *l_lcks, erts_lc_lock_t *lck)
+required_not_locked(lc_thread_t *thr, erts_lc_lock_t *lck)
{
print_lock("Required ", lck, " lock not locked!\n");
- print_curr_locks(l_lcks);
+ print_curr_locks(thr);
lc_abort();
}
@@ -621,15 +607,15 @@ required_not_locked(erts_lc_locked_locks_t *l_lcks, erts_lc_lock_t *lck)
static void
thread_exit_handler(void)
{
- erts_lc_locked_locks_t *l_lcks = get_my_locked_locks();
- if (l_lcks) {
- if (l_lcks->locked.first) {
+ lc_thread_t *thr = get_my_locked_locks();
+ if (thr) {
+ if (thr->locked.first) {
erts_fprintf(stderr,
"Thread exiting while having locked locks!\n");
- print_curr_locks(l_lcks);
+ print_curr_locks(thr);
lc_abort();
}
- destroy_locked_locks(l_lcks);
+ destroy_locked_locks(thr);
/* erts_tsd_set(locks_key, NULL); */
}
}
@@ -647,24 +633,24 @@ lc_abort(void)
void
erts_lc_set_thread_name(char *thread_name)
{
- erts_lc_locked_locks_t *l_lcks = get_my_locked_locks();
- if (!l_lcks)
- l_lcks = create_locked_locks(thread_name);
+ lc_thread_t *thr = get_my_locked_locks();
+ if (!thr)
+ thr = create_thread_data(thread_name);
else {
- ASSERT(l_lcks->thread_name);
- free((void *) l_lcks->thread_name);
- l_lcks->thread_name = strdup(thread_name ? thread_name : "unknown");
- if (!l_lcks->thread_name)
+ ASSERT(thr->thread_name);
+ free((void *) thr->thread_name);
+ thr->thread_name = strdup(thread_name ? thread_name : "unknown");
+ if (!thr->thread_name)
ERTS_INTERNAL_ERROR("strdup failed");
}
- l_lcks->emu_thread = 1;
+ thr->emu_thread = 1;
}
int
erts_lc_is_emu_thr(void)
{
- erts_lc_locked_locks_t *l_lcks = get_my_locked_locks();
- return l_lcks->emu_thread;
+ lc_thread_t *thr = get_my_locked_locks();
+ return thr->emu_thread;
}
int
@@ -699,7 +685,7 @@ erts_lc_get_lock_order_id(char *name)
erts_fprintf(stderr, "Missing lock name\n");
else {
for (i = 0; i < ERTS_LOCK_ORDER_SIZE; i++)
- if (strcmp(erts_lock_order[i].name, name) == 0)
+ if (sys_strcmp(erts_lock_order[i].name, name) == 0)
return i;
erts_fprintf(stderr,
"Lock name '%s' missing in lock order "
@@ -710,7 +696,7 @@ erts_lc_get_lock_order_id(char *name)
return (Sint16) -1;
}
-static int compare_locked_by_id(erts_lc_locked_lock_t *locked_lock, erts_lc_lock_t *comparand)
+static int compare_locked_by_id(lc_locked_lock_t *locked_lock, erts_lc_lock_t *comparand)
{
if(locked_lock->id < comparand->id) {
return -1;
@@ -721,7 +707,7 @@ static int compare_locked_by_id(erts_lc_locked_lock_t *locked_lock, erts_lc_lock
return 0;
}
-static int compare_locked_by_id_extra(erts_lc_locked_lock_t *locked_lock, erts_lc_lock_t *comparand)
+static int compare_locked_by_id_extra(lc_locked_lock_t *locked_lock, erts_lc_lock_t *comparand)
{
int order = compare_locked_by_id(locked_lock, comparand);
@@ -736,18 +722,18 @@ static int compare_locked_by_id_extra(erts_lc_locked_lock_t *locked_lock, erts_l
return 0;
}
-typedef int (*locked_compare_func)(erts_lc_locked_lock_t *, erts_lc_lock_t *);
+typedef int (*locked_compare_func)(lc_locked_lock_t *, erts_lc_lock_t *);
/* Searches through a list of taken locks, bailing when it hits an entry whose
* order relative to the search template is the opposite of the one at the
* start of the search. (*closest_neighbor) is either set to the exact match,
* or the one closest to it in the sort order. */
static int search_locked_list(locked_compare_func compare,
- erts_lc_locked_lock_t *locked_locks,
+ lc_locked_lock_t *locked_locks,
erts_lc_lock_t *search_template,
- erts_lc_locked_lock_t **closest_neighbor)
+ lc_locked_lock_t **closest_neighbor)
{
- erts_lc_locked_lock_t *iterator = locked_locks;
+ lc_locked_lock_t *iterator = locked_locks;
(*closest_neighbor) = iterator;
@@ -783,9 +769,9 @@ static int search_locked_list(locked_compare_func compare,
/* Searches for a lock in the given list that matches search_template, and sets
* (*locked_locks) to the closest lock in the sort order. */
static int
-find_lock(erts_lc_locked_lock_t **locked_locks, erts_lc_lock_t *search_template)
+find_lock(lc_locked_lock_t **locked_locks, erts_lc_lock_t *search_template)
{
- erts_lc_locked_lock_t *closest_neighbor;
+ lc_locked_lock_t *closest_neighbor;
int found_lock;
found_lock = search_locked_list(compare_locked_by_id_extra,
@@ -814,9 +800,9 @@ find_lock(erts_lc_locked_lock_t **locked_locks, erts_lc_lock_t *search_template)
/* Searches for a lock in the given list by id, and sets (*locked_locks) to the
* closest lock in the sort order. */
static int
-find_id(erts_lc_locked_lock_t **locked_locks, Sint16 id)
+find_id(lc_locked_lock_t **locked_locks, Sint16 id)
{
- erts_lc_locked_lock_t *closest_neighbor;
+ lc_locked_lock_t *closest_neighbor;
erts_lc_lock_t search_template;
int found_lock;
@@ -835,34 +821,34 @@ find_id(erts_lc_locked_lock_t **locked_locks, Sint16 id)
void
erts_lc_have_locks(int *resv, erts_lc_lock_t *locks, int len)
{
- erts_lc_locked_locks_t *l_lcks = get_my_locked_locks();
+ lc_thread_t *thr = get_my_locked_locks();
int i;
- if (!l_lcks) {
+ if (!thr) {
for (i = 0; i < len; i++)
resv[i] = 0;
}
else {
- erts_lc_locked_lock_t *l_lck = l_lcks->locked.first;
+ lc_locked_lock_t *ll = thr->locked.first;
for (i = 0; i < len; i++)
- resv[i] = find_lock(&l_lck, &locks[i]);
+ resv[i] = find_lock(&ll, &locks[i]);
}
}
void
erts_lc_have_lock_ids(int *resv, int *ids, int len)
{
- erts_lc_locked_locks_t *l_lcks = get_my_locked_locks();
+ lc_thread_t *thr = get_my_locked_locks();
int i;
- if (!l_lcks) {
+ if (!thr) {
for (i = 0; i < len; i++)
resv[i] = 0;
}
else {
- erts_lc_locked_lock_t *l_lck = l_lcks->locked.first;
+ lc_locked_lock_t *ll = thr->locked.first;
for (i = 0; i < len; i++)
- resv[i] = find_id(&l_lck, ids[i]);
+ resv[i] = find_id(&ll, ids[i]);
}
}
@@ -871,27 +857,27 @@ erts_lc_check(erts_lc_lock_t *have, int have_len,
erts_lc_lock_t *have_not, int have_not_len)
{
int i;
- erts_lc_locked_locks_t *l_lcks = get_my_locked_locks();
- erts_lc_locked_lock_t *l_lck;
+ lc_thread_t *thr = get_my_locked_locks();
+ lc_locked_lock_t *ll;
if (have && have_len > 0) {
- if (!l_lcks)
+ if (!thr)
lock_mismatch(NULL, 0,
-1, have, have_len,
-1, have_not, have_not_len);
- l_lck = l_lcks->locked.first;
+ ll = thr->locked.first;
for (i = 0; i < have_len; i++) {
- if (!find_lock(&l_lck, &have[i]))
- lock_mismatch(l_lcks, 0,
+ if (!find_lock(&ll, &have[i]))
+ lock_mismatch(thr, 0,
i, have, have_len,
-1, have_not, have_not_len);
}
}
- if (have_not && have_not_len > 0 && l_lcks) {
- l_lck = l_lcks->locked.first;
+ if (have_not && have_not_len > 0 && thr) {
+ ll = thr->locked.first;
for (i = 0; i < have_not_len; i++) {
- if (find_lock(&l_lck, &have_not[i]))
- lock_mismatch(l_lcks, 0,
+ if (find_lock(&ll, &have_not[i]))
+ lock_mismatch(thr, 0,
-1, have, have_len,
i, have_not, have_not_len);
}
@@ -901,8 +887,8 @@ erts_lc_check(erts_lc_lock_t *have, int have_len,
void
erts_lc_check_exact(erts_lc_lock_t *have, int have_len)
{
- erts_lc_locked_locks_t *l_lcks = get_my_locked_locks();
- if (!l_lcks) {
+ lc_thread_t *thr = get_my_locked_locks();
+ if (!thr) {
if (have && have_len > 0)
lock_mismatch(NULL, 1,
-1, have, have_len,
@@ -910,17 +896,17 @@ erts_lc_check_exact(erts_lc_lock_t *have, int have_len)
}
else {
int i;
- erts_lc_locked_lock_t *l_lck = l_lcks->locked.first;
+ lc_locked_lock_t *ll = thr->locked.first;
for (i = 0; i < have_len; i++) {
- if (!find_lock(&l_lck, &have[i]))
- lock_mismatch(l_lcks, 1,
+ if (!find_lock(&ll, &have[i]))
+ lock_mismatch(thr, 1,
i, have, have_len,
-1, NULL, 0);
}
- for (i = 0, l_lck = l_lcks->locked.first; l_lck; l_lck = l_lck->next)
+ for (i = 0, ll = thr->locked.first; ll; ll = ll->next)
i++;
if (i != have_len)
- lock_mismatch(l_lcks, 1,
+ lock_mismatch(thr, 1,
-1, have, have_len,
-1, NULL, 0);
}
@@ -929,16 +915,16 @@ erts_lc_check_exact(erts_lc_lock_t *have, int have_len)
void
erts_lc_check_no_locked_of_type(erts_lock_flags_t type)
{
- erts_lc_locked_locks_t *l_lcks = get_my_locked_locks();
- if (l_lcks) {
- erts_lc_locked_lock_t *l_lck = l_lcks->locked.first;
- for (l_lck = l_lcks->locked.first; l_lck; l_lck = l_lck->next) {
- if ((l_lck->flags & ERTS_LOCK_FLAGS_MASK_TYPE) == type) {
+ lc_thread_t *thr = get_my_locked_locks();
+ if (thr) {
+ lc_locked_lock_t *ll = thr->locked.first;
+ for (ll = thr->locked.first; ll; ll = ll->next) {
+ if ((ll->flags & ERTS_LOCK_FLAGS_MASK_TYPE) == type) {
erts_fprintf(stderr,
"Locked lock of type %s found which isn't "
"allowed here!\n",
- erts_lock_flags_get_type_name(l_lck->flags));
- print_curr_locks(l_lcks);
+ erts_lock_flags_get_type_name(ll->flags));
+ print_curr_locks(thr);
lc_abort();
}
}
@@ -956,7 +942,7 @@ erts_lc_trylock_force_busy_flg(erts_lc_lock_t *lck, erts_lock_options_t options)
* This in order to make sure that caller can handle
* the situation without causing a lock order violation.
*/
- erts_lc_locked_locks_t *l_lcks;
+ lc_thread_t *thr;
if (lck->inited != ERTS_LC_INITITALIZED)
uninitialized_lock();
@@ -964,25 +950,25 @@ erts_lc_trylock_force_busy_flg(erts_lc_lock_t *lck, erts_lock_options_t options)
if (lck->id < 0)
return 0;
- l_lcks = get_my_locked_locks();
+ thr = get_my_locked_locks();
- if (!l_lcks || !l_lcks->locked.first) {
- ASSERT(!l_lcks || !l_lcks->locked.last);
+ if (!thr || !thr->locked.first) {
+ ASSERT(!thr || !thr->locked.last);
return 0;
}
else {
- erts_lc_locked_lock_t *tl_lck;
+ lc_locked_lock_t *tl_lck;
- ASSERT(l_lcks->locked.last);
+ ASSERT(thr->locked.last);
#if 0 /* Ok when trylocking I guess... */
- if (LOCK_IS_TYPE_ORDER_VIOLATION(lck->flags, l_lcks->locked.last->flags))
- type_order_violation("trylocking ", l_lcks, lck);
+ if (LOCK_IS_TYPE_ORDER_VIOLATION(lck->flags, thr->locked.last->flags))
+ type_order_violation("trylocking ", thr, lck);
#endif
- if (l_lcks->locked.last->id < lck->id
- || (l_lcks->locked.last->id == lck->id
- && l_lcks->locked.last->extra < lck->extra))
+ if (thr->locked.last->id < lck->id
+ || (thr->locked.last->id == lck->id
+ && thr->locked.last->extra < lck->extra))
return 0;
/*
@@ -991,11 +977,11 @@ erts_lc_trylock_force_busy_flg(erts_lc_lock_t *lck, erts_lock_options_t options)
/* Check that we are not trying to lock this lock twice */
- for (tl_lck = l_lcks->locked.last; tl_lck; tl_lck = tl_lck->prev) {
+ for (tl_lck = thr->locked.last; tl_lck; tl_lck = tl_lck->prev) {
if (tl_lck->id < lck->id
|| (tl_lck->id == lck->id && tl_lck->extra <= lck->extra)) {
if (tl_lck->id == lck->id && tl_lck->extra == lck->extra)
- lock_twice("Trylocking", l_lcks, lck, options);
+ lock_twice("Trylocking", thr, lck, options);
break;
}
}
@@ -1020,8 +1006,8 @@ erts_lc_trylock_force_busy_flg(erts_lc_lock_t *lck, erts_lock_options_t options)
void erts_lc_trylock_flg_x(int locked, erts_lc_lock_t *lck, erts_lock_options_t options,
char *file, unsigned int line)
{
- erts_lc_locked_locks_t *l_lcks;
- erts_lc_locked_lock_t *l_lck;
+ lc_thread_t *thr;
+ lc_locked_lock_t *ll;
if (lck->inited != ERTS_LC_INITITALIZED)
uninitialized_lock();
@@ -1029,43 +1015,43 @@ void erts_lc_trylock_flg_x(int locked, erts_lc_lock_t *lck, erts_lock_options_t
if (lck->id < 0)
return;
- l_lcks = make_my_locked_locks();
- l_lck = locked ? new_locked_lock(lck, options, file, line) : NULL;
+ thr = make_my_locked_locks();
+ ll = locked ? new_locked_lock(thr, lck, options, file, line) : NULL;
- if (!l_lcks->locked.last) {
- ASSERT(!l_lcks->locked.first);
+ if (!thr->locked.last) {
+ ASSERT(!thr->locked.first);
if (locked)
- l_lcks->locked.first = l_lcks->locked.last = l_lck;
+ thr->locked.first = thr->locked.last = ll;
}
else {
- erts_lc_locked_lock_t *tl_lck;
+ lc_locked_lock_t *tl_lck;
#if 0 /* Ok when trylocking I guess... */
- if (LOCK_IS_TYPE_ORDER_VIOLATION(lck->flags, l_lcks->locked.last->flags))
- type_order_violation("trylocking ", l_lcks, lck);
+ if (LOCK_IS_TYPE_ORDER_VIOLATION(lck->flags, thr->locked.last->flags))
+ type_order_violation("trylocking ", thr, lck);
#endif
- for (tl_lck = l_lcks->locked.last; tl_lck; tl_lck = tl_lck->prev) {
+ for (tl_lck = thr->locked.last; tl_lck; tl_lck = tl_lck->prev) {
if (tl_lck->id < lck->id
|| (tl_lck->id == lck->id && tl_lck->extra <= lck->extra)) {
if (tl_lck->id == lck->id && tl_lck->extra == lck->extra)
- lock_twice("Trylocking", l_lcks, lck, options);
+ lock_twice("Trylocking", thr, lck, options);
if (locked) {
- l_lck->next = tl_lck->next;
- l_lck->prev = tl_lck;
+ ll->next = tl_lck->next;
+ ll->prev = tl_lck;
if (tl_lck->next)
- tl_lck->next->prev = l_lck;
+ tl_lck->next->prev = ll;
else
- l_lcks->locked.last = l_lck;
- tl_lck->next = l_lck;
+ thr->locked.last = ll;
+ tl_lck->next = ll;
}
return;
}
}
if (locked) {
- l_lck->next = l_lcks->locked.first;
- l_lcks->locked.first->prev = l_lck;
- l_lcks->locked.first = l_lck;
+ ll->next = thr->locked.first;
+ thr->locked.first->prev = ll;
+ thr->locked.first = ll;
}
}
@@ -1074,83 +1060,83 @@ void erts_lc_trylock_flg_x(int locked, erts_lc_lock_t *lck, erts_lock_options_t
void erts_lc_require_lock_flg(erts_lc_lock_t *lck, erts_lock_options_t options,
char *file, unsigned int line)
{
- erts_lc_locked_locks_t *l_lcks = make_my_locked_locks();
- erts_lc_locked_lock_t *l_lck = l_lcks->locked.first;
- if (!find_lock(&l_lck, lck))
- required_not_locked(l_lcks, lck);
- l_lck = new_locked_lock(lck, options, file, line);
- if (!l_lcks->required.last) {
- ASSERT(!l_lcks->required.first);
- l_lck->next = l_lck->prev = NULL;
- l_lcks->required.first = l_lcks->required.last = l_lck;
+ lc_thread_t *thr = make_my_locked_locks();
+ lc_locked_lock_t *ll = thr->locked.first;
+ if (!find_lock(&ll, lck))
+ required_not_locked(thr, lck);
+ ll = new_locked_lock(thr, lck, options, file, line);
+ if (!thr->required.last) {
+ ASSERT(!thr->required.first);
+ ll->next = ll->prev = NULL;
+ thr->required.first = thr->required.last = ll;
}
else {
- erts_lc_locked_lock_t *l_lck2;
- ASSERT(l_lcks->required.first);
- for (l_lck2 = l_lcks->required.last;
+ lc_locked_lock_t *l_lck2;
+ ASSERT(thr->required.first);
+ for (l_lck2 = thr->required.last;
l_lck2;
l_lck2 = l_lck2->prev) {
if (l_lck2->id < lck->id
|| (l_lck2->id == lck->id && l_lck2->extra < lck->extra))
break;
else if (l_lck2->id == lck->id && l_lck2->extra == lck->extra)
- require_twice(l_lcks, lck);
+ require_twice(thr, lck);
}
if (!l_lck2) {
- l_lck->next = l_lcks->required.first;
- l_lck->prev = NULL;
- l_lcks->required.first->prev = l_lck;
- l_lcks->required.first = l_lck;
+ ll->next = thr->required.first;
+ ll->prev = NULL;
+ thr->required.first->prev = ll;
+ thr->required.first = ll;
}
else {
- l_lck->next = l_lck2->next;
- if (l_lck->next) {
- ASSERT(l_lcks->required.last != l_lck2);
- l_lck->next->prev = l_lck;
+ ll->next = l_lck2->next;
+ if (ll->next) {
+ ASSERT(thr->required.last != l_lck2);
+ ll->next->prev = ll;
}
else {
- ASSERT(l_lcks->required.last == l_lck2);
- l_lcks->required.last = l_lck;
+ ASSERT(thr->required.last == l_lck2);
+ thr->required.last = ll;
}
- l_lck->prev = l_lck2;
- l_lck2->next = l_lck;
+ ll->prev = l_lck2;
+ l_lck2->next = ll;
}
}
}
void erts_lc_unrequire_lock_flg(erts_lc_lock_t *lck, erts_lock_options_t options)
{
- erts_lc_locked_locks_t *l_lcks = make_my_locked_locks();
- erts_lc_locked_lock_t *l_lck = l_lcks->locked.first;
- if (!find_lock(&l_lck, lck))
- required_not_locked(l_lcks, lck);
- l_lck = l_lcks->required.first;
- if (!find_lock(&l_lck, lck))
- unrequire_of_not_required_lock(l_lcks, lck);
- if (l_lck->prev) {
- ASSERT(l_lcks->required.first != l_lck);
- l_lck->prev->next = l_lck->next;
+ lc_thread_t *thr = make_my_locked_locks();
+ lc_locked_lock_t *ll = thr->locked.first;
+ if (!find_lock(&ll, lck))
+ required_not_locked(thr, lck);
+ ll = thr->required.first;
+ if (!find_lock(&ll, lck))
+ unrequire_of_not_required_lock(thr, lck);
+ if (ll->prev) {
+ ASSERT(thr->required.first != ll);
+ ll->prev->next = ll->next;
}
else {
- ASSERT(l_lcks->required.first == l_lck);
- l_lcks->required.first = l_lck->next;
+ ASSERT(thr->required.first == ll);
+ thr->required.first = ll->next;
}
- if (l_lck->next) {
- ASSERT(l_lcks->required.last != l_lck);
- l_lck->next->prev = l_lck->prev;
+ if (ll->next) {
+ ASSERT(thr->required.last != ll);
+ ll->next->prev = ll->prev;
}
else {
- ASSERT(l_lcks->required.last == l_lck);
- l_lcks->required.last = l_lck->prev;
+ ASSERT(thr->required.last == ll);
+ thr->required.last = ll->prev;
}
- lc_free((void *) l_lck);
+ lc_free(thr, ll);
}
void erts_lc_lock_flg_x(erts_lc_lock_t *lck, erts_lock_options_t options,
char *file, unsigned int line)
{
- erts_lc_locked_locks_t *l_lcks;
- erts_lc_locked_lock_t *l_lck;
+ lc_thread_t *thr;
+ lc_locked_lock_t *new_ll;
if (lck->inited != ERTS_LC_INITITALIZED)
uninitialized_lock();
@@ -1158,32 +1144,45 @@ void erts_lc_lock_flg_x(erts_lc_lock_t *lck, erts_lock_options_t options,
if (lck->id < 0)
return;
- l_lcks = make_my_locked_locks();
- l_lck = new_locked_lock(lck, options, file, line);
+ thr = make_my_locked_locks();
+ new_ll = new_locked_lock(thr, lck, options, file, line);
- if (!l_lcks->locked.last) {
- ASSERT(!l_lcks->locked.first);
- l_lcks->locked.last = l_lcks->locked.first = l_lck;
+ if (!thr->locked.last) {
+ ASSERT(!thr->locked.first);
+ thr->locked.last = thr->locked.first = new_ll;
+ ASSERT(0 < lck->id && lck->id < ERTS_LOCK_ORDER_SIZE);
+ thr->matrix.m[lck->id][0] = 1;
}
- else if (l_lcks->locked.last->id < lck->id
- || (l_lcks->locked.last->id == lck->id
- && l_lcks->locked.last->extra < lck->extra)) {
- if (LOCK_IS_TYPE_ORDER_VIOLATION(lck->flags, l_lcks->locked.last->flags))
- type_order_violation("locking ", l_lcks, lck);
- l_lck->prev = l_lcks->locked.last;
- l_lcks->locked.last->next = l_lck;
- l_lcks->locked.last = l_lck;
+ else if (thr->locked.last->id < lck->id
+ || (thr->locked.last->id == lck->id
+ && thr->locked.last->extra < lck->extra)) {
+ lc_locked_lock_t* ll;
+ if (LOCK_IS_TYPE_ORDER_VIOLATION(lck->flags, thr->locked.last->flags)) {
+ type_order_violation("locking ", thr, lck);
+ }
+
+ ASSERT(0 < lck->id && lck->id < ERTS_LOCK_ORDER_SIZE);
+ ll = thr->locked.last;
+ thr->matrix.m[lck->id][ll->id] |= 1;
+ for (ll = ll->prev; ll; ll = ll->prev) {
+ ASSERT(0 < ll->id && ll->id < ERTS_LOCK_ORDER_SIZE);
+ thr->matrix.m[lck->id][ll->id] |= 2;
+ }
+
+ new_ll->prev = thr->locked.last;
+ thr->locked.last->next = new_ll;
+ thr->locked.last = new_ll;
}
- else if (l_lcks->locked.last->id == lck->id && l_lcks->locked.last->extra == lck->extra)
- lock_twice("Locking", l_lcks, lck, options);
+ else if (thr->locked.last->id == lck->id && thr->locked.last->extra == lck->extra)
+ lock_twice("Locking", thr, lck, options);
else
- lock_order_violation(l_lcks, lck);
+ lock_order_violation(thr, lck);
}
void erts_lc_unlock_flg(erts_lc_lock_t *lck, erts_lock_options_t options)
{
- erts_lc_locked_locks_t *l_lcks;
- erts_lc_locked_lock_t *l_lck;
+ lc_thread_t *thr;
+ lc_locked_lock_t *ll;
if (lck->inited != ERTS_LC_INITITALIZED)
uninitialized_lock();
@@ -1191,38 +1190,38 @@ void erts_lc_unlock_flg(erts_lc_lock_t *lck, erts_lock_options_t options)
if (lck->id < 0)
return;
- l_lcks = get_my_locked_locks();
+ thr = get_my_locked_locks();
- if (l_lcks) {
- l_lck = l_lcks->required.first;
- if (find_lock(&l_lck, lck))
- unlock_of_required_lock(l_lcks, lck);
+ if (thr) {
+ ll = thr->required.first;
+ if (find_lock(&ll, lck))
+ unlock_of_required_lock(thr, lck);
}
- for (l_lck = l_lcks ? l_lcks->locked.last : NULL; l_lck; l_lck = l_lck->prev) {
- if (l_lck->id == lck->id && l_lck->extra == lck->extra) {
- if ((l_lck->taken_options & ERTS_LOCK_OPTIONS_RDWR) != options)
- unlock_op_mismatch(l_lcks, lck, options);
- if (l_lck->prev)
- l_lck->prev->next = l_lck->next;
+ for (ll = thr ? thr->locked.last : NULL; ll; ll = ll->prev) {
+ if (ll->id == lck->id && ll->extra == lck->extra) {
+ if ((ll->taken_options & ERTS_LOCK_OPTIONS_RDWR) != options)
+ unlock_op_mismatch(thr, lck, options);
+ if (ll->prev)
+ ll->prev->next = ll->next;
else
- l_lcks->locked.first = l_lck->next;
- if (l_lck->next)
- l_lck->next->prev = l_lck->prev;
+ thr->locked.first = ll->next;
+ if (ll->next)
+ ll->next->prev = ll->prev;
else
- l_lcks->locked.last = l_lck->prev;
- lc_free((void *) l_lck);
+ thr->locked.last = ll->prev;
+ lc_free(thr, ll);
return;
}
}
- unlock_of_not_locked(l_lcks, lck);
+ unlock_of_not_locked(thr, lck);
}
void erts_lc_might_unlock_flg(erts_lc_lock_t *lck, erts_lock_options_t options)
{
- erts_lc_locked_locks_t *l_lcks;
- erts_lc_locked_lock_t *l_lck;
+ lc_thread_t *thr;
+ lc_locked_lock_t *ll;
if (lck->inited != ERTS_LC_INITITALIZED)
uninitialized_lock();
@@ -1230,17 +1229,17 @@ void erts_lc_might_unlock_flg(erts_lc_lock_t *lck, erts_lock_options_t options)
if (lck->id < 0)
return;
- l_lcks = get_my_locked_locks();
+ thr = get_my_locked_locks();
- if (l_lcks) {
- l_lck = l_lcks->required.first;
- if (find_lock(&l_lck, lck))
- unlock_of_required_lock(l_lcks, lck);
+ if (thr) {
+ ll = thr->required.first;
+ if (find_lock(&ll, lck))
+ unlock_of_required_lock(thr, lck);
}
- l_lck = l_lcks->locked.first;
- if (!find_lock(&l_lck, lck))
- unlock_of_not_locked(l_lcks, lck);
+ ll = thr->locked.first;
+ if (!find_lock(&ll, lck))
+ unlock_of_not_locked(thr, lck);
}
int
@@ -1321,26 +1320,7 @@ erts_lc_destroy_lock(erts_lc_lock_t *lck)
void
erts_lc_init(void)
{
-#ifdef ERTS_LC_STATIC_ALLOC
- int i;
- static erts_lc_free_block_t fbs[ERTS_LC_FB_CHUNK_SIZE];
- for (i = 0; i < ERTS_LC_FB_CHUNK_SIZE - 1; i++) {
-#ifdef DEBUG
- memset((void *) &fbs[i], 0xdf, sizeof(erts_lc_free_block_t));
-#endif
- fbs[i].next = &fbs[i+1];
- }
-#ifdef DEBUG
- memset((void *) &fbs[ERTS_LC_FB_CHUNK_SIZE-1],
- 0xdf, sizeof(erts_lc_free_block_t));
-#endif
- fbs[ERTS_LC_FB_CHUNK_SIZE-1].next = NULL;
- free_blocks = &fbs[0];
-#else /* #ifdef ERTS_LC_STATIC_ALLOC */
- free_blocks = NULL;
-#endif /* #ifdef ERTS_LC_STATIC_ALLOC */
-
- if (ethr_spinlock_init(&free_blocks_lock) != 0)
+ if (ethr_spinlock_init(&lc_threads_lock) != 0)
ERTS_INTERNAL_ERROR("spinlock_init failed");
erts_tsd_key_create(&locks_key,"erts_lock_check_key");
@@ -1362,5 +1342,76 @@ erts_lc_pll(void)
print_curr_locks(get_my_locked_locks());
}
+static void collect_matrix(lc_matrix_t* matrix)
+{
+ int i, j;
+ for (i = 1; i < ERTS_LOCK_ORDER_SIZE; i++) {
+ for (j = 0; j <= i; j++) {
+ tot_lc_matrix.m[i][j] |= matrix->m[i][j];
+ }
+#ifdef DEBUG
+ for ( ; j < ERTS_LOCK_ORDER_SIZE; j++) {
+ ASSERT(matrix->m[i][j] == 0);
+ }
+#endif
+ }
+}
+
+Eterm
+erts_lc_dump_graph(void)
+{
+ const char* basename = "lc_graph.";
+ char filename[40];
+ lc_matrix_t* tot = &tot_lc_matrix;
+ lc_thread_t* thr;
+ int i, j, name_max = 0;
+ FILE* ff;
+
+ lc_lock_threads();
+ for (thr = lc_threads; thr; thr = thr->next) {
+ collect_matrix(&thr->matrix);
+ }
+ lc_unlock_threads();
+
+ sys_strcpy(filename, basename);
+ sys_get_pid(filename + strlen(basename),
+ sizeof(filename) - strlen(basename));
+ ff = fopen(filename, "w");
+ if (!ff)
+ return am_error;
+
+ for (i = 1; i < ERTS_LOCK_ORDER_SIZE; i++) {
+ int len = strlen(erts_lock_order[i].name);
+ if (name_max < len)
+ name_max = len;
+ }
+ fputs("%This file was generated by erts_debug:lc_graph()\n\n", ff);
+ fputs("%{ThisLockName, ThisLockId, LockedDirectlyBeforeThis, LockedIndirectlyBeforeThis}\n", ff);
+ fprintf(ff, "[{%*s, %2d}", name_max, "\"NO LOCK\"", 0);
+ for (i = 1; i < ERTS_LOCK_ORDER_SIZE; i++) {
+ char* delim = "";
+ fprintf(ff, ",\n {%*s, %2d, [", name_max, erts_lock_order[i].name, i);
+ for (j = 0; j < ERTS_LOCK_ORDER_SIZE; j++) {
+ if (tot->m[i][j] & 1) {
+ fprintf(ff, "%s%d", delim, j);
+ delim = ",";
+ }
+ }
+ fprintf(ff, "], [");
+ delim = "";
+ for (j = 0; j < ERTS_LOCK_ORDER_SIZE; j++) {
+ if (tot->m[i][j] == 2) {
+ fprintf(ff, "%s%d", delim, j);
+ delim = ",";
+ }
+ }
+ fputs("]}", ff);
+ }
+ fputs("].", ff);
+ fclose(ff);
+ erts_fprintf(stderr, "Created file '%s' in current working directory\n",
+ filename);
+ return am_ok;
+}
#endif /* #ifdef ERTS_ENABLE_LOCK_CHECK */
diff --git a/erts/emulator/beam/erl_lock_check.h b/erts/emulator/beam/erl_lock_check.h
index 5c2c38e8f2..138bc810bd 100644
--- a/erts/emulator/beam/erl_lock_check.h
+++ b/erts/emulator/beam/erl_lock_check.h
@@ -94,6 +94,8 @@ void erts_lc_unrequire_lock(erts_lc_lock_t *lck);
int erts_lc_is_emu_thr(void);
+Eterm erts_lc_dump_graph(void);
+
#define ERTS_LC_ASSERT(A) \
((void) (((A) || ERTS_SOMEONE_IS_CRASH_DUMPING) ? 1 : erts_lc_assert_failed(__FILE__, __LINE__, #A)))
#else /* #ifdef ERTS_ENABLE_LOCK_CHECK */
diff --git a/erts/emulator/beam/erl_map.c b/erts/emulator/beam/erl_map.c
index f0c54e05f7..4ec6960997 100644
--- a/erts/emulator/beam/erl_map.c
+++ b/erts/emulator/beam/erl_map.c
@@ -91,7 +91,6 @@ static BIF_RETTYPE hashmap_merge(Process *p, Eterm nodeA, Eterm nodeB, int swap_
static Export hashmap_merge_trap_export;
static BIF_RETTYPE maps_merge_trap_1(BIF_ALIST_1);
static Uint hashmap_subtree_size(Eterm node);
-static Eterm hashmap_to_list(Process *p, Eterm map, Sint n);
static Eterm hashmap_keys(Process *p, Eterm map);
static Eterm hashmap_values(Process *p, Eterm map);
static Eterm hashmap_delete(Process *p, Uint32 hx, Eterm key, Eterm node, Eterm *value);
@@ -139,80 +138,6 @@ BIF_RETTYPE map_size_1(BIF_ALIST_1) {
BIF_ERROR(BIF_P, BADMAP);
}
-/* maps:to_list/1 */
-
-BIF_RETTYPE maps_to_list_1(BIF_ALIST_1) {
- if (is_flatmap(BIF_ARG_1)) {
- Uint n;
- Eterm* hp;
- Eterm *ks,*vs, res, tup;
- flatmap_t *mp = (flatmap_t*)flatmap_val(BIF_ARG_1);
-
- ks = flatmap_get_keys(mp);
- vs = flatmap_get_values(mp);
- n = flatmap_get_size(mp);
- hp = HAlloc(BIF_P, (2 + 3) * n);
- res = NIL;
-
- while(n--) {
- tup = TUPLE2(hp, ks[n], vs[n]); hp += 3;
- res = CONS(hp, tup, res); hp += 2;
- }
-
- BIF_RET(res);
- } else if (is_hashmap(BIF_ARG_1)) {
- return hashmap_to_list(BIF_P, BIF_ARG_1, -1);
- }
-
- BIF_P->fvalue = BIF_ARG_1;
- BIF_ERROR(BIF_P, BADMAP);
-}
-
-/* erts_internal:maps_to_list/2
- *
- * This function should be removed once iterators are in place.
- * Never document it.
- * Never encourage its usage.
- *
- * A negative value in ARG 2 means the entire map.
- */
-
-BIF_RETTYPE erts_internal_maps_to_list_2(BIF_ALIST_2) {
- Sint m;
- if (term_to_Sint(BIF_ARG_2, &m)) {
- if (is_flatmap(BIF_ARG_1)) {
- Uint n;
- Eterm* hp;
- Eterm *ks,*vs, res, tup;
- flatmap_t *mp = (flatmap_t*)flatmap_val(BIF_ARG_1);
-
- ks = flatmap_get_keys(mp);
- vs = flatmap_get_values(mp);
- n = flatmap_get_size(mp);
-
- if (m >= 0) {
- n = m < n ? m : n;
- }
-
- hp = HAlloc(BIF_P, (2 + 3) * n);
- res = NIL;
-
- while(n--) {
- tup = TUPLE2(hp, ks[n], vs[n]); hp += 3;
- res = CONS(hp, tup, res); hp += 2;
- }
-
- BIF_RET(res);
- } else if (is_hashmap(BIF_ARG_1)) {
- return hashmap_to_list(BIF_P, BIF_ARG_1, m);
- }
- BIF_P->fvalue = BIF_ARG_1;
- BIF_ERROR(BIF_P, BADMAP);
- }
- BIF_ERROR(BIF_P, BADARG);
-}
-
-
/* maps:find/2
* return value if key *matches* a key in the map
*/
@@ -565,7 +490,9 @@ Eterm erts_map_from_ks_and_vs(ErtsHeapFactory *factory, Eterm *ks0, Eterm *vs0,
sys_memcpy(ks, ks0, n * sizeof(Eterm));
sys_memcpy(vs, vs0, n * sizeof(Eterm));
- erts_validate_and_sort_flatmap(mp);
+ if (!erts_validate_and_sort_flatmap(mp)) {
+ return THE_NON_VALUE;
+ }
return make_flatmap(mp);
} else {
@@ -1962,45 +1889,31 @@ BIF_RETTYPE maps_values_1(BIF_ALIST_1) {
BIF_ERROR(BIF_P, BADMAP);
}
-static Eterm hashmap_to_list(Process *p, Eterm node, Sint m) {
- DECLARE_WSTACK(stack);
- Eterm *hp, *kv;
- Eterm tup, res = NIL;
- Uint n = hashmap_size(node);
-
- if (m >= 0) {
- n = m < n ? m : n;
- }
-
- hp = HAlloc(p, n * (2 + 3));
- hashmap_iterator_init(&stack, node, 0);
- while (n--) {
- kv = hashmap_iterator_next(&stack);
- ASSERT(kv != NULL);
- tup = TUPLE2(hp, CAR(kv), CDR(kv));
- hp += 3;
- res = CONS(hp, tup, res);
- hp += 2;
- }
- DESTROY_WSTACK(stack);
- return res;
-}
-
-void hashmap_iterator_init(ErtsWStack* s, Eterm node, int reverse) {
- Eterm hdr = *hashmap_val(node);
+static ERTS_INLINE
+Uint hashmap_node_size(Eterm hdr, Eterm **nodep)
+{
Uint sz;
switch(hdr & _HEADER_MAP_SUBTAG_MASK) {
case HAMT_SUBTAG_HEAD_ARRAY:
sz = 16;
+ if (nodep) ++*nodep;
break;
case HAMT_SUBTAG_HEAD_BITMAP:
+ if (nodep) ++*nodep;
case HAMT_SUBTAG_NODE_BITMAP:
sz = hashmap_bitcount(MAP_HEADER_VAL(hdr));
+ ASSERT(sz < 17);
break;
default:
erts_exit(ERTS_ABORT_EXIT, "bad header");
}
+ return sz;
+}
+
+void hashmap_iterator_init(ErtsWStack* s, Eterm node, int reverse) {
+ Eterm hdr = *hashmap_val(node);
+ Uint sz = hashmap_node_size(hdr, NULL);
WSTACK_PUSH3((*s), (UWord)THE_NON_VALUE, /* end marker */
(UWord)(!reverse ? 0 : sz+1),
@@ -2024,20 +1937,7 @@ Eterm* hashmap_iterator_next(ErtsWStack* s) {
ptr = boxed_val(node);
hdr = *ptr;
ASSERT(is_header(hdr));
- switch(hdr & _HEADER_MAP_SUBTAG_MASK) {
- case HAMT_SUBTAG_HEAD_ARRAY:
- ptr++;
- sz = 16;
- break;
- case HAMT_SUBTAG_HEAD_BITMAP:
- ptr++;
- case HAMT_SUBTAG_NODE_BITMAP:
- sz = hashmap_bitcount(MAP_HEADER_VAL(hdr));
- ASSERT(sz < 17);
- break;
- default:
- erts_exit(ERTS_ABORT_EXIT, "bad header");
- }
+ sz = hashmap_node_size(hdr, &ptr);
idx++;
@@ -2074,20 +1974,7 @@ Eterm* hashmap_iterator_prev(ErtsWStack* s) {
ptr = boxed_val(node);
hdr = *ptr;
ASSERT(is_header(hdr));
- switch(hdr & _HEADER_MAP_SUBTAG_MASK) {
- case HAMT_SUBTAG_HEAD_ARRAY:
- ptr++;
- sz = 16;
- break;
- case HAMT_SUBTAG_HEAD_BITMAP:
- ptr++;
- case HAMT_SUBTAG_NODE_BITMAP:
- sz = hashmap_bitcount(MAP_HEADER_VAL(hdr));
- ASSERT(sz < 17);
- break;
- default:
- erts_exit(ERTS_ERROR_EXIT, "bad header");
- }
+ sz = hashmap_node_size(hdr, &ptr);
if (idx > sz)
idx = sz;
@@ -3061,6 +2948,363 @@ static Eterm hashmap_bld_tuple_uint(Uint **hpp, Uint *szp, Uint n, Uint nums[])
}
+/**
+ * In hashmap the Path is a bit pattern that describes
+ * which slot we should traverse in each hashmap node.
+ * Since each hashmap node can only be up to 16 elements
+ * large we use 4 bits per level in the path.
+ *
+ * So a Path with value 0x110 will first get the 0:th
+ * slot in the head node, and then the 1:st slot in the
+ * resulting node and then finally the 1:st slot in the
+ * node beneath. If that slot is not a leaf, then the path
+ * continues down the 0:th slot until it finds a leaf.
+ *
+ * Once the leaf has been found, the return value is created
+ * by traversing the tree using the the stack that was built
+ * when searching for the first leaf to return.
+ *
+ * The index can become a bignum, which complicates the code
+ * a bit. However it should be very rare that this happens
+ * even on a 32bit system as you would need a tree of depth
+ * 7 or more.
+ *
+ * If the number of elements remaining in the map is greater
+ * than how many we want to return, we build a new Path, using
+ * the stack, that points to the next leaf.
+ *
+ * The third argument to this function controls how the data
+ * is returned.
+ *
+ * iterator: The key-value associations are to be used by
+ * maps:iterator. The return has this format:
+ * {K1,V1,{K2,V2,none | [Path | Map]}}
+ * this makes the maps:next function very simple
+ * and performant.
+ *
+ * list(): The key-value associations are to be used by
+ * maps:to_list. The return has this format:
+ * [Path, Map | [{K1,V1},{K2,V2} | BIF_ARG_3]]
+ * or if no more associations remain
+ * [{K1,V1},{K2,V2} | BIF_ARG_3]
+ */
+
+#define PATH_ELEM_SIZE 4
+#define PATH_ELEM_MASK 0xf
+#define PATH_ELEM(PATH) ((PATH) & PATH_ELEM_MASK)
+#define PATH_ELEMS_PER_DIGIT (sizeof(ErtsDigit) * 8 / PATH_ELEM_SIZE)
+
+BIF_RETTYPE erts_internal_map_next_3(BIF_ALIST_3) {
+
+ Eterm path, map;
+ enum { iterator, list } type;
+
+ path = BIF_ARG_1;
+ map = BIF_ARG_2;
+
+ if (!is_map(map))
+ BIF_ERROR(BIF_P, BADARG);
+
+ if (BIF_ARG_3 == am_iterator) {
+ type = iterator;
+ } else if (is_nil(BIF_ARG_3) || is_list(BIF_ARG_3)) {
+ type = list;
+ } else {
+ BIF_ERROR(BIF_P, BADARG);
+ }
+
+ if (is_flatmap(map)) {
+ Uint n;
+ Eterm *ks,*vs, res, *hp;
+ flatmap_t *mp = (flatmap_t*)flatmap_val(map);
+
+ ks = flatmap_get_keys(mp);
+ vs = flatmap_get_values(mp);
+ n = flatmap_get_size(mp);
+
+ if (!is_small(BIF_ARG_1) || n < unsigned_val(BIF_ARG_1))
+ BIF_ERROR(BIF_P, BADARG);
+
+ if (type == iterator) {
+ hp = HAlloc(BIF_P, 4 * n);
+ res = am_none;
+
+ while(n--) {
+ res = TUPLE3(hp, ks[n], vs[n], res); hp += 4;
+ }
+ } else {
+ hp = HAlloc(BIF_P, (2 + 3) * n);
+ res = BIF_ARG_3;
+
+ while(n--) {
+ Eterm tup = TUPLE2(hp, ks[n], vs[n]); hp += 3;
+ res = CONS(hp, tup, res); hp += 2;
+ }
+ }
+
+ BIF_RET(res);
+ } else {
+ Uint curr_path;
+ Uint path_length = 0;
+ Uint *path_rest = NULL;
+ int i, elems, orig_elems;
+ Eterm node = map, res, *path_ptr = NULL, *hp;
+
+ /* A stack WSTACK is used when traversing the hashmap.
+ * It contains: node, idx, sz, ptr
+ *
+ * `node` is not really needed, but it is very nice to
+ * have when debugging.
+ *
+ * `idx` always points to the next un-explored entry in
+ * a node. If there are no more un-explored entries,
+ * `idx` is equal to `sz`.
+ *
+ * `sz` is the number of elements in the node.
+ *
+ * `ptr` is a pointer to where the elements of the node begins.
+ */
+ DECLARE_WSTACK(stack);
+
+ ASSERT(is_hashmap(node));
+
+/* How many elements we return in one call depends on the number of reductions
+ * that the process has left to run. In debug we return fewer elements to test
+ * the Path implementation better.
+ *
+ * Also, when the path is 0 (i.e. for the first call) we limit the number of
+ * elements to MAP_SMALL_MAP_LIMIT in order to not use a huge amount of heap
+ * when only the first X associations in the hashmap was needed.
+ */
+#if defined(DEBUG)
+#define FCALLS_ELEMS(BIF_P) ((BIF_P->fcalls / 4) & 0xF)
+#else
+#define FCALLS_ELEMS(BIF_P) (BIF_P->fcalls / 4)
+#endif
+
+ if (MAX(FCALLS_ELEMS(BIF_P), 1) < hashmap_size(map))
+ elems = MAX(FCALLS_ELEMS(BIF_P), 1);
+ else
+ elems = hashmap_size(map);
+
+#undef FCALLS_ELEMS
+
+ if (is_small(path)) {
+ curr_path = unsigned_val(path);
+
+ if (curr_path == 0 && elems > MAP_SMALL_MAP_LIMIT) {
+ elems = MAP_SMALL_MAP_LIMIT;
+ }
+ } else if (is_big(path)) {
+ Eterm *big = big_val(path);
+ if (bignum_header_is_neg(*big))
+ BIF_ERROR(BIF_P, BADARG);
+ path_length = BIG_ARITY(big) - 1;
+ curr_path = BIG_DIGIT(big, 0);
+ path_rest = BIG_V(big) + 1;
+ } else {
+ BIF_ERROR(BIF_P, BADARG);
+ }
+
+ if (type == iterator) {
+ /* iterator uses the format {K, V, {K, V, {K, V, [Path | Map]}}},
+ * so each element is 4 words large */
+ hp = HAlloc(BIF_P, 4 * elems);
+ res = am_none;
+ } else {
+ /* list used the format [Path, Map, {K,V}, {K,V} | BIF_ARG_3],
+ * so each element is 2+3 words large */
+ hp = HAlloc(BIF_P, (2 + 3) * elems);
+ res = BIF_ARG_3;
+ }
+
+ orig_elems = elems;
+
+ /* First we look for the leaf to start at using the
+ path given. While doing so, we push each map node
+ and the index onto the stack to use later. */
+ for (i = 1; ; i++) {
+ Eterm *ptr = hashmap_val(node),
+ hdr = *ptr++;
+ Uint sz;
+
+ sz = hashmap_node_size(hdr, &ptr);
+
+ if (PATH_ELEM(curr_path) >= sz)
+ goto badarg;
+
+ WSTACK_PUSH4(stack, node, PATH_ELEM(curr_path)+1, sz, (UWord)ptr);
+
+ /* We have found a leaf, return it and the next X elements */
+ if (is_list(ptr[PATH_ELEM(curr_path)])) {
+ Eterm *lst = list_val(ptr[PATH_ELEM(curr_path)]);
+ if (type == iterator) {
+ res = TUPLE3(hp, CAR(lst), CDR(lst), res); hp += 4;
+ /* Note where we should patch the Iterator is needed */
+ path_ptr = hp-1;
+ } else {
+ Eterm tup = TUPLE2(hp, CAR(lst), CDR(lst)); hp += 3;
+ res = CONS(hp, tup, res); hp += 2;
+ }
+ elems--;
+ break;
+ }
+
+ node = ptr[PATH_ELEM(curr_path)];
+
+ curr_path >>= PATH_ELEM_SIZE;
+
+ if (i == PATH_ELEMS_PER_DIGIT) {
+ /* Switch to next bignum word if available,
+ otherwise just follow 0 path */
+ i = 0;
+ if (path_length) {
+ curr_path = *path_rest;
+ path_length--;
+ path_rest++;
+ } else {
+ curr_path = 0;
+ }
+ }
+ }
+
+ /* We traverse the hashmap and return at most `elems` elements */
+ while(1) {
+ Eterm *ptr = (Eterm*)WSTACK_POP(stack);
+ Uint sz = (Uint)WSTACK_POP(stack);
+ Uint idx = (Uint)WSTACK_POP(stack);
+ Eterm node = (Eterm)WSTACK_POP(stack);
+
+ while (idx < sz && elems != 0 && is_list(ptr[idx])) {
+ Eterm *lst = list_val(ptr[idx]);
+ if (type == iterator) {
+ res = TUPLE3(hp, CAR(lst), CDR(lst), res); hp += 4;
+ } else {
+ Eterm tup = TUPLE2(hp, CAR(lst), CDR(lst)); hp += 3;
+ res = CONS(hp, tup, res); hp += 2;
+ }
+ elems--;
+ idx++;
+ }
+
+ if (elems == 0) {
+ if (idx < sz) {
+ /* There are more elements in this node to explore */
+ WSTACK_PUSH4(stack, node, idx+1, sz, (UWord)ptr);
+ } else {
+ /* pop stack to find the next value */
+ while (!WSTACK_ISEMPTY(stack)) {
+ Eterm *ptr = (Eterm*)WSTACK_POP(stack);
+ Uint sz = (Uint)WSTACK_POP(stack);
+ Uint idx = (Uint)WSTACK_POP(stack);
+ Eterm node = (Eterm)WSTACK_POP(stack);
+ if (idx < sz) {
+ WSTACK_PUSH4(stack, node, idx+1, sz, (UWord)ptr);
+ break;
+ }
+ }
+ }
+ break;
+ } else {
+ if (idx < sz) {
+ Eterm hdr;
+ /* Push next idx in current node */
+ WSTACK_PUSH4(stack, node, idx+1, sz, (UWord)ptr);
+
+ /* Push first idx in child node */
+ node = ptr[idx];
+ ptr = hashmap_val(ptr[idx]);
+ hdr = *ptr++;
+ sz = hashmap_node_size(hdr, &ptr);
+ WSTACK_PUSH4(stack, node, 0, sz, (UWord)ptr);
+ }
+ }
+
+ /* There are no more element in the hashmap */
+ if (WSTACK_ISEMPTY(stack)) {
+ break;
+ }
+
+ }
+
+ if (!WSTACK_ISEMPTY(stack)) {
+ Uint depth = WSTACK_COUNT(stack) / 4 + 1;
+ /* +1 because we already have the first element in curr_path */
+ Eterm *path_digits = NULL;
+ Uint curr_path = 0;
+
+ /* If the path cannot fit in a small, we allocate a bignum */
+ if (depth >= PATH_ELEMS_PER_DIGIT) {
+ /* We need multiple ErtsDigit's to represent the path */
+ int big_size = BIG_NEED_FOR_BITS(depth * PATH_ELEM_SIZE);
+ hp = HAlloc(BIF_P, big_size);
+ hp[0] = make_pos_bignum_header(big_size - BIG_NEED_SIZE(0));
+ path_digits = hp + big_size - 1;
+ }
+
+
+ /* Pop the stack to create the complete path to the next leaf */
+ while(!WSTACK_ISEMPTY(stack)) {
+ Uint idx;
+
+ (void)WSTACK_POP(stack);
+ (void)WSTACK_POP(stack);
+ idx = (Uint)WSTACK_POP(stack)-1;
+ /* idx - 1 because idx in the stack is pointing to
+ the next element to fetch. */
+ (void)WSTACK_POP(stack);
+
+ depth--;
+ if (depth % PATH_ELEMS_PER_DIGIT == 0) {
+ /* Switch to next bignum element */
+ path_digits[0] = curr_path;
+ path_digits--;
+ curr_path = 0;
+ }
+
+ curr_path <<= PATH_ELEM_SIZE;
+ curr_path |= idx;
+ }
+
+ if (path_digits) {
+ path_digits[0] = curr_path;
+ path = make_big(hp);
+ } else {
+ /* The Uint could be too large for a small */
+ path = erts_make_integer(curr_path, BIF_P);
+ }
+
+ if (type == iterator) {
+ hp = HAlloc(BIF_P, 2);
+ *path_ptr = CONS(hp, path, map); hp += 2;
+ } else {
+ hp = HAlloc(BIF_P, 4);
+ res = CONS(hp, map, res); hp += 2;
+ res = CONS(hp, path, res); hp += 2;
+ }
+ } else {
+ if (type == iterator) {
+ HRelease(BIF_P, hp + 4 * elems, hp);
+ } else {
+ HRelease(BIF_P, hp + (2+3) * elems, hp);
+ }
+ }
+ BIF_P->fcalls -= 4 * (orig_elems - elems);
+ DESTROY_WSTACK(stack);
+ BIF_RET(res);
+
+ badarg:
+ if (type == iterator) {
+ HRelease(BIF_P, hp + 4 * elems, hp);
+ } else {
+ HRelease(BIF_P, hp + (2+3) * elems, hp);
+ }
+ BIF_P->fcalls -= 4 * (orig_elems - elems);
+ DESTROY_WSTACK(stack);
+ BIF_ERROR(BIF_P, BADARG);
+ }
+}
+
/* implementation of builtin emulations */
#if !ERTS_AT_LEAST_GCC_VSN__(3, 4, 0)
diff --git a/erts/emulator/beam/erl_map.h b/erts/emulator/beam/erl_map.h
index c3ccf80b85..718d400e22 100644
--- a/erts/emulator/beam/erl_map.h
+++ b/erts/emulator/beam/erl_map.h
@@ -64,7 +64,6 @@ typedef struct flatmap_s {
#define hashmap_shift_hash(Heap,Hx,Lvl,Key) \
(((++(Lvl)) & 7) ? (Hx) >> 4 : hashmap_make_hash(CONS(Heap, make_small((Lvl)>>3), Key)))
-
/* erl_term.h stuff */
#define flatmap_get_values(x) (((Eterm *)(x)) + sizeof(flatmap_t)/sizeof(Eterm))
#define flatmap_get_keys(x) (((Eterm *)tuple_val(((flatmap_t *)(x))->keys)) + 1)
diff --git a/erts/emulator/beam/erl_message.c b/erts/emulator/beam/erl_message.c
index 3418a7f4df..34bd11d87c 100644
--- a/erts/emulator/beam/erl_message.c
+++ b/erts/emulator/beam/erl_message.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1997-2017. All Rights Reserved.
+ * Copyright Ericsson AB 1997-2018. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -33,6 +33,7 @@
#include "erl_binary.h"
#include "dtrace-wrapper.h"
#include "beam_bp.h"
+#include "erl_proc_sig_queue.h"
ERTS_SCHED_PREF_QUICK_ALLOC_IMPL(message_ref,
ErtsMessageRef,
@@ -207,7 +208,7 @@ erts_cleanup_messages(ErtsMessage *msgp)
while (mp) {
ErtsMessage *fmp;
ErlHeapFragment *bp;
- if (is_non_value(ERL_MESSAGE_TERM(mp))) {
+ if (ERTS_SIG_IS_EXTERNAL_MSG(mp)) {
if (is_not_immed(ERL_MESSAGE_TOKEN(mp))) {
bp = (ErlHeapFragment *) mp->data.dist_ext->ext_endp;
erts_cleanup_offheap(&bp->off_heap);
@@ -215,10 +216,13 @@ erts_cleanup_messages(ErtsMessage *msgp)
if (mp->data.dist_ext)
erts_free_dist_ext_copy(mp->data.dist_ext);
}
- else {
- if (mp->data.attached != ERTS_MSG_COMBINED_HFRAG)
+ else {
+ if (ERTS_SIG_IS_INTERNAL_MSG(mp)
+ && mp->data.attached != ERTS_MSG_COMBINED_HFRAG) {
bp = mp->data.heap_frag;
+ }
else {
+ mp->data.attached = ERTS_MSG_COMBINED_HFRAG;
bp = mp->hfrag.next;
erts_cleanup_offheap(&mp->hfrag.off_heap);
}
@@ -260,11 +264,6 @@ erts_queue_dist_message(Process *rcvr,
Eterm from)
{
ErtsMessage* mp;
-#ifdef USE_VM_PROBES
- Sint tok_label = 0;
- Sint tok_lastcnt = 0;
- Sint tok_serial = 0;
-#endif
erts_aint_t state;
ERTS_LC_ASSERT(rcvr_locks == erts_proc_lc_my_proc_locks(rcvr));
@@ -272,6 +271,7 @@ erts_queue_dist_message(Process *rcvr,
mp = erts_alloc_message(0, NULL);
mp->data.dist_ext = dist_ext;
+ ERL_MESSAGE_FROM(mp) = dist_ext->dep->sysname;
ERL_MESSAGE_TERM(mp) = THE_NON_VALUE;
#ifdef USE_VM_PROBES
ERL_MESSAGE_DT_UTAG(mp) = NIL;
@@ -294,74 +294,22 @@ erts_queue_dist_message(Process *rcvr,
}
}
+
state = erts_atomic32_read_acqb(&rcvr->state);
- if (state & (ERTS_PSFLG_PENDING_EXIT|ERTS_PSFLG_EXITING)) {
+ if (state & ERTS_PSFLG_EXITING) {
if (!(rcvr_locks & ERTS_PROC_LOCK_MSGQ))
erts_proc_unlock(rcvr, ERTS_PROC_LOCK_MSGQ);
/* Drop message if receiver is exiting or has a pending exit ... */
erts_cleanup_messages(mp);
}
- else
- if (IS_TRACED_FL(rcvr, F_TRACE_RECEIVE)) {
- if (from == am_Empty)
- from = dist_ext->dep->sysname;
-
- /* Ahh... need to decode it in order to trace it... */
- if (!(rcvr_locks & ERTS_PROC_LOCK_MSGQ))
- erts_proc_unlock(rcvr, ERTS_PROC_LOCK_MSGQ);
- if (!erts_decode_dist_message(rcvr, rcvr_locks, mp, 0))
- erts_free_message(mp);
- else {
- Eterm msg = ERL_MESSAGE_TERM(mp);
- token = ERL_MESSAGE_TOKEN(mp);
-#ifdef USE_VM_PROBES
- if (DTRACE_ENABLED(message_queued)) {
- DTRACE_CHARBUF(receiver_name, DTRACE_TERM_BUF_SIZE);
-
- dtrace_proc_str(rcvr, receiver_name);
- if (have_seqtrace(token)) {
- tok_label = signed_val(SEQ_TRACE_T_LABEL(token));
- tok_lastcnt = signed_val(SEQ_TRACE_T_LASTCNT(token));
- tok_serial = signed_val(SEQ_TRACE_T_SERIAL(token));
- }
- DTRACE6(message_queued,
- receiver_name, size_object(msg), rcvr->msg.len,
- tok_label, tok_lastcnt, tok_serial);
- }
-#endif
- erts_queue_message(rcvr, rcvr_locks, mp, msg, from);
- }
- }
else {
- /* Enqueue message on external format */
-
-#ifdef USE_VM_PROBES
- if (DTRACE_ENABLED(message_queued)) {
- DTRACE_CHARBUF(receiver_name, DTRACE_TERM_BUF_SIZE);
-
- dtrace_proc_str(rcvr, receiver_name);
- if (have_seqtrace(token)) {
- tok_label = signed_val(SEQ_TRACE_T_LABEL(token));
- tok_lastcnt = signed_val(SEQ_TRACE_T_LASTCNT(token));
- tok_serial = signed_val(SEQ_TRACE_T_SERIAL(token));
- }
- /*
- * TODO: We don't know the real size of the external message here.
- * -1 will appear to a D script as 4294967295.
- */
- DTRACE6(message_queued, receiver_name, -1, rcvr->msg.len + 1,
- tok_label, tok_lastcnt, tok_serial);
- }
-#endif
LINK_MESSAGE(rcvr, mp, &mp->next, 1);
if (!(rcvr_locks & ERTS_PROC_LOCK_MSGQ))
erts_proc_unlock(rcvr, ERTS_PROC_LOCK_MSGQ);
- erts_proc_notify_new_message(rcvr,
- rcvr_locks
- );
+ erts_proc_notify_new_message(rcvr, rcvr_locks);
}
}
@@ -372,15 +320,13 @@ queue_messages(Process* receiver,
ErtsProcLocks receiver_locks,
ErtsMessage* first,
ErtsMessage** last,
- Uint len,
- Eterm from)
+ Uint len)
{
- ErtsTracingEvent* te;
- Sint res;
int locked_msgq = 0;
erts_aint32_t state;
ASSERT(is_value(ERL_MESSAGE_TERM(first)));
+ ASSERT(is_value(ERL_MESSAGE_FROM(first)));
ASSERT(ERL_MESSAGE_TOKEN(first) == am_undefined ||
ERL_MESSAGE_TOKEN(first) == NIL ||
is_tuple(ERL_MESSAGE_TOKEN(first)));
@@ -398,7 +344,7 @@ queue_messages(Process* receiver,
state = *receiver_state;
else
state = erts_atomic32_read_nob(&receiver->state);
- if (state & (ERTS_PSFLG_EXITING|ERTS_PSFLG_PENDING_EXIT))
+ if (state & ERTS_PSFLG_EXITING)
goto exiting;
need_locks = receiver_locks & ERTS_PROC_LOCKS_HIGHER_THAN(ERTS_PROC_LOCK_MSGQ);
@@ -414,7 +360,7 @@ queue_messages(Process* receiver,
state = erts_atomic32_read_nob(&receiver->state);
- if (state & (ERTS_PSFLG_PENDING_EXIT|ERTS_PSFLG_EXITING)) {
+ if (state & ERTS_PSFLG_EXITING) {
exiting:
/* Drop message if receiver is exiting or has a pending exit... */
if (locked_msgq)
@@ -423,63 +369,14 @@ queue_messages(Process* receiver,
return 0;
}
- res = receiver->msg.len;
- if (receiver_locks & ERTS_PROC_LOCK_MAIN) {
- /*
- * We move 'in queue' to 'private queue' and place
- * message at the end of 'private queue' in order
- * to ensure that the 'in queue' doesn't contain
- * references into the heap. By ensuring this,
- * we don't need to include the 'in queue' in
- * the root set when garbage collecting.
- */
- res += receiver->msg_inq.len;
- ERTS_MSGQ_MV_INQ2PRIVQ(receiver);
- LINK_MESSAGE_PRIVQ(receiver, first, last, len);
- }
- else
- {
- LINK_MESSAGE(receiver, first, last, len);
- }
-
- if (IS_TRACED_FL(receiver, F_TRACE_RECEIVE)
- && (te = &erts_receive_tracing[erts_active_bp_ix()],
- te->on)) {
-
- ErtsMessage *msg = first;
-
-#ifdef USE_VM_PROBES
- if (DTRACE_ENABLED(message_queued)) {
- DTRACE_CHARBUF(receiver_name, DTRACE_TERM_BUF_SIZE);
- Sint tok_label = 0;
- Sint tok_lastcnt = 0;
- Sint tok_serial = 0;
- Eterm seq_trace_token = ERL_MESSAGE_TOKEN(msg);
-
- dtrace_proc_str(receiver, receiver_name);
- if (seq_trace_token != NIL && is_tuple(seq_trace_token)) {
- tok_label = signed_val(SEQ_TRACE_T_LABEL(seq_trace_token));
- tok_lastcnt = signed_val(SEQ_TRACE_T_LASTCNT(seq_trace_token));
- tok_serial = signed_val(SEQ_TRACE_T_SERIAL(seq_trace_token));
- }
- DTRACE6(message_queued,
- receiver_name, size_object(ERL_MESSAGE_TERM(msg)),
- receiver->msg.len,
- tok_label, tok_lastcnt, tok_serial);
- }
-#endif
- while (msg) {
- trace_receive(receiver, from, ERL_MESSAGE_TERM(msg), te);
- msg = msg->next;
- }
+ LINK_MESSAGE(receiver, first, last, len);
- }
if (locked_msgq) {
erts_proc_unlock(receiver, ERTS_PROC_LOCK_MSGQ);
}
erts_proc_notify_new_message(receiver, receiver_locks);
- return res;
+ return 0;
}
static Sint
@@ -489,8 +386,9 @@ queue_message(Process* receiver,
ErtsMessage* mp, Eterm msg, Eterm from)
{
ERL_MESSAGE_TERM(mp) = msg;
+ ERL_MESSAGE_FROM(mp) = from;
return queue_messages(receiver, receiver_state, receiver_locks,
- mp, &mp->next, 1, from);
+ mp, &mp->next, 1);
}
Sint
@@ -503,11 +401,10 @@ erts_queue_message(Process* receiver, ErtsProcLocks receiver_locks,
Sint
erts_queue_messages(Process* receiver, ErtsProcLocks receiver_locks,
- ErtsMessage* first, ErtsMessage** last, Uint len,
- Eterm from)
+ ErtsMessage* first, ErtsMessage** last, Uint len)
{
return queue_messages(receiver, NULL, receiver_locks,
- first, last, len, from);
+ first, last, len);
}
void
@@ -550,14 +447,11 @@ erts_msg_attached_data_size_aux(ErtsMessage *msg)
sz = erts_decode_dist_ext_size(msg->data.dist_ext);
if (sz < 0) {
- /* Bad external; remove it */
- if (is_not_nil(ERL_MESSAGE_TOKEN(msg))) {
- ErlHeapFragment *heap_frag;
- heap_frag = erts_dist_ext_trailer(msg->data.dist_ext);
- erts_cleanup_offheap(&heap_frag->off_heap);
- }
- erts_free_dist_ext_copy(msg->data.dist_ext);
- msg->data.dist_ext = NULL;
+ /* Bad external
+ * We leave the message intact in this case as it's not worth the trouble
+ * to make all callers remove it from queue. It will be detected again
+ * and removed from message queue later anyway.
+ */
return 0;
}
@@ -619,7 +513,7 @@ erts_try_alloc_message_on_heap(Process *pp,
}
else {
in_message_fragment:
- if (!((*psp) & ERTS_PSFLG_ON_HEAP_MSGQ)) {
+ if ((*psp) & ERTS_PSFLG_OFF_HEAP_MSGQ) {
mp = erts_alloc_message(sz, hpp);
*ohpp = sz == 0 ? NULL : &mp->hfrag.off_heap;
}
@@ -704,7 +598,8 @@ erts_send_message(Process* sender,
seq_trace_update_send(sender);
seq_trace_output(stoken, message, SEQ_TRACE_SEND,
receiver->common.id, sender);
- seq_trace_size = 6; /* TUPLE5 */
+
+ seq_trace_size = size_object(stoken);
}
#ifdef USE_VM_PROBES
if (DT_UTAG_FLAGS(sender) & DT_UTAG_SPREADING) {
@@ -753,7 +648,7 @@ erts_send_message(Process* sender,
}
if (DTRACE_ENABLED(message_send)) {
if (have_seqtrace(stoken)) {
- tok_label = signed_val(SEQ_TRACE_T_LABEL(stoken));
+ tok_label = SEQ_TRACE_T_DTRACE_LABEL(stoken);
tok_lastcnt = signed_val(SEQ_TRACE_T_LASTCNT(stoken));
tok_serial = signed_val(SEQ_TRACE_T_SERIAL(stoken));
}
@@ -925,70 +820,77 @@ Sint
erts_move_messages_off_heap(Process *c_p)
{
int reds = 1;
+ int i;
+ ErtsMessage *msgq[] = {c_p->sig_qs.first, c_p->sig_qs.cont};
/*
* Move all messages off heap. This *only* occurs when the
* process had off heap message disabled and just enabled
* it...
*/
- ErtsMessage *mp;
- reds += c_p->msg.len / 10;
+ reds += erts_proc_sig_privqs_len(c_p) / 10;
ASSERT(erts_atomic32_read_nob(&c_p->state)
& ERTS_PSFLG_OFF_HEAP_MSGQ);
ASSERT(c_p->flags & F_OFF_HEAP_MSGQ_CHNG);
- for (mp = c_p->msg.first; mp; mp = mp->next) {
- Uint msg_sz, token_sz;
+ for (i = 0; i < sizeof(msgq)/sizeof(msgq[0]); i++) {
+ ErtsMessage *mp;
+ for (mp = msgq[i]; mp; mp = mp->next) {
+ Uint msg_sz, token_sz;
#ifdef USE_VM_PROBES
- Uint utag_sz;
+ Uint utag_sz;
#endif
- Eterm *hp;
- ErlHeapFragment *hfrag;
+ Eterm *hp;
+ ErlHeapFragment *hfrag;
+
+ if (!ERTS_SIG_IS_INTERNAL_MSG(mp))
+ continue;
- if (mp->data.attached)
- continue;
+ if (mp->data.attached)
+ continue;
- if (is_immed(ERL_MESSAGE_TERM(mp))
+ if (is_immed(ERL_MESSAGE_TERM(mp))
#ifdef USE_VM_PROBES
- && is_immed(ERL_MESSAGE_DT_UTAG(mp))
+ && is_immed(ERL_MESSAGE_DT_UTAG(mp))
#endif
- && is_not_immed(ERL_MESSAGE_TOKEN(mp)))
- continue;
+ && is_not_immed(ERL_MESSAGE_TOKEN(mp)))
+ continue;
- /*
- * The message refers into the heap. Copy the message
- * from the heap into a heap fragment and attach
- * it to the message...
- */
- msg_sz = size_object(ERL_MESSAGE_TERM(mp));
+ /*
+ * The message refers into the heap. Copy the message
+ * from the heap into a heap fragment and attach
+ * it to the message...
+ */
+ msg_sz = size_object(ERL_MESSAGE_TERM(mp));
#ifdef USE_VM_PROBES
- utag_sz = size_object(ERL_MESSAGE_DT_UTAG(mp));
+ utag_sz = size_object(ERL_MESSAGE_DT_UTAG(mp));
#endif
- token_sz = size_object(ERL_MESSAGE_TOKEN(mp));
+ token_sz = size_object(ERL_MESSAGE_TOKEN(mp));
- hfrag = new_message_buffer(msg_sz
+ hfrag = new_message_buffer(msg_sz
#ifdef USE_VM_PROBES
- + utag_sz
+ + utag_sz
#endif
- + token_sz);
- hp = hfrag->mem;
- if (is_not_immed(ERL_MESSAGE_TERM(mp)))
- ERL_MESSAGE_TERM(mp) = copy_struct(ERL_MESSAGE_TERM(mp),
- msg_sz, &hp,
- &hfrag->off_heap);
- if (is_not_immed(ERL_MESSAGE_TOKEN(mp)))
- ERL_MESSAGE_TOKEN(mp) = copy_struct(ERL_MESSAGE_TOKEN(mp),
- token_sz, &hp,
- &hfrag->off_heap);
+ + token_sz);
+ hp = hfrag->mem;
+ if (is_not_immed(ERL_MESSAGE_TERM(mp)))
+ ERL_MESSAGE_TERM(mp) = copy_struct(ERL_MESSAGE_TERM(mp),
+ msg_sz, &hp,
+ &hfrag->off_heap);
+ if (is_not_immed(ERL_MESSAGE_TOKEN(mp)))
+ ERL_MESSAGE_TOKEN(mp) = copy_struct(ERL_MESSAGE_TOKEN(mp),
+ token_sz, &hp,
+ &hfrag->off_heap);
#ifdef USE_VM_PROBES
- if (is_not_immed(ERL_MESSAGE_DT_UTAG(mp)))
- ERL_MESSAGE_DT_UTAG(mp) = copy_struct(ERL_MESSAGE_DT_UTAG(mp),
- utag_sz, &hp,
- &hfrag->off_heap);
+ if (is_not_immed(ERL_MESSAGE_DT_UTAG(mp)))
+ ERL_MESSAGE_DT_UTAG(mp) = copy_struct(ERL_MESSAGE_DT_UTAG(mp),
+ utag_sz, &hp,
+ &hfrag->off_heap);
#endif
- mp->data.heap_frag = hfrag;
- reds += 1;
+ mp->data.heap_frag = hfrag;
+ reds += 1;
+ }
}
return reds;
@@ -1018,7 +920,7 @@ erts_complete_off_heap_message_queue_change(Process *c_p)
else {
reds += 2;
erts_proc_lock(c_p, ERTS_PROC_LOCK_MSGQ);
- ERTS_MSGQ_MV_INQ2PRIVQ(c_p);
+ erts_proc_sig_fetch(c_p);
erts_proc_unlock(c_p, ERTS_PROC_LOCK_MSGQ);
reds += erts_move_messages_off_heap(c_p);
}
@@ -1082,8 +984,6 @@ erts_change_message_queue_management(Process *c_p, Eterm new_state)
case am_on_heap:
c_p->flags |= F_ON_HEAP_MSGQ;
c_p->flags &= ~F_OFF_HEAP_MSGQ;
- erts_atomic32_read_bor_nob(&c_p->state,
- ERTS_PSFLG_ON_HEAP_MSGQ);
/*
* We are not allowed to clear ERTS_PSFLG_OFF_HEAP_MSGQ
* if a off heap change is ongoing. It will be adjusted
@@ -1109,8 +1009,6 @@ erts_change_message_queue_management(Process *c_p, Eterm new_state)
break;
case am_off_heap:
c_p->flags &= ~F_ON_HEAP_MSGQ;
- erts_atomic32_read_band_nob(&c_p->state,
- ~ERTS_PSFLG_ON_HEAP_MSGQ);
goto change_to_off_heap;
default:
res = THE_NON_VALUE; /* badarg */
@@ -1232,139 +1130,6 @@ erts_decode_dist_message(Process *proc, ErtsProcLocks proc_locks,
return 1;
}
-/*
- * ERTS_INSPECT_MSGQ_KEEP_OH_MSGS == 0 will move off heap messages
- * into the heap of the inspected process if off_heap_message_queue
- * is false when process_info(_, messages) is called. That is, the
- * following GC will have more data in the rootset compared to the
- * scenario when process_info(_, messages) had not been called.
- *
- * ERTS_INSPECT_MSGQ_KEEP_OH_MSGS != 0 will keep off heap messages
- * off heap when process_info(_, messages) is called regardless of
- * the off_heap_message_queue setting of the process. That is, it
- * will change the following execution of the process as little as
- * possible.
- */
-#define ERTS_INSPECT_MSGQ_KEEP_OH_MSGS 1
-
-Uint
-erts_prep_msgq_for_inspection(Process *c_p, Process *rp,
- ErtsProcLocks rp_locks, ErtsMessageInfo *mip)
-{
- Uint tot_heap_size;
- ErtsMessage* mp;
- Sint i;
- int self_on_heap;
-
- /*
- * Prepare the message queue for inspection
- * by process_info().
- *
- *
- * - Decode all messages on external format
- * - Remove all corrupt dist messages from queue
- * - Save pointer to, and heap size need of each
- * message in the mip array.
- * - Return total heap size need for all messages
- * that needs to be copied.
- *
- * If ERTS_INSPECT_MSGQ_KEEP_OH_MSGS == 0:
- * - In case off heap messages is disabled and
- * we are inspecting our own queue, move all
- * off heap data into the heap.
- */
-
- self_on_heap = c_p == rp && !(c_p->flags & F_OFF_HEAP_MSGQ);
-
- tot_heap_size = 0;
- i = 0;
- mp = rp->msg.first;
- while (mp) {
- Eterm msg = ERL_MESSAGE_TERM(mp);
-
- mip[i].size = 0;
-
- if (is_non_value(msg)) {
- /* Dist message on external format; decode it... */
- if (mp->data.attached)
- erts_decode_dist_message(rp, rp_locks, mp,
- ERTS_INSPECT_MSGQ_KEEP_OH_MSGS);
-
- msg = ERL_MESSAGE_TERM(mp);
-
- if (is_non_value(msg)) {
- ErtsMessage **mpp;
- ErtsMessage *bad_mp = mp;
- /*
- * Bad distribution message; remove
- * it from the queue...
- */
- ASSERT(!mp->data.attached);
-
- mpp = i == 0 ? &rp->msg.first : &mip[i-1].msgp->next;
-
- ASSERT(*mpp == bad_mp);
-
- erts_msgq_update_internal_pointers(&rp->msg, mpp, &bad_mp->next);
-
- mp = mp->next;
- *mpp = mp;
- rp->msg.len--;
- bad_mp->next = NULL;
- erts_cleanup_messages(bad_mp);
- continue;
- }
- }
-
- ASSERT(is_value(msg));
-
-#if ERTS_INSPECT_MSGQ_KEEP_OH_MSGS
- if (is_not_immed(msg) && (!self_on_heap || mp->data.attached)) {
- Uint sz = size_object(msg);
- mip[i].size = sz;
- tot_heap_size += sz;
- }
-#else
- if (self_on_heap) {
- if (mp->data.attached) {
- ErtsMessage *tmp = NULL;
- if (mp->data.attached != ERTS_MSG_COMBINED_HFRAG) {
- erts_link_mbuf_to_proc(rp, mp->data.heap_frag);
- mp->data.attached = NULL;
- }
- else {
- /*
- * Need to replace the message reference since
- * we will get references to the message data
- * from the heap...
- */
- ErtsMessage **mpp;
- tmp = erts_alloc_message(0, NULL);
- sys_memcpy((void *) tmp->m, (void *) mp->m,
- sizeof(Eterm)*ERL_MESSAGE_REF_ARRAY_SZ);
- mpp = i == 0 ? &rp->msg.first : &mip[i-1].msgp->next;
- erts_msgq_replace_msg_ref(&rp->msg, tmp, mpp);
- erts_save_message_in_proc(rp, mp);
- mp = tmp;
- }
- }
- }
- else if (is_not_immed(msg)) {
- Uint sz = size_object(msg);
- mip[i].size = sz;
- tot_heap_size += sz;
- }
-
-#endif
-
- mip[i].msgp = mp;
- i++;
- mp = mp->next;
- }
-
- return tot_heap_size;
-}
-
void erts_factory_proc_init(ErtsHeapFactory* factory,
Process* p)
{
@@ -1554,32 +1319,18 @@ void erts_factory_dummy_init(ErtsHeapFactory* factory)
factory->mode = FACTORY_CLOSED;
}
-static void reserve_heap(ErtsHeapFactory*, Uint need, Uint xtra);
-
-Eterm* erts_produce_heap(ErtsHeapFactory* factory, Uint need, Uint xtra)
-{
- Eterm* res;
-
- ASSERT((unsigned int)factory->mode > (unsigned int)FACTORY_CLOSED);
- if (factory->hp + need > factory->hp_end) {
- reserve_heap(factory, need, xtra);
- }
- res = factory->hp;
- factory->hp += need;
- return res;
-}
-
Eterm* erts_reserve_heap(ErtsHeapFactory* factory, Uint need)
{
ASSERT((unsigned int)factory->mode > (unsigned int)FACTORY_CLOSED);
if (factory->hp + need > factory->hp_end) {
- reserve_heap(factory, need, 200);
+ erts_reserve_heap__(factory, need, 200);
}
return factory->hp;
}
-static void reserve_heap(ErtsHeapFactory* factory, Uint need, Uint xtra)
+void erts_reserve_heap__(ErtsHeapFactory* factory, Uint need, Uint xtra)
{
+ /* internal... */
ErlHeapFragment* bp;
switch (factory->mode) {
@@ -1589,7 +1340,9 @@ static void reserve_heap(ErtsHeapFactory* factory, Uint need, Uint xtra)
factory->hp_end = factory->hp + need;
return;
- case FACTORY_MESSAGE:
+ case FACTORY_MESSAGE: {
+ int replace_oh;
+ int replace_msg_hfrag;
if (!factory->heap_frags) {
ASSERT(factory->message->data.attached == ERTS_MSG_COMBINED_HFRAG);
bp = &factory->message->hfrag;
@@ -1601,25 +1354,45 @@ static void reserve_heap(ErtsHeapFactory* factory, Uint need, Uint xtra)
bp = factory->heap_frags;
}
+ replace_oh = 0;
+ replace_msg_hfrag = 0;
+
if (bp) {
- ASSERT(factory->hp > bp->mem);
+ ASSERT(factory->hp >= bp->mem);
ASSERT(factory->hp <= factory->hp_end);
ASSERT(factory->hp_end == bp->mem + bp->alloc_size);
bp->used_size = factory->hp - bp->mem;
+ if (!bp->used_size && factory->heap_frags) {
+ factory->heap_frags = bp->next;
+ bp->next = NULL;
+ ASSERT(!bp->off_heap.first);
+ if (factory->off_heap == &bp->off_heap)
+ replace_oh = !0;
+ if (factory->message && factory->message->data.heap_frag == bp)
+ replace_msg_hfrag = !0;
+ free_message_buffer(bp);
+ }
}
bp = (ErlHeapFragment*) ERTS_HEAP_ALLOC(factory->alloc_type,
ERTS_HEAP_FRAG_SIZE(need+xtra));
bp->next = factory->heap_frags;
factory->heap_frags = bp;
bp->alloc_size = need + xtra;
- bp->used_size = need;
+ bp->used_size = need + xtra;
bp->off_heap.first = NULL;
bp->off_heap.overhead = 0;
-
+ if (replace_oh) {
+ factory->off_heap = &bp->off_heap;
+ factory->off_heap_saved.first = factory->off_heap->first;
+ factory->off_heap_saved.overhead = factory->off_heap->overhead;
+ }
+ if (replace_msg_hfrag)
+ factory->message->data.heap_frag = bp;
factory->hp = bp->mem;
factory->hp_end = bp->mem + bp->alloc_size;
return;
+ }
case FACTORY_STATIC:
case FACTORY_CLOSED:
@@ -1702,9 +1475,11 @@ void erts_factory_trim_and_close(ErtsHeapFactory* factory,
if (bp->next == NULL) {
Uint used_sz = factory->hp - bp->mem;
ASSERT(used_sz <= bp->alloc_size);
- if (used_sz > 0)
- bp = erts_resize_message_buffer(bp, used_sz,
- brefs, brefs_size);
+ if (used_sz > 0) {
+ if (used_sz != bp->alloc_size)
+ bp = erts_resize_message_buffer(bp, used_sz,
+ brefs, brefs_size);
+ }
else {
free_message_buffer(bp);
bp = NULL;
diff --git a/erts/emulator/beam/erl_message.h b/erts/emulator/beam/erl_message.h
index 9c8cf84e43..ee87297ba4 100644
--- a/erts/emulator/beam/erl_message.h
+++ b/erts/emulator/beam/erl_message.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1997-2016. All Rights Reserved.
+ * Copyright Ericsson AB 1997-2018. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -21,6 +21,11 @@
#ifndef __ERL_MESSAGE_H__
#define __ERL_MESSAGE_H__
+#include "sys.h"
+#define ERTS_PROC_SIG_QUEUE_TYPE_ONLY
+#include "erl_proc_sig_queue.h"
+#undef ERTS_PROC_SIG_QUEUE_TYPE_ONLY
+
struct proc_bin;
struct external_thing_;
@@ -84,12 +89,33 @@ void erts_factory_static_init(ErtsHeapFactory*, Eterm* hp, Uint size, ErlOffHeap
void erts_factory_tmp_init(ErtsHeapFactory*, Eterm* hp, Uint size, Uint32 atype);
void erts_factory_dummy_init(ErtsHeapFactory*);
-Eterm* erts_produce_heap(ErtsHeapFactory*, Uint need, Uint xtra);
+ERTS_GLB_INLINE Eterm* erts_produce_heap(ErtsHeapFactory*, Uint need, Uint xtra);
+
Eterm* erts_reserve_heap(ErtsHeapFactory*, Uint need);
void erts_factory_close(ErtsHeapFactory*);
void erts_factory_trim_and_close(ErtsHeapFactory*,Eterm *brefs, Uint brefs_size);
void erts_factory_undo(ErtsHeapFactory*);
+void erts_reserve_heap__(ErtsHeapFactory*, Uint need, Uint xtra); /* internal */
+
+#if ERTS_GLB_INLINE_INCL_FUNC_DEF
+
+ERTS_GLB_INLINE Eterm *
+erts_produce_heap(ErtsHeapFactory* factory, Uint need, Uint xtra)
+{
+ Eterm* res;
+
+ ASSERT((unsigned int)factory->mode > (unsigned int)FACTORY_CLOSED);
+ if (factory->hp + need > factory->hp_end) {
+ erts_reserve_heap__(factory, need, xtra);
+ }
+ res = factory->hp;
+ factory->hp += need;
+ return res;
+}
+
+#endif /* ERTS_GLB_INLINE_INCL_FUNC_DEF */
+
#ifdef CHECK_FOR_HOLES
# define ERTS_FACTORY_HOLE_CHECK(f) do { \
/*if ((f)->p) erts_check_for_holes((f)->p);*/ \
@@ -118,15 +144,16 @@ struct erl_heap_fragment {
};
/* m[0] = message, m[1] = seq trace token */
-#define ERL_MESSAGE_REF_ARRAY_SZ 2
+#define ERL_MESSAGE_REF_ARRAY_SZ 3
#define ERL_MESSAGE_TERM(mp) ((mp)->m[0])
#define ERL_MESSAGE_TOKEN(mp) ((mp)->m[1])
+#define ERL_MESSAGE_FROM(mp) ((mp)->m[2])
#ifdef USE_VM_PROBES
/* m[2] = dynamic trace user tag */
#undef ERL_MESSAGE_REF_ARRAY_SZ
-#define ERL_MESSAGE_REF_ARRAY_SZ 3
-#define ERL_MESSAGE_DT_UTAG(mp) ((mp)->m[2])
+#define ERL_MESSAGE_REF_ARRAY_SZ 4
+#define ERL_MESSAGE_DT_UTAG(mp) ((mp)->m[3])
#else
#endif
@@ -157,29 +184,121 @@ struct erl_mesg {
ErlHeapFragment hfrag;
};
+/*
+ * The ErtsMessage struct is only one special type
+ * of signal. All signal structs have a common
+ * begining and can be differentiated by looking
+ * at the ErtsSignal 'common.tag' field.
+ *
+ * - An ordinary message will have a value
+ * - A distribution message that has not been
+ * decoded yet will have the non-value.
+ * - Other signals will have an external pid
+ * header tag. In order to differentiate
+ * between those signals one needs to look
+ * at the arity part of the header (see
+ * erts_proc_sig_queue.h).
+ */
+
+#define ERTS_SIG_IS_NON_MSG_TAG(Tag) \
+ (is_external_pid_header((Tag)))
+
+#define ERTS_SIG_IS_NON_MSG(Sig) \
+ ERTS_SIG_IS_NON_MSG_TAG(((ErtsSignal *) (Sig))->common.tag)
+
+#define ERTS_SIG_IS_INTERNAL_MSG_TAG(Tag) \
+ (!is_header((Tag)))
+#define ERTS_SIG_IS_INTERNAL_MSG(Sig) \
+ ERTS_SIG_IS_INTERNAL_MSG_TAG(((ErtsSignal *) (Sig))->common.tag)
+
+#define ERTS_SIG_IS_EXTERNAL_MSG_TAG(Tag) \
+ ((Tag) == THE_NON_VALUE)
+#define ERTS_SIG_IS_EXTERNAL_MSG(Sig) \
+ ERTS_SIG_IS_EXTERNAL_MSG_TAG(((ErtsSignal *) (Sig))->common.tag)
+
+#define ERTS_SIG_IS_MSG_TAG(Tag) \
+ (!ERTS_SIG_IS_NON_MSG_TAG(Tag))
+#define ERTS_SIG_IS_MSG(Sig) \
+ ERTS_SIG_IS_MSG_TAG(((ErtsSignal *) (Sig))->common.tag)
+
+typedef union {
+ ErtsSignalCommon common;
+ ErtsMessageRef msg;
+} ErtsSignal;
+
+typedef struct {
+ /* pointers to next pointers pointing to... */
+ ErtsMessage **next; /* ... next (non-message) signal */
+ ErtsMessage **last; /* ... next (non-message) signal */
+} ErtsMsgQNMSigs;
+
/* Size of default message buffer (erl_message.c) */
#define ERL_MESSAGE_BUF_SZ 500
typedef struct {
- ErtsMessage* first;
- ErtsMessage** last; /* point to the last next pointer */
- ErtsMessage** save;
- Sint len; /* queue length */
-
/*
- * The following two fields are used by the recv_mark/1 and
- * recv_set/1 instructions.
+ * ** The signal queues private to a process. **
+ *
+ * These are:
+ * - an inner queue which only consists of
+ * message signals
+ * - a middle queue which contains a mixture
+ * of message and non-message signals
+ *
+ * When the process isn't processing signals in
+ * erts_proc_sig_handle_incoming():
+ * - the message queue corresponds to the inner
+ * queue. Messages in the middle queue (and
+ * in the outer queue) are in transit and
+ * have NOT been received yet!
+ *
+ * When the process is processing signals in
+ * erts_proc_sig_handle_incoming():
+ * - the message queue corresponds to the inner
+ * queue plus the head of the middle queue up
+ * to the signal currently being processed.
+ * Any messages further back in the middle queue
+ * (and in the outer queue) are still in transit
+ * and have NOT been received yet!
+ *
+ * In the general case the 'len' field of this
+ * structure does NOT correspond to the message
+ * queue length. When the process is inspected
+ * via process info it does however correspond
+ * to the message queue length, but this is a
+ * special case!
+ *
+ * When no process-info request is in transit to
+ * the process the 'len' field corresponds to
+ * the total amount of messages in inner and
+ * middle queues (which does NOT correspond to
+ * the message queue length). When process-info
+ * requests are in transit to the process, the
+ * usage of the 'len' field changes and is used
+ * as an offset which even might be negative.
*/
- BeamInstr* mark; /* address to rec_loop/2 instruction */
- ErtsMessage** saved_last; /* saved last pointer */
-} ErlMessageQueue;
+ /* inner queue */
+ ErtsMessage *first;
+ ErtsMessage **last; /* point to the last next pointer */
+ ErtsMessage **save;
+
+ /* middle queue */
+ ErtsMessage *cont;
+ ErtsMessage **cont_last;
+ ErtsMsgQNMSigs nmsigs;
+
+ /* Common for inner and middle queue */
+ ErtsMessage **saved_last; /* saved last pointer */
+ Sint len; /* NOT message queue length (see above) */
+} ErtsSignalPrivQueues;
typedef struct {
ErtsMessage* first;
ErtsMessage** last; /* point to the last next pointer */
Sint len; /* queue length */
-} ErlMessageInQueue;
+ ErtsMsgQNMSigs nmsigs;
+} ErtsSignalInQueue;
typedef struct erl_trace_message_queue__ {
struct erl_trace_message_queue__ *next; /* point to the next receiver */
@@ -189,9 +308,45 @@ typedef struct erl_trace_message_queue__ {
Sint len; /* queue length */
} ErlTraceMessageQueue;
+#define ERTS_RECV_MARK_SAVE(P) \
+ do { \
+ erts_proc_lock((P), ERTS_PROC_LOCK_MSGQ); \
+ erts_proc_sig_fetch((P)); \
+ erts_proc_unlock((P), ERTS_PROC_LOCK_MSGQ); \
+ if ((P)->sig_qs.cont) { \
+ (P)->sig_qs.saved_last = (P)->sig_qs.cont_last; \
+ (P)->flags |= F_DEFERRED_SAVED_LAST; \
+ } \
+ else { \
+ (P)->sig_qs.saved_last = (P)->sig_qs.last; \
+ (P)->flags &= ~F_DEFERRED_SAVED_LAST; \
+ } \
+ } while (0)
+
+#define ERTS_RECV_MARK_SET(P) \
+ do { \
+ if ((P)->sig_qs.saved_last) { \
+ if ((P)->flags & F_DEFERRED_SAVED_LAST) { \
+ /* Points to middle queue; use end of inner */ \
+ (P)->sig_qs.save = (P)->sig_qs.last; \
+ ASSERT(!PEEK_MESSAGE((P))); \
+ } \
+ else { \
+ /* Points to inner queue; safe to use */ \
+ (P)->sig_qs.save = (P)->sig_qs.saved_last; \
+ } \
+ } \
+ } while (0)
+
+#define ERTS_RECV_MARK_CLEAR(P) \
+ do { \
+ (P)->sig_qs.saved_last = NULL; \
+ (P)->flags &= ~F_DEFERRED_SAVED_LAST; \
+ } while (0)
+
/* Get "current" message */
-#define PEEK_MESSAGE(p) (*(p)->msg.save)
+#define PEEK_MESSAGE(p) (*(p)->sig_qs.save)
#ifdef USE_VM_PROBES
#define LINK_MESSAGE_DTAG(mp, dt) ERL_MESSAGE_DT_UTAG(mp) = dt
@@ -199,53 +354,52 @@ typedef struct erl_trace_message_queue__ {
#define LINK_MESSAGE_DTAG(mp, dt)
#endif
-#define LINK_MESSAGE_IMPL(p, first_msg, last_msg, num_msgs, where) do { \
- *(p)->where.last = (first_msg); \
- (p)->where.last = (last_msg); \
- (p)->where.len += (num_msgs); \
- } while(0)
-
+#ifdef USE_VM_PROBES
+# define ERTS_MSG_RECV_TRACED(P) \
+ ((ERTS_TRACE_FLAGS((P)) & F_TRACE_RECEIVE) \
+ || DTRACE_ENABLED(message_queued))
+#else
+# define ERTS_MSG_RECV_TRACED(P) \
+ (ERTS_TRACE_FLAGS((P)) & F_TRACE_RECEIVE)
-/* Add message last in private message queue */
-#define LINK_MESSAGE_PRIVQ(p, first_msg, last_msg, len) \
- do { \
- LINK_MESSAGE_IMPL(p, first_msg, last_msg, len, msg); \
- } while (0)
+#endif
/* Add message last_msg in message queue */
-#define LINK_MESSAGE(p, first_msg, last_msg, len) \
- LINK_MESSAGE_IMPL(p, first_msg, last_msg, len, msg_inq)
-
-#define ERTS_MSGQ_MV_INQ2PRIVQ(p) \
- do { \
- if (p->msg_inq.first) { \
- *p->msg.last = p->msg_inq.first; \
- p->msg.last = p->msg_inq.last; \
- p->msg.len += p->msg_inq.len; \
- p->msg_inq.first = NULL; \
- p->msg_inq.last = &p->msg_inq.first; \
- p->msg_inq.len = 0; \
- } \
- } while (0)
-
+#define LINK_MESSAGE(p, first_msg, last_msg, num_msgs) \
+ do { \
+ ERTS_HDBG_CHECK_SIGNAL_IN_QUEUE__((p), "before"); \
+ *(p)->sig_inq.last = (first_msg); \
+ (p)->sig_inq.last = (last_msg); \
+ (p)->sig_inq.len += (num_msgs); \
+ ERTS_HDBG_CHECK_SIGNAL_IN_QUEUE__((p), "before"); \
+ } while(0)
/* Unlink current message */
-#define UNLINK_MESSAGE(p,msgp) do { \
- ErtsMessage* __mp = (msgp)->next; \
- *(p)->msg.save = __mp; \
- (p)->msg.len--; \
- if (__mp == NULL) \
- (p)->msg.last = (p)->msg.save; \
- (p)->msg.mark = 0; \
-} while(0)
+#define UNLINK_MESSAGE(p,msgp) \
+ do { \
+ ErtsMessage *mp__ = (msgp)->next; \
+ ERTS_HDBG_CHECK_SIGNAL_PRIV_QUEUE__((p), 0, "before"); \
+ *(p)->sig_qs.save = mp__; \
+ (p)->sig_qs.len--; \
+ if (mp__ == NULL) \
+ (p)->sig_qs.last = (p)->sig_qs.save; \
+ ERTS_HDBG_CHECK_SIGNAL_PRIV_QUEUE__((p), 0, "after"); \
+ } while(0)
-/* Reset message save point (after receive match) */
-#define JOIN_MESSAGE(p) \
- (p)->msg.save = &(p)->msg.first
+/*
+ * Reset message save point (after receive match).
+ * Also invalidate the saved position since it may no
+ * longer be safe to use.
+ */
+#define JOIN_MESSAGE(p) \
+ do { \
+ (p)->sig_qs.save = &(p)->sig_qs.first; \
+ ERTS_RECV_MARK_CLEAR((p)); \
+ } while(0)
/* Save current message */
#define SAVE_MESSAGE(p) \
- (p)->msg.save = &(*(p)->msg.save)->next
+ (p)->sig_qs.save = &(*(p)->sig_qs.save)->next
#define ERTS_SND_FLG_NO_SEQ_TRACE (((unsigned) 1) << 0)
@@ -272,6 +426,7 @@ typedef struct erl_trace_message_queue__ {
(MP)->next = NULL; \
ERL_MESSAGE_TERM(MP) = THE_NON_VALUE; \
ERL_MESSAGE_TOKEN(MP) = NIL; \
+ ERL_MESSAGE_FROM(MP) = NIL; \
ERL_MESSAGE_DT_UTAG_INIT(MP); \
MP->data.attached = NULL; \
} while (0)
@@ -284,7 +439,7 @@ void free_message_buffer(ErlHeapFragment *);
void erts_queue_dist_message(Process*, ErtsProcLocks, ErtsDistExternal *, Eterm, Eterm);
Sint erts_queue_message(Process*, ErtsProcLocks,ErtsMessage*, Eterm, Eterm);
Sint erts_queue_messages(Process*, ErtsProcLocks,
- ErtsMessage*, ErtsMessage**, Uint, Eterm);
+ ErtsMessage*, ErtsMessage**, Uint);
void erts_deliver_exit_message(Eterm, Process*, ErtsProcLocks *, Eterm, Eterm);
Sint erts_send_message(Process*, Process*, ErtsProcLocks*, Eterm, unsigned);
void erts_link_mbuf_to_proc(Process *proc, ErlHeapFragment *bp);
@@ -301,16 +456,6 @@ int erts_decode_dist_message(Process *, ErtsProcLocks, ErtsMessage *, int);
void erts_cleanup_messages(ErtsMessage *mp);
-typedef struct {
- Uint size;
- ErtsMessage *msgp;
-} ErtsMessageInfo;
-
-Uint erts_prep_msgq_for_inspection(Process *c_p,
- Process *rp,
- ErtsProcLocks rp_locks,
- ErtsMessageInfo *mip);
-
void *erts_alloc_message_ref(void);
void erts_free_message_ref(void *);
@@ -352,12 +497,6 @@ ERTS_GLB_FORCE_INLINE ErtsMessage *erts_shrink_message(ErtsMessage *mp, Uint sz,
ERTS_GLB_FORCE_INLINE void erts_free_message(ErtsMessage *mp);
ERTS_GLB_INLINE Uint erts_used_frag_sz(const ErlHeapFragment*);
ERTS_GLB_INLINE Uint erts_msg_attached_data_size(ErtsMessage *msg);
-ERTS_GLB_INLINE void erts_msgq_update_internal_pointers(ErlMessageQueue *msgq,
- ErtsMessage **newpp,
- ErtsMessage **oldpp);
-ERTS_GLB_INLINE void erts_msgq_replace_msg_ref(ErlMessageQueue *msgq,
- ErtsMessage *newp,
- ErtsMessage **oldpp);
#define ERTS_MSG_COMBINED_HFRAG ((void *) 0x1)
@@ -461,28 +600,6 @@ ERTS_GLB_INLINE Uint erts_msg_attached_data_size(ErtsMessage *msg)
}
}
-ERTS_GLB_INLINE void
-erts_msgq_update_internal_pointers(ErlMessageQueue *msgq,
- ErtsMessage **newpp,
- ErtsMessage **oldpp)
-{
- if (msgq->save == oldpp)
- msgq->save = newpp;
- if (msgq->last == oldpp)
- msgq->last = newpp;
- if (msgq->saved_last == oldpp)
- msgq->saved_last = newpp;
-}
-
-ERTS_GLB_INLINE void
-erts_msgq_replace_msg_ref(ErlMessageQueue *msgq, ErtsMessage *newp, ErtsMessage **oldpp)
-{
- ErtsMessage *oldp = *oldpp;
- newp->next = oldp->next;
- erts_msgq_update_internal_pointers(msgq, &newp->next, &oldp->next);
- *oldpp = newp;
-}
-
#endif
Uint erts_mbuf_size(Process *p);
@@ -496,4 +613,17 @@ Uint erts_mbuf_size(Process *p);
# define ERTS_CHK_MBUF_SZ(P) ((void) 1)
#endif
+#define ERTS_FOREACH_SIG_PRIVQS(PROC, MVAR, CODE) \
+ do { \
+ int i__; \
+ ErtsMessage *msgs__[] = {(PROC)->sig_qs.first, \
+ (PROC)->sig_qs.cont}; \
+ for (i__ = 0; i__ < sizeof(msgs__)/sizeof(msgs__[0]); i__++) { \
+ ErtsMessage *MVAR; \
+ for (MVAR = msgs__[i__]; MVAR; MVAR = MVAR->next) { \
+ CODE; \
+ } \
+ } \
+ } while (0)
+
#endif
diff --git a/erts/emulator/beam/erl_monitor_link.c b/erts/emulator/beam/erl_monitor_link.c
new file mode 100644
index 0000000000..70f36fb6b7
--- /dev/null
+++ b/erts/emulator/beam/erl_monitor_link.c
@@ -0,0 +1,1341 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2018. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * %CopyrightEnd%
+ */
+
+/*
+ * Description: Monitor and link implementation.
+ *
+ * Author: Rickard Green
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "sys.h"
+#include <stddef.h>
+#include "global.h"
+#include "erl_node_tables.h"
+#include "erl_monitor_link.h"
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\
+ * Red-black tree implementation used for monitors and links. *
+\* */
+
+static ERTS_INLINE Eterm
+ml_get_key(ErtsMonLnkNode *mln)
+{
+ char *ptr = (char *) mln;
+ ptr += mln->key_offset;
+
+#ifdef ERTS_ML_DEBUG
+ switch (mln->type) {
+ case ERTS_MON_TYPE_PROC:
+ case ERTS_MON_TYPE_PORT:
+ case ERTS_MON_TYPE_DIST_PROC:
+ case ERTS_MON_TYPE_TIME_OFFSET:
+ case ERTS_MON_TYPE_RESOURCE: {
+ ErtsMonitorData *mdp = erts_monitor_to_data(mln);
+ ERTS_ML_ASSERT(&mdp->ref == (Eterm *) ptr);
+ break;
+ }
+ case ERTS_LNK_TYPE_PROC:
+ case ERTS_LNK_TYPE_DIST_PROC:
+ case ERTS_LNK_TYPE_PORT:
+ case ERTS_MON_TYPE_NODE:
+ case ERTS_MON_TYPE_NODES:
+ case ERTS_MON_TYPE_SUSPEND:
+ ERTS_ML_ASSERT(&mln->other.item == (Eterm *) ptr);
+ break;
+ default:
+ ERTS_INTERNAL_ERROR("Invalid type");
+ break;
+ }
+#endif
+
+ return *((Eterm *) ptr);
+}
+
+/*
+ * Comparison functions for valid *and only valid* keys. That
+ * is, if the set of valid keys is changed, this function needs
+ * to be updated...
+ *
+ * Note that this function does *not* order terms according to
+ * term order, and that the order may vary between emulator
+ * restarts...
+ */
+static int
+ml_cmp_keys(Eterm key1, Eterm key2)
+{
+ /*
+ * A monitor key is either an internal pid, a (nodename) atom,
+ * a small (monitor nodes bitmask), an ordinary internal ref,
+ * or an external ref.
+ *
+ * Order between monitors with keys of different types:
+ * internal pid < atom < small < internal ref < external ref
+ *
+ * A link key is either a pid, an internal port
+ *
+ * Order between links with keys of different types:
+ * internal pid < internal port < external pid
+ *
+ */
+ ERTS_ML_ASSERT(is_internal_pid(key1)
+ || is_internal_port(key1)
+ || is_atom(key1)
+ || is_small(key1)
+ || is_external_pid(key1)
+ || is_internal_ordinary_ref(key1)
+ || is_external_ref(key1));
+
+ ERTS_ML_ASSERT(is_internal_pid(key2)
+ || is_internal_port(key2)
+ || is_atom(key2)
+ || is_small(key2)
+ || is_external_pid(key2)
+ || is_internal_ordinary_ref(key2)
+ || is_external_ref(key2));
+
+ if (is_immed(key1)) {
+ int key1_tag, key2_tag;
+
+ ERTS_ML_ASSERT(is_internal_pid(key1)
+ || is_internal_port(key1)
+ || is_atom(key1)
+ || is_small(key1));
+
+ if (key1 == key2)
+ return 0;
+
+ if (is_boxed(key2))
+ return -1;
+
+ ERTS_ML_ASSERT(is_internal_pid(key2)
+ || is_internal_port(key2)
+ || is_atom(key2)
+ || is_small(key2));
+
+ key1_tag = (int) (key1 & _TAG_IMMED1_MASK);
+ key2_tag = (int) (key2 & _TAG_IMMED1_MASK);
+
+ if (key1_tag != key2_tag)
+ return key1_tag - key2_tag;
+
+ ASSERT((is_atom(key1) && is_atom(key2))
+ || (is_small(key1) && is_small(key2))
+ || (is_internal_pid(key1) && is_internal_pid(key2))
+ || (is_internal_port(key1) && is_internal_port(key2)));
+
+ return key1 < key2 ? -1 : 1;
+ }
+ else {
+ Eterm *w1, hdr1;
+
+ ERTS_ML_ASSERT(is_boxed(key1));
+
+ w1 = boxed_val(key1);
+ hdr1 = *w1;
+
+ if ((hdr1 & _TAG_HEADER_MASK) == _TAG_HEADER_REF) {
+ Eterm *w2;
+
+ if (!is_internal_ref(key2))
+ return is_immed(key2) ? 1 : -1;
+
+ w2 = internal_ref_val(key2);
+
+ ERTS_ML_ASSERT(w1[0] == ERTS_REF_THING_HEADER);
+ ERTS_ML_ASSERT(w2[0] == ERTS_REF_THING_HEADER);
+
+ return sys_memcmp((void *) &w1[1], (void *) &w2[1],
+ (ERTS_REF_THING_SIZE - 1)*sizeof(Eterm));
+ }
+
+ ERTS_ML_ASSERT(is_external(key1));
+
+ if (is_not_external(key2))
+ return 1;
+ else {
+ Uint ndw1, ndw2;
+ ExternalThing *et1, *et2;
+ ErlNode *n1, *n2;
+
+ ERTS_ML_ASSERT((is_external_ref(key1) && is_external_ref(key2))
+ || (is_external_pid(key1) && is_external_pid(key2)));
+
+ et1 = (ExternalThing *) w1;
+ et2 = (ExternalThing *) external_val(key2);
+
+ n1 = et1->node;
+ n2 = et2->node;
+
+ if (n1 != n2) {
+ if (n1->sysname != n2->sysname)
+ return n1->sysname < n2->sysname ? -1 : 1;
+ ASSERT(n1->creation != n2->creation);
+ return n1->creation < n2->creation ? -1 : 1;
+ }
+
+ ndw1 = external_thing_data_words(et1);
+ ndw2 = external_thing_data_words(et1);
+ if (ndw1 != ndw2)
+ return ndw1 < ndw2 ? -1 : 1;
+
+ return sys_memcmp((void *) &et1->data.ui[0],
+ (void *) &et2->data.ui[0],
+ ndw1*sizeof(Eterm));
+ }
+ }
+}
+
+#define ERTS_ML_TPFLG_RED (((UWord) 1) << 0)
+
+#define ERTS_ML_TPFLGS_MASK (ERTS_ML_TPFLG_RED)
+
+#define ERTS_RBT_PREFIX mon_lnk
+#define ERTS_RBT_T ErtsMonLnkNode
+#define ERTS_RBT_KEY_T Eterm
+#define ERTS_RBT_FLAGS_T UWord
+#define ERTS_RBT_INIT_EMPTY_TNODE(T) \
+ do { \
+ (T)->node.tree.parent = (UWord) NULL; \
+ (T)->node.tree.right = NULL; \
+ (T)->node.tree.left = NULL; \
+ } while (0)
+#define ERTS_RBT_IS_RED(T) \
+ (!!((T)->node.tree.parent & ERTS_ML_TPFLG_RED))
+#define ERTS_RBT_SET_RED(T) \
+ ((T)->node.tree.parent |= ERTS_ML_TPFLG_RED)
+#define ERTS_RBT_IS_BLACK(T) \
+ (!ERTS_RBT_IS_RED((T)))
+#define ERTS_RBT_SET_BLACK(T) \
+ ((T)->node.tree.parent &= ~ERTS_ML_TPFLG_RED)
+#define ERTS_RBT_GET_FLAGS(T) \
+ ((T)->node.tree.parent & ERTS_ML_TPFLGS_MASK)
+#define ERTS_RBT_SET_FLAGS(T, F) \
+ do { \
+ ERTS_ML_ASSERT((((UWord) (F)) & ~ERTS_ML_TPFLGS_MASK) == 0); \
+ (T)->node.tree.parent &= ~ERTS_ML_TPFLGS_MASK; \
+ (T)->node.tree.parent |= (F); \
+ } while (0)
+#define ERTS_RBT_GET_PARENT(T) \
+ ((ERTS_RBT_T *) ((T)->node.tree.parent & ~ERTS_ML_TPFLGS_MASK))
+#define ERTS_RBT_SET_PARENT(T, P) \
+ do { \
+ ERTS_ML_ASSERT((((UWord) (P)) & ERTS_ML_TPFLGS_MASK) == 0); \
+ (T)->node.tree.parent &= ERTS_ML_TPFLGS_MASK; \
+ (T)->node.tree.parent |= (UWord) (P); \
+ } while (0)
+#define ERTS_RBT_GET_RIGHT(T) ((T)->node.tree.right)
+#define ERTS_RBT_SET_RIGHT(T, R) ((T)->node.tree.right = (R))
+#define ERTS_RBT_GET_LEFT(T) ((T)->node.tree.left)
+#define ERTS_RBT_SET_LEFT(T, L) ((T)->node.tree.left = (L))
+#define ERTS_RBT_GET_KEY(T) (ml_get_key((T)))
+#define ERTS_RBT_CMP_KEYS(KX, KY) (ml_cmp_keys((KX), (KY)))
+#define ERTS_RBT_WANT_DELETE
+#define ERTS_RBT_WANT_LOOKUP
+#define ERTS_RBT_WANT_LOOKUP_INSERT
+#define ERTS_RBT_WANT_LOOKUP_CREATE
+#define ERTS_RBT_WANT_INSERT
+#define ERTS_RBT_WANT_REPLACE
+#define ERTS_RBT_WANT_FOREACH
+#define ERTS_RBT_WANT_FOREACH_YIELDING
+#define ERTS_RBT_WANT_FOREACH_DESTROY
+#define ERTS_RBT_WANT_FOREACH_DESTROY_YIELDING
+#define ERTS_RBT_UNDEF
+
+#include "erl_rbtree.h"
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\
+ * Tree Operations *
+\* */
+
+static ErtsMonLnkNode *
+ml_rbt_lookup(ErtsMonLnkNode *root, Eterm ref)
+{
+ ErtsMonLnkNode *ml = mon_lnk_rbt_lookup(root, ref);
+ ASSERT(!ml || (ml->flags & ERTS_ML_FLG_IN_TABLE));
+ return ml;
+}
+
+static ErtsMonLnkNode *
+ml_rbt_lookup_insert(ErtsMonLnkNode **root, ErtsMonLnkNode *ml)
+{
+ ErtsMonLnkNode *res;
+ ERTS_ML_ASSERT(!(ml->flags & ERTS_ML_FLG_IN_TABLE));
+ res = mon_lnk_rbt_lookup_insert(root, ml);
+ if (!res)
+ ml->flags |= ERTS_ML_FLG_IN_TABLE;
+ return res;
+}
+
+static ErtsMonLnkNode *
+ml_rbt_lookup_create(ErtsMonLnkNode ** root, Eterm key,
+ ErtsMonLnkNode *(*create)(Eterm, void *),
+ void *carg, int *created)
+{
+ ErtsMonLnkNode *ml;
+ ml = mon_lnk_rbt_lookup_create(root, key, create, carg, created);
+ if (*created)
+ ml->flags |= ERTS_ML_FLG_IN_TABLE;
+ return ml;
+}
+
+static void
+ml_rbt_insert(ErtsMonLnkNode **root, ErtsMonLnkNode *ml)
+{
+#ifdef ERTS_ML_DEBUG
+ ErtsMonLnkNode *res;
+ ERTS_ML_ASSERT(!(ml->flags & ERTS_ML_FLG_IN_TABLE));
+ res = ml_rbt_lookup(*root, ml_get_key(ml));
+ ERTS_ML_ASSERT(res == NULL);
+#endif
+
+ mon_lnk_rbt_insert(root, ml);
+ ml->flags |= ERTS_ML_FLG_IN_TABLE;
+}
+
+static void
+ml_rbt_replace(ErtsMonLnkNode **root, ErtsMonLnkNode *old, ErtsMonLnkNode *new)
+{
+ ERTS_ML_ASSERT(old->flags & ERTS_ML_FLG_IN_TABLE);
+ ERTS_ML_ASSERT(!(new->flags & ERTS_ML_FLG_IN_TABLE));
+
+ mon_lnk_rbt_replace(root, old, new);
+ old->flags &= ~ERTS_ML_FLG_IN_TABLE;
+ new->flags |= ERTS_ML_FLG_IN_TABLE;
+}
+
+static void
+ml_rbt_delete(ErtsMonLnkNode **root, ErtsMonLnkNode *ml)
+{
+ ERTS_ML_ASSERT(ml->flags & ERTS_ML_FLG_IN_TABLE);
+ mon_lnk_rbt_delete(root, ml);
+ ml->flags &= ~ERTS_ML_FLG_IN_TABLE;
+}
+
+
+static void
+ml_rbt_foreach(ErtsMonLnkNode *root,
+ void (*func)(ErtsMonLnkNode *, void *),
+ void *arg)
+{
+ mon_lnk_rbt_foreach(root, func, arg);
+}
+
+typedef struct {
+ ErtsMonLnkNode *root;
+ mon_lnk_rbt_yield_state_t rbt_ystate;
+} ErtsMonLnkYieldState;
+
+static int
+ml_rbt_foreach_yielding(ErtsMonLnkNode *root,
+ void (*func)(ErtsMonLnkNode *, void *),
+ void *arg,
+ void **vyspp,
+ Sint limit)
+{
+ int res;
+ ErtsMonLnkYieldState ys = {root, ERTS_RBT_YIELD_STAT_INITER};
+ ErtsMonLnkYieldState *ysp;
+
+ ysp = (ErtsMonLnkYieldState *) *vyspp;
+ if (!ysp)
+ ysp = &ys;
+ res = mon_lnk_rbt_foreach_yielding(ysp->root, func, arg,
+ &ysp->rbt_ystate, limit);
+ if (res == 0) {
+ if (ysp != &ys)
+ erts_free(ERTS_ALC_T_ML_YIELD_STATE, ysp);
+ *vyspp = NULL;
+ }
+ else {
+
+ if (ysp == &ys) {
+ ysp = erts_alloc(ERTS_ALC_T_ML_YIELD_STATE,
+ sizeof(ErtsMonLnkYieldState));
+ sys_memcpy((void *) ysp, (void *) &ys,
+ sizeof(ErtsMonLnkYieldState));
+ }
+
+ *vyspp = (void *) ysp;
+ }
+
+ return res;
+}
+
+typedef struct {
+ void (*func)(ErtsMonLnkNode *, void *);
+ void *arg;
+} ErtsMonLnkForeachDeleteContext;
+
+static void
+rbt_wrap_foreach_delete(ErtsMonLnkNode *ml, void *vctxt)
+{
+ ErtsMonLnkForeachDeleteContext *ctxt = vctxt;
+ ERTS_ML_ASSERT(ml->flags & ERTS_ML_FLG_IN_TABLE);
+ ml->flags &= ~ERTS_ML_FLG_IN_TABLE;
+ ctxt->func(ml, ctxt->arg);
+}
+
+static void
+ml_rbt_foreach_delete(ErtsMonLnkNode **root,
+ void (*func)(ErtsMonLnkNode *, void *),
+ void *arg)
+{
+ ErtsMonLnkForeachDeleteContext ctxt;
+ ctxt.func = func;
+ ctxt.arg = arg;
+ mon_lnk_rbt_foreach_destroy(root,
+ rbt_wrap_foreach_delete,
+ (void *) &ctxt);
+}
+
+static int
+ml_rbt_foreach_delete_yielding(ErtsMonLnkNode **root,
+ void (*func)(ErtsMonLnkNode *, void *),
+ void *arg,
+ void **vyspp,
+ Sint limit)
+{
+ int res;
+ ErtsMonLnkYieldState ys = {*root, ERTS_RBT_YIELD_STAT_INITER};
+ ErtsMonLnkYieldState *ysp;
+ ErtsMonLnkForeachDeleteContext ctxt;
+ ctxt.func = func;
+ ctxt.arg = arg;
+
+ ysp = (ErtsMonLnkYieldState *) *vyspp;
+ if (!ysp) {
+ *root = NULL;
+ ysp = &ys;
+ }
+ res = mon_lnk_rbt_foreach_destroy_yielding(&ysp->root,
+ rbt_wrap_foreach_delete,
+ (void *) &ctxt,
+ &ysp->rbt_ystate,
+ limit);
+ if (res == 0) {
+ if (ysp != &ys)
+ erts_free(ERTS_ALC_T_ML_YIELD_STATE, ysp);
+ *vyspp = NULL;
+ }
+ else {
+
+ if (ysp == &ys) {
+ ysp = erts_alloc(ERTS_ALC_T_ML_YIELD_STATE,
+ sizeof(ErtsMonLnkYieldState));
+ sys_memcpy((void *) ysp, (void *) &ys,
+ sizeof(ErtsMonLnkYieldState));
+ }
+
+ *vyspp = (void *) ysp;
+ }
+
+ return res;
+}
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\
+ * List Operations *
+\* */
+
+static int
+ml_dl_list_foreach_yielding(ErtsMonLnkNode *list,
+ void (*func)(ErtsMonLnkNode *, void *),
+ void *arg,
+ void **vyspp,
+ Sint limit)
+{
+ Sint cnt = 0;
+ ErtsMonLnkNode *ml = (ErtsMonLnkNode *) *vyspp;
+
+ ERTS_ML_ASSERT(!ml || list);
+
+ if (!ml)
+ ml = list;
+
+ if (ml) {
+ do {
+ ERTS_ML_ASSERT(ml->flags & ERTS_ML_FLG_IN_TABLE);
+ func(ml, arg);
+ ml = ml->node.list.next;
+ cnt++;
+ } while (ml != list && cnt < limit);
+ if (ml != list) {
+ *vyspp = (void *) ml;
+ return 1; /* yield */
+ }
+ }
+
+ *vyspp = NULL;
+ return 0; /* done */
+}
+
+static int
+ml_dl_list_foreach_delete_yielding(ErtsMonLnkNode **list,
+ void (*func)(ErtsMonLnkNode *, void *),
+ void *arg,
+ void **vyspp,
+ Sint limit)
+{
+ Sint cnt = 0;
+ ErtsMonLnkNode *first = *list;
+ ErtsMonLnkNode *ml = (ErtsMonLnkNode *) *vyspp;
+
+ ERTS_ML_ASSERT(!ml || first);
+
+ if (!ml)
+ ml = first;
+
+ if (ml) {
+ do {
+ ErtsMonLnkNode *next = ml->node.list.next;
+ ERTS_ML_ASSERT(ml->flags & ERTS_ML_FLG_IN_TABLE);
+ ml->flags &= ~ERTS_ML_FLG_IN_TABLE;
+ func(ml, arg);
+ ml = next;
+ cnt++;
+ } while (ml != first && cnt < limit);
+ if (ml != first) {
+ *vyspp = (void *) ml;
+ return 1; /* yield */
+ }
+ }
+
+ *vyspp = NULL;
+ *list = NULL;
+ return 0; /* done */
+}
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\
+ * Misc *
+\* */
+
+ErtsMonLnkDist *
+erts_mon_link_dist_create(Eterm nodename)
+{
+ ErtsMonLnkDist *mld = erts_alloc(ERTS_ALC_T_ML_DIST,
+ sizeof(ErtsMonLnkDist));
+ mld->nodename = nodename;
+ mld->connection_id = ~((Uint32) 0);
+ erts_atomic_init_nob(&mld->refc, 1);
+ erts_mtx_init(&mld->mtx, "dist_entry_links", nodename,
+ ERTS_LOCK_FLAGS_CATEGORY_DISTRIBUTION);
+ mld->alive = !0;
+ mld->links = NULL;
+ mld->monitors = NULL;
+ mld->orig_name_monitors = NULL;
+ return mld;
+}
+
+void
+erts_mon_link_dist_destroy__(ErtsMonLnkDist *mld)
+{
+ ERTS_ML_ASSERT(erts_atomic_read_nob(&mld->refc) == 0);
+ ERTS_ML_ASSERT(!mld->alive);
+ ERTS_ML_ASSERT(!mld->links);
+ ERTS_ML_ASSERT(!mld->monitors);
+ ERTS_ML_ASSERT(!mld->orig_name_monitors);
+
+ erts_mtx_destroy(&mld->mtx);
+ erts_free(ERTS_ALC_T_ML_DIST, mld);
+}
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\
+ * Monitor Operations *
+\* */
+
+#ifdef ERTS_ML_DEBUG
+size_t erts_monitor_origin_offset;
+size_t erts_monitor_origin_key_offset;
+size_t erts_monitor_target_offset;
+size_t erts_monitor_target_key_offset;
+size_t erts_monitor_node_key_offset;
+#endif
+
+static ERTS_INLINE void
+monitor_init(void)
+{
+#ifdef ERTS_ML_DEBUG
+ erts_monitor_origin_offset = offsetof(ErtsMonitorData, origin);
+ erts_monitor_origin_key_offset = offsetof(ErtsMonitorData, ref);
+ ASSERT(erts_monitor_origin_key_offset >= erts_monitor_origin_offset);
+ erts_monitor_origin_key_offset -= erts_monitor_origin_offset;
+ erts_monitor_target_offset = offsetof(ErtsMonitorData, target);
+ erts_monitor_target_key_offset = offsetof(ErtsMonitorData, ref);
+ ASSERT(erts_monitor_target_key_offset >= erts_monitor_target_offset);
+ erts_monitor_target_key_offset -= erts_monitor_target_offset;
+ erts_monitor_node_key_offset = offsetof(ErtsMonitor, other.item);
+#endif
+}
+
+ErtsMonitor *
+erts_monitor_tree_lookup(ErtsMonitor *root, Eterm key)
+{
+ ERTS_ML_ASSERT(is_internal_ordinary_ref(key)
+ || is_external_ref(key)
+ || is_atom(key)
+ || is_small(key)
+ || is_internal_pid(key));
+ return (ErtsMonitor *) ml_rbt_lookup((ErtsMonLnkNode *) root, key);
+}
+
+ErtsMonitor *
+erts_monotor_tree_lookup_insert(ErtsMonitor **root, ErtsMonitor *mon)
+{
+ return (ErtsMonitor *) ml_rbt_lookup_insert((ErtsMonLnkNode **) root,
+ (ErtsMonLnkNode *) mon);
+}
+
+typedef struct {
+ Uint16 type;
+ Eterm origin;
+} ErtsMonitorCreateCtxt;
+
+static ErtsMonLnkNode *
+create_monitor(Eterm target, void *vcctxt)
+{
+ ErtsMonitorCreateCtxt *cctxt = vcctxt;
+ ErtsMonitorData *mdp = erts_monitor_create(cctxt->type,
+ NIL,
+ cctxt->origin,
+ target,
+ NIL);
+ ERTS_ML_ASSERT(ml_cmp_keys(ml_get_key(&mdp->origin), target) == 0);
+ return (ErtsMonLnkNode *) &mdp->origin;
+}
+
+ErtsMonitor *
+erts_monitor_tree_lookup_create(ErtsMonitor **root, int *created, Uint16 type,
+ Eterm origin, Eterm target)
+{
+ ErtsMonitor *res;
+ ErtsMonitorCreateCtxt cctxt = {type, origin};
+
+ ERTS_ML_ASSERT(type == ERTS_MON_TYPE_NODE || type == ERTS_MON_TYPE_NODES);
+
+ res = (ErtsMonitor *) ml_rbt_lookup_create((ErtsMonLnkNode **) root,
+ target, create_monitor,
+ (void *) &cctxt,
+ created);
+
+ ERTS_ML_ASSERT(res && erts_monitor_is_origin(res));
+
+ return res;
+}
+
+void
+erts_monitor_tree_insert(ErtsMonitor **root, ErtsMonitor *mon)
+{
+ ml_rbt_insert((ErtsMonLnkNode **) root, (ErtsMonLnkNode *) mon);
+}
+
+void
+erts_monitor_tree_replace(ErtsMonitor **root, ErtsMonitor *old, ErtsMonitor *new)
+{
+ ml_rbt_replace((ErtsMonLnkNode **) root,
+ (ErtsMonLnkNode *) old,
+ (ErtsMonLnkNode *) new);
+}
+
+void
+erts_monitor_tree_delete(ErtsMonitor **root, ErtsMonitor *mon)
+{
+ ml_rbt_delete((ErtsMonLnkNode **) root, (ErtsMonLnkNode *) mon);
+}
+
+void
+erts_monitor_tree_foreach(ErtsMonitor *root,
+ void (*func)(ErtsMonitor *, void *),
+ void *arg)
+{
+ ml_rbt_foreach((ErtsMonLnkNode *) root,
+ (void (*)(ErtsMonLnkNode*, void*)) func,
+ arg);
+}
+
+int
+erts_monitor_tree_foreach_yielding(ErtsMonitor *root,
+ void (*func)(ErtsMonitor *, void *),
+ void *arg,
+ void **vyspp,
+ Sint limit)
+{
+ return ml_rbt_foreach_yielding((ErtsMonLnkNode *) root,
+ (void (*)(ErtsMonLnkNode*, void*)) func,
+ arg, vyspp, limit);
+}
+
+void
+erts_monitor_tree_foreach_delete(ErtsMonitor **root,
+ void (*func)(ErtsMonitor *, void *),
+ void *arg)
+{
+ ml_rbt_foreach_delete((ErtsMonLnkNode **) root,
+ (void (*)(ErtsMonLnkNode*, void*)) func,
+ arg);
+}
+
+int
+erts_monitor_tree_foreach_delete_yielding(ErtsMonitor **root,
+ void (*func)(ErtsMonitor *, void *),
+ void *arg,
+ void **vyspp,
+ Sint limit)
+{
+ return ml_rbt_foreach_delete_yielding((ErtsMonLnkNode **) root,
+ (void (*)(ErtsMonLnkNode*, void*)) func,
+ arg, vyspp, limit);
+}
+
+void
+erts_monitor_list_foreach(ErtsMonitor *list,
+ void (*func)(ErtsMonitor *, void *),
+ void *arg)
+{
+ void *ystate = NULL;
+ while (ml_dl_list_foreach_yielding((ErtsMonLnkNode *) list,
+ (void (*)(ErtsMonLnkNode *, void *)) func,
+ arg, &ystate, (Sint) INT_MAX));
+}
+
+int
+erts_monitor_list_foreach_yielding(ErtsMonitor *list,
+ void (*func)(ErtsMonitor *, void *),
+ void *arg,
+ void **vyspp,
+ Sint limit)
+{
+ return ml_dl_list_foreach_yielding((ErtsMonLnkNode *) list,
+ (void (*)(ErtsMonLnkNode *, void *)) func,
+ arg, vyspp, limit);
+}
+
+void
+erts_monitor_list_foreach_delete(ErtsMonitor **list,
+ void (*func)(ErtsMonitor *, void *),
+ void *arg)
+{
+ void *ystate = NULL;
+ while (ml_dl_list_foreach_delete_yielding((ErtsMonLnkNode **) list,
+ (void (*)(ErtsMonLnkNode*, void*)) func,
+ arg, &ystate, (Sint) INT_MAX));
+}
+
+int
+erts_monitor_list_foreach_delete_yielding(ErtsMonitor **list,
+ void (*func)(ErtsMonitor *, void *),
+ void *arg,
+ void **vyspp,
+ Sint limit)
+{
+ return ml_dl_list_foreach_delete_yielding((ErtsMonLnkNode **) list,
+ (void (*)(ErtsMonLnkNode*, void*)) func,
+ arg, vyspp, limit);
+}
+
+ErtsMonitorData *
+erts_monitor_create(Uint16 type, Eterm ref, Eterm orgn, Eterm trgt, Eterm name)
+{
+ ErtsMonitorData *mdp;
+
+ switch (type) {
+ case ERTS_MON_TYPE_PROC:
+ case ERTS_MON_TYPE_PORT:
+ case ERTS_MON_TYPE_TIME_OFFSET:
+ if (is_nil(name)) {
+ ErtsMonitorDataHeap *mdhp;
+ ErtsORefThing *ortp;
+
+ ERTS_ML_ASSERT(is_immed(orgn) && is_immed(trgt));
+ ERTS_ML_ASSERT(is_internal_ordinary_ref(ref));
+
+ mdhp = erts_alloc(ERTS_ALC_T_MONITOR, sizeof(ErtsMonitorDataHeap));
+ mdp = &mdhp->md;
+ ERTS_ML_ASSERT(((void *) mdp) == ((void *) mdhp));
+
+ ortp = (ErtsORefThing *) (char *) internal_ref_val(ref);
+ mdhp->oref_thing = *ortp;
+ mdp->ref = make_internal_ref(&mdhp->oref_thing.header);
+
+ mdp->origin.other.item = trgt;
+ mdp->origin.offset = (Uint16) offsetof(ErtsMonitorData, origin);
+ mdp->origin.key_offset = (Uint16) offsetof(ErtsMonitorData, ref);
+ ERTS_ML_ASSERT(mdp->origin.key_offset >= mdp->origin.offset);
+ mdp->origin.key_offset -= mdp->origin.offset;
+ mdp->origin.flags = (Uint16) 0;
+ mdp->origin.type = type;
+
+ mdp->target.other.item = orgn;
+ mdp->target.offset = (Uint16) offsetof(ErtsMonitorData, target);
+ mdp->target.key_offset = (Uint16) offsetof(ErtsMonitorData, ref);
+ ERTS_ML_ASSERT(mdp->target.key_offset >= mdp->target.offset);
+ mdp->target.key_offset -= mdp->target.offset;
+ mdp->target.flags = ERTS_ML_FLG_TARGET;
+ mdp->target.type = type;
+ break;
+ }
+ case ERTS_MON_TYPE_DIST_PROC:
+ case ERTS_MON_TYPE_RESOURCE:
+ case ERTS_MON_TYPE_NODE:
+ case ERTS_MON_TYPE_NODES: {
+ ErtsMonitorDataExtended *mdep;
+ Uint size = sizeof(ErtsMonitorDataExtended) - sizeof(Eterm);
+ Uint rsz, osz, tsz;
+ Eterm *hp;
+ ErlOffHeap oh;
+ Uint16 name_flag = is_nil(name) ? ((Uint16) 0) : ERTS_ML_FLG_NAME;
+
+ rsz = is_immed(ref) ? 0 : size_object(ref);
+ tsz = is_immed(trgt) ? 0 : size_object(trgt);
+ if (type == ERTS_MON_TYPE_RESOURCE)
+ osz = 0;
+ else
+ osz = is_immed(orgn) ? 0 : size_object(orgn);
+
+ size += (rsz + osz + tsz) * sizeof(Eterm);
+
+ mdep = erts_alloc(ERTS_ALC_T_MONITOR_EXT, size);
+
+ ERTS_INIT_OFF_HEAP(&oh);
+
+ hp = &mdep->heap[0];
+
+ mdp = &mdep->md;
+ ERTS_ML_ASSERT(((void *) mdp) == ((void *) mdep));
+
+ mdp->ref = rsz ? copy_struct(ref, rsz, &hp, &oh) : ref;
+
+ mdp->origin.other.item = tsz ? copy_struct(trgt, tsz, &hp, &oh) : trgt;
+ mdp->origin.offset = (Uint16) offsetof(ErtsMonitorData, origin);
+ mdp->origin.flags = ERTS_ML_FLG_EXTENDED|name_flag;
+ mdp->origin.type = type;
+
+ if (type == ERTS_MON_TYPE_RESOURCE)
+ mdp->target.other.ptr = (void *) orgn;
+ else
+ mdp->target.other.item = osz ? copy_struct(orgn, osz, &hp, &oh) : orgn;
+ mdp->target.offset = (Uint16) offsetof(ErtsMonitorData, target);
+ mdp->target.flags = ERTS_ML_FLG_TARGET|ERTS_ML_FLG_EXTENDED|name_flag;
+ mdp->target.type = type;
+
+ if (type == ERTS_MON_TYPE_NODE || type == ERTS_MON_TYPE_NODES) {
+ mdep->u.refc = 0;
+ mdp->origin.key_offset = (Uint16) offsetof(ErtsMonitor, other.item);
+ mdp->target.key_offset = (Uint16) offsetof(ErtsMonitor, other.item);
+ ERTS_ML_ASSERT(!oh.first);
+ mdep->uptr.node_monitors = NULL;
+ }
+ else {
+ mdep->u.name = name;
+
+ mdp->origin.key_offset = (Uint16) offsetof(ErtsMonitorData, ref);
+ ERTS_ML_ASSERT(mdp->origin.key_offset >= mdp->origin.offset);
+ mdp->origin.key_offset -= mdp->origin.offset;
+
+ mdp->target.key_offset = (Uint16) offsetof(ErtsMonitorData, ref);
+ ERTS_ML_ASSERT(mdp->target.key_offset >= mdp->target.offset);
+ mdp->target.key_offset -= mdp->target.offset;
+
+ mdep->uptr.ohhp = oh.first;
+ }
+ mdep->dist = NULL;
+ break;
+ }
+ case ERTS_MON_TYPE_SUSPEND:
+ ERTS_INTERNAL_ERROR("Use erts_monitor_suspend_create() instead...");
+ mdp = NULL;
+ break;
+ default:
+ ERTS_INTERNAL_ERROR("Invalid monitor type");
+ mdp = NULL;
+ break;
+ }
+
+ erts_atomic32_init_nob(&mdp->refc, 2);
+
+ return mdp;
+}
+
+/*
+ * erts_monitor_destroy__() should only be called from
+ * erts_monitor_release() or erts_monitor_release_both().
+ */
+void
+erts_monitor_destroy__(ErtsMonitorData *mdp)
+{
+ ERTS_ML_ASSERT(erts_atomic32_read_nob(&mdp->refc) == 0);
+ ERTS_ML_ASSERT(!(mdp->origin.flags & ERTS_ML_FLG_IN_TABLE));
+ ERTS_ML_ASSERT(!(mdp->target.flags & ERTS_ML_FLG_IN_TABLE));
+ ERTS_ML_ASSERT((mdp->origin.flags & ERTS_ML_FLGS_SAME)
+ == (mdp->target.flags & ERTS_ML_FLGS_SAME));
+ ERTS_ML_ASSERT(mdp->origin.type != ERTS_MON_TYPE_SUSPEND);
+
+ if (!(mdp->origin.flags & ERTS_ML_FLG_EXTENDED))
+ erts_free(ERTS_ALC_T_MONITOR, mdp);
+ else {
+ ErtsMonitorDataExtended *mdep = (ErtsMonitorDataExtended *) mdp;
+ ErlOffHeap oh;
+ if (mdp->origin.type == ERTS_MON_TYPE_NODE)
+ ERTS_ML_ASSERT(!mdep->uptr.node_monitors);
+ else if (mdep->uptr.ohhp) {
+ ERTS_INIT_OFF_HEAP(&oh);
+ oh.first = mdep->uptr.ohhp;
+ erts_cleanup_offheap(&oh);
+ }
+ if (mdep->dist)
+ erts_mon_link_dist_dec_refc(mdep->dist);
+ erts_free(ERTS_ALC_T_MONITOR_EXT, mdp);
+ }
+}
+
+void
+erts_monitor_set_dead_dist(ErtsMonitor *mon, Eterm nodename)
+{
+ ErtsMonitorDataExtended *mdep;
+ mdep = (ErtsMonitorDataExtended *) erts_monitor_to_data(mon);
+
+ ERTS_ML_ASSERT(mon->flags & ERTS_ML_FLG_EXTENDED);
+ ERTS_ML_ASSERT(mon->type == ERTS_MON_TYPE_DIST_PROC);
+ ERTS_ML_ASSERT(!mdep->dist);
+
+ mdep->dist = erts_mon_link_dist_create(nodename);
+ mdep->dist->alive = 0;
+}
+
+Uint
+erts_monitor_size(ErtsMonitor *mon)
+{
+ Uint size, refc;
+ ErtsMonitorData *mdp = erts_monitor_to_data(mon);
+
+ ERTS_ML_ASSERT(mon->type != ERTS_MON_TYPE_SUSPEND);
+
+ if (!(mon->flags & ERTS_ML_FLG_EXTENDED))
+ size = sizeof(ErtsMonitorDataHeap);
+ else {
+ ErtsMonitorDataExtended *mdep;
+ Uint hsz = 0;
+
+ mdep = (ErtsMonitorDataExtended *) mdp;
+
+ if (mon->type != ERTS_MON_TYPE_NODE
+ && mon->type != ERTS_MON_TYPE_NODES) {
+ if (!is_immed(mdep->md.ref))
+ hsz += NC_HEAP_SIZE(mdep->md.ref);
+ if (mon->type == ERTS_MON_TYPE_DIST_PROC) {
+ if (!is_immed(mdep->md.origin.other.item))
+ hsz += NC_HEAP_SIZE(mdep->md.origin.other.item);
+ if (!is_immed(mdep->md.target.other.item))
+ hsz += NC_HEAP_SIZE(mdep->md.target.other.item);
+ }
+ }
+ size = sizeof(ErtsMonitorDataExtended) + (hsz - 1)*sizeof(Eterm);
+ }
+
+ refc = (Uint) erts_atomic32_read_nob(&mdp->refc);
+ ASSERT(refc > 0);
+
+ return size / refc;
+}
+
+
+/* suspend monitors... */
+
+ErtsMonitorSuspend *
+erts_monitor_suspend_create(Eterm pid)
+{
+ ErtsMonitorSuspend *msp;
+
+ ERTS_ML_ASSERT(is_internal_pid(pid));
+
+ msp = erts_alloc(ERTS_ALC_T_SUSPEND_MON,
+ sizeof(ErtsMonitorSuspend));
+ msp->mon.offset = (Uint16) offsetof(ErtsMonitorSuspend, mon);
+ msp->mon.key_offset = (Uint16) offsetof(ErtsMonitor, other.item);
+ msp->mon.other.item = pid;
+ msp->mon.flags = 0;
+ msp->mon.type = ERTS_MON_TYPE_SUSPEND;
+ msp->pending = 0;
+ msp->active = 0;
+ return msp;
+}
+
+static ErtsMonLnkNode *
+create_monitor_suspend(Eterm pid, void *unused)
+{
+ ErtsMonitorSuspend *msp = erts_monitor_suspend_create(pid);
+ return (ErtsMonLnkNode *) &msp->mon;
+}
+
+ErtsMonitorSuspend *
+erts_monitor_suspend_tree_lookup_create(ErtsMonitor **root, int *created,
+ Eterm pid)
+{
+ ErtsMonitor *mon;
+ mon = (ErtsMonitor *) ml_rbt_lookup_create((ErtsMonLnkNode **) root,
+ pid, create_monitor_suspend,
+ NULL,
+ created);
+ return erts_monitor_suspend(mon);
+}
+
+void
+erts_monitor_suspend_destroy(ErtsMonitorSuspend *msp)
+{
+ ERTS_ML_ASSERT(!(msp->mon.flags & ERTS_ML_FLG_IN_TABLE));
+ erts_free(ERTS_ALC_T_SUSPEND_MON, msp);
+}
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\
+ * Link Operations *
+ * *
+\* */
+
+#ifdef ERTS_ML_DEBUG
+size_t erts_link_a_offset;
+size_t erts_link_b_offset;
+size_t erts_link_key_offset;
+#endif
+
+static ERTS_INLINE void
+link_init(void)
+{
+#ifdef ERTS_ML_DEBUG
+ erts_link_a_offset = offsetof(ErtsLinkData, a);
+ erts_link_b_offset = offsetof(ErtsLinkData, b);
+ erts_link_key_offset = offsetof(ErtsLink, other.item);
+#endif
+}
+
+ErtsLink *
+erts_link_tree_lookup(ErtsLink *root, Eterm key)
+{
+ ASSERT(is_pid(key) || is_internal_port(key));
+ return (ErtsLink *) ml_rbt_lookup((ErtsMonLnkNode *) root, key);
+}
+
+ErtsLink *
+erts_link_tree_lookup_insert(ErtsLink **root, ErtsLink *lnk)
+{
+ return (ErtsLink *) ml_rbt_lookup_insert((ErtsMonLnkNode **) root,
+ (ErtsMonLnkNode *) lnk);
+}
+
+typedef struct {
+ Uint16 type;
+ Eterm a;
+} ErtsLinkCreateCtxt;
+
+static ErtsMonLnkNode *
+create_link(Eterm b, void *vcctxt)
+{
+ ErtsLinkCreateCtxt *cctxt = vcctxt;
+ ErtsLinkData *ldp = erts_link_create(cctxt->type, cctxt->a, b);
+ ERTS_ML_ASSERT(ml_cmp_keys(ldp->a.other.item, b) == 0);
+ return (ErtsMonLnkNode *) &ldp->a;
+}
+
+ErtsLink *erts_link_tree_lookup_create(ErtsLink **root, int *created,
+ Uint16 type, Eterm this,
+ Eterm other)
+{
+ ErtsLinkCreateCtxt cctxt;
+ cctxt.type = type;
+ cctxt.a = this;
+ return (ErtsLink *) ml_rbt_lookup_create((ErtsMonLnkNode **) root,
+ other, create_link,
+ (void *) &cctxt,
+ created);
+}
+
+void
+erts_link_tree_insert(ErtsLink **root, ErtsLink *lnk)
+{
+ ml_rbt_insert((ErtsMonLnkNode **) root, (ErtsMonLnkNode *) lnk);
+}
+
+void
+erts_link_tree_replace(ErtsLink **root, ErtsLink *old, ErtsLink *new)
+{
+ ml_rbt_replace((ErtsMonLnkNode **) root,
+ (ErtsMonLnkNode *) old,
+ (ErtsMonLnkNode *) new);
+}
+
+void
+erts_link_tree_delete(ErtsLink **root, ErtsLink *lnk)
+{
+ ml_rbt_delete((ErtsMonLnkNode **) root, (ErtsMonLnkNode *) lnk);
+}
+
+void
+erts_link_tree_foreach(ErtsLink *root,
+ void (*func)(ErtsLink *, void *),
+ void *arg)
+{
+ ml_rbt_foreach((ErtsMonLnkNode *) root,
+ (void (*)(ErtsMonLnkNode*, void*)) func,
+ arg);
+
+}
+
+int
+erts_link_tree_foreach_yielding(ErtsLink *root,
+ void (*func)(ErtsLink *, void *),
+ void *arg,
+ void **vyspp,
+ Sint limit)
+{
+ return ml_rbt_foreach_yielding((ErtsMonLnkNode *) root,
+ (void (*)(ErtsMonLnkNode*, void*)) func,
+ arg, vyspp, limit);
+}
+
+void
+erts_link_tree_foreach_delete(ErtsLink **root,
+ void (*func)(ErtsLink *, void *),
+ void *arg)
+{
+ ml_rbt_foreach_delete((ErtsMonLnkNode **) root,
+ (void (*)(ErtsMonLnkNode*, void*)) func,
+ arg);
+}
+
+int
+erts_link_tree_foreach_delete_yielding(ErtsLink **root,
+ void (*func)(ErtsLink *, void *),
+ void *arg,
+ void **vyspp,
+ Sint limit)
+{
+ return ml_rbt_foreach_delete_yielding((ErtsMonLnkNode **) root,
+ (void (*)(ErtsMonLnkNode*, void*)) func,
+ arg, vyspp, limit);
+}
+
+void
+erts_link_list_foreach(ErtsLink *list,
+ void (*func)(ErtsLink *, void *),
+ void *arg)
+{
+ void *ystate = NULL;
+ while (ml_dl_list_foreach_yielding((ErtsMonLnkNode *) list,
+ (void (*)(ErtsMonLnkNode *, void *)) func,
+ arg, &ystate, (Sint) INT_MAX));
+}
+
+int
+erts_link_list_foreach_yielding(ErtsLink *list,
+ void (*func)(ErtsLink *, void *),
+ void *arg,
+ void **vyspp,
+ Sint limit)
+{
+ return ml_dl_list_foreach_yielding((ErtsMonLnkNode *) list,
+ (void (*)(ErtsMonLnkNode *, void *)) func,
+ arg, vyspp, limit);
+}
+
+void
+erts_link_list_foreach_delete(ErtsLink **list,
+ void (*func)(ErtsLink *, void *),
+ void *arg)
+{
+ void *ystate = NULL;
+ while (ml_dl_list_foreach_delete_yielding((ErtsMonLnkNode **) list,
+ (void (*)(ErtsMonLnkNode*, void*)) func,
+ arg, &ystate, (Sint) INT_MAX));
+}
+
+int
+erts_link_list_foreach_delete_yielding(ErtsLink **list,
+ void (*func)(ErtsLink *, void *),
+ void *arg,
+ void **vyspp,
+ Sint limit)
+{
+ return ml_dl_list_foreach_delete_yielding((ErtsMonLnkNode **) list,
+ (void (*)(ErtsMonLnkNode*, void*)) func,
+ arg, vyspp, limit);
+}
+
+ErtsLinkData *
+erts_link_create(Uint16 type, Eterm a, Eterm b)
+{
+ ErtsLinkData *ldp;
+
+#ifdef ERTS_ML_DEBUG
+ switch (type) {
+ case ERTS_LNK_TYPE_PROC:
+ ERTS_ML_ASSERT(is_internal_pid(a) && is_internal_pid(a));
+ break;
+ case ERTS_LNK_TYPE_PORT:
+ ERTS_ML_ASSERT(is_internal_pid(a) || is_internal_pid(b));
+ ERTS_ML_ASSERT(is_internal_port(a) || is_internal_port(b));
+ break;
+ case ERTS_LNK_TYPE_DIST_PROC:
+ ERTS_ML_ASSERT(is_internal_pid(a) || is_internal_pid(b));
+ ERTS_ML_ASSERT(is_external_pid(a) || is_external_pid(b));
+ break;
+ default:
+ ERTS_INTERNAL_ERROR("Invalid link type");
+ break;
+ }
+#endif
+
+ if (type != ERTS_LNK_TYPE_DIST_PROC) {
+ ldp = erts_alloc(ERTS_ALC_T_LINK, sizeof(ErtsLinkData));
+
+ ldp->a.other.item = b;
+ ldp->a.flags = (Uint16) 0;
+
+ ldp->b.other.item = a;
+ ldp->b.flags = (Uint16) 0;
+ }
+ else {
+ ErtsLinkDataExtended *ldep;
+ Uint size, hsz;
+ Eterm *hp;
+ ErlOffHeap oh;
+
+ if (is_internal_pid(a))
+ hsz = NC_HEAP_SIZE(b);
+ else
+ hsz = NC_HEAP_SIZE(a);
+ ERTS_ML_ASSERT(hsz > 0);
+
+ size = sizeof(ErtsLinkDataExtended) - sizeof(Eterm);
+ size += hsz*sizeof(Eterm);
+
+ ldp = erts_alloc(ERTS_ALC_T_LINK_EXT, size);
+
+ ldp->a.flags = ERTS_ML_FLG_EXTENDED;
+ ldp->b.flags = ERTS_ML_FLG_EXTENDED;
+
+ ldep = (ErtsLinkDataExtended *) ldp;
+ hp = &ldep->heap[0];
+
+ ERTS_INIT_OFF_HEAP(&oh);
+
+ if (is_internal_pid(a)) {
+ ldp->a.other.item = STORE_NC(&hp, &oh, b);
+ ldp->b.other.item = a;
+ }
+ else {
+ ldp->a.other.item = b;
+ ldp->b.other.item = STORE_NC(&hp, &oh, a);
+ }
+
+ ldep->ohhp = oh.first;
+ ldep->dist = NULL;
+ }
+
+ erts_atomic32_init_nob(&ldp->refc, 2);
+
+ ldp->a.key_offset = (Uint16) offsetof(ErtsLink, other.item);
+ ldp->a.offset = (Uint16) offsetof(ErtsLinkData, a);
+ ldp->a.type = type;
+
+ ldp->b.key_offset = (Uint16) offsetof(ErtsLink, other.item);
+ ldp->b.offset = (Uint16) offsetof(ErtsLinkData, b);
+ ldp->b.type = type;
+
+ return ldp;
+}
+
+/*
+ * erts_link_destroy__() should only be called from
+ * erts_link_release() or erts_link_release_both().
+ */
+void
+erts_link_destroy__(ErtsLinkData *ldp)
+{
+ ERTS_ML_ASSERT(erts_atomic32_read_nob(&ldp->refc) == 0);
+ ERTS_ML_ASSERT(!(ldp->a.flags & ERTS_ML_FLG_IN_TABLE));
+ ERTS_ML_ASSERT(!(ldp->b.flags & ERTS_ML_FLG_IN_TABLE));
+ ERTS_ML_ASSERT((ldp->a.flags & ERTS_ML_FLGS_SAME)
+ == (ldp->b.flags & ERTS_ML_FLGS_SAME));
+
+ if (!(ldp->a.flags & ERTS_ML_FLG_EXTENDED))
+ erts_free(ERTS_ALC_T_LINK, ldp);
+ else {
+ ErtsLinkDataExtended *ldep = (ErtsLinkDataExtended *) ldp;
+ ErlOffHeap oh;
+ if (ldep->ohhp) {
+ ERTS_INIT_OFF_HEAP(&oh);
+ oh.first = ldep->ohhp;
+ erts_cleanup_offheap(&oh);
+ }
+ if (ldep->dist)
+ erts_mon_link_dist_dec_refc(ldep->dist);
+ erts_free(ERTS_ALC_T_LINK_EXT, ldep);
+ }
+}
+
+void
+erts_link_set_dead_dist(ErtsLink *lnk, Eterm nodename)
+{
+ ErtsLinkDataExtended *ldep;
+ ldep = (ErtsLinkDataExtended *) erts_link_to_data(lnk);
+
+ ERTS_ML_ASSERT(lnk->flags & ERTS_ML_FLG_EXTENDED);
+ ERTS_ML_ASSERT(lnk->type == ERTS_LNK_TYPE_DIST_PROC);
+ ERTS_ML_ASSERT(!ldep->dist);
+
+ ldep->dist = erts_mon_link_dist_create(nodename);
+ ldep->dist->alive = 0;
+}
+
+Uint
+erts_link_size(ErtsLink *lnk)
+{
+ Uint size, refc;
+ ErtsLinkData *ldp = erts_link_to_data(lnk);
+
+ if (!(lnk->flags & ERTS_ML_FLG_EXTENDED))
+ size = sizeof(ErtsLinkData);
+ else {
+ ErtsLinkDataExtended *ldep = (ErtsLinkDataExtended *) ldp;
+
+ ASSERT(lnk->type == ERTS_LNK_TYPE_DIST_PROC);
+ ASSERT(is_external_pid_header(ldep->heap[0]));
+
+ size = sizeof(ErtsLinkDataExtended);
+ size += thing_arityval(ldep->heap[0])*sizeof(Eterm);
+ }
+
+ refc = (Uint) erts_atomic32_read_nob(&ldp->refc);
+ ASSERT(refc > 0);
+
+ return size / refc;
+}
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\
+ * Misc *
+\* */
+
+void
+erts_monitor_link_init(void)
+{
+ monitor_init();
+ link_init();
+}
diff --git a/erts/emulator/beam/erl_monitor_link.h b/erts/emulator/beam/erl_monitor_link.h
new file mode 100644
index 0000000000..603aead8cc
--- /dev/null
+++ b/erts/emulator/beam/erl_monitor_link.h
@@ -0,0 +1,2326 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2018. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * %CopyrightEnd%
+ */
+
+/*
+ * Description: Monitor and link implementation.
+ *
+ * === Monitors ==================================================
+ *
+ * The monitor data structure contains:
+ * - an 'origin' part that should be inserted in a data structure
+ * of the origin entity, i.e., the entity monitoring another
+ * entity
+ * - a 'target' part that should be inserted in a data structure
+ * of the target entity, i.e., the entity being monitored by
+ * another entity
+ * - a shared part that contains information shared between both
+ * origin and target entities
+ *
+ * That is, the two halves of the monitor as well as shared data
+ * are allocated in one single continuous memory block. The
+ * origin and target parts can separately each be inserted in
+ * either a (red-black) tree, a (circular double linked) list, or
+ * in a process signal queue.
+ *
+ * Each process and port contains:
+ * - a monitor list for local target monitors that is accessed
+ * via the ERTS_P_LT_MONITORS() macro, and
+ * - a monitor tree for other monitors that is accessed via the
+ * ERTS_P_MONITORS() macro
+ *
+ * These fields of processes/ports are protected by the main lock
+ * of the process/port. These are only intended to be accessed by
+ * the process/port itself. When setting up or tearing down a
+ * monitor one should *only* operate on the monitor tree/list of
+ * the currently executing process/port and send signals to the
+ * other involved process/port so it can modify its own monitor
+ * tree/list by itself (see erl_proc_sig_queue.h). One should
+ * absolutely *not* acquire the lock of the other involved
+ * process/port and operate on its monitor tree/list directly.
+ *
+ * Each dist entry contains a monitor/link dist structure that
+ * contains:
+ * - a monitor tree for origin named monitors that is accessed via
+ * the field 'orig_name_monitors', and
+ * - a monitor list for other monitors that is accessed via the
+ * 'monitors' field.
+ * Monitors in these fields contain information about all monitors
+ * over this specific connection.
+ *
+ * The fields of the dist structure are protected by a mutex in
+ * the same dist structure. Operations on these fields are
+ * normally performed by the locally involved process only,
+ * except when a connection is taken down. However in the case
+ * of distributed named monitors that originates from another
+ * node this is not possible. That is this operation is also
+ * performed from another context that the locally involved
+ * process.
+ *
+ * Access to monitor trees are performed using the
+ * erts_monitor_tree_* functions below. Access to monitor lists
+ * are performed using the erts_monitor_list_* functions below.
+ *
+ *
+ * The different monitor types:
+ *
+ * --- ERTS_MON_TYPE_PROC ----------------------------------------
+ *
+ * A local process (origin) monitors another local process
+ * (target).
+ *
+ * Origin:
+ * Other Item: Target process identifier
+ * Target:
+ * Other Item: Origin process identifier
+ * Shared:
+ * Key: Reference
+ * Name: Name (atom) if by name
+ *
+ * Valid keys are only ordinary internal references.
+ *
+ * Origin part of the monitor is stored in the monitor tree of
+ * origin process and target part of the monitor is stored in
+ * monitor list for local targets on the target process.
+ *
+ * --- ERTS_MON_TYPE_PORT ----------------------------------------
+ *
+ * A local process (origin) monitors a local port (target), or a
+ * local port (origin) monitors a local process (target).
+ *
+ * Origin:
+ * Other Item: Target process/port identifier
+ * Target:
+ * Other Item: Origin process/port identifier
+ * Shared:
+ * Key: Reference
+ * Name: Name (atom) if by name
+ *
+ * Valid keys are only ordinary internal references.
+ *
+ * Origin part of the monitor is stored in the monitor tree of
+ * origin process/port and target part of the monitor is stored
+ * in monitor list for local targets on the target process/port.
+ *
+ *
+ * --- ERTS_MON_TYPE_TIME_OFFSET ---------------------------------
+ *
+ * A local process (origin) monitors time offset (target)
+ *
+ * Origin:
+ * Other Item: clock_service
+ * Target:
+ * Other Item: Origin process identifier
+ * Shared:
+ * Key: Reference
+ *
+ * Valid keys are only ordinary internal references.
+ *
+ * Origin part of the monitor is stored in the monitor tree of
+ * origin process and target part of the monitor is stored in
+ * monitor list referred by the variable 'time_offset_monitors'
+ * (see erl_time_sup.c).
+ *
+ *
+ * --- ERTS_MON_TYPE_DIST_PROC -----------------------------------
+ *
+ * A local process (origin) monitors a remote process (target).
+ * Origin node on local process and target node on dist entry.
+ *
+ * Origin:
+ * Other Item: Remote process identifier/Node name
+ * if by name
+ * Target:
+ * Other Item: Local process identifier
+ * Shared:
+ * Key: Reference
+ * Name: Name (atom) if by name
+ * Dist: Pointer to dist structure
+ *
+ * Valid keys are only ordinary internal references.
+ *
+ * Origin part of the monitor is stored in the monitor tree of
+ * origin process and target part of the monitor is stored in
+ * monitor list referred by 'monitors' field of the dist
+ * structure.
+ *
+ *
+ * A remote process (origin) monitors a local process (target).
+ * Origin node on dist entry and target node on local process.
+ *
+ * Origin:
+ * Other Item: Local process identifier
+ * Target:
+ * Other Item: Remote process identifier
+ * Shared:
+ * Key: Reference
+ * Name: Name (atom) if by name
+ *
+ * Valid keys are only external references.
+ *
+ * If monitor by name, the origin part of the monitor is stored
+ * in the monitor tree referred by 'orig_name_monitors' field in
+ * dist structure; otherwise in the monitor list referred by
+ * 'monitors' field in dist structure. The target part of the
+ * monitor is stored in the monitor tree of the local target
+ * process.
+ *
+ *
+ * --- ERTS_MON_TYPE_RESOURCE ------------------------------------
+ *
+ * A NIF resource (origin) monitors a process (target).
+ *
+ * Origin:
+ * Other Item: Target process identifier
+ * Target:
+ * Other Ptr: Pointer to resource
+ * Shared:
+ * Key: Reference
+ *
+ * Valid keys are only ordinary internal references.
+ *
+ * Origin part of the monitor is stored in the monitor tree of
+ * origin resource (see erl_nif.c) and target part of the
+ * monitor is stored in monitor list for local targets on the
+ * target process.
+ *
+ * --- ERTS_MON_TYPE_NODE ----------------------------------------
+ *
+ * A local process (origin) monitors a distribution connection
+ * (target) via erlang:monitor_node().
+ *
+ * Origin:
+ * Other Item: Node name (atom)
+ * Key: Node name
+ * Target:
+ * Other Item: Origin process identifier
+ * Key: Origin process identifier
+ * Shared:
+ * Refc: Number of invocations
+ *
+ * Valid keys are only node-name atoms and internal process
+ * identifiers.
+ *
+ * Origin part of the monitor is stored in the monitor tree of
+ * origin process and target part of the monitor is stored in
+ * monitor list referred by 'monitors' field of the dist
+ * structure.
+ *
+ * --- ERTS_MON_TYPE_NODES ---------------------------------------
+ *
+ * A local process (origin) monitors all connections (target),
+ * via net_kernel:monitor_nodes().
+ *
+ * Origin:
+ * Other Item: Bit mask (small)
+ * Key: Bit mask
+ * Target:
+ * Other Item: Origin process identifier
+ * Key: Origin process identifier
+ * Shared:
+ * Refc: Number of invocations
+ *
+ * Valid keys are only small integers and internal process
+ * identifiers.
+ *
+ * Origin part of the monitor is stored in the monitor tree of
+ * origin process and target part of the monitor is stored in
+ * monitor list referred by the variable 'nodes_monitors' (see
+ * dist.c).
+ *
+ * --- ERTS_MON_TYPE_SUSPEND -------------------------------------
+ *
+ * Suspend monitor.
+ *
+ * Other Item: Suspendee process identifier
+ * Key: Suspendee process identifier
+ *
+ * Valid keys are only ordinary internal references.
+ *
+ * This type of monitor is a bit strange and the whole process
+ * suspend functionality should be improved...
+ *
+ *
+ *
+ * === Links =====================================================
+ *
+ * The link data structure contains:
+ * - an 'a' part that should be inserted in a data structure of
+ * one entity and contains the identifier of the other involved
+ * entity (b)
+ * - a 'b' part that should be inserted in a data structure of
+ * the other involved entity and contains the identifier of the
+ * other involved entity (a)
+ * - shared part that contains information shared between both
+ * involved entities
+ *
+ * That is, the two halves of the link as well as shared data
+ * are allocated in one single continuous memory block. The 'a'
+ * and the 'b' parts can separately each be inserted in either
+ * a (red-black) tree, a (circular double linked) list, or in a
+ * process signal queue.
+ *
+ * Each process and port contains:
+ * - a link tree for links that is accessed via the
+ * ERTS_P_LINKS() macro
+ *
+ * This field of processes/ports is protected by the main lock of
+ * the process/port. It is only intended to be accessed by the
+ * process/port itself. When setting up or tearing down a link
+ * one should *only* operate on the link tree of the currently
+ * executing process/port and send signals to the other involved
+ * process/port so it can modify its own monitor tree by itself
+ * (see erl_proc_sig_queue.h). One should absolutely *not*
+ * acquire the lock of the other involved process/port and
+ * operate on its link tree directly.
+ *
+ * Each dist entry contains a monitor/link dist structure that
+ * contains:
+ * - a link list for links via the 'links' field.
+ * Links in this field contain information about all links over
+ * this specific connection.
+ *
+ * The fields of the dist structure are protected by a mutex in
+ * the same dist structure. Operation on the 'links' fields are
+ * normally performed by the locally involved process only,
+ * except when a connection is taken down.
+ *
+ * Access to link trees are performed using the erts_link_tree_*
+ * functions below. Access to link lists are performed using the
+ * erts_link_list_* functions below.
+ *
+ * There can only be one link between the same pair of
+ * processes/ports. Since a link can be simultaneously initiated
+ * from both ends we always save the link data structure with the
+ * lowest address if multiple links should appear between the
+ * same pair of processes/ports.
+ *
+ *
+ * The different link types:
+ *
+ * --- ERTS_LNK_TYPE_PROC -----------------------------------------
+ *
+ * A link between a local process A and a local process B.
+ *
+ * A:
+ * Other Item: B process identifier
+ * Key: B process identifier
+ * B:
+ * Other Item: A process identifier
+ * Key: A process identifier
+ *
+ * Valid keys are only internal process identifiers.
+ *
+ * 'A' part of the link stored in the link tree of process A and
+ * 'B' part of the link is stored in link tree of process B.
+ *
+ * --- ERTS_LNK_TYPE_PORT -----------------------------------------
+ *
+ * A link between a local process/port A and a local process/port
+ * B.
+ *
+ * A:
+ * Other Item: B process/port identifier
+ * Key: B process/port identifier
+ * B:
+ * Other Item: A process/port identifier
+ * Key: A process/port identifier
+ *
+ * Valid keys are internal process identifiers and internal port
+ * identifiers.
+ *
+ * 'A' part of the link stored in the link tree of process/port
+ * A and 'B' part of the link is stored in link tree of
+ * process/port B.
+ *
+ * --- ERTS_LNK_TYPE_DIST_PROC ------------------------------------
+ *
+ * A link between a local process and a remote process. Either of
+ * the processes can be used as A or B.
+ *
+ * A:
+ * Other Item: B process identifier
+ * Key: B process identifier
+ * B:
+ * Other Item: A process identifier
+ * Key: A process identifier
+ * Shared:
+ * Dist: Pointer to dist structure
+ *
+ * Valid keys are internal and external process identifiers.
+ *
+ * The part of the link with a remote pid as "other item" is
+ * stored in the link tree of the local process. The part of
+ * the link with a local pid as "other item" is stored in the
+ * links list of the dist structure.
+ *
+ * ===============================================================
+ *
+ * Author: Rickard Green
+ *
+ */
+
+#ifndef ERL_MONITOR_LINK_H__
+#define ERL_MONITOR_LINK_H__
+
+#define ERTS_PROC_SIG_QUEUE_TYPE_ONLY
+#include "erl_proc_sig_queue.h"
+#undef ERTS_PROC_SIG_QUEUE_TYPE_ONLY
+
+#if defined(DEBUG) || 0
+# define ERTS_ML_DEBUG
+#else
+# undef ERTS_ML_DEBUG
+#endif
+
+#ifdef ERTS_ML_DEBUG
+# define ERTS_ML_ASSERT ERTS_ASSERT
+#else
+# define ERTS_ML_ASSERT(E) ((void) 1)
+#endif
+
+#define ERTS_MON_TYPE_MAX ((Uint16) 7)
+
+#define ERTS_MON_TYPE_PROC ((Uint16) 0)
+#define ERTS_MON_TYPE_PORT ((Uint16) 1)
+#define ERTS_MON_TYPE_TIME_OFFSET ((Uint16) 2)
+#define ERTS_MON_TYPE_DIST_PROC ((Uint16) 3)
+#define ERTS_MON_TYPE_RESOURCE ((Uint16) 4)
+#define ERTS_MON_TYPE_NODE ((Uint16) 5)
+#define ERTS_MON_TYPE_NODES ((Uint16) 6)
+#define ERTS_MON_TYPE_SUSPEND ERTS_MON_TYPE_MAX
+
+#define ERTS_MON_LNK_TYPE_MAX (ERTS_MON_TYPE_MAX + ((Uint16) 3))
+#define ERTS_LNK_TYPE_MAX ERTS_MON_LNK_TYPE_MAX
+
+#define ERTS_LNK_TYPE_PROC (ERTS_MON_TYPE_MAX + ((Uint16) 1))
+#define ERTS_LNK_TYPE_PORT (ERTS_MON_TYPE_MAX + ((Uint16) 2))
+#define ERTS_LNK_TYPE_DIST_PROC ERTS_LNK_TYPE_MAX
+
+#define ERTS_ML_FLG_TARGET (((Uint16) 1) << 0)
+#define ERTS_ML_FLG_IN_TABLE (((Uint16) 1) << 1)
+#define ERTS_ML_FLG_IN_SUBTABLE (((Uint16) 1) << 2)
+#define ERTS_ML_FLG_NAME (((Uint16) 1) << 3)
+#define ERTS_ML_FLG_EXTENDED (((Uint16) 1) << 4)
+
+#define ERTS_ML_FLG_DBG_VISITED (((Uint16) 1) << 15)
+
+/* Flags that should be the same on both monitor/link halves */
+#define ERTS_ML_FLGS_SAME \
+ (ERTS_ML_FLG_EXTENDED|ERTS_ML_FLG_NAME)
+
+typedef struct ErtsMonLnkNode__ ErtsMonLnkNode;
+
+typedef struct {
+ UWord parent; /* Parent ptr and flags... */
+ ErtsMonLnkNode *right;
+ ErtsMonLnkNode *left;
+} ErtsMonLnkTreeNode;
+
+typedef struct {
+ ErtsMonLnkNode *next;
+ ErtsMonLnkNode *prev;
+} ErtsMonLnkListNode;
+
+struct ErtsMonLnkNode__ {
+ union {
+ ErtsSignalCommon signal;
+ ErtsMonLnkTreeNode tree;
+ ErtsMonLnkListNode list;
+ } node;
+ union {
+ Eterm item;
+ void *ptr;
+ } other;
+ Uint16 offset; /* offset from monitor/link data to this structure (node) */
+ Uint16 key_offset; /* offset from this structure (node) to key */
+ Uint16 flags;
+ Uint16 type;
+};
+
+typedef struct {
+ Eterm nodename;
+ Uint32 connection_id;
+ erts_atomic_t refc;
+ erts_mtx_t mtx;
+ int alive;
+ ErtsMonLnkNode *links; /* Link double linked circular list */
+ ErtsMonLnkNode *monitors; /* Monitor double linked circular list */
+ ErtsMonLnkNode *orig_name_monitors; /* Origin named monitors
+ read-black tree */
+} ErtsMonLnkDist;
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\
+ * Misc *
+\* */
+
+/**
+ *
+ * @brief Initialize monitor/link implementation
+ *
+ */
+void erts_monitor_link_init(void);
+
+/**
+ *
+ * @brief Create monitor/link dist structure to attach to dist entry
+ *
+ * Create dist structure containing monitor and link containers. This
+ * structure is to be attached to a connected dist entry.
+ *
+ * @param[in] nodename Node name as an atom
+ *
+ * @returns Pointer to dist structure
+ *
+ */
+ErtsMonLnkDist *erts_mon_link_dist_create(Eterm nodename);
+
+/**
+ *
+ * @brief Increase reference count of monitor/link dist structure
+ *
+ * @param[in] mld Pointer to dist structure
+ *
+ */
+ERTS_GLB_INLINE void erts_mon_link_dist_inc_refc(ErtsMonLnkDist *mld);
+
+/**
+ *
+ * @brief Decrease reference count of monitor/link dist structure
+ *
+ * @param[in] mld Pointer to dist structure
+ *
+ */
+ERTS_GLB_INLINE void erts_mon_link_dist_dec_refc(ErtsMonLnkDist *mld);
+
+/* internal functions... */
+ERTS_GLB_INLINE void erts_ml_dl_list_insert__(ErtsMonLnkNode **list,
+ ErtsMonLnkNode *ml);
+ERTS_GLB_INLINE void erts_ml_dl_list_delete__(ErtsMonLnkNode **list,
+ ErtsMonLnkNode *ml);
+ERTS_GLB_INLINE ErtsMonLnkNode *erts_ml_dl_list_first__(ErtsMonLnkNode *list);
+ERTS_GLB_INLINE ErtsMonLnkNode *erts_ml_dl_list_last__(ErtsMonLnkNode *list);
+void erts_mon_link_dist_destroy__(ErtsMonLnkDist *mld);
+ERTS_GLB_INLINE void *erts_ml_node_to_main_struct__(ErtsMonLnkNode *mln);
+
+/* implementations for globally inlined misc functions... */
+#if ERTS_GLB_INLINE_INCL_FUNC_DEF
+
+ERTS_GLB_INLINE void
+erts_mon_link_dist_inc_refc(ErtsMonLnkDist *mld)
+{
+ ERTS_ML_ASSERT(erts_atomic_read_nob(&mld->refc) > 0);
+ erts_atomic_inc_nob(&mld->refc);
+}
+
+ERTS_GLB_INLINE void
+erts_mon_link_dist_dec_refc(ErtsMonLnkDist *mld)
+{
+ ERTS_ML_ASSERT(erts_atomic_read_nob(&mld->refc) > 0);
+ if (erts_atomic_dec_read_nob(&mld->refc) == 0)
+ erts_mon_link_dist_destroy__(mld);
+}
+
+ERTS_GLB_INLINE void *
+erts_ml_node_to_main_struct__(ErtsMonLnkNode *mln)
+{
+ return (void *) (((char *) mln) - ((size_t) mln->offset));
+}
+
+ERTS_GLB_INLINE void
+erts_ml_dl_list_insert__(ErtsMonLnkNode **list, ErtsMonLnkNode *ml)
+{
+ ErtsMonLnkNode *first = *list;
+ ERTS_ML_ASSERT(!(ml->flags & ERTS_ML_FLG_IN_TABLE));
+ if (!first) {
+ ml->node.list.next = ml->node.list.prev = ml;
+ *list = ml;
+ }
+ else {
+ ERTS_ML_ASSERT(first->node.list.prev->node.list.next == first);
+ ERTS_ML_ASSERT(first->node.list.next->node.list.prev == first);
+ ml->node.list.next = first;
+ ml->node.list.prev = first->node.list.prev;
+ first->node.list.prev = ml;
+ ml->node.list.prev->node.list.next = ml;
+ }
+ ml->flags |= ERTS_ML_FLG_IN_TABLE;
+}
+
+ERTS_GLB_INLINE void
+erts_ml_dl_list_delete__(ErtsMonLnkNode **list, ErtsMonLnkNode *ml)
+{
+ ERTS_ML_ASSERT(ml->flags & ERTS_ML_FLG_IN_TABLE);
+ if (ml->node.list.next == ml) {
+ ERTS_ML_ASSERT(ml->node.list.prev == ml);
+ ERTS_ML_ASSERT(*list == ml);
+
+ *list = NULL;
+ }
+ else {
+ ERTS_ML_ASSERT(ml->node.list.prev->node.list.next == ml);
+ ERTS_ML_ASSERT(ml->node.list.prev != ml);
+ ERTS_ML_ASSERT(ml->node.list.next->node.list.prev == ml);
+ ERTS_ML_ASSERT(ml->node.list.next != ml);
+
+ if (*list == ml)
+ *list = ml->node.list.next;
+ ml->node.list.prev->node.list.next = ml->node.list.next;
+ ml->node.list.next->node.list.prev = ml->node.list.prev;
+ }
+ ml->flags &= ~ERTS_ML_FLG_IN_TABLE;
+}
+
+ERTS_GLB_INLINE ErtsMonLnkNode *
+erts_ml_dl_list_first__(ErtsMonLnkNode *list)
+{
+ return list;
+}
+
+ERTS_GLB_INLINE ErtsMonLnkNode *
+erts_ml_dl_list_last__(ErtsMonLnkNode *list)
+{
+ if (!list)
+ return NULL;
+ return list->node.list.prev;
+}
+
+#endif /* ERTS_GLB_INLINE_INCL_FUNC_DEF */
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\
+ * Monitor Operations *
+\* */
+
+
+typedef struct ErtsMonLnkNode__ ErtsMonitor;
+
+typedef struct {
+ ErtsMonitor origin;
+ ErtsMonitor target;
+ Eterm ref;
+ erts_atomic32_t refc;
+} ErtsMonitorData;
+
+typedef struct {
+ ErtsMonitorData md;
+ ErtsORefThing oref_thing;
+} ErtsMonitorDataHeap;
+
+typedef struct ErtsMonitorDataExtended__ ErtsMonitorDataExtended;
+
+struct ErtsMonitorDataExtended__ {
+ ErtsMonitorData md;
+ union {
+ Eterm name;
+ Uint refc;
+ } u;
+ union {
+ struct erl_off_heap_header *ohhp;
+ ErtsMonitor *node_monitors;
+ } uptr;
+ ErtsMonLnkDist *dist;
+ Eterm heap[1]; /* heap start... */
+};
+
+typedef struct {
+ ErtsMonitor mon;
+ int pending;
+ int active;
+} ErtsMonitorSuspend;
+
+/*
+ * --- Monitor tree operations ---
+ */
+
+/**
+ *
+ * @brief Lookup a monitor in a monitor tree
+ *
+ *
+ * @param[in] root Pointer to root of monitor tree
+ *
+ * @param[in] key Key of monitor to lookup
+ *
+ * @returns Pointer to a monitor with the
+ * key 'key', or NULL if no such
+ * monitor was found
+ *
+ */
+ErtsMonitor *erts_monitor_tree_lookup(ErtsMonitor *root, Eterm key);
+
+/**
+ *
+ * @brief Lookup or insert a monitor in a monitor tree
+ *
+ * When the funcion is called it is assumed that:
+ * - 'mon' monitor is not part of any tree or list
+ * If the above is not true, bad things will happen.
+ *
+ * @param[in,out] root Pointer to pointer to root of monitor tree
+ *
+ * @param[in] mon Monitor to insert if no monitor
+ * with the same key already exists
+ *
+ * @returns Pointer to a monitor with the
+ * key 'key'. If no monitor with the key
+ * 'key' was found and 'mon' was inserted
+ * 'mon' is returned.
+ *
+ */
+ErtsMonitor *erts_monotor_tree_lookup_insert(ErtsMonitor **root,
+ ErtsMonitor *mon);
+
+/**
+ *
+ * @brief Lookup or create a node or a nodes monitor in a monitor tree.
+ *
+ * Looks up an origin monitor with the key 'target' in the monitor tree.
+ * If it is not found, creates a monitor and returns a pointer to the
+ * origin monitor.
+ *
+ * When the funcion is called it is assumed that:
+ * - no target monitors with the key 'target' exists in the tree.
+ * If the above is not true, bad things will happen.
+ *
+ * @param[in,out] root Pointer to pointer to root of monitor tree
+ *
+ * @param[out] created Pointer to integer. The integer is set to
+ * a non-zero value if no monitor with key
+ * 'target' was found, and a new monitor
+ * was created. If a monitor was found, it
+ * is set to zero.
+ *
+ * @param[in] type ERTS_MON_TYPE_NODE | ERTS_MON_TYPE_NODES
+ *
+ * @param[in] origin The key of the origin
+ *
+ * @param[in] target The key of the target
+ *
+ */
+ErtsMonitor *erts_monitor_tree_lookup_create(ErtsMonitor **root, int *created,
+ Uint16 type, Eterm origin,
+ Eterm target);
+
+/**
+ *
+ * @brief Insert a monitor in a monitor tree
+ *
+ * When the funcion is called it is assumed that:
+ * - no monitors with the same key that 'mon' exist in the tree
+ * - 'mon' is not part of any list of tree
+ * If the above are not true, bad things will happen.
+ *
+ * @param[in,out] root Pointer to pointer to root of monitor tree
+ *
+ * @param[in] mon Monitor to insert.
+ *
+ */
+void erts_monitor_tree_insert(ErtsMonitor **root, ErtsMonitor *mon);
+
+/**
+ *
+ * @brief Replace a monitor in a monitor tree
+ *
+ * When the funcion is called it is assumed that:
+ * - 'old' monitor and 'new' monitor have exactly the same key
+ * - 'old' monitor is part of the tree
+ * - 'new' monitor is not part of any tree or list
+ * If the above are not true, bad things will happen.
+ *
+ * @param[in,out] root Pointer to pointer to root of monitor tree
+ *
+ * @param[in] old Monitor to remove from the tree
+ *
+ * @param[in] new Monitor to insert into the tree
+ *
+ */
+void erts_monitor_tree_replace(ErtsMonitor **root, ErtsMonitor *old,
+ ErtsMonitor *new);
+
+/**
+ *
+ * @brief Delete a monitor from a monitor tree
+ *
+ * When the funcion is called it is assumed that:
+ * - 'mon' monitor is part of the tree
+ * If the above is not true, bad things will happen.
+ *
+ * @param[in,out] root Pointer to pointer to root of monitor tree
+ *
+ * @param[in] mon Monitor to remove from the tree
+ *
+ */
+void erts_monitor_tree_delete(ErtsMonitor **root, ErtsMonitor *mon);
+
+/**
+ *
+ * @brief Call a function for each monitor in a monitor tree
+ *
+ * The funcion 'func' will be called with a pointer to a monitor
+ * as first argument and 'arg' as second argument for each monitor
+ * in the tree referred to by 'root'.
+ *
+ * @param[in] root Pointer to root of monitor tree
+ *
+ * @param[in] func Pointer to function to call
+ *
+ * @param[in] arg Argument to pass as second argument in
+ * calls to 'func'
+ *
+ */
+void erts_monitor_tree_foreach(ErtsMonitor *root,
+ void (*func)(ErtsMonitor *, void *),
+ void *arg);
+
+/**
+ *
+ * @brief Call a function for each monitor in a monitor tree. Yield
+ * if lots of monitors exist.
+ *
+ * The funcion 'func' will be called with a pointer to a monitor
+ * as first argument and 'arg' as second argument for each monitor
+ * in the tree referred to by 'root'.
+ *
+ * It is assumed that:
+ * - *yspp equals NULL on first call
+ * - this function is repetedly called with *yspp set
+ * as set when previous call returned until a non-zero
+ * value is returned.
+ * - no modifications are made on the tree between first call
+ * and the call that returns a non-zero value
+ * If the above are not true, bad things will happen.
+ *
+ * @param[in] root Pointer to root of monitor tree
+ *
+ * @param[in] func Pointer to function to call
+ *
+ * @param[in] arg Argument to pass as second argument in
+ * calls to 'func'
+ *
+ * @param[in,out] vyspp Pointer to a pointer to an internal state
+ * used by this function. At initial call
+ * *yspp should be NULL. When done *yspp
+ * will be NULL.
+ *
+ * @param[in] limit Maximum amount of monitors to process
+ * before yielding.
+ *
+ * @returns A non-zero value when all monitors has been
+ * processed, and zero when more work is needed.
+ *
+ */
+int erts_monitor_tree_foreach_yielding(ErtsMonitor *root,
+ void (*func)(ErtsMonitor *, void *),
+ void *arg,
+ void **vyspp,
+ Sint limit);
+
+/**
+ *
+ * @brief Delete all monitors from a monitor tree and call a function for
+ * each monitor
+ *
+ * The funcion 'func' will be called with a pointer to a monitor
+ * as first argument and 'arg' as second argument for each monitor
+ * in the tree referred to by 'root'.
+ *
+ * @param[in,out] root Pointer to pointer to root of monitor tree
+ *
+ * @param[in] func Pointer to function to call
+ *
+ * @param[in] arg Argument to pass as second argument in
+ * calls to 'func'
+ *
+ */
+void erts_monitor_tree_foreach_delete(ErtsMonitor **root,
+ void (*func)(ErtsMonitor *, void *),
+ void *arg);
+
+/**
+ *
+ * @brief Delete all monitors from a monitor tree and call a function for
+ * each monitor
+ *
+ * The funcion 'func' will be called with a pointer to a monitor
+ * as first argument and 'arg' as second argument for each monitor
+ * in the tree referred to by 'root'.
+ *
+ * It is assumed that:
+ * - *yspp equals NULL on first call
+ * - this function is repetededly called with *yspp set
+ * as set when previous call returned until a non-zero
+ * value is returned.
+ * - no modifications are made on the tree between first call
+ * and the call that returns a non-zero value
+ * If the above are not true, bad things will happen.
+ *
+ * @param[in,out] root Pointer to pointer to root of monitor tree
+ *
+ * @param[in] func Pointer to function to call
+ *
+ * @param[in] arg Argument to pass as second argument in
+ * calls to 'func'
+ *
+ * @param[in,out] vyspp Pointer to a pointer to an internal state
+ * used by this function. At initial call
+ * *yspp should be NULL. When done *yspp
+ * will be NULL.
+ *
+ * @param[in] limit Maximum amount of monitors to process
+ * before yielding.
+ *
+ * @returns A non-zero value when all monitors has been
+ * processed, and zero when more work is needed.
+ *
+ */
+int erts_monitor_tree_foreach_delete_yielding(ErtsMonitor **root,
+ void (*func)(ErtsMonitor *, void *),
+ void *arg,
+ void **vyspp,
+ Sint limit);
+
+/*
+ * --- Monitor list operations --
+ */
+
+/**
+ *
+ * @brief Insert a monitor in a monitor list
+ *
+ * When the funcion is called it is assumed that:
+ * - 'mon' monitor is not part of any list or tree
+ * If the above is not true, bad things will happen.
+ *
+ * @param[in,out] list Pointer to pointer to monitor list
+ *
+ * @param[in] mon Monitor to insert
+ *
+ */
+ERTS_GLB_INLINE void erts_monitor_list_insert(ErtsMonitor **list, ErtsMonitor *mon);
+
+/**
+ *
+ * @brief Delete a monitor from a monitor list
+ *
+ * When the funcion is called it is assumed that:
+ * - 'mon' monitor is part of the list
+ * If the above is not true, bad things will happen.
+ *
+ * @param[in,out] list Pointer to pointer to monitor list
+ *
+ * @param[in] mon Monitor to remove from the list
+ *
+ */
+ERTS_GLB_INLINE void erts_monitor_list_delete(ErtsMonitor **list, ErtsMonitor *mon);
+
+/**
+ *
+ * @brief Get a pointer to first monitor in a monitor list
+ *
+ * The monitor will still remain in the list after the return
+ *
+ * @param[in] list Pointer to monitor list
+ *
+ * @returns Pointer to first monitor in list if
+ * list is not empty. If list is empty
+ * NULL is returned.
+ *
+ */
+ERTS_GLB_INLINE ErtsMonitor *erts_monitor_list_first(ErtsMonitor *list);
+
+/**
+ *
+ * @brief Get a pointer to last monitor in a monitor list
+ *
+ * The monitor will still remain in the list after the return
+ *
+ * @param[in] list Pointer to monitor list
+ *
+ * @returns Pointer to last monitor in list if
+ * list is not empty. If list is empty
+ * NULL is returned.
+ *
+ */
+ERTS_GLB_INLINE ErtsMonitor *erts_monitor_list_last(ErtsMonitor *list);
+
+/**
+ *
+ * @brief Call a function for each monitor in a monitor list
+ *
+ * The funcion 'func' will be called with a pointer to a monitor
+ * as first argument and 'arg' as second argument for each monitor
+ * in the tree referred to by 'list'.
+ *
+ * @param[in] list Pointer to root of monitor list
+ *
+ * @param[in] func Pointer to function to call
+ *
+ * @param[in] arg Argument to pass as second argument in
+ * calls to 'func'
+ *
+ */
+void erts_monitor_list_foreach(ErtsMonitor *list,
+ void (*func)(ErtsMonitor *, void *),
+ void *arg);
+
+/**
+ *
+ * @brief Call a function for each monitor in a monitor list. Yield
+ * if lots of monitors exist.
+ *
+ * The funcion 'func' will be called with a pointer to a monitor
+ * as first argument and 'arg' as second argument for each monitor
+ * in the tree referred to by 'root'.
+ *
+ * It is assumed that:
+ * - *yspp equals NULL on first call
+ * - this function is repetedly called with *yspp set
+ * as set when previous call returned until a non-zero
+ * value is returned.
+ * - no modifications are made on the tree between first call
+ * and the call that returns a non-zero value
+ * If the above are not true, bad things will happen.
+ *
+ * @param[in] list Pointer to monitor list
+ *
+ * @param[in] func Pointer to function to call
+ *
+ * @param[in] arg Argument to pass as second argument in
+ * calls to 'func'
+ *
+ * @param[in,out] vyspp Pointer to a pointer to an internal state
+ * used by this function. At initial call
+ * *yspp should be NULL. When done *yspp
+ * will be NULL.
+ *
+ * @param[in] limit Maximum amount of monitors to process
+ * before yielding.
+ *
+ * @returns A non-zero value when all monitors has been
+ * processed, and zero when more work is needed.
+ *
+ */
+int erts_monitor_list_foreach_yielding(ErtsMonitor *list,
+ void (*func)(ErtsMonitor *, void *),
+ void *arg,
+ void **vyspp,
+ Sint limit);
+
+/**
+ *
+ * @brief Delete all monitors from a monitor list and call a function for
+ * each monitor
+ *
+ * The funcion 'func' will be called with a pointer to a monitor
+ * as first argument and 'arg' as second argument for each monitor
+ * in the tree referred to by 'root'.
+ *
+ * @param[in,out] list Pointer to pointer to monitor list
+ *
+ * @param[in] func Pointer to function to call
+ *
+ * @param[in] arg Argument to pass as second argument in
+ * calls to 'func'
+ *
+ */
+void erts_monitor_list_foreach_delete(ErtsMonitor **list,
+ void (*func)(ErtsMonitor *, void *),
+ void *arg);
+
+/**
+ *
+ * @brief Delete all monitors from a monitor list and call a function for
+ * each monitor
+ *
+ * The funcion 'func' will be called with a pointer to a monitor
+ * as first argument and 'arg' as second argument for each monitor
+ * in the tree referred to by 'root'.
+ *
+ * It is assumed that:
+ * - *yspp equals NULL on first call
+ * - this function is repetededly called with *yspp set
+ * as set when previous call returned until a non-zero
+ * value is returned.
+ * - no modifications are made on the tree between first
+ * and the call that returns a non-zero value
+ * If the above are not true, bad things will happen.
+ *
+ * @param[in,out] list Pointer to pointer to monitor list
+ *
+ * @param[in] func Pointer to function to call
+ *
+ * @param[in] arg Argument to pass as second argument in
+ * calls to 'func'
+ *
+ * @param[in,out] vyspp Pointer to a pointer to an internal state
+ * used by this function. At initial call
+ * *yspp should be NULL. When done *yspp
+ * will be NULL.
+ *
+ * @param[in] limit Maximum amount of monitors to process
+ * before yielding.
+ *
+ * @returns A non-zero value when all monitors has been
+ * processed, and zero when more work is needed.
+ *
+ */
+int erts_monitor_list_foreach_delete_yielding(ErtsMonitor **list,
+ void (*func)(ErtsMonitor *, void *),
+ void *arg,
+ void **vyspp,
+ Sint limit);
+
+/*
+ * --- Misc monitor operations ---
+ */
+
+/**
+ *
+ * @brief Create a monitor
+ *
+ * Can create all types of monitors exept for suspend monitors
+ *
+ * When the funcion is called it is assumed that:
+ * - 'ref' is an internal ordinary reference if type is ERTS_MON_TYPE_PROC,
+ * ERTS_MON_TYPE_PORT, ERTS_MON_TYPE_TIME_OFFSET, or ERTS_MON_TYPE_RESOURCE
+ * - 'ref' is NIL if type is ERTS_MON_TYPE_NODE or ERTS_MON_TYPE_NODES
+ * - 'ref' is and ordinary internal reference or an external reference if
+ * type is ERTS_MON_TYPE_DIST_PROC
+ * - 'name' is an atom or NIL if type is ERTS_MON_TYPE_PROC,
+ * ERTS_MON_TYPE_PORT, or ERTS_MON_TYPE_DIST_PROC
+ * - 'name is NIL if type is ERTS_MON_TYPE_TIME_OFFSET, ERTS_MON_TYPE_RESOURCE,
+ * ERTS_MON_TYPE_NODE, or ERTS_MON_TYPE_NODES
+ * If the above is not true, bad things will happen.
+ *
+ * @param[in] type ERTS_MON_TYPE_PROC, ERTS_MON_TYPE_PORT,
+ * ERTS_MON_TYPE_TIME_OFFSET, ERTS_MON_TYPE_DIST_PROC,
+ * ERTS_MON_TYPE_RESOURCE, ERTS_MON_TYPE_NODE,
+ * or ERTS_MON_TYPE_NODES
+ *
+ * @param[in] ref A reference or NIL depending on type
+ *
+ * @param[in] origin The key of the origin
+ *
+ * @param[in] target The key of the target
+ *
+ */
+ErtsMonitorData *erts_monitor_create(Uint16 type, Eterm ref, Eterm origin,
+ Eterm target, Eterm name);
+
+/**
+ *
+ * @brief Get pointer to monitor data structure
+ *
+ * @param[in] mon Pointer to monitor
+ *
+ * @returns Pointer to monitor data structure
+ *
+ */
+ERTS_GLB_INLINE ErtsMonitorData *erts_monitor_to_data(ErtsMonitor *mon);
+
+/**
+ *
+ * @brief Check if monitor is a target monitor
+ *
+ * @param[in] mon Pointer to monitor to check
+ *
+ * @returns A non-zero value if target monitor;
+ * otherwise zero
+ *
+ */
+ERTS_GLB_INLINE int erts_monitor_is_target(ErtsMonitor *mon);
+
+/**
+ *
+ * @brief Check if monitor is an origin monitor
+ *
+ * @param[in] mon Pointer to monitor to check
+ *
+ * @returns A non-zero value if origin monitor;
+ * otherwise zero
+ *
+ */
+ERTS_GLB_INLINE int erts_monitor_is_origin(ErtsMonitor *mon);
+
+/**
+ *
+ * @brief Check if monitor is in tree or list
+ *
+ * @param[in] mon Pointer to monitor to check
+ *
+ * @returns A non-zero value if in tree or list;
+ * otherwise zero
+ *
+ */
+ERTS_GLB_INLINE int erts_monitor_is_in_table(ErtsMonitor *mon);
+
+/**
+ *
+ * @brief Release monitor
+ *
+ * When both the origin and the target part of the monitor have
+ * been released the monitor structure will be deallocated.
+ *
+ * When the funcion is called it is assumed that:
+ * - 'mon' monitor is not part of any list or tree
+ * - 'mon' is not referred to by any other structures
+ * If the above are not true, bad things will happen.
+ *
+ * @param[in] mon Pointer to monitor
+ *
+ */
+ERTS_GLB_INLINE void erts_monitor_release(ErtsMonitor *mon);
+
+/**
+ *
+ * @brief Release both target and origin monitor structures simultaneously
+ *
+ * Release both the origin and target parts of the monitor
+ * simultaneously and deallocate the structure.
+ *
+ * When the funcion is called it is assumed that:
+ * - Neither the origin part nor the target part of the monitor
+ * are not part of any list or tree
+ * - Neither the origin part nor the target part of the monitor
+ * are referred to by any other structures
+ * If the above are not true, bad things will happen.
+ *
+ * @param[in] mdp Pointer to monitor data structure
+ *
+ */
+ERTS_GLB_INLINE void erts_monitor_release_both(ErtsMonitorData *mdp);
+
+/**
+ *
+ * @brief Insert monitor in dist monitor tree or list
+ *
+ * When the funcion is called it is assumed that:
+ * - 'mon' monitor is not part of any list or tree
+ * If the above is not true, bad things will happen.
+ *
+ * @param[in] mon Pointer to monitor
+ *
+ * @param[in] dist Pointer to dist structure
+ *
+ * @returns A non-zero value if inserted;
+ * otherwise, zero. The monitor
+ * is not inserted if the dist
+ * structure has been set in a
+ * dead state.
+ *
+ */
+ERTS_GLB_INLINE int erts_monitor_dist_insert(ErtsMonitor *mon, ErtsMonLnkDist *dist);
+
+/**
+ *
+ * @brief Delete monitor from dist monitor tree or list
+ *
+ * When the funcion is called it is assumed that:
+ * - 'mon' monitor earler has been inserted into 'dist'
+ * If the above is not true, bad things will happen.
+ *
+ * @param[in] mon Pointer to monitor
+ *
+ * @param[in] dist Pointer to dist structure
+ *
+ * @returns A non-zero value if deleted;
+ * otherwise, zero. The monitor
+ * is not deleted if the dist
+ * structure has been set in a
+ * dead state or if it has already
+ * been deleted.
+ *
+ */
+ERTS_GLB_INLINE int erts_monitor_dist_delete(ErtsMonitor *mon);
+
+/**
+ *
+ * @brief Set dead dist structure on monitor
+ *
+ * @param[in] mon Pointer to monitor
+ *
+ * @param[in] nodename Name of remote node
+ *
+ */
+void
+erts_monitor_set_dead_dist(ErtsMonitor *mon, Eterm nodename);
+
+/**
+ *
+ * @brief Get charged size of monitor
+ *
+ * If the other side of the monitor has been released, the
+ * whole size of the monitor data structure is returned; otherwise,
+ * half of the size is returned.
+ *
+ * When the funcion is called it is assumed that:
+ * - 'mon' has not been released
+ * If the above is not true, bad things will happen.
+ *
+ * @param[in] mon Pointer to monitor
+ *
+ * @returns Charged size in bytes
+ *
+ */
+Uint erts_monitor_size(ErtsMonitor *mon);
+
+
+/* internal function... */
+void erts_monitor_destroy__(ErtsMonitorData *mdp);
+
+/* implementations for globally inlined monitor functions... */
+#if ERTS_GLB_INLINE_INCL_FUNC_DEF
+
+ERTS_GLB_INLINE int
+erts_monitor_is_target(ErtsMonitor *mon)
+{
+ return !!(mon->flags & ERTS_ML_FLG_TARGET);
+}
+
+ERTS_GLB_INLINE int
+erts_monitor_is_origin(ErtsMonitor *mon)
+{
+ return !(mon->flags & ERTS_ML_FLG_TARGET);
+}
+
+ERTS_GLB_INLINE int
+erts_monitor_is_in_table(ErtsMonitor *mon)
+{
+ return !!(mon->flags & ERTS_ML_FLG_IN_TABLE);
+}
+
+ERTS_GLB_INLINE void
+erts_monitor_list_insert(ErtsMonitor **list, ErtsMonitor *mon)
+{
+ erts_ml_dl_list_insert__((ErtsMonLnkNode **) list, (ErtsMonLnkNode *) mon);
+}
+
+ERTS_GLB_INLINE void
+erts_monitor_list_delete(ErtsMonitor **list, ErtsMonitor *mon)
+{
+ erts_ml_dl_list_delete__((ErtsMonLnkNode **) list, (ErtsMonLnkNode *) mon);
+}
+
+ERTS_GLB_INLINE ErtsMonitor *
+erts_monitor_list_first(ErtsMonitor *list)
+{
+ return (ErtsMonitor *) erts_ml_dl_list_first__((ErtsMonLnkNode *) list);
+}
+
+ERTS_GLB_INLINE ErtsMonitor *
+erts_monitor_list_last(ErtsMonitor *list)
+{
+ return (ErtsMonitor *) erts_ml_dl_list_last__((ErtsMonLnkNode *) list);
+}
+
+#ifdef ERTS_ML_DEBUG
+extern size_t erts_monitor_origin_offset;
+extern size_t erts_monitor_origin_key_offset;
+extern size_t erts_monitor_target_offset;
+extern size_t erts_monitor_target_key_offset;
+extern size_t erts_monitor_node_key_offset;
+#endif
+
+ERTS_GLB_INLINE ErtsMonitorData *
+erts_monitor_to_data(ErtsMonitor *mon)
+{
+ ErtsMonitorData *mdp = erts_ml_node_to_main_struct__((ErtsMonLnkNode *) mon);
+
+#ifdef ERTS_ML_DEBUG
+ ERTS_ML_ASSERT(!(mdp->origin.flags & ERTS_ML_FLG_TARGET));
+ ERTS_ML_ASSERT(erts_monitor_origin_offset == (size_t) mdp->origin.offset);
+ ERTS_ML_ASSERT(!!(mdp->target.flags & ERTS_ML_FLG_TARGET));
+ ERTS_ML_ASSERT(erts_monitor_target_offset == (size_t) mdp->target.offset);
+ if (mon->type == ERTS_MON_TYPE_NODE || mon->type == ERTS_MON_TYPE_NODES) {
+ ERTS_ML_ASSERT(erts_monitor_node_key_offset == (size_t) mdp->origin.key_offset);
+ ERTS_ML_ASSERT(erts_monitor_node_key_offset == (size_t) mdp->target.key_offset);
+ }
+ else {
+ ERTS_ML_ASSERT(erts_monitor_origin_key_offset == (size_t) mdp->origin.key_offset);
+ ERTS_ML_ASSERT(erts_monitor_target_key_offset == (size_t) mdp->target.key_offset);
+ }
+#endif
+
+ return mdp;
+}
+
+ERTS_GLB_INLINE void
+erts_monitor_release(ErtsMonitor *mon)
+{
+ ErtsMonitorData *mdp = erts_monitor_to_data(mon);
+ ERTS_ML_ASSERT(!(mon->flags & ERTS_ML_FLG_IN_TABLE));
+ ERTS_ML_ASSERT(erts_atomic32_read_nob(&mdp->refc) > 0);
+
+ if (erts_atomic32_dec_read_nob(&mdp->refc) == 0)
+ erts_monitor_destroy__(mdp);
+}
+
+ERTS_GLB_INLINE void
+erts_monitor_release_both(ErtsMonitorData *mdp)
+{
+ ERTS_ML_ASSERT((mdp->origin.flags & ERTS_ML_FLGS_SAME)
+ == (mdp->target.flags & ERTS_ML_FLGS_SAME));
+ ERTS_ML_ASSERT(!(mdp->origin.flags & ERTS_ML_FLG_IN_TABLE));
+ ERTS_ML_ASSERT(!(mdp->target.flags & ERTS_ML_FLG_IN_TABLE));
+ ERTS_ML_ASSERT(erts_atomic32_read_nob(&mdp->refc) >= 2);
+
+ if (erts_atomic32_add_read_nob(&mdp->refc, (erts_aint32_t) -2) == 0)
+ erts_monitor_destroy__(mdp);
+}
+
+ERTS_GLB_INLINE int
+erts_monitor_dist_insert(ErtsMonitor *mon, ErtsMonLnkDist *dist)
+{
+ ErtsMonitorDataExtended *mdep;
+ int insert;
+
+ ERTS_ML_ASSERT(mon->flags & ERTS_ML_FLG_EXTENDED);
+ ERTS_ML_ASSERT(mon->type == ERTS_MON_TYPE_DIST_PROC
+ || mon->type == ERTS_MON_TYPE_NODE);
+
+ mdep = (ErtsMonitorDataExtended *) erts_monitor_to_data(mon);
+
+ ERTS_ML_ASSERT(!mdep->dist);
+ ERTS_ML_ASSERT(dist);
+ mdep->dist = dist;
+
+ erts_mon_link_dist_inc_refc(dist);
+
+ erts_mtx_lock(&dist->mtx);
+
+ insert = dist->alive;
+ if (insert) {
+ if ((mon->flags & (ERTS_ML_FLG_NAME
+ | ERTS_ML_FLG_TARGET)) == ERTS_ML_FLG_NAME)
+ erts_monitor_tree_insert(&dist->orig_name_monitors, mon);
+ else
+ erts_monitor_list_insert(&dist->monitors, mon);
+ }
+
+ erts_mtx_unlock(&dist->mtx);
+
+ return insert;
+}
+
+ERTS_GLB_INLINE int
+erts_monitor_dist_delete(ErtsMonitor *mon)
+{
+ ErtsMonitorDataExtended *mdep;
+ ErtsMonLnkDist *dist;
+ Uint16 flags;
+ int delete;
+
+ ERTS_ML_ASSERT(mon->flags & ERTS_ML_FLG_EXTENDED);
+ ERTS_ML_ASSERT(mon->type == ERTS_MON_TYPE_DIST_PROC
+ || mon->type == ERTS_MON_TYPE_NODE);
+
+ mdep = (ErtsMonitorDataExtended *) erts_monitor_to_data(mon);
+ dist = mdep->dist;
+ ERTS_ML_ASSERT(dist);
+
+ erts_mtx_lock(&dist->mtx);
+
+ flags = mon->flags;
+ delete = !!dist->alive & !!(flags & ERTS_ML_FLG_IN_TABLE);
+ if (delete) {
+ if ((flags & (ERTS_ML_FLG_NAME
+ | ERTS_ML_FLG_TARGET)) == ERTS_ML_FLG_NAME)
+ erts_monitor_tree_delete(&dist->orig_name_monitors, mon);
+ else
+ erts_monitor_list_delete(&dist->monitors, mon);
+ }
+
+ erts_mtx_unlock(&dist->mtx);
+
+ return delete;
+}
+
+
+#endif /* ERTS_GLB_INLINE_INCL_FUNC_DEF */
+
+/* suspend monitors... */
+ErtsMonitorSuspend *erts_monitor_suspend_create(Eterm pid);
+ErtsMonitorSuspend *erts_monitor_suspend_tree_lookup_create(ErtsMonitor **root,
+ int *created,
+ Eterm pid);
+void erts_monitor_suspend_destroy(ErtsMonitorSuspend *msp);
+
+ERTS_GLB_INLINE ErtsMonitorSuspend *erts_monitor_suspend(ErtsMonitor *mon);
+
+#if ERTS_GLB_INLINE_INCL_FUNC_DEF
+
+ERTS_GLB_INLINE ErtsMonitorSuspend *erts_monitor_suspend(ErtsMonitor *mon)
+{
+ ERTS_ML_ASSERT(!mon || mon->type == ERTS_MON_TYPE_SUSPEND);
+ return (ErtsMonitorSuspend *) mon;
+}
+
+#endif
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\
+ * Link Operations *
+\* */
+
+typedef struct ErtsMonLnkNode__ ErtsLink;
+
+typedef struct {
+ ErtsLink a;
+ ErtsLink b;
+ erts_atomic32_t refc;
+} ErtsLinkData;
+
+typedef struct {
+ ErtsLinkData ld;
+ struct erl_off_heap_header *ohhp;
+ ErtsMonLnkDist *dist;
+ Eterm heap[1]; /* heap start... */
+} ErtsLinkDataExtended;
+
+/*
+ * --- Link tree operations ---
+ */
+
+/**
+ *
+ * @brief Lookup a link in a link tree
+ *
+ *
+ * @param[in] root Pointer to root of link tree
+ *
+ * @param[in] key Key of link to lookup
+ *
+ * @returns Pointer to a link with the
+ * key 'key', or NULL if no such
+ * link was found
+ *
+ */
+ErtsLink *erts_link_tree_lookup(ErtsLink *root, Eterm item);
+
+/**
+ *
+ * @brief Lookup or insert a link in a link tree
+ *
+ * When the funcion is called it is assumed that:
+ * - 'lnk' link is not part of any tree or list
+ * If the above is not true, bad things will happen.
+ *
+ * @param[in,out] root Pointer to pointer to root of link tree
+ *
+ * @param[in] lnk Link to insert if no link
+ * with the same key already exists
+ *
+ * @returns Pointer to a link with the
+ * key 'key'. If no link with the key
+ * 'key' was found and 'lnk' was inserted
+ * 'lnk' is returned.
+ *
+ */
+ErtsLink *erts_link_tree_lookup_insert(ErtsLink **root, ErtsLink *lnk);
+
+/**
+ *
+ * @brief Lookup or create a link in a link tree.
+ *
+ * Looks up a link with the key 'other' in the link tree. If it is not
+ * found, creates and insert a link with the key 'other'.
+ *
+ * @param[in,out] root Pointer to pointer to root of link tree
+ *
+ * @param[out] created Pointer to integer. The integer is set to
+ * a non-zero value if no link with key
+ * 'other' was found, and a new link
+ * was created. If a link was found, it
+ * is set to zero.
+ *
+ * @param[in] type Type of link
+ *
+ * @param[in] this Id of this entity
+ *
+ * @param[in] other Id of other entity
+ *
+ */
+ErtsLink *erts_link_tree_lookup_create(ErtsLink **root, int *created,
+ Uint16 type, Eterm this, Eterm other);
+
+/**
+ *
+ * @brief Insert a link in a link tree
+ *
+ * When the funcion is called it is assumed that:
+ * - no links with the same key that 'lnk' exist in the tree
+ * - 'lnk' is not part of any list of tree
+ * If the above are not true, bad things will happen.
+ *
+ * @param[in,out] root Pointer to pointer to root of link tree
+ *
+ * @param[in] lnk Link to insert.
+ *
+ */
+void erts_link_tree_insert(ErtsLink **root, ErtsLink *lnk);
+
+/**
+ *
+ * @brief Replace a link in a link tree
+ *
+ * When the funcion is called it is assumed that:
+ * - 'old' link and 'new' link have exactly the same key
+ * - 'old' link is part of the tree
+ * - 'new' link is not part of any tree or list
+ * If the above are not true, bad things will happen.
+ *
+ * @param[in,out] root Pointer to pointer to root of link tree
+ *
+ * @param[in] old Link to remove from the tree
+ *
+ * @param[in] new Link to insert into the tree
+ *
+ */
+void erts_link_tree_replace(ErtsLink **root, ErtsLink *old, ErtsLink *new);
+
+/**
+ *
+ * @brief Replace a link in a link tree if key already exist based on adress
+ *
+ * Inserts the link 'lnk' in the tree if no link with the same key
+ * already exists in tree. If a link with the same key exists in
+ * the tree and 'lnk' has a lower address than the link in the
+ * tree, the existing link in the tree is replaced by 'lnk'.
+ *
+ * When the funcion is called it is assumed that:
+ * - 'lnk' link is not part of any tree or list
+ * If the above are not true, bad things will happen.
+ *
+ * @param[in,out] root Pointer to pointer to root of link tree
+ *
+ * @param[in] lnk Link to insert into the tree
+ *
+ * @returns A pointer to the link that is not part
+ * of the tree after this operation.
+ *
+ */
+ERTS_GLB_INLINE ErtsLink *erts_link_tree_insert_addr_replace(ErtsLink **root,
+ ErtsLink *lnk);
+
+/**
+ *
+ * @brief Delete a link from a link tree
+ *
+ * When the funcion is called it is assumed that:
+ * - 'lnk' link is part of the tree
+ * If the above is not true, bad things will happen.
+ *
+ * @param[in,out] root Pointer to pointer to root of link tree
+ *
+ * @param[in] lnk Link to remove from the tree
+ *
+ */
+void erts_link_tree_delete(ErtsLink **root, ErtsLink *lnk);
+
+/**
+ *
+ * @brief Delete a link from a link tree based on key
+ *
+ * If link 'lnk' is in the tree, 'lnk' is deleted from the tree.
+ * If link 'lnk' is not in the tree, another link with the same
+ * key as 'lnk' is deleted from the tree if such a link exist.
+ *
+ * When the funcion is called it is assumed that:
+ * - if 'lnk' link is part of a tree or list, it is part of this tree
+ * If the above is not true, bad things will happen.
+ *
+ * @param[in,out] root Pointer to pointer to root of link tree
+ *
+ * @param[in] lnk Link to remove from the tree
+ *
+ * @returns A pointer to the link that was deleted
+ * from the tree, or NULL in case no link
+ * was deleted from the tree
+ *
+ */
+ERTS_GLB_INLINE ErtsLink *erts_link_tree_key_delete(ErtsLink **root, ErtsLink *lnk);
+
+/**
+ *
+ * @brief Call a function for each link in a link tree
+ *
+ * The funcion 'func' will be called with a pointer to a link
+ * as first argument and 'arg' as second argument for each link
+ * in the tree referred to by 'root'.
+ *
+ * @param[in] root Pointer to root of link tree
+ *
+ * @param[in] func Pointer to function to call
+ *
+ * @param[in] arg Argument to pass as second argument in
+ * calls to 'func'
+ *
+ */
+void erts_link_tree_foreach(ErtsLink *root,
+ void (*func)(ErtsLink *, void *),
+ void *arg);
+
+/**
+ *
+ * @brief Call a function for each link in a link tree. Yield if lots
+ * of links exist.
+ *
+ * The funcion 'func' will be called with a pointer to a link
+ * as first argument and 'arg' as second argument for each link
+ * in the tree referred to by 'root'.
+ *
+ * It is assumed that:
+ * - *yspp equals NULL on first call
+ * - this function is repetedly called with *yspp set
+ * as set when previous call returned until a non-zero
+ * value is returned.
+ * - no modifications are made on the tree between first call
+ * and the call that returns a non-zero value
+ * If the above are not true, bad things will happen.
+ *
+ * @param[in] root Pointer to root of link tree
+ *
+ * @param[in] func Pointer to function to call
+ *
+ * @param[in] arg Argument to pass as second argument in
+ * calls to 'func'
+ *
+ * @param[in,out] vyspp Pointer to a pointer to an internal state
+ * used by this function. At initial call
+ * *yspp should be NULL. When done *yspp
+ * will be NULL.
+ *
+ * @param[in] limit Maximum amount of links to process
+ * before yielding.
+ *
+ * @returns A non-zero value when all links has been
+ * processed, and zero when more work is needed.
+ *
+ */
+int erts_link_tree_foreach_yielding(ErtsLink *root,
+ void (*func)(ErtsLink *, void *),
+ void *arg,
+ void **vyspp,
+ Sint limit);
+
+/**
+ *
+ * @brief Delete all links from a link tree and call a function for
+ * each link
+ *
+ * The funcion 'func' will be called with a pointer to a link
+ * as first argument and 'arg' as second argument for each link
+ * in the tree referred to by 'root'.
+ *
+ * @param[in,out] root Pointer to pointer to root of link tree
+ *
+ * @param[in] func Pointer to function to call
+ *
+ * @param[in] arg Argument to pass as second argument in
+ * calls to 'func'
+ *
+ */
+void erts_link_tree_foreach_delete(ErtsLink **root,
+ void (*func)(ErtsLink *, void *),
+ void *arg);
+
+/**
+ *
+ * @brief Delete all links from a link tree and call a function for
+ * each link
+ *
+ * The funcion 'func' will be called with a pointer to a link
+ * as first argument and 'arg' as second argument for each link
+ * in the tree referred to by 'root'.
+ *
+ * It is assumed that:
+ * - *yspp equals NULL on first call
+ * - this function is repetededly called with *yspp set
+ * as set when previous call returned until a non-zero
+ * value is returned.
+ * - no modifications are made on the tree between first call
+ * and the call that returns a non-zero value
+ * If the above are not true, bad things will happen.
+ *
+ * @param[in,out] root Pointer to pointer to root of link tree
+ *
+ * @param[in] func Pointer to function to call
+ *
+ * @param[in] arg Argument to pass as second argument in
+ * calls to 'func'
+ *
+ * @param[in,out] vyspp Pointer to a pointer to an internal state
+ * used by this function. At initial call
+ * *yspp should be NULL. When done *yspp
+ * will be NULL.
+ *
+ * @param[in] limit Maximum amount of links to process
+ * before yielding.
+ *
+ * @returns A non-zero value when all links has been
+ * processed, and zero when more work is needed.
+ *
+ */
+int erts_link_tree_foreach_delete_yielding(ErtsLink **root,
+ void (*func)(ErtsLink *, void *),
+ void *arg,
+ void **vyspp,
+ Sint limit);
+
+/*
+ * --- Link list operations ---
+ */
+
+/**
+ *
+ * @brief Insert a link in a link list
+ *
+ * When the funcion is called it is assumed that:
+ * - 'lnk' link is not part of any list or tree
+ * If the above is not true, bad things will happen.
+ *
+ * @param[in,out] list Pointer to pointer to link list
+ *
+ * @param[in] lnk Link to insert
+ *
+ */
+ERTS_GLB_INLINE void erts_link_list_insert(ErtsLink **list, ErtsLink *lnk);
+
+/**
+ *
+ * @brief Delete a link from a link list
+ *
+ * When the funcion is called it is assumed that:
+ * - 'lnk' link is part of the list
+ * If the above is not true, bad things will happen.
+ *
+ * @param[in,out] list Pointer to pointer to link list
+ *
+ * @param[in] lnk Link to remove from the list
+ *
+ */
+ERTS_GLB_INLINE void erts_link_list_delete(ErtsLink **list, ErtsLink *lnk);
+
+/**
+ *
+ * @brief Get a pointer to first link in a link list
+ *
+ * The link will still remain in the list after the return
+ *
+ * @param[in] list Pointer to link list
+ *
+ * @returns Pointer to first link in list if
+ * list is not empty. If list is empty
+ * NULL is returned.
+ *
+ */
+ERTS_GLB_INLINE ErtsLink *erts_link_list_first(ErtsLink *list);
+
+/**
+ *
+ * @brief Get a pointer to last link in a link list
+ *
+ * The link will still remain in the list after the return
+ *
+ * @param[in] list Pointer to link list
+ *
+ * @returns Pointer to last link in list if
+ * list is not empty. If list is empty
+ * NULL is returned.
+ *
+ */
+ERTS_GLB_INLINE ErtsLink *erts_link_list_last(ErtsLink *list);
+
+/**
+ *
+ * @brief Call a function for each link in a link list
+ *
+ * The funcion 'func' will be called with a pointer to a link
+ * as first argument and 'arg' as second argument for each link
+ * in the tree referred to by 'list'.
+ *
+ * @param[in] list Pointer to root of link list
+ *
+ * @param[in] func Pointer to function to call
+ *
+ * @param[in] arg Argument to pass as second argument in
+ * calls to 'func'
+ *
+ */
+void erts_link_list_foreach(ErtsLink *list,
+ void (*func)(ErtsLink *, void *),
+ void *arg);
+
+/**
+ *
+ * @brief Call a function for each link in a link list. Yield
+ * if lots of links exist.
+ *
+ * The funcion 'func' will be called with a pointer to a link
+ * as first argument and 'arg' as second argument for each link
+ * in the tree referred to by 'root'.
+ *
+ * It is assumed that:
+ * - *yspp equals NULL on first call
+ * - this function is repetedly called with *yspp set
+ * as set when previous call returned until a non-zero
+ * value is returned.
+ * - no modifications are made on the tree between first call
+ * and the call that returns a non-zero value
+ * If the above are not true, bad things will happen.
+ *
+ * @param[in] list Pointer to link list
+ *
+ * @param[in] func Pointer to function to call
+ *
+ * @param[in] arg Argument to pass as second argument in
+ * calls to 'func'
+ *
+ * @param[in,out] vyspp Pointer to a pointer to an internal state
+ * used by this function. At initial call
+ * *yspp should be NULL. When done *yspp
+ * will be NULL.
+ *
+ * @param[in] limit Maximum amount of links to process
+ * before yielding.
+ *
+ * @returns A non-zero value when all links has been
+ * processed, and zero when more work is needed.
+ *
+ */
+int erts_link_list_foreach_yielding(ErtsLink *list,
+ void (*func)(ErtsLink *, void *),
+ void *arg,
+ void **vyspp,
+ Sint limit);
+
+/**
+ *
+ * @brief Delete all links from a link list and call a function for
+ * each link
+ *
+ * The funcion 'func' will be called with a pointer to a link
+ * as first argument and 'arg' as second argument for each link
+ * in the tree referred to by 'root'.
+ *
+ * @param[in,out] list Pointer to pointer to link list
+ *
+ * @param[in] func Pointer to function to call
+ *
+ * @param[in] arg Argument to pass as second argument in
+ * calls to 'func'
+ *
+ */
+void erts_link_list_foreach_delete(ErtsLink **list,
+ void (*func)(ErtsLink *, void *),
+ void *arg);
+
+/**
+ *
+ * @brief Delete all links from a link list and call a function for
+ * each link
+ *
+ * The funcion 'func' will be called with a pointer to a link
+ * as first argument and 'arg' as second argument for each link
+ * in the tree referred to by 'root'.
+ *
+ * It is assumed that:
+ * - *yspp equals NULL on first call
+ * - this function is repetededly called with *yspp set
+ * as set when previous call returned until a non-zero
+ * value is returned.
+ * - no modifications are made on the tree between first
+ * and the call that returns a non-zero value
+ * If the above are not true, bad things will happen.
+ *
+ * @param[in,out] list Pointer to pointer to link list
+ *
+ * @param[in] func Pointer to function to call
+ *
+ * @param[in] arg Argument to pass as second argument in
+ * calls to 'func'
+ *
+ * @param[in,out] vyspp Pointer to a pointer to an internal state
+ * used by this function. At initial call
+ * *yspp should be NULL. When done *yspp
+ * will be NULL.
+ *
+ * @param[in] limit Maximum amount of links to process
+ * before yielding.
+ *
+ * @returns A non-zero value when all links has been
+ * processed, and zero when more work is needed.
+ *
+ */
+int erts_link_list_foreach_delete_yielding(ErtsLink **list,
+ void (*func)(ErtsLink *, void *),
+ void *arg,
+ void **vyspp,
+ Sint limit);
+
+/*
+ * --- Misc link operations ---
+ */
+
+/**
+ *
+ * @brief Create a link
+ *
+ * Can create all types of links
+ *
+ * When the funcion is called it is assumed that:
+ * - 'ref' is an internal ordinary reference if type is ERTS_MON_TYPE_PROC,
+ * ERTS_MON_TYPE_PORT, ERTS_MON_TYPE_TIME_OFFSET, or ERTS_MON_TYPE_RESOURCE
+ * - 'ref' is NIL if type is ERTS_MON_TYPE_NODE or ERTS_MON_TYPE_NODES
+ * - 'ref' is and ordinary internal reference or an external reference if
+ * type is ERTS_MON_TYPE_DIST_PROC
+ * - 'name' is an atom or NIL if type is ERTS_MON_TYPE_PROC,
+ * ERTS_MON_TYPE_PORT, or ERTS_MON_TYPE_DIST_PROC
+ * - 'name is NIL if type is ERTS_MON_TYPE_TIME_OFFSET, ERTS_MON_TYPE_RESOURCE,
+ * ERTS_MON_TYPE_NODE, or ERTS_MON_TYPE_NODES
+ * If the above is not true, bad things will happen.
+ *
+ * @param[in] type ERTS_MON_TYPE_PROC, ERTS_MON_TYPE_PORT,
+ * ERTS_MON_TYPE_TIME_OFFSET, ERTS_MON_TYPE_DIST_PROC,
+ * ERTS_MON_TYPE_RESOURCE, ERTS_MON_TYPE_NODE,
+ * or ERTS_MON_TYPE_NODES
+ *
+ * @param[in] a The key of entity a. Link structure a will
+ * have field other.item set to 'b'.
+ *
+ * @param[in] b The key of entity b. Link structure b will
+ * have field other.item set to 'a'.
+ *
+ */
+ErtsLinkData *erts_link_create(Uint16 type, Eterm a, Eterm b);
+
+/**
+ *
+ * @brief Get pointer to link data structure
+ *
+ * @param[in] lnk Pointer to link
+ *
+ * @returns Pointer to link data structure
+ *
+ */
+ERTS_GLB_INLINE ErtsLinkData *erts_link_to_data(ErtsLink *lnk);
+
+/**
+ *
+ * @brief Get pointer to the other link structure part of the link
+ *
+ * @param[in] lnk Pointer to link
+ *
+ * @param[out] ldpp Pointer to pointer to link data structure,
+ * if a non-NULL value is passed in the call
+ *
+ * @returns Pointer to other link
+ *
+ */
+ERTS_GLB_INLINE ErtsLink *erts_link_to_other(ErtsLink *lnk, ErtsLinkData **ldpp);
+
+/**
+ *
+ * @brief Check if link is in tree or list
+ *
+ * @param[in] lnk Pointer to lnk to check
+ *
+ * @returns A non-zero value if in tree or list;
+ * otherwise zero
+ *
+ */
+ERTS_GLB_INLINE int erts_link_is_in_table(ErtsLink *lnk);
+
+/**
+ *
+ * @brief Release link
+ *
+ * When both link halves part of the link have been released the link
+ * structure will be deallocated.
+ *
+ * When the funcion is called it is assumed that:
+ * - 'lnk' link is not part of any list or tree
+ * - 'lnk' is not referred to by any other structures
+ * If the above are not true, bad things will happen.
+ *
+ * @param[in] lnk Pointer to link
+ *
+ */
+ERTS_GLB_INLINE void erts_link_release(ErtsLink *lnk);
+
+/**
+ *
+ * @brief Release both link halves of a link simultaneously
+ *
+ * Release both halves of a link simultaneously and deallocate
+ * the structure.
+ *
+ * When the funcion is called it is assumed that:
+ * - Neither of the parts of the link are part of any list or tree
+ * - Neither of the parts of the link or the link data structure
+ * are referred to by any other structures
+ * If the above are not true, bad things will happen.
+ *
+ * @param[in] mdp Pointer to link data structure
+ *
+ */
+ERTS_GLB_INLINE void erts_link_release_both(ErtsLinkData *ldp);
+
+/**
+ *
+ * @brief Insert link in dist link list
+ *
+ * When the funcion is called it is assumed that:
+ * - 'lnk' link is not part of any list or tree
+ * If the above is not true, bad things will happen.
+ *
+ * @param[in] lnk Pointer to link
+ *
+ * @param[in] dist Pointer to dist structure
+ *
+ * @returns A non-zero value if inserted;
+ * otherwise, zero. The link
+ * is not inserted if the dist
+ * structure has been set in a
+ * dead state.
+ *
+ */
+ERTS_GLB_INLINE int erts_link_dist_insert(ErtsLink *lnk, ErtsMonLnkDist *dist);
+
+/**
+ *
+ * @brief Delete link from dist link list
+ *
+ * When the funcion is called it is assumed that:
+ * - 'lnk' link earler has been inserted into 'dist'
+ * If the above is not true, bad things will happen.
+ *
+ * @param[in] lnk Pointer to link
+ *
+ * @param[in] dist Pointer to dist structure
+ *
+ * @returns A non-zero value if deleted;
+ * otherwise, zero. The link
+ * is not deleted if the dist
+ * structure has been set in a
+ * dead state or if it has already
+ * been deleted.
+ *
+ */
+ERTS_GLB_INLINE int erts_link_dist_delete(ErtsLink *lnk);
+
+/**
+ *
+ * @brief Set dead dist structure on link
+ *
+ * @param[in] lnk Pointer to link
+ *
+ * @param[in] nodename Name of remote node
+ *
+ */
+void
+erts_link_set_dead_dist(ErtsLink *lnk, Eterm nodename);
+
+/**
+ *
+ * @brief Get charged size of link
+ *
+ * If the other side of the link has been released, the
+ * whole size of the link data structure is returned; otherwise,
+ * half of the size is returned.
+ *
+ * When the funcion is called it is assumed that:
+ * - 'lnk' has not been released
+ * If the above is not true, bad things will happen.
+ *
+ * @param[in] lnk Pointer to link
+ *
+ * @returns Charged size in bytes
+ *
+ */
+Uint erts_link_size(ErtsLink *lnk);
+
+/* internal function... */
+void erts_link_destroy__(ErtsLinkData *ldp);
+
+/* implementations for globally inlined link functions... */
+#if ERTS_GLB_INLINE_INCL_FUNC_DEF
+
+#ifdef ERTS_ML_DEBUG
+extern size_t erts_link_a_offset;
+extern size_t erts_link_b_offset;
+extern size_t erts_link_key_offset;
+#endif
+
+ERTS_GLB_INLINE ErtsLinkData *
+erts_link_to_data(ErtsLink *lnk)
+{
+ ErtsLinkData *ldp = erts_ml_node_to_main_struct__((ErtsMonLnkNode *) lnk);
+
+#ifdef ERTS_ML_DEBUG
+ ERTS_ML_ASSERT(erts_link_a_offset == (size_t) ldp->a.offset);
+ ERTS_ML_ASSERT(erts_link_key_offset == (size_t) ldp->a.key_offset);
+ ERTS_ML_ASSERT(erts_link_b_offset == (size_t) ldp->b.offset);
+ ERTS_ML_ASSERT(erts_link_key_offset == (size_t) ldp->b.key_offset);
+#endif
+
+ return ldp;
+}
+
+ERTS_GLB_INLINE ErtsLink *
+erts_link_to_other(ErtsLink *lnk, ErtsLinkData **ldpp)
+{
+ ErtsLinkData *ldp = erts_link_to_data(lnk);
+ if (ldpp)
+ *ldpp = ldp;
+ return lnk == &ldp->a ? &ldp->b : &ldp->a;
+}
+
+ERTS_GLB_INLINE int
+erts_link_is_in_table(ErtsLink *lnk)
+{
+ return !!(lnk->flags & ERTS_ML_FLG_IN_TABLE);
+}
+
+ERTS_GLB_INLINE void
+erts_link_list_insert(ErtsLink **list, ErtsLink *lnk)
+{
+ erts_ml_dl_list_insert__((ErtsMonLnkNode **) list, (ErtsMonLnkNode *) lnk);
+}
+
+ERTS_GLB_INLINE void
+erts_link_list_delete(ErtsLink **list, ErtsLink *lnk)
+{
+ erts_ml_dl_list_delete__((ErtsMonLnkNode **) list, (ErtsMonLnkNode *) lnk);
+}
+
+ERTS_GLB_INLINE ErtsLink *
+erts_link_list_first(ErtsLink *list)
+{
+ return (ErtsLink *) erts_ml_dl_list_first__((ErtsMonLnkNode *) list);
+}
+
+ERTS_GLB_INLINE ErtsLink *
+erts_link_list_last(ErtsLink *list)
+{
+ return (ErtsLink *) erts_ml_dl_list_last__((ErtsMonLnkNode *) list);
+}
+
+ERTS_GLB_INLINE void
+erts_link_release(ErtsLink *lnk)
+{
+ ErtsLinkData *ldp = erts_link_to_data(lnk);
+ ERTS_ML_ASSERT(!(lnk->flags & ERTS_ML_FLG_IN_TABLE));
+ ERTS_ML_ASSERT(erts_atomic32_read_nob(&ldp->refc) > 0);
+ if (erts_atomic32_dec_read_nob(&ldp->refc) == 0)
+ erts_link_destroy__(ldp);
+}
+
+ERTS_GLB_INLINE void
+erts_link_release_both(ErtsLinkData *ldp)
+{
+ ERTS_ML_ASSERT(!(ldp->a.flags & ERTS_ML_FLG_IN_TABLE));
+ ERTS_ML_ASSERT(!(ldp->b.flags & ERTS_ML_FLG_IN_TABLE));
+ ERTS_ML_ASSERT(erts_atomic32_read_nob(&ldp->refc) >= 2);
+ if (erts_atomic32_add_read_nob(&ldp->refc, (erts_aint32_t) -2) == 0)
+ erts_link_destroy__(ldp);
+}
+
+ERTS_GLB_INLINE ErtsLink *
+erts_link_tree_insert_addr_replace(ErtsLink **root, ErtsLink *lnk)
+{
+ ErtsLink *lnk2 = erts_link_tree_lookup_insert(root, lnk);
+ if (!lnk2)
+ return NULL;
+ if (lnk2 < lnk)
+ return lnk;
+ erts_link_tree_replace(root, lnk2, lnk);
+ return lnk2;
+}
+
+ERTS_GLB_INLINE ErtsLink *
+erts_link_tree_key_delete(ErtsLink **root, ErtsLink *lnk)
+{
+ ErtsLink *dlnk;
+ if (erts_link_is_in_table(lnk))
+ dlnk = lnk;
+ else
+ dlnk = erts_link_tree_lookup(*root, lnk->other.item);
+ if (dlnk)
+ erts_link_tree_delete(root, dlnk);
+ return dlnk;
+}
+
+ERTS_GLB_INLINE int
+erts_link_dist_insert(ErtsLink *lnk, ErtsMonLnkDist *dist)
+{
+ ErtsLinkDataExtended *ldep;
+ int insert;
+
+ ERTS_ML_ASSERT(lnk->flags & ERTS_ML_FLG_EXTENDED);
+ ERTS_ML_ASSERT(lnk->type == ERTS_LNK_TYPE_DIST_PROC);
+
+ ldep = (ErtsLinkDataExtended *) erts_link_to_data(lnk);
+
+ ERTS_ML_ASSERT(!ldep->dist);
+ ERTS_ML_ASSERT(dist);
+ ldep->dist = dist;
+
+ erts_mon_link_dist_inc_refc(dist);
+
+ erts_mtx_lock(&dist->mtx);
+
+ insert = dist->alive;
+ if (insert)
+ erts_link_list_insert(&dist->links, lnk);
+
+ erts_mtx_unlock(&dist->mtx);
+
+ return insert;
+}
+
+ERTS_GLB_INLINE int
+erts_link_dist_delete(ErtsLink *lnk)
+{
+ ErtsLinkDataExtended *ldep;
+ ErtsMonLnkDist *dist;
+ int delete;
+
+ ERTS_ML_ASSERT(lnk->flags & ERTS_ML_FLG_EXTENDED);
+ ERTS_ML_ASSERT(lnk->type == ERTS_LNK_TYPE_DIST_PROC);
+
+ ldep = (ErtsLinkDataExtended *) erts_link_to_data(lnk);
+ dist = ldep->dist;
+ if (!dist)
+ return -1;
+
+ erts_mtx_lock(&dist->mtx);
+
+ delete = !!dist->alive & !!(lnk->flags & ERTS_ML_FLG_IN_TABLE);
+ if (delete)
+ erts_link_list_delete(&dist->links, lnk);
+
+ erts_mtx_unlock(&dist->mtx);
+
+ return delete;
+}
+
+
+#endif /* ERTS_GLB_INLINE_INCL_FUNC_DEF */
+
+#endif /* ERL_MONITOR_LINK_H__ */
diff --git a/erts/emulator/beam/erl_monitors.c b/erts/emulator/beam/erl_monitors.c
deleted file mode 100644
index 1c840d89f6..0000000000
--- a/erts/emulator/beam/erl_monitors.c
+++ /dev/null
@@ -1,1073 +0,0 @@
-/*
- * %CopyrightBegin%
- *
- * Copyright Ericsson AB 2004-2017. All Rights Reserved.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- * %CopyrightEnd%
- */
-
-/**************************************************************************
- * Monitors and links data structure manipulation.
- * Monitors and links are organized as AVL trees with the reference as
- * key in the monitor case and the pid of the linked process as key in the
- * link case. Lookups the order of the references is somewhat special. Local
- * references are strictly smaller than remote references and are sorted
- * by inlined comparison functionality. Remote references are handled by the
- * usual cmp function.
- * Each Monitor is tagged with different tags depending on which end of the
- * monitor it is.
- * A monitor is removed either explicitly by reference or all monitors are
- * removed when the process exits. No need to access the monitor by pid.
- **************************************************************************/
-
-#ifdef HAVE_CONFIG_H
-# include "config.h"
-#endif
-
-#include "sys.h"
-#include "erl_vm.h"
-#include "global.h"
-#include "erl_process.h"
-#include "error.h"
-#include "erl_db.h"
-#include "bif.h"
-#include "big.h"
-#include "erl_monitors.h"
-#include "erl_bif_unique.h"
-
-#define STACK_NEED 50
-#define MAX_MONITORS 0xFFFFFFFFUL
-
-#define DIR_LEFT 0
-#define DIR_RIGHT 1
-#define DIR_END 2
-
-static erts_atomic_t tot_link_lh_size;
-
-/* Implements the sort order in monitor trees, which is different from
- the ordinary term order.
- No short local ref's should ever exist (the ref is created by the bif's
- in runtime), therefore:
- All local ref's are less than external ref's
- Local ref's are inline-compared,
- External ref's are compared by cmp */
-
-#if 0
-#define CMP_MON_REF(Ref1,Ref2) \
-cmp((Ref1),(Ref2)) /* XXX, the inline comparision yet to be done */
-#else
-#define CMP_MON_REF(Ref1,Ref2) cmp_mon_ref((Ref1),(Ref2))
-#endif
-
-static ERTS_INLINE int cmp_mon_ref(Eterm ref1, Eterm ref2)
-{
- Eterm *b1, *b2;
-
-
- b1 = boxed_val(ref1);
- b2 = boxed_val(ref2);
- if (is_ref_thing_header(*b1)) {
- if (is_ref_thing_header(*b2)) {
- Uint32 *num1, *num2;
- if (is_ordinary_ref_thing(b1)) {
- ErtsORefThing *rtp = (ErtsORefThing *) b1;
- num1 = rtp->num;
- }
- else {
- ErtsMRefThing *mrtp = (ErtsMRefThing *) b1;
- num1 = mrtp->mb->refn;
- }
- if (is_ordinary_ref_thing(b2)) {
- ErtsORefThing *rtp = (ErtsORefThing *) b2;
- num2 = rtp->num;
- }
- else {
- ErtsMRefThing *mrtp = (ErtsMRefThing *) b2;
- num2 = mrtp->mb->refn;
- }
- return erts_internal_ref_number_cmp(num1, num2);
- }
- return -1;
- }
- if (is_ref_thing_header(*b2)) {
- return 1;
- }
- return CMP(ref1,ref2);
-}
-
-#define CP_LINK_VAL(To, Hp, From) \
-do { \
- if (is_immed(From)) \
- (To) = (From); \
- else { \
- Uint i__; \
- Uint len__; \
- ASSERT((Hp)); \
- ASSERT(is_internal_ordinary_ref((From)) \
- || is_external((From))); \
- (To) = make_boxed((Hp)); \
- len__ = thing_arityval(*boxed_val((From))) + 1; \
- for(i__ = 0; i__ < len__; i__++) \
- (*((Hp)++)) = boxed_val((From))[i__]; \
- if (is_external((To))) { \
- external_thing_ptr((To))->next = NULL; \
- erts_refc_inc(&(external_thing_ptr((To))->node->refc), 2);\
- } \
- } \
-} while (0)
-
-static ErtsMonitor *create_monitor(Uint type, Eterm ref, UWord entity, Eterm name)
-{
- Uint mon_size = ERTS_MONITOR_SIZE;
- ErtsMonitor *n;
- Eterm *hp;
-
- mon_size += NC_HEAP_SIZE(ref);
- if (type != MON_NIF_TARGET && is_not_immed(entity)) {
- mon_size += NC_HEAP_SIZE(entity);
- }
-
- if (mon_size <= ERTS_MONITOR_SH_SIZE) {
- n = (ErtsMonitor *) erts_alloc(ERTS_ALC_T_MONITOR_SH,
- mon_size*sizeof(Uint));
- } else {
- n = (ErtsMonitor *) erts_alloc(ERTS_ALC_T_MONITOR_LH,
- mon_size*sizeof(Uint));
- erts_atomic_add_nob(&tot_link_lh_size, mon_size*sizeof(Uint));
- }
- hp = n->heap;
-
-
- n->left = n->right = NULL; /* Always the same initial value*/
- n->type = (Uint16) type;
- n->balance = 0; /* Always the same initial value */
- n->name = name; /* atom() or [] */
- CP_LINK_VAL(n->ref, hp, ref); /*XXX Unnecessary check, never immediate*/
- if (type == MON_NIF_TARGET)
- n->u.resource = (ErtsResource*)entity;
- else
- CP_LINK_VAL(n->u.pid, hp, (Eterm)entity);
-
- return n;
-}
-
-static ErtsLink *create_link(Uint type, Eterm pid)
-{
- Uint lnk_size = ERTS_LINK_SIZE;
- ErtsLink *n;
- Eterm *hp;
-
- if (is_not_immed(pid)) {
- lnk_size += NC_HEAP_SIZE(pid);
- }
-
- if (lnk_size <= ERTS_LINK_SH_SIZE) {
- n = (ErtsLink *) erts_alloc(ERTS_ALC_T_NLINK_SH,
- lnk_size*sizeof(Uint));
- } else {
- n = (ErtsLink *) erts_alloc(ERTS_ALC_T_NLINK_LH,
- lnk_size*sizeof(Uint));
- erts_atomic_add_nob(&tot_link_lh_size, lnk_size*sizeof(Uint));
- }
- hp = n->heap;
-
-
- n->left = n->right = NULL; /* Always the same initial value*/
- n->type = (Uint16) type;
- n->balance = 0; /* Always the same initial value */
- if (n->type == LINK_NODE) {
- ERTS_LINK_REFC(n) = 0;
- } else {
- ERTS_LINK_ROOT(n) = NULL;
- }
- CP_LINK_VAL(n->pid, hp, pid);
-
- return n;
-}
-
-#undef CP_LINK_VAL
-
-static ErtsSuspendMonitor *create_suspend_monitor(Eterm pid)
-{
- ErtsSuspendMonitor *smon = erts_alloc(ERTS_ALC_T_SUSPEND_MON,
- sizeof(ErtsSuspendMonitor));
- smon->left = smon->right = NULL; /* Always the same initial value */
- smon->balance = 0; /* Always the same initial value */
- smon->pending = 0;
- smon->active = 0;
- smon->pid = pid;
- return smon;
-}
-
-void
-erts_init_monitors(void)
-{
- erts_atomic_init_nob(&tot_link_lh_size, 0);
-}
-
-Uint
-erts_tot_link_lh_size(void)
-{
- return (Uint) erts_atomic_read_nob(&tot_link_lh_size);
-}
-
-void erts_destroy_monitor(ErtsMonitor *mon)
-{
- Uint mon_size = ERTS_MONITOR_SIZE;
- ErlNode *node;
-
- ASSERT(is_not_immed(mon->ref));
- mon_size += NC_HEAP_SIZE(mon->ref);
- if (is_external(mon->ref)) {
- node = external_thing_ptr(mon->ref)->node;
- erts_deref_node_entry(node);
- }
- if (mon->type != MON_NIF_TARGET && is_not_immed(mon->u.pid)) {
- mon_size += NC_HEAP_SIZE(mon->u.pid);
- if (is_external(mon->u.pid)) {
- node = external_thing_ptr(mon->u.pid)->node;
- erts_deref_node_entry(node);
- }
- }
- if (mon_size <= ERTS_MONITOR_SH_SIZE) {
- erts_free(ERTS_ALC_T_MONITOR_SH, (void *) mon);
- } else {
- erts_free(ERTS_ALC_T_MONITOR_LH, (void *) mon);
- erts_atomic_add_nob(&tot_link_lh_size, -1*mon_size*sizeof(Uint));
- }
-}
-
-void erts_destroy_link(ErtsLink *lnk)
-{
- Uint lnk_size = ERTS_LINK_SIZE;
- ErlNode *node;
-
- ASSERT(lnk->type == LINK_NODE || ERTS_LINK_ROOT(lnk) == NULL);
-
- if (is_not_immed(lnk->pid)) {
- lnk_size += NC_HEAP_SIZE(lnk->pid);
- if (is_external(lnk->pid)) {
- node = external_thing_ptr(lnk->pid)->node;
- erts_deref_node_entry(node);
- }
- }
- if (lnk_size <= ERTS_LINK_SH_SIZE) {
- erts_free(ERTS_ALC_T_NLINK_SH, (void *) lnk);
- } else {
- erts_free(ERTS_ALC_T_NLINK_LH, (void *) lnk);
- erts_atomic_add_nob(&tot_link_lh_size, -1*lnk_size*sizeof(Uint));
- }
-}
-
-void erts_destroy_suspend_monitor(ErtsSuspendMonitor *smon)
-{
- erts_free(ERTS_ALC_T_SUSPEND_MON, smon);
-}
-
-static void insertion_rotation(int dstack[], int dpos,
- void *tstack[], int tpos,
- int state) {
-
- ErtsMonitorOrLink **this;
- ErtsMonitorOrLink *p1, *p2, *p;
- int dir;
-
- while (state && ( dir = dstack[--dpos] ) != DIR_END) {
- this = tstack[--tpos];
- p = *this;
- if (dir == DIR_LEFT) {
- switch (p->balance) {
- case 1:
- p->balance = 0;
- state = 0;
- break;
- case 0:
- p->balance = -1;
- break;
- case -1: /* The icky case */
- p1 = p->left;
- if (p1->balance == -1) { /* Single LL rotation */
- p->left = p1->right;
- p1->right = p;
- p->balance = 0;
- (*this) = p1;
- } else { /* Double RR rotation */
- p2 = p1->right;
- p1->right = p2->left;
- p2->left = p1;
- p->left = p2->right;
- p2->right = p;
- p->balance = (p2->balance == -1) ? +1 : 0;
- p1->balance = (p2->balance == 1) ? -1 : 0;
- (*this) = p2;
- }
- (*this)->balance = 0;
- state = 0;
- break;
- }
- } else { /* dir == DIR_RIGHT */
- switch (p->balance) {
- case -1:
- p->balance = 0;
- state = 0;
- break;
- case 0:
- p->balance = 1;
- break;
- case 1:
- p1 = p->right;
- if (p1->balance == 1) { /* Single RR rotation */
- p->right = p1->left;
- p1->left = p;
- p->balance = 0;
- (*this) = p1;
- } else { /* Double RL rotation */
- p2 = p1->left;
- p1->left = p2->right;
- p2->right = p1;
- p->right = p2->left;
- p2->left = p;
- p->balance = (p2->balance == 1) ? -1 : 0;
- p1->balance = (p2->balance == -1) ? 1 : 0;
- (*this) = p2;
- }
- (*this)->balance = 0;
- state = 0;
- break;
- }
- }
- }
-}
-
-void erts_add_monitor(ErtsMonitor **root, Uint type, Eterm ref, UWord entity,
- Eterm name)
-{
- void *tstack[STACK_NEED];
- int tpos = 0;
- int dstack[STACK_NEED+1];
- int dpos = 1;
- int state = 0;
- ErtsMonitor **this = root;
- Sint c;
-
- ASSERT(is_internal_ordinary_ref(ref) || is_external_ref(ref));
-
- dstack[0] = DIR_END;
- for (;;) {
- if (!*this) { /* Found our place */
- state = 1;
- *this = create_monitor(type,ref,entity,name);
- break;
- } else if ((c = CMP_MON_REF(ref,(*this)->ref)) < 0) {
- /* go left */
- dstack[dpos++] = DIR_LEFT;
- tstack[tpos++] = this;
- this = &((*this)->left);
- } else if (c > 0) { /* go right */
- dstack[dpos++] = DIR_RIGHT;
- tstack[tpos++] = this;
- this = &((*this)->right);
- } else { /* Equal key is an error for monitors */
- erts_exit(ERTS_ERROR_EXIT,"Insertion of already present monitor!");
- break;
- }
- }
- insertion_rotation(dstack, dpos, tstack, tpos, state);
-}
-
-
-/* Returns 0 if OK, < 0 if already present */
-int erts_add_link(ErtsLink **root, Uint type, Eterm pid)
-{
- void *tstack[STACK_NEED];
- int tpos = 0;
- int dstack[STACK_NEED+1];
- int dpos = 1;
- int state = 0;
- ErtsLink **this = root;
- Sint c;
-
- dstack[0] = DIR_END;
- for (;;) {
- if (!*this) { /* Found our place */
- state = 1;
- *this = create_link(type,pid);
- break;
- } else if ((c = CMP(pid,(*this)->pid)) < 0) {
- /* go left */
- dstack[dpos++] = DIR_LEFT;
- tstack[tpos++] = this;
- this = &((*this)->left);
- } else if (c > 0) { /* go right */
- dstack[dpos++] = DIR_RIGHT;
- tstack[tpos++] = this;
- this = &((*this)->right);
- } else { /* Equal key is an error for monitors */
- return -1;
- }
- }
- insertion_rotation(dstack, dpos, tstack, tpos, state);
- return 0;
-}
-
-ErtsSuspendMonitor *
-erts_add_or_lookup_suspend_monitor(ErtsSuspendMonitor **root, Eterm pid)
-{
- void *tstack[STACK_NEED];
- int tpos = 0;
- int dstack[STACK_NEED+1];
- int dpos = 1;
- int state = 0;
- ErtsSuspendMonitor **this = root;
- ErtsSuspendMonitor *res;
- Sint c;
-
- dstack[0] = DIR_END;
- for (;;) {
- if (!*this) { /* Found our place */
- state = 1;
- res = *this = create_suspend_monitor(pid);
- break;
- } else if ((c = CMP(pid,(*this)->pid)) < 0) {
- /* go left */
- dstack[dpos++] = DIR_LEFT;
- tstack[tpos++] = this;
- this = &((*this)->left);
- } else if (c > 0) { /* go right */
- dstack[dpos++] = DIR_RIGHT;
- tstack[tpos++] = this;
- this = &((*this)->right);
- } else { /* Already here... */
- ASSERT((*this)->pid == pid);
- return *this;
- }
- }
- insertion_rotation(dstack, dpos, tstack, tpos, state);
- return res;
-}
-
-
-/* Returns the new or old link structure */
-ErtsLink *erts_add_or_lookup_link(ErtsLink **root, Uint type, Eterm pid)
-{
- void *tstack[STACK_NEED];
- int tpos = 0;
- int dstack[STACK_NEED+1];
- int dpos = 1;
- int state = 0;
- ErtsLink **this = root;
- Sint c;
- ErtsLink *ret = NULL;
-
- dstack[0] = DIR_END;
- for (;;) {
- if (!*this) { /* Found our place */
- state = 1;
- *this = create_link(type,pid);
- ret = *this;
- break;
- } else if ((c = CMP(pid,(*this)->pid)) < 0) {
- /* go left */
- dstack[dpos++] = DIR_LEFT;
- tstack[tpos++] = this;
- this = &((*this)->left);
- } else if (c > 0) { /* go right */
- dstack[dpos++] = DIR_RIGHT;
- tstack[tpos++] = this;
- this = &((*this)->right);
- } else { /* Equal key is an error for monitors */
- return *this;
- }
- }
- insertion_rotation(dstack, dpos, tstack, tpos, state);
- return ret;
-}
-
-
-/*
- * Deletion helpers
- */
-static int balance_left(ErtsMonitorOrLink **this)
-{
- ErtsMonitorOrLink *p, *p1, *p2;
- int b1, b2, h = 1;
-
- p = *this;
- switch (p->balance) {
- case -1:
- p->balance = 0;
- break;
- case 0:
- p->balance = 1;
- h = 0;
- break;
- case 1:
- p1 = p->right;
- b1 = p1->balance;
- if (b1 >= 0) { /* Single RR rotation */
- p->right = p1->left;
- p1->left = p;
- if (b1 == 0) {
- p->balance = 1;
- p1->balance = -1;
- h = 0;
- } else {
- p->balance = p1->balance = 0;
- }
- (*this) = p1;
- } else { /* Double RL rotation */
- p2 = p1->left;
- b2 = p2->balance;
- p1->left = p2->right;
- p2->right = p1;
- p->right = p2->left;
- p2->left = p;
- p->balance = (b2 == 1) ? -1 : 0;
- p1->balance = (b2 == -1) ? 1 : 0;
- p2->balance = 0;
- (*this) = p2;
- }
- break;
- }
- return h;
-}
-
-static int balance_right(ErtsMonitorOrLink **this)
-{
- ErtsMonitorOrLink *p, *p1, *p2;
- int b1, b2, h = 1;
-
- p = *this;
- switch (p->balance) {
- case 1:
- p->balance = 0;
- break;
- case 0:
- p->balance = -1;
- h = 0;
- break;
- case -1:
- p1 = p->left;
- b1 = p1->balance;
- if (b1 <= 0) { /* Single LL rotation */
- p->left = p1->right;
- p1->right = p;
- if (b1 == 0) {
- p->balance = -1;
- p1->balance = 1;
- h = 0;
- } else {
- p->balance = p1->balance = 0;
- }
- (*this) = p1;
- } else { /* Double LR rotation */
- p2 = p1->right;
- b2 = p2->balance;
- p1->right = p2->left;
- p2->left = p1;
- p->left = p2->right;
- p2->right = p;
- p->balance = (b2 == -1) ? 1 : 0;
- p1->balance = (b2 == 1) ? -1 : 0;
- p2->balance = 0;
- (*this) = p2;
- }
- }
- return h;
-}
-
-static int delsub(ErtsMonitorOrLink **this)
-{
- ErtsMonitorOrLink **tstack[STACK_NEED];
- int tpos = 0;
- ErtsMonitorOrLink *q = (*this);
- ErtsMonitorOrLink **r = &(q->left);
- int h;
-
- /*
- * Walk down the tree to the right and search
- * for a void right child, pick that child out
- * and return it to be put in the deleted
- * object's place.
- */
-
- while ((*r)->right != NULL) {
- tstack[tpos++] = r;
- r = &((*r)->right);
- }
- *this = *r;
- *r = (*r)->left;
- (*this)->left = q->left;
- (*this)->right = q->right;
- (*this)->balance = q->balance;
- tstack[0] = &((*this)->left);
- h = 1;
- while (tpos && h) {
- r = tstack[--tpos];
- h = balance_right(r);
- }
- return h;
-}
-
-ErtsMonitor *erts_remove_monitor(ErtsMonitor **root, Eterm ref)
-{
- ErtsMonitor **tstack[STACK_NEED];
- int tpos = 0;
- int dstack[STACK_NEED+1];
- int dpos = 1;
- int state = 0;
- ErtsMonitor **this = root;
- Sint c;
- int dir;
- ErtsMonitor *q = NULL;
-
- dstack[0] = DIR_END;
- for (;;) {
- if (!*this) { /* Failure */
- return NULL;
- } else if ((c = CMP_MON_REF(ref,(*this)->ref)) < 0) {
- dstack[dpos++] = DIR_LEFT;
- tstack[tpos++] = this;
- this = &((*this)->left);
- } else if (c > 0) { /* go right */
- dstack[dpos++] = DIR_RIGHT;
- tstack[tpos++] = this;
- this = &((*this)->right);
- } else { /* Equal key, found the one to delete */
- q = (*this);
- if (q->right == NULL) {
- (*this) = q->left;
- state = 1;
- } else if (q->left == NULL) {
- (*this) = q->right;
- state = 1;
- } else {
- dstack[dpos++] = DIR_LEFT;
- tstack[tpos++] = this;
- state = delsub((ErtsMonitorOrLink **) this);
- }
- break;
- }
- }
- while (state && ( dir = dstack[--dpos] ) != DIR_END) {
- this = tstack[--tpos];
- if (dir == DIR_LEFT) {
- state = balance_left((ErtsMonitorOrLink **) this);
- } else {
- state = balance_right((ErtsMonitorOrLink **) this);
- }
- }
- return q;
-}
-
-ErtsLink *erts_remove_link(ErtsLink **root, Eterm pid)
-{
- ErtsLink **tstack[STACK_NEED];
- int tpos = 0;
- int dstack[STACK_NEED+1];
- int dpos = 1;
- int state = 0;
- ErtsLink **this = root;
- Sint c;
- int dir;
- ErtsLink *q = NULL;
-
- dstack[0] = DIR_END;
- for (;;) {
- if (!*this) { /* Failure */
- return NULL;
- } else if ((c = CMP(pid,(*this)->pid)) < 0) {
- dstack[dpos++] = DIR_LEFT;
- tstack[tpos++] = this;
- this = &((*this)->left);
- } else if (c > 0) { /* go right */
- dstack[dpos++] = DIR_RIGHT;
- tstack[tpos++] = this;
- this = &((*this)->right);
- } else { /* Equal key, found the one to delete */
- q = (*this);
- if (q->right == NULL) {
- (*this) = q->left;
- state = 1;
- } else if (q->left == NULL) {
- (*this) = q->right;
- state = 1;
- } else {
- dstack[dpos++] = DIR_LEFT;
- tstack[tpos++] = this;
- state = delsub((ErtsMonitorOrLink **) this);
- }
- break;
- }
- }
- while (state && ( dir = dstack[--dpos] ) != DIR_END) {
- this = tstack[--tpos];
- if (dir == DIR_LEFT) {
- state = balance_left((ErtsMonitorOrLink **) this);
- } else {
- state = balance_right((ErtsMonitorOrLink **) this);
- }
- }
- return q;
-}
-
-void
-erts_delete_suspend_monitor(ErtsSuspendMonitor **root, Eterm pid)
-{
- ErtsSuspendMonitor **tstack[STACK_NEED];
- int tpos = 0;
- int dstack[STACK_NEED+1];
- int dpos = 1;
- int state = 0;
- ErtsSuspendMonitor **this = root;
- Sint c;
- int dir;
- ErtsSuspendMonitor *q = NULL;
-
- dstack[0] = DIR_END;
- for (;;) {
- if (!*this) { /* Nothing found */
- return;
- } else if ((c = CMP(pid,(*this)->pid)) < 0) {
- dstack[dpos++] = DIR_LEFT;
- tstack[tpos++] = this;
- this = &((*this)->left);
- } else if (c > 0) { /* go right */
- dstack[dpos++] = DIR_RIGHT;
- tstack[tpos++] = this;
- this = &((*this)->right);
- } else { /* Equal key, found the one to delete */
- q = (*this);
- ASSERT(q->pid == pid);
- if (q->right == NULL) {
- (*this) = q->left;
- state = 1;
- } else if (q->left == NULL) {
- (*this) = q->right;
- state = 1;
- } else {
- dstack[dpos++] = DIR_LEFT;
- tstack[tpos++] = this;
- state = delsub((ErtsMonitorOrLink **) this);
- }
- erts_destroy_suspend_monitor(q);
- break;
- }
- }
- while (state && ( dir = dstack[--dpos] ) != DIR_END) {
- this = tstack[--tpos];
- if (dir == DIR_LEFT) {
- state = balance_left((ErtsMonitorOrLink **) this);
- } else {
- state = balance_right((ErtsMonitorOrLink **) this);
- }
- }
-}
-
-ErtsMonitor *erts_lookup_monitor(ErtsMonitor *root, Eterm ref)
-{
- Sint c;
-
- for (;;) {
- if (root == NULL || (c = CMP_MON_REF(ref,root->ref)) == 0) {
- return root;
- } else if (c < 0) {
- root = root->left;
- } else { /* c > 0 */
- root = root->right;
- }
- }
-}
-
-ErtsLink *erts_lookup_link(ErtsLink *root, Eterm pid)
-{
- Sint c;
-
- for (;;) {
- if (root == NULL || (c = CMP(pid,root->pid)) == 0) {
- return root;
- } else if (c < 0) {
- root = root->left;
- } else { /* c > 0 */
- root = root->right;
- }
- }
-}
-
-ErtsSuspendMonitor *
-erts_lookup_suspend_monitor(ErtsSuspendMonitor *root, Eterm pid)
-{
- Sint c;
-
- for (;;) {
- if (root == NULL || (c = CMP(pid,root->pid)) == 0) {
- return root;
- } else if (c < 0) {
- root = root->left;
- } else { /* c > 0 */
- root = root->right;
- }
- }
-}
-
-void erts_sweep_monitors(ErtsMonitor *root,
- void (*doit)(ErtsMonitor *, void *),
- void *context)
-{
- ErtsMonitor *tstack[STACK_NEED];
- int tpos = 0;
- int dstack[STACK_NEED+1];
- int dpos = 1;
- int dir;
-
- dstack[0] = DIR_END;
-
- for (;;) {
- if (root == NULL) {
- if ((dir = dstack[dpos-1]) == DIR_END) {
- return;
- }
- if (dir == DIR_LEFT) {
- /* Still has DIR_RIGHT to do */
- dstack[dpos-1] = DIR_RIGHT;
- root = (tstack[tpos-1])->right;
- } else {
- /* stacktop is an object to be deleted */
- (*doit)(tstack[--tpos],context); /* expeted to do the
- deletion */
- --dpos;
- root = NULL;
- }
- } else {
- dstack[dpos++] = DIR_LEFT;
- tstack[tpos++] = root;
- root = root->left;
- }
- }
-}
-
-void erts_sweep_links(ErtsLink *root,
- void (*doit)(ErtsLink *, void *),
- void *context)
-{
- ErtsLink *tstack[STACK_NEED];
- int tpos = 0;
- int dstack[STACK_NEED+1];
- int dpos = 1;
- int dir;
-
- dstack[0] = DIR_END;
-
- for (;;) {
- if (root == NULL) {
- if ((dir = dstack[dpos-1]) == DIR_END) {
- return;
- }
- if (dir == DIR_LEFT) {
- /* Still has DIR_RIGHT to do */
- dstack[dpos-1] = DIR_RIGHT;
- root = (tstack[tpos-1])->right;
- } else {
- /* stacktop is an object to be deleted */
- (*doit)(tstack[--tpos],context); /* expeted to do the
- deletion */
- --dpos;
- root = NULL;
- }
- } else {
- dstack[dpos++] = DIR_LEFT;
- tstack[tpos++] = root;
- root = root->left;
- }
- }
-}
-
-void erts_sweep_suspend_monitors(ErtsSuspendMonitor *root,
- void (*doit)(ErtsSuspendMonitor *, void *),
- void *context)
-{
- ErtsSuspendMonitor *tstack[STACK_NEED];
- int tpos = 0;
- int dstack[STACK_NEED+1];
- int dpos = 1;
- int dir;
-
- dstack[0] = DIR_END;
-
- for (;;) {
- if (root == NULL) {
- if ((dir = dstack[dpos-1]) == DIR_END) {
- return;
- }
- if (dir == DIR_LEFT) {
- /* Still has DIR_RIGHT to do */
- dstack[dpos-1] = DIR_RIGHT;
- root = (tstack[tpos-1])->right;
- } else {
- /* stacktop is an object to be deleted */
- (*doit)(tstack[--tpos],context); /* expeted to do the
- deletion */
- --dpos;
- root = NULL;
- }
- } else {
- dstack[dpos++] = DIR_LEFT;
- tstack[tpos++] = root;
- root = root->left;
- }
- }
-}
-
-
-/* Debug BIF, always present, but undocumented... */
-
-static void erts_dump_monitors(ErtsMonitor *root, int indent)
-{
- if (root == NULL)
- return;
- erts_dump_monitors(root->right,indent+2);
- erts_printf("%*s[%b16d:%b16u:%T:%T", indent, "", root->balance,
- root->type, root->ref, root->name);
- if (root->type == MON_NIF_TARGET)
- erts_printf(":%p]\n", root->u.resource);
- else
- erts_printf(":%T]\n", root->u.pid);
- erts_dump_monitors(root->left,indent+2);
-}
-
-static void erts_dump_links_aux(ErtsLink *root, int indent,
- erts_dsprintf_buf_t *dsbufp)
-{
- if (root == NULL)
- return;
- erts_dump_links_aux(root->right, indent+2, dsbufp);
- dsbufp->str_len = 0;
- erts_dsprintf(dsbufp, "%*s[%b16d:%b16u:%T:%p]", indent, "",
- root->balance, root->type, root->pid, ERTS_LINK_ROOT(root));
- if (ERTS_LINK_ROOT(root) != NULL) {
- ErtsLink *sub = ERTS_LINK_ROOT(root);
- int len = dsbufp->str_len;
- erts_dump_links_aux(sub->right, indent+len+5, dsbufp);
- erts_dsprintf(dsbufp, "-> %*s[%b16d:%b16u:%T:%p]", indent, "",
- sub->balance, sub->type, sub->pid, ERTS_LINK_ROOT(sub));
- erts_printf("%s\n", dsbufp->str);
- erts_dump_links_aux(sub->left, indent+len+5, dsbufp);
- } else {
- erts_printf("%s\n", dsbufp->str);
- }
- erts_dump_links_aux(root->left, indent+2, dsbufp);
-}
-
-static void erts_dump_links(ErtsLink *root, int indent)
-{
- erts_dsprintf_buf_t *dsbufp = erts_create_tmp_dsbuf(0);
- erts_dump_links_aux(root, indent, dsbufp);
- erts_destroy_tmp_dsbuf(dsbufp);
-}
-
-Eterm erts_debug_dump_monitors_1(BIF_ALIST_1)
-{
- Process *p = BIF_P;
- Eterm pid = BIF_ARG_1;
- Process *rp;
- DistEntry *dep;
- rp = erts_pid2proc(p, ERTS_PROC_LOCK_MAIN, pid, ERTS_PROC_LOCK_LINK);
- if (!rp) {
- ERTS_ASSERT_IS_NOT_EXITING(p);
- if (is_atom(pid) && is_node_name_atom(pid) &&
- (dep = erts_find_dist_entry(pid)) != NULL) {
- erts_printf("Dumping dist monitors-------------------\n");
- erts_de_links_lock(dep);
- erts_dump_monitors(dep->monitors,0);
- erts_de_links_unlock(dep);
- erts_printf("Monitors dumped-------------------------\n");
- BIF_RET(am_true);
- } else {
- BIF_ERROR(p,BADARG);
- }
- } else {
- erts_printf("Dumping pid monitors--------------------\n");
- erts_dump_monitors(ERTS_P_MONITORS(rp),0);
- erts_printf("Monitors dumped-------------------------\n");
- erts_proc_unlock(rp, ERTS_PROC_LOCK_LINK);
- BIF_RET(am_true);
- }
-}
-
-Eterm erts_debug_dump_links_1(BIF_ALIST_1)
-{
- Process *p = BIF_P;
- Eterm pid = BIF_ARG_1;
- Process *rp;
- DistEntry *dep;
- if (is_internal_port(pid)) {
- Port *rport = erts_id2port_sflgs(pid,
- p,
- ERTS_PROC_LOCK_MAIN,
- ERTS_PORT_SFLGS_INVALID_LOOKUP);
- if (rport) {
- erts_printf("Dumping port links----------------------\n");
- erts_dump_links(ERTS_P_LINKS(rport), 0);
- erts_printf("Links dumped----------------------------\n");
- erts_port_release(rport);
- BIF_RET(am_true);
- } else {
- BIF_ERROR(p,BADARG);
- }
- } else {
- rp = erts_pid2proc(p, ERTS_PROC_LOCK_MAIN, pid, ERTS_PROC_LOCK_LINK);
- if (!rp) {
- ERTS_ASSERT_IS_NOT_EXITING(p);
- if (is_atom(pid) && is_node_name_atom(pid) &&
- (dep = erts_find_dist_entry(pid)) != NULL) {
- erts_printf("Dumping dist links----------------------\n");
- erts_de_links_lock(dep);
- erts_dump_links(dep->nlinks,0);
- erts_de_links_unlock(dep);
- erts_printf("Links dumped----------------------------\n");
- BIF_RET(am_true);
- } else {
- BIF_ERROR(p,BADARG);
- }
-
- } else {
- erts_printf("Dumping pid links-----------------------\n");
- erts_dump_links(ERTS_P_LINKS(rp), 0);
- erts_printf("Links dumped----------------------------\n");
- erts_proc_unlock(rp, ERTS_PROC_LOCK_LINK);
- BIF_RET(am_true);
- }
- }
-}
-
-void erts_one_link_size(ErtsLink *lnk, void *vpu)
-{
- Uint *pu = vpu;
- *pu += ERTS_LINK_SIZE*sizeof(Uint);
- if(is_not_immed(lnk->pid))
- *pu += NC_HEAP_SIZE(lnk->pid)*sizeof(Uint);
- if (lnk->type != LINK_NODE && ERTS_LINK_ROOT(lnk) != NULL) {
- erts_doforall_links(ERTS_LINK_ROOT(lnk),&erts_one_link_size,vpu);
- }
-}
-void erts_one_mon_size(ErtsMonitor *mon, void *vpu)
-{
- Uint *pu = vpu;
- *pu += ERTS_MONITOR_SIZE*sizeof(Uint);
- if(mon->type != MON_NIF_TARGET && is_not_immed(mon->u.pid))
- *pu += NC_HEAP_SIZE(mon->u.pid)*sizeof(Uint);
- if(is_not_immed(mon->ref))
- *pu += NC_HEAP_SIZE(mon->ref)*sizeof(Uint);
-}
diff --git a/erts/emulator/beam/erl_monitors.h b/erts/emulator/beam/erl_monitors.h
deleted file mode 100644
index 1cacecd7e9..0000000000
--- a/erts/emulator/beam/erl_monitors.h
+++ /dev/null
@@ -1,187 +0,0 @@
-/*
- * %CopyrightBegin%
- *
- * Copyright Ericsson AB 2004-2017. All Rights Reserved.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- * %CopyrightEnd%
- */
-
-/**********************************************************************
- * Header for monitors and links data structures.
- * Monitors are kept in an AVL tree and the data structures for
- * the four different types of monitors are like this:
- **********************************************************************
- * Local monitor by pid/port:
- * (Ref is always same in all involved data structures)
- **********************************************************************
- * Process/Port X Process Y
- * +-------------+ +-------------+
- * Type: | MON_ORIGIN | | MON_TARGET |
- * +-------------+ +-------------+
- * Pid: | Pid(Y) | | Pid/Port(X) |
- * +-------------+ +-------------+
- * Name: | [] | | [] |
- * +-------------+ +-------------+
- **********************************************************************
- * Local monitor by name: (Ref is always same in all involved data structures)
- **********************************************************************
- * Process X Process Y (name foo)
- * +-------------+ +-------------+
- * Type: | MON_ORIGIN | | MON_TARGET |
- * +-------------+ +-------------+
- * Pid: | Pid(Y) | | Pid(X) |
- * +-------------+ +-------------+
- * Name: | Atom(foo) | | Atom(foo) |
- * +-------------+ +-------------+
- **********************************************************************
- * Remote monitor by pid: (Ref is always same in all involved data structures)
- **********************************************************************
- * Node A | Node B
- * ---------------------------------+----------------------------------
- * Process X (@A) Distentry @A Distentry @B Process Y (@B)
- * for node B for node A
- * +-------------+ +-------------+ +-------------+ +-------------+
- * Type: | MON_ORIGIN | | MON_TARGET | | MON_ORIGIN | | MON_TARGET |
- * +-------------+ +-------------+ +-------------+ +-------------+
- * Pid: | Pid(Y) | | Pid(X) | | Pid(Y) | | Pid(X) |
- * +-------------+ +-------------+ +-------------+ +-------------+
- * Name: | [] | | [] | | [] | | [] |
- * +-------------+ +-------------+ +-------------+ +-------------+
- **********************************************************************
- * Remote monitor by name: (Ref is always same in all involved data structures)
- **********************************************************************
- * Node A | Node B
- * ---------------------------------+----------------------------------
- * Process X (@A) Distentry @A Distentry @B Process Y (@B)
- * for node B for node A (name foo)
- * +-------------+ +-------------+ +-------------+ +-------------+
- * Type: | MON_ORIGIN | | MON_TARGET | | MON_ORIGIN | | MON_TARGET |
- * +-------------+ +-------------+ +-------------+ +-------------+
- * Pid: | Atom(node B)| | Pid(X) | | Pid(Y) | | Pid(X) |
- * +-------------+ +-------------+ +-------------+ +-------------+
- * Name: | Atom(foo) | | Atom(foo) | | Atom(foo) | | Atom(foo) |
- * +-------------+ +-------------+ +-------------+ +-------------+
- * The reason for the node atom in X->pid is that we don't know the actual
- * pid of the monitored process on the other node when setting the monitor
- * (which is done asyncronously).
- **********************************************************************/
-#ifndef _ERL_MONITORS_H
-#define _ERL_MONITORS_H
-
-/* Type tags for monitors */
-#define MON_ORIGIN 1
-#define MON_TARGET 2
-#define MON_NIF_TARGET 3
-#define MON_TIME_OFFSET 4
-
-/* Type tags for links */
-#define LINK_PID 1 /* ...Or port */
-#define LINK_NODE 3 /* "Node monitor" */
-
-/* Size of a monitor without heap, in words (fixalloc) */
-#define ERTS_MONITOR_SIZE ((sizeof(ErtsMonitor) - sizeof(Uint))/sizeof(Uint))
-#define ERTS_MONITOR_SH_SIZE (ERTS_MONITOR_SIZE + ERTS_REF_THING_SIZE)
-#define ERTS_LINK_SIZE ((sizeof(ErtsLink) - sizeof(Uint))/sizeof(Uint))
-#define ERTS_LINK_SH_SIZE ERTS_LINK_SIZE /* Size of fix-alloced links */
-
-/* ErtsMonitor and ErtsLink *need* to begin in a similar way as
- ErtsMonitorOrLink */
-typedef struct erts_monitor_or_link {
- struct erts_monitor_or_link *left, *right;
- Sint16 balance;
-} ErtsMonitorOrLink;
-
-typedef struct erts_monitor {
- struct erts_monitor *left, *right;
- Sint16 balance;
- Uint16 type; /* MON_ORIGIN | MON_TARGET | MON_NIF_TARGET | MON_TIME_OFFSET */
- Eterm ref;
- union {
- Eterm pid; /* In case of distributed named monitor, this is the
- * nodename atom in MON_ORIGIN process, otherwise a pid or,
- * in case of a MON_TARGET, a port
- */
- struct ErtsResource_* resource; /* MON_NIF_TARGET */
- }u;
- Eterm name; /* When monitoring a named process: atom() else [] */
- Uint heap[1]; /* Larger in reality */
-} ErtsMonitor;
-
-typedef struct erts_link {
- struct erts_link *left, *right;
- Sint16 balance;
- Uint16 type; /* LINK_PID | LINK_NODE */
- Eterm pid; /* When node monitor,
- the node atom is here instead */
- union {
- struct erts_link *root; /* Used only in dist entries */
- Uint refc;
- } shared;
- Uint heap[1]; /* Larger in reality */
-} ErtsLink;
-
-typedef struct erts_suspend_monitor {
- struct erts_suspend_monitor *left, *right;
- Sint16 balance;
-
- int pending;
- int active;
- Eterm pid;
-} ErtsSuspendMonitor;
-
-#define ERTS_LINK_ROOT(Linkp) ((Linkp)->shared.root)
-#define ERTS_LINK_REFC(Linkp) ((Linkp)->shared.refc)
-
-Uint erts_tot_link_lh_size(void);
-
-
-/* Prototypes */
-void erts_destroy_monitor(ErtsMonitor *mon);
-void erts_add_monitor(ErtsMonitor **root, Uint type, Eterm ref, UWord entity,
- Eterm name);
-ErtsMonitor *erts_remove_monitor(ErtsMonitor **root, Eterm ref);
-ErtsMonitor *erts_lookup_monitor(ErtsMonitor *root, Eterm ref);
-void erts_sweep_monitors(ErtsMonitor *root,
- void (*doit)(ErtsMonitor *, void *),
- void *context);
-
-void erts_destroy_link(ErtsLink *lnk);
-/* Returns 0 if OK, < 0 if already present */
-int erts_add_link(ErtsLink **root, Uint type, Eterm pid);
-ErtsLink *erts_add_or_lookup_link(ErtsLink **root, Uint type, Eterm pid);
-ErtsLink *erts_remove_link(ErtsLink **root, Eterm pid);
-ErtsLink *erts_lookup_link(ErtsLink *root, Eterm pid);
-void erts_sweep_links(ErtsLink *root,
- void (*doit)(ErtsLink *, void *),
- void *context);
-
-void erts_destroy_suspend_monitor(ErtsSuspendMonitor *sproc);
-void erts_sweep_suspend_monitors(ErtsSuspendMonitor *root,
- void (*doit)(ErtsSuspendMonitor *, void *),
- void *context);
-ErtsSuspendMonitor *erts_add_or_lookup_suspend_monitor(ErtsSuspendMonitor **root,
- Eterm pid);
-ErtsSuspendMonitor *erts_lookup_suspend_monitor(ErtsSuspendMonitor *root,
- Eterm pid);
-void erts_delete_suspend_monitor(ErtsSuspendMonitor **root, Eterm pid);
-void erts_init_monitors(void);
-void erts_one_link_size(ErtsLink *lnk, void *vpu);
-void erts_one_mon_size(ErtsMonitor *mon, void *vpu);
-
-#define erts_doforall_monitors erts_sweep_monitors
-#define erts_doforall_links erts_sweep_links
-#define erts_doforall_suspend_monitors erts_sweep_suspend_monitors
-
-#endif /* _ERL_MONITORS_H */
diff --git a/erts/emulator/beam/erl_msacc.c b/erts/emulator/beam/erl_msacc.c
index d659842b7e..d13d6080e1 100644
--- a/erts/emulator/beam/erl_msacc.c
+++ b/erts/emulator/beam/erl_msacc.c
@@ -81,7 +81,7 @@ void erts_msacc_init(void) {
sizeof(Eterm)*ERTS_MSACC_STATE_COUNT);
for (i = 0; i < ERTS_MSACC_STATE_COUNT; i++) {
erts_msacc_state_atoms[i] = am_atom_put(erts_msacc_states[i],
- strlen(erts_msacc_states[i]));
+ sys_strlen(erts_msacc_states[i]));
}
}
@@ -191,7 +191,7 @@ Eterm erts_msacc_gather_stats(ErtsMsAcc *msacc, ErtsHeapFactory *factory) {
map->keys = key;
hp[0] = state_map;
hp[1] = msacc->id;
- hp[2] = am_atom_put(msacc->type,strlen(msacc->type));
+ hp[2] = am_atom_put(msacc->type,sys_strlen(msacc->type));
return make_flatmap(map);
}
diff --git a/erts/emulator/beam/erl_msacc.h b/erts/emulator/beam/erl_msacc.h
index 2d4637f800..895b1ae319 100644
--- a/erts/emulator/beam/erl_msacc.h
+++ b/erts/emulator/beam/erl_msacc.h
@@ -159,12 +159,12 @@ struct erl_msacc_t_ {
#ifdef ERTS_ENABLE_MSACC
-extern erts_tsd_key_t erts_msacc_key;
+extern erts_tsd_key_t ERTS_WRITE_UNLIKELY(erts_msacc_key);
#ifdef ERTS_MSACC_ALWAYS_ON
#define erts_msacc_enabled 1
#else
-extern int erts_msacc_enabled;
+extern int ERTS_WRITE_UNLIKELY(erts_msacc_enabled);
#endif
#define ERTS_MSACC_TSD_GET() erts_tsd_get(erts_msacc_key)
@@ -270,18 +270,32 @@ void erts_msacc_init_thread(char *type, int id, int liberty);
#define ERTS_MSACC_PUSH_STATE_M() \
ERTS_MSACC_DECLARE_CACHE(); \
ERTS_MSACC_PUSH_STATE_CACHED_M()
-#define ERTS_MSACC_PUSH_STATE_CACHED_M() \
- __erts_msacc_state = ERTS_MSACC_IS_ENABLED_CACHED() ? \
- erts_msacc_get_state_m__(__erts_msacc_cache) : ERTS_MSACC_STATE_OTHER
+#define ERTS_MSACC_PUSH_STATE_CACHED_M() \
+ do { \
+ if (ERTS_MSACC_IS_ENABLED_CACHED()) { \
+ ASSERT(!__erts_msacc_cache->unmanaged); \
+ __erts_msacc_state = erts_msacc_get_state_m__(__erts_msacc_cache); \
+ } else { \
+ __erts_msacc_state = ERTS_MSACC_STATE_OTHER; \
+ } \
+ } while(0)
#define ERTS_MSACC_SET_STATE_M(state) \
ERTS_MSACC_DECLARE_CACHE(); \
ERTS_MSACC_SET_STATE_CACHED_M(state)
-#define ERTS_MSACC_SET_STATE_CACHED_M(state) \
- if (ERTS_MSACC_IS_ENABLED_CACHED()) \
- erts_msacc_set_state_m__(__erts_msacc_cache, state, 1)
-#define ERTS_MSACC_POP_STATE_M() \
- if (ERTS_MSACC_IS_ENABLED_CACHED()) \
- erts_msacc_set_state_m__(__erts_msacc_cache, __erts_msacc_state, 0)
+#define ERTS_MSACC_SET_STATE_CACHED_M(state) \
+ do { \
+ if (ERTS_MSACC_IS_ENABLED_CACHED()) { \
+ ASSERT(!__erts_msacc_cache->unmanaged); \
+ erts_msacc_set_state_m__(__erts_msacc_cache, state, 1); \
+ } \
+ } while(0)
+#define ERTS_MSACC_POP_STATE_M() \
+ do { \
+ if (ERTS_MSACC_IS_ENABLED_CACHED()) { \
+ ASSERT(!__erts_msacc_cache->unmanaged); \
+ erts_msacc_set_state_m__(__erts_msacc_cache, __erts_msacc_state, 0); \
+ } \
+ } while(0)
#define ERTS_MSACC_PUSH_AND_SET_STATE_M(state) \
ERTS_MSACC_PUSH_STATE_M(); ERTS_MSACC_SET_STATE_CACHED_M(state)
diff --git a/erts/emulator/beam/erl_mtrace.c b/erts/emulator/beam/erl_mtrace.c
index f2a660f085..2807b443a1 100644
--- a/erts/emulator/beam/erl_mtrace.c
+++ b/erts/emulator/beam/erl_mtrace.c
@@ -314,7 +314,7 @@ disable_trace(int error, char *reason, int eno)
erts_fprintf(stderr, "%s: %s\n", mt_dis, reason);
else {
eno_str = erl_errno_id(eno);
- if (strcmp(eno_str, "unknown") == 0)
+ if (sys_strcmp(eno_str, "unknown") == 0)
erts_fprintf(stderr, "%s: %s: %d\n", mt_dis, reason, eno);
else
erts_fprintf(stderr, "%s: %s: %s\n", mt_dis, reason, eno_str);
@@ -401,9 +401,9 @@ write_trace_header(char *nodename, char *pid, char *hostname)
PUT_UI32(tracep, ERTS_MT_MAJOR_VSN);
PUT_UI32(tracep, ERTS_MT_MINOR_VSN);
- n_len = strlen(nodename);
- h_len = strlen(hostname);
- p_len = strlen(pid);
+ n_len = sys_strlen(nodename);
+ h_len = sys_strlen(hostname);
+ p_len = sys_strlen(pid);
hdr_prolog_len = (2*UI32_SZ
+ 3*UI16_SZ
+ 3*UI32_SZ
@@ -436,15 +436,15 @@ write_trace_header(char *nodename, char *pid, char *hostname)
PUT_UI32(tracep, start_time.usec);
PUT_UI8(tracep, (byte) n_len);
- memcpy((void *) tracep, (void *) nodename, n_len);
+ sys_memcpy((void *) tracep, (void *) nodename, n_len);
tracep += n_len;
PUT_UI8(tracep, (byte) h_len);
- memcpy((void *) tracep, (void *) hostname, h_len);
+ sys_memcpy((void *) tracep, (void *) hostname, h_len);
tracep += h_len;
PUT_UI8(tracep, (byte) p_len);
- memcpy((void *) tracep, (void *) pid, p_len);
+ sys_memcpy((void *) tracep, (void *) pid, p_len);
tracep += p_len;
ASSERT(startp + hdr_prolog_len == tracep);
@@ -472,7 +472,7 @@ write_trace_header(char *nodename, char *pid, char *hostname)
str = ERTS_ALC_A2AD(i);
ASSERT(str);
- str_len = strlen(str);
+ str_len = sys_strlen(str);
if (str_len >= (1 << 8)) {
disable_trace(1, "Excessively large allocator string", 0);
return 0;
@@ -493,7 +493,7 @@ write_trace_header(char *nodename, char *pid, char *hostname)
PUT_UI16(tracep, aflags);
PUT_UI16(tracep, (Uint16) i);
PUT_UI8( tracep, (byte) str_len);
- memcpy((void *) tracep, (void *) str, str_len);
+ sys_memcpy((void *) tracep, (void *) str, str_len);
tracep += str_len;
if (erts_allctrs_info[i].alloc_util) {
PUT_UI8(tracep, 2);
@@ -519,7 +519,7 @@ write_trace_header(char *nodename, char *pid, char *hostname)
str = ERTS_ALC_N2TD(i);
ASSERT(str);
- str_len = strlen(str);
+ str_len = sys_strlen(str);
if (str_len >= (1 << 8)) {
disable_trace(1, "Excessively large type string", 0);
return 0;
@@ -543,7 +543,7 @@ write_trace_header(char *nodename, char *pid, char *hostname)
PUT_UI16(tracep, nflags);
PUT_UI16(tracep, (Uint16) i);
PUT_UI8(tracep, (byte) str_len);
- memcpy((void *) tracep, (void *) str, str_len);
+ sys_memcpy((void *) tracep, (void *) str, str_len);
tracep += str_len;
PUT_UI16(tracep, no);
ASSERT(startp + entry_sz == tracep);
diff --git a/erts/emulator/beam/erl_nfunc_sched.h b/erts/emulator/beam/erl_nfunc_sched.h
index 9be0e6f7c7..b8a4e4ebc3 100644
--- a/erts/emulator/beam/erl_nfunc_sched.h
+++ b/erts/emulator/beam/erl_nfunc_sched.h
@@ -211,8 +211,8 @@ erts_proc_shadow2real(Process *c_p)
#define ERTS_NFUNC_SCHED_INTERNALS__
#define ERTS_I_BEAM_OP_TO_NIF_EXPORT(I) \
- (ASSERT(BeamOp(op_apply_bif) == (BeamInstr *) (*(I)) \
- || BeamOp(op_call_nif) == (BeamInstr *) (*(I))), \
+ (ASSERT(BeamIsOpCode(*(I), op_apply_bif) || \
+ BeamIsOpCode(*(I), op_call_nif)), \
((NifExport *) (((char *) (I)) - offsetof(NifExport, exp.beam[0]))))
diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c
index 43441e0228..260656e2d3 100644
--- a/erts/emulator/beam/erl_nif.c
+++ b/erts/emulator/beam/erl_nif.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2009-2017. All Rights Reserved.
+ * Copyright Ericsson AB 2009-2018. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -57,6 +57,7 @@
#include "erl_bif_unique.h"
#include "erl_utils.h"
#include "erl_io_queue.h"
+#include "erl_proc_sig_queue.h"
#undef ERTS_WANT_NFUNC_SCHED_INTERNALS__
#define ERTS_WANT_NFUNC_SCHED_INTERNALS__
#include "erl_nfunc_sched.h"
@@ -237,9 +238,11 @@ static void cache_env(ErlNifEnv* env);
static void full_flush_env(ErlNifEnv *env);
static void flush_env(ErlNifEnv* env);
-/* Temporary object header, auto-deallocated when NIF returns
- * or when independent environment is cleared.
- */
+/* Temporary object header, auto-deallocated when NIF returns or when
+ * independent environment is cleared.
+ *
+ * The payload can be accessed with &tmp_obj_ptr[1] but keep in mind that its
+ * first element must not require greater alignment than `next`. */
struct enif_tmp_obj_t {
struct enif_tmp_obj_t* next;
void (*dtor)(struct enif_tmp_obj_t*);
@@ -256,6 +259,46 @@ static ERTS_INLINE void free_tmp_objs(ErlNifEnv* env)
}
}
+/* Whether the given environment is bound to a process and will be cleaned up
+ * when the NIF returns. It's safe to use temp_alloc for objects in
+ * env->tmp_obj_list when this is true. */
+static ERTS_INLINE int is_proc_bound(ErlNifEnv *env)
+{
+ return env->mod_nif != NULL;
+}
+
+/* Allocates and attaches an object to the given environment, running its
+ * destructor when the environment is cleared. To avoid temporary variables the
+ * address of the allocated object is returned instead of the enif_tmp_obj_t.
+ *
+ * The destructor *must* call `erts_free(tmp_obj->allocator, tmp_obj)` to free
+ * the object. If the destructor needs to refer to the allocated object its
+ * address will be &tmp_obj[1]. */
+static ERTS_INLINE void *alloc_tmp_obj(ErlNifEnv *env, size_t size,
+ void (*dtor)(struct enif_tmp_obj_t*)) {
+ struct enif_tmp_obj_t *tmp_obj;
+ ErtsAlcType_t allocator;
+
+ allocator = is_proc_bound(env) ? ERTS_ALC_T_TMP : ERTS_ALC_T_NIF;
+
+ tmp_obj = erts_alloc(allocator, sizeof(struct enif_tmp_obj_t) + MAX(1, size));
+
+ tmp_obj->next = env->tmp_obj_list;
+ tmp_obj->allocator = allocator;
+ tmp_obj->dtor = dtor;
+
+ env->tmp_obj_list = tmp_obj;
+
+ return (void*)&tmp_obj[1];
+}
+
+/* Generic destructor for objects allocated through alloc_tmp_obj that don't
+ * care about their payload. */
+static void tmp_alloc_dtor(struct enif_tmp_obj_t *tmp_obj)
+{
+ erts_free(tmp_obj->allocator, tmp_obj);
+}
+
void erts_post_nif(ErlNifEnv* env)
{
erts_unblock_fpe(env->fpe_was_unmasked);
@@ -292,7 +335,7 @@ schedule(ErlNifEnv* env, NativeFunPtr direct_fp, NativeFunPtr indirect_fp,
ep = erts_nif_export_schedule(c_p, dirty_shadow_proc,
c_p->current,
c_p->cp,
- (BeamInstr) em_call_nif,
+ BeamOpCodeAddr(op_call_nif),
direct_fp, indirect_fp,
mod, func_name,
argc, (const Eterm *) argv);
@@ -345,12 +388,18 @@ erts_call_dirty_nif(ErtsSchedulerData *esdp, Process *c_p, BeamInstr *I, Eterm *
erts_atomic32_read_band_mb(&c_p->state, ~(ERTS_PSFLG_DIRTY_CPU_PROC
| ERTS_PSFLG_DIRTY_IO_PROC));
+ ASSERT(esdp->current_nif == NULL);
+ esdp->current_nif = &env;
+
erts_proc_unlock(c_p, ERTS_PROC_LOCK_MAIN);
result = (*dirty_nif)(&env, codemfa->arity, argv); /* Call dirty NIF */
erts_proc_lock(c_p, ERTS_PROC_LOCK_MAIN);
+ ASSERT(esdp->current_nif == &env);
+ esdp->current_nif = NULL;
+
ASSERT(env.proc->static_flags & ERTS_STC_FLG_SHADOW_PROC);
ASSERT(env.proc->next == c_p);
@@ -446,6 +495,7 @@ static void cache_env(ErlNifEnv* env)
env->hp_end = env->heap_frag->mem + env->heap_frag->alloc_size;
}
}
+
void* enif_priv_data(ErlNifEnv* env)
{
return env->mod_nif->priv_data;
@@ -486,7 +536,7 @@ setup_nif_env(struct enif_msg_environment_t* msg_env,
msg_env->env.tmp_obj_list = NULL;
msg_env->env.proc = &msg_env->phony_proc;
msg_env->env.exception_thrown = 0;
- memset(&msg_env->phony_proc, 0, sizeof(Process));
+ sys_memset(&msg_env->phony_proc, 0, sizeof(Process));
HEAP_START(&msg_env->phony_proc) = phony_heap;
HEAP_TOP(&msg_env->phony_proc) = phony_heap;
HEAP_LIMIT(&msg_env->phony_proc) = phony_heap;
@@ -544,6 +594,9 @@ void enif_clear_env(ErlNifEnv* env)
ASSERT(p == menv->env.proc);
ASSERT(p->common.id == ERTS_INVALID_PID);
ASSERT(MBUF(p) == menv->env.heap_frag);
+
+ free_tmp_objs(env);
+
if (MBUF(p) != NULL) {
erts_cleanup_offheap(&MSO(p));
clear_offheap(&MSO(p));
@@ -555,12 +608,11 @@ void enif_clear_env(ErlNifEnv* env)
menv->env.hp = menv->env.hp_end = HEAP_TOP(p);
ASSERT(!is_offheap(&MSO(p)));
- free_tmp_objs(env);
}
#ifdef DEBUG
static int enif_send_delay = 0;
-#define ERTS_FORCE_ENIF_SEND_DELAY() (enif_send_delay++ % 2 == 0)
+#define ERTS_FORCE_ENIF_SEND_DELAY() (enif_send_delay++ % 32 == 0)
#else
#ifdef ERTS_PROC_LOCK_OWN_IMPL
#define ERTS_FORCE_ENIF_SEND_DELAY() 0
@@ -613,7 +665,7 @@ int erts_flush_trace_messages(Process *c_p, ErtsProcLocks c_p_locks)
rp_locks = 0;
if (rp->common.id == c_p->common.id)
rp_locks = c_p_locks;
- erts_queue_messages(rp, rp_locks, first, last, len, c_p->common.id);
+ erts_queue_messages(rp, rp_locks, first, last, len);
if (rp->common.id == c_p->common.id)
rp_locks &= ~c_p_locks;
if (rp_locks)
@@ -655,6 +707,7 @@ int enif_send(ErlNifEnv* env, const ErlNifPid* to_pid,
Process* rp;
Process* c_p;
ErtsMessage *mp;
+ Eterm from;
Eterm receiver = to_pid->pid;
int scheduler;
@@ -733,7 +786,7 @@ int enif_send(ErlNifEnv* env, const ErlNifPid* to_pid,
msg = copy_struct_litopt(msg, sz, &hp, ohp, &litarea);
}
- ERL_MESSAGE_TERM(mp) = msg;
+ from = c_p ? c_p->common.id : am_undefined;
if (!env || !env->tracee) {
@@ -752,7 +805,6 @@ int enif_send(ErlNifEnv* env, const ErlNifPid* to_pid,
ErlTraceMessageQueue *msgq;
Process *t_p = env->tracee;
-
erts_proc_lock(t_p, ERTS_PROC_LOCK_TRACE);
msgq = t_p->trace_msg_q;
@@ -772,6 +824,9 @@ int enif_send(ErlNifEnv* env, const ErlNifPid* to_pid,
rp_locks & ERTS_PROC_LOCK_MSGQ ||
erts_proc_trylock(rp, ERTS_PROC_LOCK_MSGQ) == EBUSY) {
+ ERL_MESSAGE_TERM(mp) = msg;
+ ERL_MESSAGE_FROM(mp) = from;
+
if (!msgq) {
msgq = erts_alloc(ERTS_ALC_T_TRACE_MSG_QUEUE,
sizeof(ErlTraceMessageQueue));
@@ -801,8 +856,7 @@ int enif_send(ErlNifEnv* env, const ErlNifPid* to_pid,
}
}
- erts_queue_message(rp, rp_locks, mp, msg,
- c_p ? c_p->common.id : am_undefined);
+ erts_queue_message(rp, rp_locks, mp, msg, from);
done:
if (c_p == rp)
@@ -1017,11 +1071,6 @@ int enif_is_number(ErlNifEnv* env, ERL_NIF_TERM term)
return is_number(term);
}
-static ERTS_INLINE int is_proc_bound(ErlNifEnv* env)
-{
- return env->mod_nif != NULL;
-}
-
static void aligned_binary_dtor(struct enif_tmp_obj_t* obj)
{
erts_free_aligned_binary_bytes_extra((byte*)obj, obj->allocator);
@@ -1056,22 +1105,14 @@ int enif_inspect_binary(ErlNifEnv* env, Eterm bin_term, ErlNifBinary* bin)
u.tmp->dtor = &aligned_binary_dtor;
env->tmp_obj_list = u.tmp;
}
- bin->bin_term = bin_term;
bin->size = binary_size(bin_term);
bin->ref_bin = NULL;
ADD_READONLY_CHECK(env, bin->data, bin->size);
return 1;
}
-static void tmp_alloc_dtor(struct enif_tmp_obj_t* obj)
-{
- erts_free(obj->allocator, obj);
-}
-
int enif_inspect_iolist_as_binary(ErlNifEnv* env, Eterm term, ErlNifBinary* bin)
{
- struct enif_tmp_obj_t* tobj;
- ErtsAlcType_t allocator;
ErlDrvSizeT sz;
if (is_binary(term)) {
return enif_inspect_binary(env,term,bin);
@@ -1079,7 +1120,6 @@ int enif_inspect_iolist_as_binary(ErlNifEnv* env, Eterm term, ErlNifBinary* bin)
if (is_nil(term)) {
bin->data = (unsigned char*) &bin->data; /* dummy non-NULL */
bin->size = 0;
- bin->bin_term = THE_NON_VALUE;
bin->ref_bin = NULL;
return 1;
}
@@ -1087,16 +1127,8 @@ int enif_inspect_iolist_as_binary(ErlNifEnv* env, Eterm term, ErlNifBinary* bin)
return 0;
}
- allocator = is_proc_bound(env) ? ERTS_ALC_T_TMP : ERTS_ALC_T_NIF;
- tobj = erts_alloc(allocator, sz + sizeof(struct enif_tmp_obj_t));
- tobj->allocator = allocator;
- tobj->next = env->tmp_obj_list;
- tobj->dtor = &tmp_alloc_dtor;
- env->tmp_obj_list = tobj;
-
- bin->data = (unsigned char*) &tobj[1];
+ bin->data = alloc_tmp_obj(env, sz, &tmp_alloc_dtor);
bin->size = sz;
- bin->bin_term = THE_NON_VALUE;
bin->ref_bin = NULL;
erts_iolist_to_buf(term, (char*) bin->data, sz);
ADD_READONLY_CHECK(env, bin->data, bin->size);
@@ -1114,7 +1146,6 @@ int enif_alloc_binary(size_t size, ErlNifBinary* bin)
bin->size = size;
bin->data = (unsigned char*) refbin->orig_bytes;
- bin->bin_term = THE_NON_VALUE;
bin->ref_bin = refbin;
return 1;
}
@@ -1148,12 +1179,10 @@ void enif_release_binary(ErlNifBinary* bin)
{
if (bin->ref_bin != NULL) {
Binary* refbin = bin->ref_bin;
- ASSERT(bin->bin_term == THE_NON_VALUE);
erts_bin_release(refbin);
}
#ifdef DEBUG
bin->data = NULL;
- bin->bin_term = THE_NON_VALUE;
bin->ref_bin = NULL;
#endif
}
@@ -1201,11 +1230,12 @@ size_t enif_binary_to_term(ErlNifEnv *dst_env,
Sint size;
ErtsHeapFactory factory;
byte *bp = (byte*) data;
+ Uint32 flags = 0;
- ERTS_CT_ASSERT(ERL_NIF_BIN2TERM_SAFE == ERTS_DIST_EXT_BTT_SAFE);
-
- if (opts & ~ERL_NIF_BIN2TERM_SAFE) {
- return 0;
+ switch ((Uint32)opts) {
+ case 0: break;
+ case ERL_NIF_BIN2TERM_SAFE: flags = ERTS_DIST_EXT_BTT_SAFE; break;
+ default: return 0;
}
if ((size = erts_decode_ext_size(bp, data_sz)) < 0)
return 0;
@@ -1217,7 +1247,7 @@ size_t enif_binary_to_term(ErlNifEnv *dst_env,
erts_factory_dummy_init(&factory);
}
- *term = erts_decode_ext(&factory, &bp, (Uint32)opts);
+ *term = erts_decode_ext(&factory, &bp, flags);
if (is_non_value(*term)) {
return 0;
@@ -1309,39 +1339,51 @@ int enif_get_string(ErlNifEnv *env, ERL_NIF_TERM list, char* buf, unsigned len,
Eterm enif_make_binary(ErlNifEnv* env, ErlNifBinary* bin)
{
- if (bin->bin_term != THE_NON_VALUE) {
- return bin->bin_term;
- }
- else if (bin->ref_bin != NULL) {
- Binary* bptr = bin->ref_bin;
- ProcBin* pb;
- Eterm bin_term;
-
- /* !! Copy-paste from new_binary() !! */
- pb = (ProcBin *) alloc_heap(env, PROC_BIN_SIZE);
- pb->thing_word = HEADER_PROC_BIN;
- pb->size = bptr->orig_size;
- pb->next = MSO(env->proc).first;
- MSO(env->proc).first = (struct erl_off_heap_header*) pb;
- pb->val = bptr;
- pb->bytes = (byte*) bptr->orig_bytes;
- pb->flags = 0;
-
- OH_OVERHEAD(&(MSO(env->proc)), pb->size / sizeof(Eterm));
- bin_term = make_binary(pb);
- if (erts_refc_read(&bptr->intern.refc, 1) == 1) {
- /* Total ownership transfer */
- bin->ref_bin = NULL;
- bin->bin_term = bin_term;
- }
- return bin_term;
- }
- else {
- flush_env(env);
- bin->bin_term = new_binary(env->proc, bin->data, bin->size);
- cache_env(env);
- return bin->bin_term;
+ Eterm bin_term;
+
+ if (bin->ref_bin != NULL) {
+ Binary* binary = bin->ref_bin;
+
+ /* If the binary is smaller than the heap binary limit we'll return a
+ * heap binary to reduce the number of small refc binaries in the
+ * system. We can't simply release the refc binary right away however;
+ * the documentation states that the binary should be considered
+ * read-only from this point on, which implies that it should still be
+ * readable.
+ *
+ * We could keep it alive until we return by adding it to the temporary
+ * object list, but that requires an off-heap allocation which is
+ * potentially quite slow, so we create a dummy ProcBin instead and
+ * rely on the next minor GC to get rid of it. */
+ if (bin->size <= ERL_ONHEAP_BIN_LIMIT) {
+ ErlHeapBin* hb;
+
+ hb = (ErlHeapBin*)alloc_heap(env, heap_bin_size(bin->size));
+ hb->thing_word = header_heap_bin(bin->size);
+ hb->size = bin->size;
+
+ sys_memcpy(hb->data, bin->data, bin->size);
+
+ erts_build_proc_bin(&MSO(env->proc),
+ alloc_heap(env, PROC_BIN_SIZE),
+ binary);
+
+ bin_term = make_binary(hb);
+ } else {
+ bin_term = erts_build_proc_bin(&MSO(env->proc),
+ alloc_heap(env, PROC_BIN_SIZE),
+ binary);
+ }
+
+ /* Our (possibly shared) ownership has been transferred to the term. */
+ bin->ref_bin = NULL;
+ } else {
+ flush_env(env);
+ bin_term = new_binary(env->proc, bin->data, bin->size);
+ cache_env(env);
}
+
+ return bin_term;
}
Eterm enif_make_sub_binary(ErlNifEnv* env, ERL_NIF_TERM bin_term,
@@ -1930,6 +1972,12 @@ ErlNifTid enif_thread_self(void) { return erl_drv_thread_self(); }
int enif_equal_tids(ErlNifTid tid1, ErlNifTid tid2) { return erl_drv_equal_tids(tid1,tid2); }
void enif_thread_exit(void *resp) { erl_drv_thread_exit(resp); }
int enif_thread_join(ErlNifTid tid, void **respp) { return erl_drv_thread_join(tid,respp); }
+
+char* enif_mutex_name(ErlNifMutex *mtx) {return erl_drv_mutex_name(mtx); }
+char* enif_cond_name(ErlNifCond *cnd) { return erl_drv_cond_name(cnd); }
+char* enif_rwlock_name(ErlNifRWLock* rwlck) { return erl_drv_rwlock_name(rwlck); }
+char* enif_thread_name(ErlNifTid tid) { return erl_drv_thread_name(tid); }
+
int enif_getenv(const char *key, char *value, size_t *value_size) { return erl_drv_getenv(key, value, value_size); }
ErlNifTime enif_monotonic_time(ErlNifTimeUnit time_unit)
@@ -1952,16 +2000,21 @@ enif_convert_time_unit(ErlNifTime val,
(int) to);
}
-int enif_fprintf(void* filep, const char* format, ...)
+int enif_fprintf(FILE* filep, const char* format, ...)
{
int ret;
va_list arglist;
va_start(arglist, format);
- ret = erts_vfprintf((FILE*)filep, format, arglist);
+ ret = erts_vfprintf(filep, format, arglist);
va_end(arglist);
return ret;
}
+int enif_vfprintf(FILE* filep, const char *format, va_list ap)
+{
+ return erts_vfprintf(filep, format, ap);
+}
+
int enif_snprintf(char *buffer, size_t size, const char* format, ...)
{
int ret;
@@ -1972,6 +2025,12 @@ int enif_snprintf(char *buffer, size_t size, const char* format, ...)
return ret;
}
+int enif_vsnprintf(char* buffer, size_t size, const char *format, va_list ap)
+{
+ return erts_vsnprintf(buffer, size, format, ap);
+}
+
+
/***********************************************************
** Memory managed (GC'ed) "resource" objects **
***********************************************************/
@@ -2173,71 +2232,61 @@ static void rollback_opened_resource_types(void)
}
}
-struct destroy_monitor_ctx
-{
- ErtsResource* resource;
- int exiting_procs;
- int scheduler;
-};
+#ifdef ARCH_64
+# define ERTS_RESOURCE_DYING_FLAG (((Uint) 1) << 63)
+#else
+# define ERTS_RESOURCE_DYING_FLAG (((Uint) 1) << 31)
+#endif
+#define ERTS_RESOURCE_REFC_MASK (~ERTS_RESOURCE_DYING_FLAG)
-static void destroy_one_monitor(ErtsMonitor* mon, void* context)
+static ERTS_INLINE void
+rmon_set_dying(ErtsResourceMonitors *rms)
{
- struct destroy_monitor_ctx* ctx = (struct destroy_monitor_ctx*) context;
- Process* rp;
- ErtsMonitor *rmon = NULL;
- int is_exiting;
+ rms->refc |= ERTS_RESOURCE_DYING_FLAG;
+}
- ASSERT(mon->type == MON_ORIGIN);
- ASSERT(is_internal_pid(mon->u.pid));
- ASSERT(is_internal_ref(mon->ref));
+static ERTS_INLINE int
+rmon_is_dying(ErtsResourceMonitors *rms)
+{
+ return !!(rms->refc & ERTS_RESOURCE_DYING_FLAG);
+}
- if (ctx->scheduler > 0) { /* Normal scheduler */
- rp = erts_proc_lookup(mon->u.pid);
- }
- else {
- rp = erts_proc_lookup_inc_refc(mon->u.pid);
- }
+static ERTS_INLINE void
+rmon_refc_inc(ErtsResourceMonitors *rms)
+{
+ rms->refc++;
+}
- if (!rp) {
- is_exiting = 1;
- }
- if (rp) {
- erts_proc_lock(rp, ERTS_PROC_LOCK_LINK);
- if (ERTS_PROC_IS_EXITING(rp)) {
- is_exiting = 1;
- } else {
- rmon = erts_remove_monitor(&ERTS_P_MONITORS(rp), mon->ref);
- ASSERT(rmon);
- is_exiting = 0;
- }
- erts_proc_unlock(rp, ERTS_PROC_LOCK_LINK);
- if (ctx->scheduler <= 0)
- erts_proc_dec_refc(rp);
- }
- if (is_exiting) {
- ctx->resource->monitors->pending_failed_fire++;
- }
+static ERTS_INLINE Uint
+rmon_refc_dec_read(ErtsResourceMonitors *rms)
+{
+ Uint res;
+ ASSERT((rms->refc & ERTS_RESOURCE_REFC_MASK) != 0);
+ res = --rms->refc;
+ return res & ERTS_RESOURCE_REFC_MASK;
+}
- /* ToDo: Delay destruction after monitor_locks */
- if (rmon) {
- ASSERT(rmon->type == MON_NIF_TARGET);
- ASSERT(rmon->u.resource == ctx->resource);
- erts_destroy_monitor(rmon);
- }
- erts_destroy_monitor(mon);
+static ERTS_INLINE void
+rmon_refc_dec(ErtsResourceMonitors *rms)
+{
+ ASSERT((rms->refc & ERTS_RESOURCE_REFC_MASK) != 0);
+ --rms->refc;
}
-static void destroy_all_monitors(ErtsMonitor* monitors, ErtsResource* resource)
+static ERTS_INLINE Uint
+rmon_refc_read(ErtsResourceMonitors *rms)
{
- struct destroy_monitor_ctx ctx;
+ return rms->refc & ERTS_RESOURCE_REFC_MASK;
+}
- execution_state(NULL, NULL, &ctx.scheduler);
+static void dtor_demonitor(ErtsMonitor* mon, void* context)
+{
+ ASSERT(erts_monitor_is_origin(mon));
+ ASSERT(is_internal_pid(mon->other.item));
- ctx.resource = resource;
- erts_sweep_monitors(monitors, &destroy_one_monitor, &ctx);
+ erts_proc_sig_send_demonitor(mon);
}
-
# define NIF_RESOURCE_DTOR &nif_resource_dtor
static int nif_resource_dtor(Binary* bin)
@@ -2248,34 +2297,36 @@ static int nif_resource_dtor(Binary* bin)
if (resource->monitors) {
ErtsResourceMonitors* rm = resource->monitors;
+ int kill;
+ ErtsMonitor *root;
+ Uint refc;
ASSERT(type->down);
erts_mtx_lock(&rm->lock);
ASSERT(erts_refc_read(&bin->intern.refc, 0) == 0);
- if (rm->root) {
- ASSERT(!rm->is_dying);
- destroy_all_monitors(rm->root, resource);
+ kill = !rmon_is_dying(rm);
+ if (kill) {
+ rmon_set_dying(rm);
+ root = rm->root;
rm->root = NULL;
}
- if (rm->pending_failed_fire) {
- /*
- * Resource death struggle prolonged to serve exiting process(es).
- * Destructor will be called again when last exiting process
- * tries to fire its MON_NIF_TARGET monitor (and fails).
- *
- * This resource is doomed. It has no "real" references and
- * should get not get called upon to do anything except the
- * final destructor call.
- *
- * We keep refc at 0 and use a separate counter for exiting
- * processes to avoid resource getting revived by "dec_term".
- */
- ASSERT(!rm->is_dying);
- rm->is_dying = 1;
- erts_mtx_unlock(&rm->lock);
- return 0;
- }
+ refc = rmon_refc_read(rm);
erts_mtx_unlock(&rm->lock);
+
+ if (kill)
+ erts_monitor_tree_foreach_delete(&root,
+ dtor_demonitor,
+ NULL);
+
+ /*
+ * If resource->monitors->refc != 0 there are
+ * outstanding references to the resource from
+ * monitors that has not been removed yet.
+ * nif_resource_dtor() will be called again this
+ * reference count reach zero.
+ */
+ if (refc != 0)
+ return 0; /* we'll be back... */
erts_mtx_destroy(&rm->lock);
}
@@ -2305,54 +2356,82 @@ void erts_resource_stop(ErtsResource* resource, ErlNifEvent e,
post_nif_noproc(&msg_env);
}
-void erts_fire_nif_monitor(ErtsResource* resource, Eterm pid, Eterm ref)
+void erts_nif_demonitored(ErtsResource* resource)
{
- ErtsMonitor* rmon;
+ ErtsResourceMonitors* rmp = resource->monitors;
ErtsBinary* bin = ERTS_MAGIC_BIN_FROM_UNALIGNED_DATA(resource);
+ int free_me;
+
+ ASSERT(rmp);
+ ASSERT(resource->type->down);
+
+ erts_mtx_lock(&rmp->lock);
+ free_me = ((rmon_refc_dec_read(rmp) == 0) & !!rmon_is_dying(rmp));
+ erts_mtx_unlock(&rmp->lock);
+
+ if (free_me)
+ erts_bin_free(&bin->binary);
+}
+
+void erts_fire_nif_monitor(ErtsMonitor *tmon)
+{
+ ErtsResource* resource;
+ ErtsMonitorData *mdp;
+ ErtsMonitor *omon;
+ ErtsBinary* bin;
struct enif_msg_environment_t msg_env;
ErlNifPid nif_pid;
ErlNifMonitor nif_monitor;
- ErtsResourceMonitors* rmp = resource->monitors;
+ ErtsResourceMonitors* rmp;
+ Uint mrefc, brefc;
+ int active, is_dying;
+
+ ASSERT(tmon->type == ERTS_MON_TYPE_RESOURCE);
+ ASSERT(erts_monitor_is_target(tmon));
+
+ resource = tmon->other.ptr;
+ bin = ERTS_MAGIC_BIN_FROM_UNALIGNED_DATA(resource);
+ rmp = resource->monitors;
+
+ mdp = erts_monitor_to_data(tmon);
+ omon = &mdp->origin;
ASSERT(rmp);
ASSERT(resource->type->down);
erts_mtx_lock(&rmp->lock);
- rmon = erts_remove_monitor(&rmp->root, ref);
- if (!rmon) {
- int free_me = (--rmp->pending_failed_fire == 0) && rmp->is_dying;
- ASSERT(rmp->pending_failed_fire >= 0);
- erts_mtx_unlock(&rmp->lock);
-
- if (free_me) {
- ASSERT(erts_refc_read(&bin->binary.intern.refc, 0) == 0);
- erts_bin_free(&bin->binary);
- }
- return;
+
+ mrefc = rmon_refc_dec_read(rmp);
+ is_dying = rmon_is_dying(rmp);
+ active = !is_dying && erts_monitor_is_in_table(omon);
+
+ if (active) {
+ erts_monitor_tree_delete(&rmp->root, omon);
+ brefc = (Uint) erts_refc_inc_unless(&bin->binary.intern.refc, 0, 0);
}
- ASSERT(!rmp->is_dying);
- if (erts_refc_inc_unless(&bin->binary.intern.refc, 0, 0) == 0) {
- /*
- * Racing resource destruction.
- * To avoid a more complex refc-dance with destructing thread
- * we avoid calling 'down' and just silently remove the monitor.
- * This can happen even for non smp as destructor calls may be scheduled.
- */
- erts_mtx_unlock(&rmp->lock);
+
+ erts_mtx_unlock(&rmp->lock);
+
+ if (!active) {
+ ASSERT(!is_dying || erts_refc_read(&bin->binary.intern.refc, 0) == 0);
+ if (is_dying && mrefc == 0)
+ erts_bin_free(&bin->binary);
+ erts_monitor_release(tmon);
}
else {
- erts_mtx_unlock(&rmp->lock);
-
- ASSERT(rmon->u.pid == pid);
- erts_ref_to_driver_monitor(ref, &nif_monitor);
- nif_pid.pid = pid;
- pre_nif_noproc(&msg_env, resource->type->owner, NULL);
- resource->type->down(&msg_env.env, resource->data, &nif_pid, &nif_monitor);
- post_nif_noproc(&msg_env);
+ if (brefc > 0) {
+ ASSERT(is_internal_pid(omon->other.item));
+ erts_ref_to_driver_monitor(mdp->ref, &nif_monitor);
+ nif_pid.pid = omon->other.item;
+ pre_nif_noproc(&msg_env, resource->type->owner, NULL);
+ resource->type->down(&msg_env.env, resource->data, &nif_pid, &nif_monitor);
+ post_nif_noproc(&msg_env);
+
+ erts_bin_release(&bin->binary);
+ }
- erts_bin_release(&bin->binary);
+ erts_monitor_release_both(mdp);
}
- erts_destroy_monitor(rmon);
}
void* enif_alloc_resource(ErlNifResourceType* type, size_t data_sz)
@@ -2389,8 +2468,7 @@ void* enif_alloc_resource(ErlNifResourceType* type, size_t data_sz)
erts_mtx_init(&resource->monitors->lock, "resource_monitors", NIL,
ERTS_LOCK_FLAGS_CATEGORY_GENERIC);
resource->monitors->root = NULL;
- resource->monitors->pending_failed_fire = 0;
- resource->monitors->is_dying = 0;
+ resource->monitors->refc = 0;
resource->monitors->user_data_sz = data_sz;
}
else {
@@ -2405,7 +2483,7 @@ void enif_release_resource(void* obj)
ErtsBinary* bin = ERTS_MAGIC_BIN_FROM_UNALIGNED_DATA(resource);
ASSERT(ERTS_MAGIC_BIN_DESTRUCTOR(bin) == NIF_RESOURCE_DTOR);
- ASSERT(!(resource->monitors && resource->monitors->is_dying));
+ ASSERT(erts_refc_read(&bin->binary.intern.refc, 0) != 0);
#ifdef DEBUG
erts_refc_dec(&resource->nif_refc, 0);
#endif
@@ -2418,7 +2496,7 @@ void enif_keep_resource(void* obj)
ErtsBinary* bin = ERTS_MAGIC_BIN_FROM_UNALIGNED_DATA(resource);
ASSERT(ERTS_MAGIC_BIN_DESTRUCTOR(bin) == NIF_RESOURCE_DTOR);
- ASSERT(!(resource->monitors && resource->monitors->is_dying));
+ ASSERT(erts_refc_read(&bin->binary.intern.refc, 0) != 0);
#ifdef DEBUG
erts_refc_inc(&resource->nif_refc, 1);
#endif
@@ -2428,7 +2506,7 @@ void enif_keep_resource(void* obj)
Eterm erts_bld_resource_ref(Eterm** hpp, ErlOffHeap* oh, ErtsResource* resource)
{
ErtsBinary* bin = ERTS_MAGIC_BIN_FROM_UNALIGNED_DATA(resource);
- ASSERT(!(resource->monitors && resource->monitors->is_dying));
+ ASSERT(erts_refc_read(&bin->binary.intern.refc, 0) != 0);
return erts_mk_magic_ref(hpp, oh, &bin->binary);
}
@@ -2437,7 +2515,7 @@ ERL_NIF_TERM enif_make_resource(ErlNifEnv* env, void* obj)
ErtsResource* resource = DATA_TO_RESOURCE(obj);
ErtsBinary* bin = ERTS_MAGIC_BIN_FROM_UNALIGNED_DATA(resource);
Eterm* hp = alloc_heap(env, ERTS_MAGIC_REF_THING_SIZE);
- ASSERT(!(resource->monitors && resource->monitors->is_dying));
+ ASSERT(erts_refc_read(&bin->binary.intern.refc, 0) != 0);
return erts_mk_magic_ref(&hp, &MSO(env->proc), &bin->binary);
}
@@ -2862,6 +2940,44 @@ ERL_NIF_TERM enif_make_new_map(ErlNifEnv* env)
return make_flatmap(mp);
}
+int enif_make_map_from_arrays(ErlNifEnv *env,
+ ERL_NIF_TERM keys[],
+ ERL_NIF_TERM values[],
+ size_t cnt,
+ ERL_NIF_TERM *map_out)
+{
+ ErtsHeapFactory factory;
+ int succeeded;
+
+#ifdef ERTS_NIF_ASSERT_IN_ENV
+ size_t index = 0;
+
+ while (index < cnt) {
+ ASSERT_IN_ENV(env, keys[index], index, "key");
+ ASSERT_IN_ENV(env, values[index], index, "value");
+ index++;
+ }
+#endif
+
+ flush_env(env);
+
+ erts_factory_proc_prealloc_init(&factory, env->proc,
+ cnt * 2 + MAP_HEADER_FLATMAP_SZ + 1);
+
+ (*map_out) = erts_map_from_ks_and_vs(&factory, keys, values, cnt);
+ succeeded = (*map_out) != THE_NON_VALUE;
+
+ if (!succeeded) {
+ erts_factory_undo(&factory);
+ }
+
+ erts_factory_close(&factory);
+
+ cache_env(env);
+
+ return succeeded;
+}
+
int enif_make_map_put(ErlNifEnv* env,
Eterm map_in,
Eterm key,
@@ -3117,123 +3233,88 @@ int enif_map_iterator_get_pair(ErlNifEnv *env,
int enif_monitor_process(ErlNifEnv* env, void* obj, const ErlNifPid* target_pid,
ErlNifMonitor* monitor)
{
- int scheduler;
ErtsResource* rsrc = DATA_TO_RESOURCE(obj);
- Process *rp;
Eterm tmp[ERTS_REF_THING_SIZE];
Eterm ref;
- int retval;
+ ErtsResourceMonitors *rm;
+ ErtsMonitorData *mdp;
ASSERT(ERTS_MAGIC_BIN_FROM_UNALIGNED_DATA(rsrc)->magic_binary.destructor
== NIF_RESOURCE_DTOR);
- ASSERT(!(rsrc->monitors && rsrc->monitors->is_dying));
+ ASSERT(erts_refc_read(&ERTS_MAGIC_BIN_FROM_UNALIGNED_DATA(rsrc)->binary.intern.refc, 0) != 0);
ASSERT(!rsrc->monitors == !rsrc->type->down);
-
- if (!rsrc->monitors) {
+ rm = rsrc->monitors;
+ if (!rm) {
ASSERT(!rsrc->type->down);
return -1;
}
ASSERT(rsrc->type->down);
- execution_state(env, NULL, &scheduler);
+ ref = erts_make_ref_in_buffer(tmp);
- if (scheduler > 0) /* Normal scheduler */
- rp = erts_proc_lookup_raw(target_pid->pid);
- else
- rp = erts_proc_lookup_raw_inc_refc(target_pid->pid);
+ mdp = erts_monitor_create(ERTS_MON_TYPE_RESOURCE, ref,
+ (Eterm) rsrc, target_pid->pid, NIL);
+ erts_mtx_lock(&rm->lock);
+ ASSERT(!rmon_is_dying(rm));
+ erts_monitor_tree_insert(&rm->root, &mdp->origin);
+ rmon_refc_inc(rm);
+ erts_mtx_unlock(&rm->lock);
- if (!rp)
- return 1;
+ if (!erts_proc_sig_send_monitor(&mdp->target, target_pid->pid)) {
+ /* Failed to send monitor signal; cleanup... */
+#ifdef DEBUG
+ ErtsBinary* bin = ERTS_MAGIC_BIN_FROM_UNALIGNED_DATA(rsrc);
+#endif
- ref = erts_make_ref_in_buffer(tmp);
+ erts_mtx_lock(&rm->lock);
+ ASSERT(!rmon_is_dying(rm));
+ erts_monitor_tree_delete(&rm->root, &mdp->origin);
+ rmon_refc_dec(rm);
+ ASSERT(erts_refc_read(&bin->binary.intern.refc, 1) != 0);
+ erts_mtx_unlock(&rm->lock);
+ erts_monitor_release_both(mdp);
- erts_mtx_lock(&rsrc->monitors->lock);
- erts_proc_lock(rp, ERTS_PROC_LOCK_LINK);
- if (ERTS_PSFLG_FREE & erts_atomic32_read_nob(&rp->state)) {
- retval = 1;
- }
- else {
- erts_add_monitor(&rsrc->monitors->root, MON_ORIGIN, ref, rp->common.id, NIL);
- erts_add_monitor(&ERTS_P_MONITORS(rp), MON_NIF_TARGET, ref, (UWord)rsrc, NIL);
- retval = 0;
+ return 1;
}
- erts_proc_unlock(rp, ERTS_PROC_LOCK_LINK);
- erts_mtx_unlock(&rsrc->monitors->lock);
- if (scheduler <= 0)
- erts_proc_dec_refc(rp);
if (monitor)
erts_ref_to_driver_monitor(ref,monitor);
- return retval;
+ return 0;
}
int enif_demonitor_process(ErlNifEnv* env, void* obj, const ErlNifMonitor* monitor)
{
- int scheduler;
ErtsResource* rsrc = DATA_TO_RESOURCE(obj);
#ifdef DEBUG
ErtsBinary* bin = ERTS_MAGIC_BIN_FROM_UNALIGNED_DATA(rsrc);
#endif
- Process *rp;
+ ErtsResourceMonitors *rm;
ErtsMonitor *mon;
- ErtsMonitor *rmon = NULL;
Eterm ref_heap[ERTS_REF_THING_SIZE];
Eterm ref;
- int is_exiting;
ASSERT(bin->magic_binary.destructor == NIF_RESOURCE_DTOR);
- ASSERT(!(rsrc->monitors && rsrc->monitors->is_dying));
-
- execution_state(env, NULL, &scheduler);
+ ASSERT(erts_refc_read(&bin->binary.intern.refc, 0) != 0);
ref = erts_driver_monitor_to_ref(ref_heap, monitor);
- erts_mtx_lock(&rsrc->monitors->lock);
- mon = erts_remove_monitor(&rsrc->monitors->root, ref);
+ rm = rsrc->monitors;
+ erts_mtx_lock(&rm->lock);
+ ASSERT(!rmon_is_dying(rm));
+ mon = erts_monitor_tree_lookup(rm->root, ref);
+ if (mon)
+ erts_monitor_tree_delete(&rm->root, mon);
+ erts_mtx_unlock(&rm->lock);
- if (mon == NULL) {
- erts_mtx_unlock(&rsrc->monitors->lock);
+ if (!mon)
return 1;
- }
- ASSERT(mon->type == MON_ORIGIN);
- ASSERT(is_internal_pid(mon->u.pid));
+ ASSERT(erts_monitor_is_origin(mon));
+ ASSERT(is_internal_pid(mon->other.item));
- if (scheduler > 0) /* Normal scheduler */
- rp = erts_proc_lookup(mon->u.pid);
- else
- rp = erts_proc_lookup_inc_refc(mon->u.pid);
-
- if (!rp) {
- is_exiting = 1;
- }
- else {
- erts_proc_lock(rp, ERTS_PROC_LOCK_LINK);
- if (ERTS_PROC_IS_EXITING(rp)) {
- is_exiting = 1;
- } else {
- rmon = erts_remove_monitor(&ERTS_P_MONITORS(rp), ref);
- ASSERT(rmon);
- is_exiting = 0;
- }
- erts_proc_unlock(rp, ERTS_PROC_LOCK_LINK);
-
- if (scheduler <= 0)
- erts_proc_dec_refc(rp);
- }
- if (is_exiting) {
- rsrc->monitors->pending_failed_fire++;
- }
- erts_mtx_unlock(&rsrc->monitors->lock);
-
- if (rmon) {
- ASSERT(rmon->type == MON_NIF_TARGET);
- ASSERT(rmon->u.resource == rsrc);
- erts_destroy_monitor(rmon);
- }
- erts_destroy_monitor(mon);
+ erts_proc_sig_send_demonitor(mon);
return 0;
}
@@ -3291,8 +3372,8 @@ typedef struct {
Eterm sublist_start;
Eterm sublist_end;
- UWord offheap_size;
- UWord onheap_size;
+ UWord referenced_size;
+ UWord copied_size;
UWord iovec_len;
} iovec_slice_t;
@@ -3302,16 +3383,16 @@ static int examine_iovec_term(Eterm list, UWord max_length, iovec_slice_t *resul
result->sublist_start = list;
result->sublist_length = 0;
- result->offheap_size = 0;
- result->onheap_size = 0;
+ result->referenced_size = 0;
+ result->copied_size = 0;
result->iovec_len = 0;
lookahead = result->sublist_start;
while (is_list(lookahead)) {
- Eterm *binary_header, binary;
+ UWord byte_size;
+ Eterm binary;
Eterm *cell;
- UWord size;
cell = list_val(lookahead);
binary = CAR(cell);
@@ -3320,39 +3401,42 @@ static int examine_iovec_term(Eterm list, UWord max_length, iovec_slice_t *resul
return 0;
}
- size = binary_size(binary);
- binary_header = binary_val(binary);
+ byte_size = binary_size(binary);
+
+ if (byte_size > 0) {
+ int bit_offset, bit_size;
+ Eterm parent_binary;
+ UWord byte_offset;
+
+ int requires_copying;
- /* If we're a sub-binary we'll need to check our underlying binary to
- * determine whether we're on-heap or not. */
- if(thing_subtag(*binary_header) == SUB_BINARY_SUBTAG) {
- ErlSubBin *sb = (ErlSubBin*)binary_header;
+ ERTS_GET_REAL_BIN(binary, parent_binary, byte_offset,
+ bit_offset, bit_size);
- /* Reject bitstrings */
- if((sb->bitoffs + sb->bitsize) > 0) {
+ (void)byte_offset;
+
+ if (bit_size != 0) {
return 0;
}
- ASSERT(size <= binary_size(sb->orig));
- binary_header = binary_val(sb->orig);
- }
-
- if(thing_subtag(*binary_header) == HEAP_BINARY_SUBTAG) {
- ASSERT(size <= ERL_ONHEAP_BIN_LIMIT);
+ /* If we're unaligned or an on-heap binary we'll need to copy
+ * ourselves over to a temporary buffer. */
+ requires_copying = (bit_offset != 0) ||
+ thing_subtag(*binary_val(parent_binary)) == HEAP_BINARY_SUBTAG;
- result->iovec_len += 1;
- result->onheap_size += size;
- } else {
- ASSERT(thing_subtag(*binary_header) == REFC_BINARY_SUBTAG);
+ if (requires_copying) {
+ result->copied_size += byte_size;
+ } else {
+ result->referenced_size += byte_size;
+ }
- result->iovec_len += 1 + size / MAX_SYSIOVEC_IOVLEN;
- result->offheap_size += size;
+ result->iovec_len += 1 + byte_size / MAX_SYSIOVEC_IOVLEN;
}
result->sublist_length += 1;
lookahead = CDR(cell);
- if(result->sublist_length >= max_length) {
+ if (result->sublist_length >= max_length) {
break;
}
}
@@ -3366,7 +3450,9 @@ static int examine_iovec_term(Eterm list, UWord max_length, iovec_slice_t *resul
return 1;
}
-static void inspect_raw_binary_data(Eterm binary, ErlNifBinary *result) {
+static void marshal_iovec_binary(Eterm binary, ErlNifBinary *copy_buffer,
+ UWord *copy_offset, ErlNifBinary *result) {
+
Eterm *parent_header;
Eterm parent_binary;
@@ -3377,14 +3463,19 @@ static void inspect_raw_binary_data(Eterm binary, ErlNifBinary *result) {
ERTS_GET_REAL_BIN(binary, parent_binary, byte_offset, bit_offset, bit_size);
+ ASSERT(bit_size == 0);
+
parent_header = binary_val(parent_binary);
result->size = binary_size(binary);
- result->bin_term = binary;
if (thing_subtag(*parent_header) == REFC_BINARY_SUBTAG) {
ProcBin *pb = (ProcBin*)parent_header;
+ if (pb->flags & (PB_IS_WRITABLE | PB_ACTIVE_WRITER)) {
+ erts_emasculate_writable_binary(pb);
+ }
+
ASSERT(pb->val != NULL);
ASSERT(byte_offset < pb->size);
ASSERT(&pb->bytes[byte_offset] >= (byte*)(pb->val)->orig_bytes);
@@ -3399,24 +3490,48 @@ static void inspect_raw_binary_data(Eterm binary, ErlNifBinary *result) {
result->data = &((unsigned char*)&hb->data)[byte_offset];
result->ref_bin = NULL;
}
+
+ /* If this isn't an *aligned* refc binary, copy its contents to the buffer
+ * and reference that instead. */
+
+ if (result->ref_bin == NULL || bit_offset != 0) {
+ ASSERT(copy_buffer->ref_bin != NULL && copy_buffer->data != NULL);
+ ASSERT(result->size <= (copy_buffer->size - *copy_offset));
+
+ if (bit_offset == 0) {
+ sys_memcpy(&copy_buffer->data[*copy_offset],
+ result->data, result->size);
+ } else {
+ erts_copy_bits(result->data, bit_offset, 1,
+ (byte*)&copy_buffer->data[*copy_offset], 0, 1,
+ result->size * 8);
+ }
+
+ result->data = &copy_buffer->data[*copy_offset];
+ result->ref_bin = copy_buffer->ref_bin;
+
+ *copy_offset += result->size;
+ }
}
static int fill_iovec_with_slice(ErlNifEnv *env,
iovec_slice_t *slice,
ErlNifIOVec *iovec) {
- UWord onheap_offset, iovec_idx;
- ErlNifBinary onheap_data;
+ ErlNifBinary copy_buffer = {0};
+ UWord copy_offset, iovec_idx;
Eterm sublist_iterator;
- /* Set up a common refc binary for all on-heap binaries. */
- if (slice->onheap_size > 0) {
- if (!enif_alloc_binary(slice->onheap_size, &onheap_data)) {
+ /* Set up a common refc binary for all on-heap and unaligned binaries. */
+ if (slice->copied_size > 0) {
+ if (!enif_alloc_binary(slice->copied_size, &copy_buffer)) {
return 0;
}
+
+ ASSERT(copy_buffer.ref_bin != NULL);
}
sublist_iterator = slice->sublist_start;
- onheap_offset = 0;
+ copy_offset = 0;
iovec_idx = 0;
while (sublist_iterator != slice->sublist_end) {
@@ -3424,27 +3539,13 @@ static int fill_iovec_with_slice(ErlNifEnv *env,
Eterm *cell;
cell = list_val(sublist_iterator);
- inspect_raw_binary_data(CAR(cell), &raw_data);
-
- /* If this isn't a refc binary, copy its contents to the onheap buffer
- * and reference that instead. */
- if (raw_data.ref_bin == NULL) {
- ASSERT(onheap_offset < onheap_data.size);
- ASSERT(slice->onheap_size > 0);
-
- sys_memcpy(&onheap_data.data[onheap_offset],
- raw_data.data, raw_data.size);
-
- raw_data.data = &onheap_data.data[onheap_offset];
- raw_data.ref_bin = onheap_data.ref_bin;
- }
-
- ASSERT(raw_data.ref_bin != NULL);
+ marshal_iovec_binary(CAR(cell), &copy_buffer, &copy_offset, &raw_data);
while (raw_data.size > 0) {
UWord chunk_len = MIN(raw_data.size, MAX_SYSIOVEC_IOVLEN);
ASSERT(iovec_idx < iovec->iovcnt);
+ ASSERT(raw_data.ref_bin != NULL);
iovec->iov[iovec_idx].iov_base = raw_data.data;
iovec->iov[iovec_idx].iov_len = chunk_len;
@@ -3469,16 +3570,18 @@ static int fill_iovec_with_slice(ErlNifEnv *env,
erts_refc_inc(&refc_binary->intern.refc, 1);
}
- if (slice->onheap_size > 0) {
+ if (slice->copied_size > 0) {
/* Transfer ownership to the iovec; we've taken references to it in
* the above loop. */
- enif_release_binary(&onheap_data);
+ enif_release_binary(&copy_buffer);
}
} else {
- if (slice->onheap_size > 0) {
- /* Attach the binary to our environment and let the GC take care of
- * it after returning. */
- enif_make_binary(env, &onheap_data);
+ if (slice->copied_size > 0) {
+ /* Attach the binary to our environment and let the next minor GC
+ * get rid of it. This is slightly faster than using the tmp object
+ * list since it avoids off-heap allocations. */
+ erts_build_proc_bin(&MSO(env->proc),
+ alloc_heap(env, PROC_BIN_SIZE), copy_buffer.ref_bin);
}
}
@@ -3504,19 +3607,14 @@ static int create_iovec_from_slice(ErlNifEnv *env,
alloc_size = binv_offset;
alloc_size += slice->iovec_len * sizeof(Binary*);
- /* If we have an environment we'll attach the allocated data to it. The
- * GC will take care of releasing it later on. */
+ /* When the user passes an environment, we attach the iovec to it so
+ * the user won't have to bother managing it (similar to
+ * enif_inspect_binary). It'll disappear once the environment is
+ * cleaned up. */
if (env != NULL) {
- ErlNifBinary gc_bin;
-
- if (!enif_alloc_binary(alloc_size, &gc_bin)) {
- return 0;
- }
-
- alloc_base = (char*)gc_bin.data;
- enif_make_binary(env, &gc_bin);
+ alloc_base = alloc_tmp_obj(env, alloc_size, &tmp_alloc_dtor);
} else {
- alloc_base = enif_alloc(alloc_size);
+ alloc_base = erts_alloc(ERTS_ALC_T_NIF, alloc_size);
}
iovec = (ErlNifIOVec*)alloc_base;
@@ -3525,12 +3623,12 @@ static int create_iovec_from_slice(ErlNifEnv *env,
iovec->flags = 0;
}
- iovec->size = slice->offheap_size + slice->onheap_size;
+ iovec->size = slice->referenced_size + slice->copied_size;
iovec->iovcnt = slice->iovec_len;
if(!fill_iovec_with_slice(env, slice, iovec)) {
if (env == NULL && !(iovec->flags & ERL_NIF_IOVEC_FLAGS_PREALLOC)) {
- enif_free(iovec);
+ erts_free(ERTS_ALC_T_NIF, iovec);
}
return 0;
@@ -3597,6 +3695,55 @@ int enif_ioq_deq(ErlNifIOQueue *q, size_t elems, size_t *size)
return 1;
}
+int enif_ioq_peek_head(ErlNifEnv *env, ErlNifIOQueue *q, size_t *size, ERL_NIF_TERM *bin_term) {
+ SysIOVec *iov_entry;
+ Binary *ref_bin;
+
+ if (q->size == 0) {
+ return 0;
+ }
+
+ ASSERT(q->b_head != q->b_tail && q->v_head != q->v_tail);
+
+ ref_bin = &q->b_head[0]->nif;
+ iov_entry = &q->v_head[0];
+
+ if (size != NULL) {
+ *size = iov_entry->iov_len;
+ }
+
+ if (iov_entry->iov_len > ERL_ONHEAP_BIN_LIMIT) {
+ ProcBin *pb = (ProcBin*)alloc_heap(env, PROC_BIN_SIZE);
+
+ pb->thing_word = HEADER_PROC_BIN;
+ pb->next = MSO(env->proc).first;
+ pb->val = ref_bin;
+ pb->flags = 0;
+
+ ASSERT((byte*)iov_entry->iov_base >= (byte*)ref_bin->orig_bytes);
+ ASSERT(iov_entry->iov_len <= ref_bin->orig_size);
+
+ pb->bytes = (byte*)iov_entry->iov_base;
+ pb->size = iov_entry->iov_len;
+
+ MSO(env->proc).first = (struct erl_off_heap_header*) pb;
+ OH_OVERHEAD(&(MSO(env->proc)), pb->size / sizeof(Eterm));
+
+ erts_refc_inc(&ref_bin->intern.refc, 2);
+ *bin_term = make_binary(pb);
+ } else {
+ ErlHeapBin* hb = (ErlHeapBin*)alloc_heap(env, heap_bin_size(iov_entry->iov_len));
+
+ hb->thing_word = header_heap_bin(iov_entry->iov_len);
+ hb->size = iov_entry->iov_len;
+
+ sys_memcpy(hb->data, iov_entry->iov_base, iov_entry->iov_len);
+ *bin_term = make_binary(hb);
+ }
+
+ return 1;
+}
+
SysIOVec *enif_ioq_peek(ErlNifIOQueue *q, int *iovlen)
{
return erts_ioq_peekq(q, iovlen);
@@ -3613,7 +3760,7 @@ static ErtsCodeInfo** get_func_pp(BeamCodeHeader* mod_code, Eterm f_atom, unsign
int j;
for (j = 0; j < n; ++j) {
ErtsCodeInfo* ci = mod_code->functions[j];
- ASSERT(ci->op == (BeamInstr) BeamOp(op_i_func_info_IaaI));
+ ASSERT(BeamIsOpCode(ci->op, op_i_func_info_IaaI));
if (f_atom == ci->mfa.function
&& arity == ci->mfa.arity) {
return mod_code->functions+j;
@@ -3627,16 +3774,26 @@ static Eterm mkatom(const char *str)
return am_atom_put(str, sys_strlen(str));
}
-static struct tainted_module_t
+struct tainted_module_t
{
struct tainted_module_t* next;
Eterm module_atom;
-}*first_tainted_module = NULL;
+};
-static void add_taint(Eterm mod_atom)
+erts_atomic_t first_taint; /* struct tainted_module_t* */
+
+void erts_add_taint(Eterm mod_atom)
{
- struct tainted_module_t* t;
- for (t=first_tainted_module ; t!=NULL; t=t->next) {
+#ifdef ERTS_ENABLE_LOCK_CHECK
+ extern erts_rwmtx_t erts_driver_list_lock; /* Mutex for driver list */
+#endif
+ struct tainted_module_t *first, *t;
+
+ ERTS_LC_ASSERT(erts_lc_rwmtx_is_rwlocked(&erts_driver_list_lock)
+ || erts_thr_progress_is_blocking());
+
+ first = (struct tainted_module_t*) erts_atomic_read_nob(&first_taint);
+ for (t=first ; t; t=t->next) {
if (t->module_atom == mod_atom) {
return;
}
@@ -3644,22 +3801,24 @@ static void add_taint(Eterm mod_atom)
t = erts_alloc_fnf(ERTS_ALC_T_TAINT, sizeof(*t));
if (t != NULL) {
t->module_atom = mod_atom;
- t->next = first_tainted_module;
- first_tainted_module = t;
+ t->next = first;
+ erts_atomic_set_nob(&first_taint, (erts_aint_t)t);
}
}
Eterm erts_nif_taints(Process* p)
{
- struct tainted_module_t* t;
+ struct tainted_module_t *first, *t;
unsigned cnt = 0;
Eterm list = NIL;
Eterm* hp;
- for (t=first_tainted_module ; t!=NULL; t=t->next) {
+
+ first = (struct tainted_module_t*) erts_atomic_read_nob(&first_taint);
+ for (t=first ; t!=NULL; t=t->next) {
cnt++;
}
hp = HAlloc(p,cnt*2);
- for (t=first_tainted_module ; t!=NULL; t=t->next) {
+ for (t=first ; t!=NULL; t=t->next) {
list = CONS(hp, t->module_atom, list);
hp += 2;
}
@@ -3668,9 +3827,11 @@ Eterm erts_nif_taints(Process* p)
void erts_print_nif_taints(fmtfn_t to, void* to_arg)
{
- struct tainted_module_t* t;
+ struct tainted_module_t *t;
const char* delim = "";
- for (t=first_tainted_module ; t!=NULL; t=t->next) {
+
+ t = (struct tainted_module_t*) erts_atomic_read_nob(&first_taint);
+ for ( ; t; t = t->next) {
const Atom* atom = atom_tab(atom_val(t->module_atom));
erts_cbprintf(to,to_arg,"%s%.*s", delim, atom->len, atom->name);
delim = ",";
@@ -3757,6 +3918,11 @@ static struct erl_module_nif* create_lib(const ErlNifEntry* src)
} else {
dst->sizeof_ErlNifResourceTypeInit = 0;
}
+ if (AT_LEAST_VERSION(src, 2, 14)) {
+ dst->min_erts = src->min_erts;
+ } else {
+ dst->min_erts = "erts-?";
+ }
return lib;
};
@@ -3779,6 +3945,7 @@ BIF_RETTYPE load_nif_2(BIF_ALIST_2)
ErtsSysDdllError errdesc = ERTS_SYS_DDLL_ERROR_INIT;
Eterm ret = am_ok;
int veto;
+ int taint = 1;
struct erl_module_nif* lib = NULL;
struct erl_module_instance* this_mi;
struct erl_module_instance* prev_mi;
@@ -3825,10 +3992,15 @@ BIF_RETTYPE load_nif_2(BIF_ALIST_2)
ASSERT(module_p != NULL);
mod_atomp = atom_tab(atom_val(mod_atom));
- init_func = erts_static_nif_get_nif_init((char*)mod_atomp->name, mod_atomp->len);
- if (init_func != NULL)
- handle = init_func;
-
+ {
+ ErtsStaticNifEntry* sne;
+ sne = erts_static_nif_get_nif_init((char*)mod_atomp->name, mod_atomp->len);
+ if (sne != NULL) {
+ init_func = sne->nif_init;
+ handle = init_func;
+ taint = sne->taint;
+ }
+ }
this_mi = &module_p->curr;
prev_mi = &module_p->old;
if (in_area(caller, module_p->old.code_hdr, module_p->old.code_length)) {
@@ -3865,18 +4037,24 @@ BIF_RETTYPE load_nif_2(BIF_ALIST_2)
" function: '%s'", errdesc.str);
}
- else if ((add_taint(mod_atom),
+ 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");
}
+ else if (entry->major > ERL_NIF_MAJOR_VERSION
+ || (entry->major == ERL_NIF_MAJOR_VERSION
+ && entry->minor > ERL_NIF_MINOR_VERSION)) {
+ char* fmt = "That '%T' NIF library needs %s or newer. Either try to"
+ " recompile the NIF lib or use a newer erts runtime.";
+ ret = load_nif_error(BIF_P, bad_lib, fmt, mod_atom, entry->min_erts);
+ }
else if (entry->major < ERL_NIF_MIN_REQUIRED_MAJOR_VERSION_ON_LOAD
- || (ERL_NIF_MAJOR_VERSION < entry->major
- || (ERL_NIF_MAJOR_VERSION == entry->major
- && ERL_NIF_MINOR_VERSION < entry->minor))
|| (entry->major==2 && entry->minor == 5)) { /* experimental maps */
- ret = load_nif_error(BIF_P, bad_lib, "Library version (%d.%d) not compatible (with %d.%d).",
- entry->major, entry->minor, ERL_NIF_MAJOR_VERSION, ERL_NIF_MINOR_VERSION);
+ char* fmt = "That old NIF library (%d.%d) is not compatible with this "
+ "erts runtime (%d.%d). Try recompile the NIF lib.";
+ ret = load_nif_error(BIF_P, bad_lib, fmt, entry->major, entry->minor,
+ ERL_NIF_MAJOR_VERSION, ERL_NIF_MINOR_VERSION);
}
else if (AT_LEAST_VERSION(entry, 2, 1)
&& sys_strcmp(entry->vm_variant, ERL_NIF_VM_VARIANT) != 0) {
@@ -3982,13 +4160,12 @@ BIF_RETTYPE load_nif_2(BIF_ALIST_2)
code_ptr = erts_codeinfo_to_code(ci);
if (ci->u.gen_bp == NULL) {
- code_ptr[0] = (BeamInstr) BeamOp(op_call_nif);
+ code_ptr[0] = BeamOpCodeAddr(op_call_nif);
}
else { /* Function traced, patch the original instruction word */
GenericBp* g = ci->u.gen_bp;
- ASSERT(code_ptr[0] ==
- (BeamInstr) BeamOp(op_i_generic_breakpoint));
- g->orig_instr = (BeamInstr) BeamOp(op_call_nif);
+ 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;
@@ -4087,6 +4264,10 @@ int erts_nif_get_funcs(struct erl_module_nif* mod,
return mod->entry.num_of_funcs;
}
+Module *erts_nif_get_module(struct erl_module_nif *nif_mod) {
+ return nif_mod->mod;
+}
+
Eterm erts_nif_call_function(Process *p, Process *tracee,
struct erl_module_nif* mod,
ErlNifFunc *fun, int argc, Eterm *argv)
@@ -4172,34 +4353,31 @@ static unsigned calc_checksum(unsigned char* ptr, unsigned size);
struct readonly_check_t
{
- struct enif_tmp_obj_t hdr;
unsigned char* ptr;
unsigned size;
unsigned checksum;
};
static void add_readonly_check(ErlNifEnv* env, unsigned char* ptr, unsigned sz)
{
- ErtsAlcType_t allocator = is_proc_bound(env) ? ERTS_ALC_T_TMP : ERTS_ALC_T_NIF;
- struct readonly_check_t* obj = erts_alloc(allocator,
- sizeof(struct readonly_check_t));
- obj->hdr.allocator = allocator;
- obj->hdr.next = env->tmp_obj_list;
- env->tmp_obj_list = &obj->hdr;
- obj->hdr.dtor = &readonly_check_dtor;
+ struct readonly_check_t* obj;
+
+ obj = alloc_tmp_obj(env, sizeof(struct readonly_check_t),
+ &readonly_check_dtor);
+
obj->ptr = ptr;
obj->size = sz;
- obj->checksum = calc_checksum(ptr, sz);
+ obj->checksum = calc_checksum(ptr, sz);
}
-static void readonly_check_dtor(struct enif_tmp_obj_t* o)
+static void readonly_check_dtor(struct enif_tmp_obj_t* tmp_obj)
{
- struct readonly_check_t* obj = (struct readonly_check_t*) o;
- unsigned chksum = calc_checksum(obj->ptr, obj->size);
- if (chksum != obj->checksum) {
+ struct readonly_check_t* ro_check = (struct readonly_check_t*)&tmp_obj[1];
+ unsigned chksum = calc_checksum(ro_check->ptr, ro_check->size);
+ if (chksum != ro_check->checksum) {
fprintf(stderr, "\r\nReadonly data written by NIF, checksums differ"
- " %x != %x\r\nABORTING\r\n", chksum, obj->checksum);
+ " %x != %x\r\nABORTING\r\n", chksum, ro_check->checksum);
abort();
}
- erts_free(obj->hdr.allocator, obj);
+ erts_free(tmp_obj->allocator, tmp_obj);
}
static unsigned calc_checksum(unsigned char* ptr, unsigned size)
{
@@ -4274,7 +4452,7 @@ static void get_string_maybe(ErlNifEnv *env, const ERL_NIF_TERM term,
str_bin.size > bufsiz) {
*ptr = NULL;
} else {
- memcpy(buf, (char *) str_bin.data, str_bin.size);
+ sys_memcpy(buf, (char *) str_bin.data, str_bin.size);
buf[str_bin.size] = '\0';
*ptr = buf;
}
@@ -4291,7 +4469,7 @@ ERL_NIF_TERM erl_nif_user_trace_s1(ErlNifEnv* env, int argc,
message_bin.size > MESSAGE_BUFSIZ) {
return am_badarg;
}
- memcpy(messagebuf, (char *) message_bin.data, message_bin.size);
+ sys_memcpy(messagebuf, (char *) message_bin.data, message_bin.size);
messagebuf[message_bin.size] = '\0';
DTRACE1(user_trace_s1, messagebuf);
return am_true;
diff --git a/erts/emulator/beam/erl_nif.h b/erts/emulator/beam/erl_nif.h
index d195721054..1906da732b 100644
--- a/erts/emulator/beam/erl_nif.h
+++ b/erts/emulator/beam/erl_nif.h
@@ -50,10 +50,14 @@
** 2.9: 18.2 enif_getenv
** 2.10: Time API
** 2.11: 19.0 enif_snprintf
-** 2.12: 20.0 add enif_queue
+** 2.12: 20.0 add enif_select, enif_open_resource_type_x
+** 2.13: 20.1 add enif_ioq
+** 2.14: 21.0 add enif_ioq_peek_head, enif_(mutex|cond|rwlock|thread)_name
+** enif_vfprintf, enif_vsnprintf, enif_make_map_from_arrays
*/
#define ERL_NIF_MAJOR_VERSION 2
-#define ERL_NIF_MINOR_VERSION 12
+#define ERL_NIF_MINOR_VERSION 14
+#define ERL_NIF_MIN_ERTS_VERSION "erts-10.0 (OTP-21)"
/*
* The emulator will refuse to load a nif-lib with a major version
@@ -68,6 +72,8 @@
#define ERL_NIF_MIN_REQUIRED_MAJOR_VERSION_ON_LOAD 2
#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
#ifdef __cplusplus
extern "C" {
@@ -126,6 +132,9 @@ typedef struct enif_entry_t
/* Added in 2.12 */
size_t sizeof_ErlNifResourceTypeInit;
+
+ /* Added in 2.14 */
+ const char* min_erts;
}ErlNifEntry;
@@ -135,8 +144,9 @@ typedef struct
unsigned char* data;
/* Internals (avert your eyes) */
- ERL_NIF_TERM bin_term;
void* ref_bin;
+ /* for future additions to be ABI compatible (same struct size) */
+ void* __spare__[2];
}ErlNifBinary;
#if (defined(__WIN32__) || defined(_WIN32) || defined(_WIN32_))
@@ -347,7 +357,8 @@ ERL_NIF_INIT_DECL(NAME) \
LOAD, RELOAD, UPGRADE, UNLOAD, \
ERL_NIF_VM_VARIANT, \
1, \
- sizeof(ErlNifResourceTypeInit) \
+ sizeof(ErlNifResourceTypeInit), \
+ ERL_NIF_MIN_ERTS_VERSION \
}; \
ERL_NIF_INIT_BODY; \
return &entry; \
diff --git a/erts/emulator/beam/erl_nif_api_funcs.h b/erts/emulator/beam/erl_nif_api_funcs.h
index 9e573307d8..61f8fcf6ed 100644
--- a/erts/emulator/beam/erl_nif_api_funcs.h
+++ b/erts/emulator/beam/erl_nif_api_funcs.h
@@ -92,7 +92,7 @@ ERL_NIF_API_FUNC_DECL(int,enif_thread_join,(ErlNifTid, void **respp));
ERL_NIF_API_FUNC_DECL(void*,enif_realloc,(void* ptr, size_t size));
ERL_NIF_API_FUNC_DECL(void,enif_system_info,(ErlNifSysInfo *sip, size_t si_size));
-ERL_NIF_API_FUNC_DECL(int,enif_fprintf,(void/* FILE* */ *filep, const char *format, ...));
+ERL_NIF_API_FUNC_DECL(int,enif_fprintf,(FILE* filep, const char *format, ...));
ERL_NIF_API_FUNC_DECL(int,enif_inspect_iolist_as_binary,(ErlNifEnv*, ERL_NIF_TERM term, ErlNifBinary* bin));
ERL_NIF_API_FUNC_DECL(ERL_NIF_TERM,enif_make_sub_binary,(ErlNifEnv*, ERL_NIF_TERM bin_term, size_t pos, size_t size));
ERL_NIF_API_FUNC_DECL(int,enif_get_string,(ErlNifEnv*, ERL_NIF_TERM list, char* buf, unsigned len, ErlNifCharEncoding));
@@ -198,6 +198,17 @@ ERL_NIF_API_FUNC_DECL(SysIOVec*,enif_ioq_peek,(ErlNifIOQueue *q, int *iovlen));
ERL_NIF_API_FUNC_DECL(int,enif_inspect_iovec,(ErlNifEnv *env, size_t max_length, ERL_NIF_TERM iovec_term, ERL_NIF_TERM *tail, ErlNifIOVec **iovec));
ERL_NIF_API_FUNC_DECL(void,enif_free_iovec,(ErlNifIOVec *iov));
+ERL_NIF_API_FUNC_DECL(int,enif_ioq_peek_head,(ErlNifEnv *env, ErlNifIOQueue *q, size_t *size, ERL_NIF_TERM *head));
+
+ERL_NIF_API_FUNC_DECL(char*,enif_mutex_name,(ErlNifMutex*));
+ERL_NIF_API_FUNC_DECL(char*,enif_cond_name,(ErlNifCond*));
+ERL_NIF_API_FUNC_DECL(char*,enif_rwlock_name,(ErlNifRWLock*));
+ERL_NIF_API_FUNC_DECL(char*,enif_thread_name,(ErlNifTid));
+
+ERL_NIF_API_FUNC_DECL(int,enif_vfprintf,(FILE*, const char *fmt, va_list));
+ERL_NIF_API_FUNC_DECL(int,enif_vsnprintf,(char*, size_t, const char *fmt, va_list));
+
+ERL_NIF_API_FUNC_DECL(int,enif_make_map_from_arrays,(ErlNifEnv *env, ERL_NIF_TERM keys[], ERL_NIF_TERM values[], size_t cnt, ERL_NIF_TERM *map_out));
/*
** ADD NEW ENTRIES HERE (before this comment) !!!
@@ -373,6 +384,14 @@ ERL_NIF_API_FUNC_DECL(void,enif_free_iovec,(ErlNifIOVec *iov));
# define enif_ioq_peek ERL_NIF_API_FUNC_MACRO(enif_ioq_peek)
# define enif_inspect_iovec ERL_NIF_API_FUNC_MACRO(enif_inspect_iovec)
# define enif_free_iovec ERL_NIF_API_FUNC_MACRO(enif_free_iovec)
+# define enif_ioq_peek_head ERL_NIF_API_FUNC_MACRO(enif_ioq_peek_head)
+# define enif_mutex_name ERL_NIF_API_FUNC_MACRO(enif_mutex_name)
+# define enif_cond_name ERL_NIF_API_FUNC_MACRO(enif_cond_name)
+# define enif_rwlock_name ERL_NIF_API_FUNC_MACRO(enif_rwlock_name)
+# define enif_thread_name ERL_NIF_API_FUNC_MACRO(enif_thread_name)
+# define enif_vfprintf ERL_NIF_API_FUNC_MACRO(enif_vfprintf)
+# define enif_vsnprintf ERL_NIF_API_FUNC_MACRO(enif_vsnprintf)
+# define enif_make_map_from_arrays ERL_NIF_API_FUNC_MACRO(enif_make_map_from_arrays)
/*
** ADD NEW ENTRIES HERE (before this comment)
diff --git a/erts/emulator/beam/erl_node_container_utils.h b/erts/emulator/beam/erl_node_container_utils.h
index 6ec428e282..99e938266b 100644
--- a/erts/emulator/beam/erl_node_container_utils.h
+++ b/erts/emulator/beam/erl_node_container_utils.h
@@ -318,5 +318,3 @@ extern ErtsPTab erts_port;
#define is_not_ref(x) (!is_ref(x))
#endif
-
-
diff --git a/erts/emulator/beam/erl_node_tables.c b/erts/emulator/beam/erl_node_tables.c
index 0f3dfa797c..1f147011a8 100644
--- a/erts/emulator/beam/erl_node_tables.c
+++ b/erts/emulator/beam/erl_node_tables.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2001-2017. All Rights Reserved.
+ * Copyright Ericsson AB 2001-2018. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -31,6 +31,7 @@
#include "dtrace-wrapper.h"
#include "erl_binary.h"
#include "erl_bif_unique.h"
+#include "erl_proc_sig_queue.h"
Hash erts_dist_table;
Hash erts_node_table;
@@ -39,9 +40,11 @@ erts_rwmtx_t erts_node_table_rwmtx;
DistEntry *erts_hidden_dist_entries;
DistEntry *erts_visible_dist_entries;
+DistEntry *erts_pending_dist_entries;
DistEntry *erts_not_connected_dist_entries; /* including erts_this_dist_entry */
Sint erts_no_of_hidden_dist_entries;
Sint erts_no_of_visible_dist_entries;
+Sint erts_no_of_pending_dist_entries;
Sint erts_no_of_not_connected_dist_entries; /* including erts_this_dist_entry */
DistEntry *erts_this_dist_entry;
@@ -101,7 +104,9 @@ void
erts_ref_dist_entry(DistEntry *dep)
{
ASSERT(dep);
- de_refc_inc(dep, 1);
+ if (de_refc_inc_read(dep, 1) == 1) {
+ de_refc_inc(dep, 2); /* Pending delete */
+ }
}
void
@@ -139,9 +144,7 @@ dist_table_cmp(void *dep1, void *dep2)
static void*
dist_table_alloc(void *dep_tmpl)
{
-#ifdef DEBUG
erts_aint_t refc;
-#endif
Eterm sysname;
Binary *bin;
DistEntry *dep;
@@ -158,13 +161,8 @@ dist_table_alloc(void *dep_tmpl)
dist_entries++;
-#ifdef DEBUG
- refc =
-#else
- (void)
-#endif
- de_refc_dec_read(dep, -1);
- ASSERT(refc == -1);
+ refc = de_refc_dec_read(dep, -1);
+ ASSERT(refc == -1); (void)refc;
dep->prev = NULL;
erts_rwmtx_init_opt(&dep->rwmtx, &rwmtx_opt, "dist_entry", sysname,
@@ -173,15 +171,11 @@ dist_table_alloc(void *dep_tmpl)
dep->cid = NIL;
erts_atomic_init_nob(&dep->input_handler, (erts_aint_t) NIL);
dep->connection_id = 0;
- dep->status = 0;
+ dep->state = ERTS_DE_STATE_IDLE;
dep->flags = 0;
dep->version = 0;
- erts_mtx_init(&dep->lnk_mtx, "dist_entry_links", sysname,
- ERTS_LOCK_FLAGS_CATEGORY_DISTRIBUTION);
- dep->node_links = NULL;
- dep->nlinks = NULL;
- dep->monitors = NULL;
+ dep->mld = NULL;
erts_mtx_init(&dep->qlock, "dist_entry_out_queue", sysname,
ERTS_LOCK_FLAGS_CATEGORY_DISTRIBUTION);
@@ -202,6 +196,7 @@ dist_table_alloc(void *dep_tmpl)
erts_port_task_handle_init(&dep->dist_cmd);
dep->send = NULL;
dep->cache = NULL;
+ dep->transcode_ctx = NULL;
/* Link in */
@@ -224,10 +219,10 @@ dist_table_free(void *vdep)
{
DistEntry *dep = (DistEntry *) vdep;
+ ASSERT(de_refc_read(dep, -1) == -1);
+ ASSERT(dep->state == ERTS_DE_STATE_IDLE);
ASSERT(is_nil(dep->cid));
- ASSERT(dep->nlinks == NULL);
- ASSERT(dep->node_links == NULL);
- ASSERT(dep->monitors == NULL);
+ ASSERT(dep->mld == NULL);
/* Link out */
@@ -250,7 +245,6 @@ dist_table_free(void *vdep)
ASSERT(!dep->cache);
erts_rwmtx_destroy(&dep->rwmtx);
- erts_mtx_destroy(&dep->lnk_mtx);
erts_mtx_destroy(&dep->qlock);
#ifdef DEBUG
@@ -384,56 +378,92 @@ erts_dhandle_to_dist_entry(Eterm dhandle)
}
Eterm
-erts_make_dhandle(Process *c_p, DistEntry *dep)
+erts_build_dhandle(Eterm **hpp, ErlOffHeap* ohp, DistEntry *dep)
{
- Binary *bin;
- Eterm *hp;
-
- bin = ErtsDistEntry2Bin(dep);
+ Binary *bin = ErtsDistEntry2Bin(dep);
ASSERT(bin);
ASSERT(ERTS_MAGIC_BIN_DESTRUCTOR(bin) == erts_dist_entry_destructor);
- hp = HAlloc(c_p, ERTS_MAGIC_REF_THING_SIZE);
- return erts_mk_magic_ref(&hp, &c_p->off_heap, bin);
+ return erts_mk_magic_ref(hpp, ohp, bin);
+}
+
+Eterm
+erts_make_dhandle(Process *c_p, DistEntry *dep)
+{
+ Eterm *hp = HAlloc(c_p, ERTS_MAGIC_REF_THING_SIZE);
+ return erts_build_dhandle(&hp, &c_p->off_heap, dep);
}
-static void try_delete_dist_entry(void *vbin);
+static void start_timer_delete_dist_entry(void *vdep);
+static void prepare_try_delete_dist_entry(void *vdep);
+static void try_delete_dist_entry(DistEntry*);
+
+static void schedule_delete_dist_entry(DistEntry* dep)
+{
+ /*
+ * Here we need thread progress to wait for other threads, that may have
+ * done lookup without refc++, to do either refc++ or drop their refs.
+ *
+ * Note that timeouts do not guarantee thread progress.
+ */
+ erts_schedule_thr_prgr_later_op(start_timer_delete_dist_entry,
+ dep, &dep->later_op);
+}
static void
-prepare_try_delete_dist_entry(void *vbin)
+start_timer_delete_dist_entry(void *vdep)
{
- Binary *bin = (Binary *) vbin;
- DistEntry *dep = ErtsBin2DistEntry(bin);
- Uint size;
+ if (node_tab_delete_delay == 0) {
+ prepare_try_delete_dist_entry(vdep);
+ }
+ else {
+ ASSERT(node_tab_delete_delay > 0);
+ erts_start_timer_callback(node_tab_delete_delay,
+ prepare_try_delete_dist_entry,
+ vdep);
+ }
+}
+
+static void
+prepare_try_delete_dist_entry(void *vdep)
+{
+ DistEntry *dep = vdep;
erts_aint_t refc;
+ /*
+ * Time has passed since we decremented refc to zero and DistEntry may
+ * have been revived. Do a fast check without table lock first.
+ */
refc = de_refc_read(dep, 0);
- if (refc > 0)
- return;
-
- size = ERTS_MAGIC_BIN_SIZE(sizeof(DistEntry));
- erts_schedule_thr_prgr_later_cleanup_op(try_delete_dist_entry,
- vbin, &dep->later_op, size);
+ if (refc == 0) {
+ try_delete_dist_entry(dep);
+ }
+ else {
+ /*
+ * Someone has done lookup and done refc++ for us.
+ */
+ refc = de_refc_dec_read(dep, 0);
+ if (refc == 0)
+ schedule_delete_dist_entry(dep);
+ }
}
-static void try_delete_dist_entry(void *vbin)
+static void try_delete_dist_entry(DistEntry* dep)
{
- Binary *bin = (Binary *) vbin;
- DistEntry *dep = ErtsBin2DistEntry(bin);
erts_aint_t refc;
erts_rwmtx_rwlock(&erts_dist_table_rwmtx);
/*
* Another thread might have looked up this dist entry after
* we decided to delete it (refc became zero). If so, the other
- * thread incremented refc twice. Once for the new reference
- * and once for this thread.
+ * thread incremented refc one extra step for this thread.
*
- * If refc reach -1, no one has used the entry since we
- * set up the timer. Delete the entry.
+ * If refc reach -1, no one has done lookup and no one can do lookup
+ * as we have table lock. Delete the entry.
*
- * If refc reach 0, the entry is currently not in use
- * but has been used since we set up the timer. Set up a
- * new timer.
+ * If refc reach 0, someone raced us and either
+ * (1) did lookup with own refc++ and already released it again
+ * (2) did lookup without own refc++
+ * Schedule new delete operation.
*
* If refc > 0, the entry is in use. Keep the entry.
*/
@@ -443,12 +473,7 @@ static void try_delete_dist_entry(void *vbin)
erts_rwmtx_rwunlock(&erts_dist_table_rwmtx);
if (refc == 0) {
- if (node_tab_delete_delay == 0)
- prepare_try_delete_dist_entry(vbin);
- else if (node_tab_delete_delay > 0)
- erts_start_timer_callback(node_tab_delete_delay,
- prepare_try_delete_dist_entry,
- vbin);
+ schedule_delete_dist_entry(dep);
}
}
@@ -462,12 +487,7 @@ int erts_dist_entry_destructor(Binary *bin)
if (refc == -1)
return 1; /* Allow deallocation of structure... */
- if (node_tab_delete_delay == 0)
- prepare_try_delete_dist_entry((void *) bin);
- else if (node_tab_delete_delay > 0)
- erts_start_timer_callback(node_tab_delete_delay,
- prepare_try_delete_dist_entry,
- (void *) bin);
+ schedule_delete_dist_entry(dep);
return 0;
}
@@ -498,12 +518,17 @@ erts_dist_table_size(void)
i++;
ASSERT(i == erts_no_of_hidden_dist_entries);
i = 0;
+ for(dep = erts_pending_dist_entries; dep; dep = dep->next)
+ i++;
+ ASSERT(i == erts_no_of_pending_dist_entries);
+ i = 0;
for(dep = erts_not_connected_dist_entries; dep; dep = dep->next)
i++;
ASSERT(i == erts_no_of_not_connected_dist_entries);
ASSERT(dist_entries == (erts_no_of_visible_dist_entries
+ erts_no_of_hidden_dist_entries
+ + erts_no_of_pending_dist_entries
+ erts_no_of_not_connected_dist_entries));
#endif
@@ -518,43 +543,46 @@ erts_dist_table_size(void)
void
erts_set_dist_entry_not_connected(DistEntry *dep)
{
+ DistEntry** head;
+
ERTS_LC_ASSERT(erts_lc_is_de_rwlocked(dep));
erts_rwmtx_rwlock(&erts_dist_table_rwmtx);
ASSERT(dep != erts_this_dist_entry);
- ASSERT(is_internal_port(dep->cid) || is_internal_pid(dep->cid));
- if(dep->flags & DFLAG_PUBLISHED) {
- if(dep->prev) {
- ASSERT(is_in_de_list(dep, erts_visible_dist_entries));
- dep->prev->next = dep->next;
- }
- else {
- ASSERT(erts_visible_dist_entries == dep);
- erts_visible_dist_entries = dep->next;
- }
-
- ASSERT(erts_no_of_visible_dist_entries > 0);
- erts_no_of_visible_dist_entries--;
+ if (dep->state == ERTS_DE_STATE_PENDING) {
+ ASSERT(is_nil(dep->cid));
+ ASSERT(erts_no_of_pending_dist_entries > 0);
+ erts_no_of_pending_dist_entries--;
+ head = &erts_pending_dist_entries;
}
else {
- if(dep->prev) {
- ASSERT(is_in_de_list(dep, erts_hidden_dist_entries));
- dep->prev->next = dep->next;
- }
- else {
- ASSERT(erts_hidden_dist_entries == dep);
- erts_hidden_dist_entries = dep->next;
- }
-
- ASSERT(erts_no_of_hidden_dist_entries > 0);
- erts_no_of_hidden_dist_entries--;
+ ASSERT(dep->state != ERTS_DE_STATE_IDLE);
+ ASSERT(is_internal_port(dep->cid) || is_internal_pid(dep->cid));
+ if (dep->flags & DFLAG_PUBLISHED) {
+ ASSERT(erts_no_of_visible_dist_entries > 0);
+ erts_no_of_visible_dist_entries--;
+ head = &erts_visible_dist_entries;
+ }
+ else {
+ ASSERT(erts_no_of_hidden_dist_entries > 0);
+ erts_no_of_hidden_dist_entries--;
+ head = &erts_hidden_dist_entries;
+ }
}
+ if(dep->prev) {
+ ASSERT(is_in_de_list(dep, *head));
+ dep->prev->next = dep->next;
+ }
+ else {
+ ASSERT(*head == dep);
+ *head = dep->next;
+ }
if(dep->next)
dep->next->prev = dep->prev;
- dep->status &= ~ERTS_DE_SFLG_CONNECTED;
+ dep->state = ERTS_DE_STATE_EXITING;
dep->flags = 0;
dep->prev = NULL;
dep->cid = NIL;
@@ -570,46 +598,94 @@ erts_set_dist_entry_not_connected(DistEntry *dep)
}
void
+erts_set_dist_entry_pending(DistEntry *dep)
+{
+ ErtsMonLnkDist *mld = erts_mon_link_dist_create(dep->sysname);
+ ERTS_LC_ASSERT(erts_lc_is_de_rwlocked(dep));
+
+ erts_rwmtx_rwlock(&erts_dist_table_rwmtx);
+
+ ASSERT(dep != erts_this_dist_entry);
+ ASSERT(dep->state == ERTS_DE_STATE_IDLE);
+ ASSERT(is_nil(dep->cid));
+
+ if(dep->prev) {
+ ASSERT(is_in_de_list(dep, erts_not_connected_dist_entries));
+ dep->prev->next = dep->next;
+ }
+ else {
+ ASSERT(dep == erts_not_connected_dist_entries);
+ erts_not_connected_dist_entries = dep->next;
+ }
+
+ if(dep->next)
+ dep->next->prev = dep->prev;
+
+ 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->connection_id = (dep->connection_id + 1) & ERTS_DIST_CON_ID_MASK;
+
+ ASSERT(!dep->mld);
+ mld->connection_id = dep->connection_id;
+ dep->mld = mld;
+
+ dep->prev = NULL;
+ dep->next = erts_pending_dist_entries;
+ if(erts_pending_dist_entries) {
+ ASSERT(erts_pending_dist_entries->prev == NULL);
+ erts_pending_dist_entries->prev = dep;
+ }
+ erts_pending_dist_entries = dep;
+ erts_no_of_pending_dist_entries++;
+ erts_rwmtx_rwunlock(&erts_dist_table_rwmtx);
+}
+
+void
erts_set_dist_entry_connected(DistEntry *dep, Eterm cid, Uint flags)
{
+ erts_aint32_t set_qflgs;
+
+ ASSERT(dep->mld);
+
ERTS_LC_ASSERT(erts_lc_is_de_rwlocked(dep));
erts_rwmtx_rwlock(&erts_dist_table_rwmtx);
ASSERT(dep != erts_this_dist_entry);
ASSERT(is_nil(dep->cid));
+ ASSERT(dep->state == ERTS_DE_STATE_PENDING);
ASSERT(is_internal_port(cid) || is_internal_pid(cid));
if(dep->prev) {
- ASSERT(is_in_de_list(dep, erts_not_connected_dist_entries));
+ ASSERT(is_in_de_list(dep, erts_pending_dist_entries));
dep->prev->next = dep->next;
}
else {
- ASSERT(erts_not_connected_dist_entries == dep);
- erts_not_connected_dist_entries = dep->next;
+ ASSERT(erts_pending_dist_entries == dep);
+ erts_pending_dist_entries = dep->next;
}
if(dep->next)
dep->next->prev = dep->prev;
- ASSERT(erts_no_of_not_connected_dist_entries > 0);
- erts_no_of_not_connected_dist_entries--;
+ ASSERT(erts_no_of_pending_dist_entries > 0);
+ erts_no_of_pending_dist_entries--;
- dep->status |= ERTS_DE_SFLG_CONNECTED;
- dep->flags = flags;
+ dep->state = ERTS_DE_STATE_CONNECTED;
+ dep->flags = flags & ~DFLAG_NO_MAGIC;
dep->cid = cid;
erts_atomic_set_nob(&dep->input_handler,
(erts_aint_t) cid);
- dep->connection_id++;
- dep->connection_id &= ERTS_DIST_EXT_CON_ID_MASK;
dep->prev = NULL;
erts_atomic64_set_nob(&dep->in, 0);
erts_atomic64_set_nob(&dep->out, 0);
- erts_atomic32_set_nob(&dep->qflgs,
- (is_internal_port(cid)
- ? ERTS_DE_QFLG_PORT_CTRL
- : ERTS_DE_QFLG_PROC_CTRL));
+ set_qflgs = (is_internal_port(cid) ?
+ ERTS_DE_QFLG_PORT_CTRL : ERTS_DE_QFLG_PROC_CTRL);
+ erts_atomic32_read_bor_nob(&dep->qflgs, set_qflgs);
+
if(flags & DFLAG_PUBLISHED) {
dep->next = erts_visible_dist_entries;
if(erts_visible_dist_entries) {
@@ -929,9 +1005,11 @@ void erts_init_node_tables(int dd_sec)
erts_hidden_dist_entries = NULL;
erts_visible_dist_entries = NULL;
+ erts_pending_dist_entries = NULL;
erts_not_connected_dist_entries = NULL;
erts_no_of_hidden_dist_entries = 0;
erts_no_of_visible_dist_entries = 0;
+ erts_no_of_pending_dist_entries = 0;
erts_no_of_not_connected_dist_entries = 0;
node_tmpl.sysname = am_Noname;
@@ -973,14 +1051,16 @@ static void erts_lcnt_enable_dist_lock_count(void *dep_raw, void *enable) {
if(enable) {
erts_lcnt_install_new_lock_info(&dep->rwmtx.lcnt, "dist_entry", dep->sysname,
ERTS_LOCK_TYPE_RWMUTEX | ERTS_LOCK_FLAGS_CATEGORY_DISTRIBUTION);
- erts_lcnt_install_new_lock_info(&dep->lnk_mtx.lcnt, "dist_entry_links", dep->sysname,
- ERTS_LOCK_TYPE_MUTEX | ERTS_LOCK_FLAGS_CATEGORY_DISTRIBUTION);
erts_lcnt_install_new_lock_info(&dep->qlock.lcnt, "dist_entry_out_queue", dep->sysname,
ERTS_LOCK_TYPE_MUTEX | ERTS_LOCK_FLAGS_CATEGORY_DISTRIBUTION);
+ if (dep->mld)
+ erts_lcnt_install_new_lock_info(&dep->mld->mtx.lcnt, "dist_entry_links", dep->sysname,
+ ERTS_LOCK_TYPE_MUTEX | ERTS_LOCK_FLAGS_CATEGORY_DISTRIBUTION);
} else {
erts_lcnt_uninstall(&dep->rwmtx.lcnt);
- erts_lcnt_uninstall(&dep->lnk_mtx.lcnt);
erts_lcnt_uninstall(&dep->qlock.lcnt);
+ if (dep->mld)
+ erts_lcnt_uninstall(&dep->mld->mtx.lcnt);
}
}
@@ -1023,6 +1103,7 @@ static Eterm AM_system;
static Eterm AM_timer;
static Eterm AM_delayed_delete_timer;
static Eterm AM_thread_progress_delete_timer;
+static Eterm AM_signal;
static void setup_reference_table(void);
static Eterm reference_table_term(Uint **hpp, ErlOffHeap *ohp, Uint *szp);
@@ -1044,6 +1125,7 @@ typedef struct node_referrer_ {
int bin_ref;
int timer_ref;
int system_ref;
+ int signal_ref;
Eterm id;
Uint id_heap[ID_HEAP_SIZE];
ErlOffHeap off_heap;
@@ -1057,9 +1139,11 @@ typedef struct {
typedef struct dist_referrer_ {
struct dist_referrer_ *next;
int heap_ref;
+ int ets_ref;
int node_ref;
int ctrl_ref;
int system_ref;
+ int signal_ref;
Eterm id;
Uint creation;
Uint id_heap[ID_HEAP_SIZE];
@@ -1113,6 +1197,7 @@ erts_get_node_and_dist_references(struct process *proc)
INIT_AM(system);
INIT_AM(delayed_delete_timer);
INIT_AM(thread_progress_delete_timer);
+ INIT_AM(signal);
references_atoms_need_init = 0;
}
@@ -1149,6 +1234,7 @@ erts_get_node_and_dist_references(struct process *proc)
#define MONITOR_REF 7
#define TIMER_REF 8
#define SYSTEM_REF 9
+#define SIGNAL_REF 10
#define INC_TAB_SZ 10
@@ -1175,20 +1261,24 @@ insert_dist_referrer(ReferredDist *referred_dist,
else {
Uint *hp = &drp->id_heap[0];
ASSERT(is_tuple(id));
- drp->id = copy_struct(id, size_object(id), &hp, NULL);
+ drp->id = copy_struct(id, size_object(id), &hp, NULL);
}
drp->creation = creation;
drp->heap_ref = 0;
+ drp->ets_ref = 0;
drp->node_ref = 0;
drp->ctrl_ref = 0;
drp->system_ref = 0;
+ drp->signal_ref = 0;
}
switch (type) {
case NODE_REF: drp->node_ref++; break;
case CTRL_REF: drp->ctrl_ref++; break;
case HEAP_REF: drp->heap_ref++; break;
+ case ETS_REF: drp->ets_ref++; break;
case SYSTEM_REF: drp->system_ref++; break;
+ case SIGNAL_REF: drp->signal_ref++; break;
default: ASSERT(0);
}
}
@@ -1242,6 +1332,7 @@ insert_node_referrer(ReferredNode *referred_node, int type, Eterm id)
nrp->bin_ref = 0;
nrp->timer_ref = 0;
nrp->system_ref = 0;
+ nrp->signal_ref = 0;
}
switch (type) {
@@ -1252,6 +1343,7 @@ insert_node_referrer(ReferredNode *referred_node, int type, Eterm id)
case MONITOR_REF: nrp->monitor_ref++; break;
case TIMER_REF: nrp->timer_ref++; break;
case SYSTEM_REF: nrp->system_ref++; break;
+ case SIGNAL_REF: nrp->signal_ref++; break;
default: ASSERT(0);
}
}
@@ -1300,6 +1392,11 @@ insert_offheap2(ErlOffHeap *oh, void *arg)
(((Bin)->intern.flags & BIN_FLAG_MAGIC) \
&& ERTS_MAGIC_BIN_DESTRUCTOR((Bin)) == erts_dist_entry_destructor)
+#define IsSendCtxBinary(Bin) \
+ (((Bin)->intern.flags & BIN_FLAG_MAGIC) \
+ && ERTS_MAGIC_BIN_DESTRUCTOR((Bin)) == erts_dsend_context_dtor)
+
+
static void
insert_offheap(ErlOffHeap *oh, int type, Eterm id)
{
@@ -1336,7 +1433,12 @@ insert_offheap(ErlOffHeap *oh, int type, Eterm id)
inserted_bins = nib;
UnUseTmpHeapNoproc(BIG_UINT_HEAP_SIZE);
}
- }
+ }
+ else if (IsSendCtxBinary(u.mref->mb)) {
+ ErtsSendContext* ctx = ERTS_MAGIC_BIN_DATA(u.mref->mb);
+ if (ctx->deref_dep)
+ insert_dist_entry(ctx->dep, type, id, 0);
+ }
break;
case REFC_BINARY_SUBTAG:
case FUN_SUBTAG:
@@ -1349,49 +1451,145 @@ insert_offheap(ErlOffHeap *oh, int type, Eterm id)
}
}
-static void doit_insert_monitor(ErtsMonitor *monitor, void *p)
+static void insert_monitor_data(ErtsMonitor *mon, int type, Eterm id)
+{
+ ErtsMonitorData *mdp = erts_monitor_to_data(mon);
+ if ((mdp->origin.flags & (ERTS_ML_FLG_DBG_VISITED
+ | ERTS_ML_FLG_EXTENDED)) == ERTS_ML_FLG_EXTENDED) {
+ if (mon->type != ERTS_MON_TYPE_NODE) {
+ ErtsMonitorDataExtended *mdep = (ErtsMonitorDataExtended *) mdp;
+ ASSERT(mon->flags & ERTS_ML_FLG_EXTENDED);
+ if (mdep->uptr.ohhp) {
+ ErlOffHeap oh;
+ ERTS_INIT_OFF_HEAP(&oh);
+ oh.first = mdep->uptr.ohhp;
+ insert_offheap(&oh, type, id);
+ }
+ }
+ }
+ mdp->origin.flags |= ERTS_ML_FLG_DBG_VISITED;
+}
+
+static void insert_monitor(ErtsMonitor *mon, void *idp)
+{
+ Eterm id = *((Eterm *) idp);
+ insert_monitor_data(mon, MONITOR_REF, id);
+}
+
+static void clear_visited_monitor(ErtsMonitor *mon, void *p)
{
- Eterm *idp = p;
- if(monitor->type != MON_NIF_TARGET && is_external(monitor->u.pid))
- insert_node(external_thing_ptr(monitor->u.pid)->node, MONITOR_REF, *idp);
- if(is_external(monitor->ref))
- insert_node(external_thing_ptr(monitor->ref)->node, MONITOR_REF, *idp);
+ ErtsMonitorData *mdp = erts_monitor_to_data(mon);
+ mdp->origin.flags &= ~ERTS_ML_FLG_DBG_VISITED;
}
-static void doit_insert_link(ErtsLink *lnk, void *p)
+static void
+insert_p_monitors(ErtsPTabElementCommon *p)
{
- Eterm *idp = p;
- if(is_external(lnk->pid))
- insert_node(external_thing_ptr(lnk->pid)->node, LINK_REF,
- *idp);
+ Eterm id = p->id;
+ erts_monitor_tree_foreach(p->u.alive.monitors,
+ insert_monitor,
+ (void *) &id);
+ erts_monitor_list_foreach(p->u.alive.lt_monitors,
+ insert_monitor,
+ (void *) &id);
}
+static void
+insert_dist_monitors(DistEntry *dep)
+{
+ if (dep->mld) {
+ erts_monitor_list_foreach(dep->mld->monitors,
+ insert_monitor,
+ (void *) &dep->sysname);
+ erts_monitor_tree_foreach(dep->mld->orig_name_monitors,
+ insert_monitor,
+ (void *) &dep->sysname);
+ }
+}
static void
-insert_monitors(ErtsMonitor *monitors, Eterm id)
+clear_visited_p_monitors(ErtsPTabElementCommon *p)
{
- erts_doforall_monitors(monitors,&doit_insert_monitor,&id);
+ erts_monitor_tree_foreach(p->u.alive.monitors,
+ clear_visited_monitor,
+ NULL);
+ erts_monitor_list_foreach(p->u.alive.lt_monitors,
+ clear_visited_monitor,
+ NULL);
}
static void
-insert_links(ErtsLink *lnk, Eterm id)
+clear_visited_dist_monitors(DistEntry *dep)
+{
+ if (dep->mld) {
+ erts_monitor_list_foreach(dep->mld->monitors,
+ clear_visited_monitor,
+ NULL);
+ erts_monitor_tree_foreach(dep->mld->orig_name_monitors,
+ clear_visited_monitor,
+ NULL);
+ }
+}
+
+static void insert_link_data(ErtsLink *lnk, int type, Eterm id)
{
- erts_doforall_links(lnk,&doit_insert_link,&id);
+ ErtsLinkData *ldp = erts_link_to_data(lnk);
+ if ((ldp->a.flags & (ERTS_ML_FLG_DBG_VISITED
+ | ERTS_ML_FLG_EXTENDED)) == ERTS_ML_FLG_EXTENDED) {
+ ErtsLinkDataExtended *ldep = (ErtsLinkDataExtended *) ldp;
+ if (ldep->ohhp) {
+ ErlOffHeap oh;
+ ERTS_INIT_OFF_HEAP(&oh);
+ oh.first = ldep->ohhp;
+ insert_offheap(&oh, type, id);
+ }
+ }
+ ldp->a.flags |= ERTS_ML_FLG_DBG_VISITED;
}
-static void doit_insert_link2(ErtsLink *lnk, void *p)
+static void insert_link(ErtsLink *lnk, void *idp)
{
- Eterm *idp = p;
- if(is_external(lnk->pid))
- insert_node(external_thing_ptr(lnk->pid)->node, LINK_REF,
- *idp);
- insert_links(ERTS_LINK_ROOT(lnk), *idp);
+ Eterm id = *((Eterm *) idp);
+ insert_link_data(lnk, LINK_REF, id);
+}
+
+static void clear_visited_link(ErtsLink *lnk, void *p)
+{
+ ErtsLinkData *ldp = erts_link_to_data(lnk);
+ ldp->a.flags &= ~ERTS_ML_FLG_DBG_VISITED;
+}
+
+static void
+insert_p_links(ErtsPTabElementCommon *p)
+{
+ Eterm id = p->id;
+ erts_link_tree_foreach(p->u.alive.links, insert_link, (void *) &id);
+}
+
+static void
+insert_dist_links(DistEntry *dep)
+{
+ if (dep->mld)
+ erts_link_list_foreach(dep->mld->links,
+ insert_link,
+ (void *) &dep->sysname);
}
static void
-insert_links2(ErtsLink *lnk, Eterm id)
+clear_visited_p_links(ErtsPTabElementCommon *p)
{
- erts_doforall_links(lnk,&doit_insert_link2,&id);
+ erts_link_tree_foreach(p->u.alive.links,
+ clear_visited_link,
+ NULL);
+}
+
+static void
+clear_visited_dist_links(DistEntry *dep)
+{
+ if (dep->mld)
+ erts_link_list_foreach(dep->mld->links,
+ clear_visited_link,
+ NULL);
}
static void
@@ -1463,9 +1661,9 @@ insert_delayed_delete_node(void *state,
}
static void
-insert_thr_prgr_delete_dist_entry(void *arg, ErtsThrPrgrVal thr_prgr, void *vbin)
+insert_thr_prgr_delete_dist_entry(void *arg, ErtsThrPrgrVal thr_prgr, void *vdep)
{
- DistEntry *dep = ErtsBin2DistEntry(vbin);
+ DistEntry *dep = vdep;
Eterm heap[3];
insert_dist_entry(dep,
SYSTEM_REF,
@@ -1476,9 +1674,9 @@ insert_thr_prgr_delete_dist_entry(void *arg, ErtsThrPrgrVal thr_prgr, void *vbin
static void
insert_delayed_delete_dist_entry(void *state,
ErtsMonotonicTime timeout_pos,
- void *vbin)
+ void *vdep)
{
- DistEntry *dep = ErtsBin2DistEntry(vbin);
+ DistEntry *dep = vdep;
Eterm heap[3];
insert_dist_entry(dep,
SYSTEM_REF,
@@ -1487,6 +1685,60 @@ insert_delayed_delete_dist_entry(void *state,
}
static void
+insert_message(ErtsMessage *msg, int type, Process *proc)
+{
+ ErlHeapFragment *heap_frag = NULL;
+
+ ASSERT(ERTS_SIG_IS_MSG(msg));
+ if (msg->data.attached) {
+ if (msg->data.attached == ERTS_MSG_COMBINED_HFRAG)
+ heap_frag = &msg->hfrag;
+ else if (ERTS_SIG_IS_INTERNAL_MSG(msg))
+ heap_frag = msg->data.heap_frag;
+ else {
+ if (msg->data.dist_ext->dep)
+ insert_dist_entry(msg->data.dist_ext->dep,
+ type, proc->common.id, 0);
+ if (is_not_nil(ERL_MESSAGE_TOKEN(msg)))
+ heap_frag = erts_dist_ext_trailer(msg->data.dist_ext);
+ }
+ }
+ while (heap_frag) {
+ insert_offheap(&(heap_frag->off_heap),
+ type,
+ proc->common.id);
+ heap_frag = heap_frag->next;
+ }
+}
+
+static void
+insert_sig_msg(ErtsMessage *msg, void *arg)
+{
+ insert_message(msg, SIGNAL_REF, (Process *) arg);
+}
+
+static void
+insert_sig_offheap(ErlOffHeap *ohp, void *arg)
+{
+ Process *proc = arg;
+ insert_offheap(ohp, SIGNAL_REF, proc->common.id);
+}
+
+static void
+insert_sig_monitor(ErtsMonitor *mon, void *arg)
+{
+ Process *proc = arg;
+ insert_monitor_data(mon, SIGNAL_REF, proc->common.id);
+}
+
+static void
+insert_sig_link(ErtsLink *lnk, void *arg)
+{
+ Process *proc = arg;
+ insert_link_data(lnk, SIGNAL_REF, proc->common.id);
+}
+
+static void
setup_reference_table(void)
{
ErlHeapFragment *hfp;
@@ -1520,7 +1772,7 @@ setup_reference_table(void)
erts_debug_callback_timer_foreach(prepare_try_delete_dist_entry,
insert_delayed_delete_dist_entry,
NULL);
- erts_debug_later_op_foreach(try_delete_dist_entry,
+ erts_debug_later_op_foreach(start_timer_delete_dist_entry,
insert_thr_prgr_delete_dist_entry,
NULL);
@@ -1541,10 +1793,7 @@ setup_reference_table(void)
Process *proc = erts_pix2proc(i);
if (proc) {
int mli;
- ErtsMessage *msg_list[] = {
- proc->msg.first,
- proc->msg_inq.first,
- proc->msg_frag};
+ ErtsMessage *msg_list[] = {proc->msg_frag};
/* Insert Heap */
insert_offheap(&(proc->off_heap),
@@ -1559,34 +1808,24 @@ setup_reference_table(void)
/* Insert msg buffers */
for (mli = 0; mli < sizeof(msg_list)/sizeof(msg_list[0]); mli++) {
ErtsMessage *msg;
- for (msg = msg_list[mli]; msg; msg = msg->next) {
- ErlHeapFragment *heap_frag = NULL;
- if (msg->data.attached) {
- if (msg->data.attached == ERTS_MSG_COMBINED_HFRAG)
- heap_frag = &msg->hfrag;
- else if (is_value(ERL_MESSAGE_TERM(msg)))
- heap_frag = msg->data.heap_frag;
- else {
- if (msg->data.dist_ext->dep)
- insert_dist_entry(msg->data.dist_ext->dep,
- HEAP_REF, proc->common.id, 0);
- if (is_not_nil(ERL_MESSAGE_TOKEN(msg)))
- heap_frag = erts_dist_ext_trailer(msg->data.dist_ext);
- }
- }
- while (heap_frag) {
- insert_offheap(&(heap_frag->off_heap),
- HEAP_REF,
- proc->common.id);
- heap_frag = heap_frag->next;
- }
- }
+ for (msg = msg_list[mli]; msg; msg = msg->next)
+ insert_message(msg, HEAP_REF, proc);
}
+
+ /* Insert signal queue */
+ erts_proc_sig_debug_foreach_sig(proc,
+ insert_sig_msg,
+ insert_sig_offheap,
+ insert_sig_monitor,
+ insert_sig_link,
+ (void *) proc);
+
/* Insert links */
- if (ERTS_P_LINKS(proc))
- insert_links(ERTS_P_LINKS(proc), proc->common.id);
- if (ERTS_P_MONITORS(proc))
- insert_monitors(ERTS_P_MONITORS(proc), proc->common.id);
+ insert_p_links(&proc->common);
+
+ /* Insert monitors */
+ insert_p_monitors(&proc->common);
+
{
DistEntry *dep = ERTS_PROC_GET_DIST_ENTRY(proc);
if (dep)
@@ -1616,11 +1855,9 @@ setup_reference_table(void)
continue;
/* Insert links */
- if (ERTS_P_LINKS(prt))
- insert_links(ERTS_P_LINKS(prt), prt->common.id);
+ insert_p_links(&prt->common);
/* Insert monitors */
- if (ERTS_P_MONITORS(prt))
- insert_monitors(ERTS_P_MONITORS(prt), prt->common.id);
+ insert_p_monitors(&prt->common);
/* Insert port data */
ohp = erts_port_data_offheap(prt);
if (ohp)
@@ -1669,32 +1906,25 @@ setup_reference_table(void)
/* Insert all dist links */
for(dep = erts_visible_dist_entries; dep; dep = dep->next) {
- if(dep->nlinks)
- insert_links2(dep->nlinks, dep->sysname);
- if(dep->node_links)
- insert_links(dep->node_links, dep->sysname);
- if(dep->monitors)
- insert_monitors(dep->monitors, dep->sysname);
+ insert_dist_links(dep);
+ insert_dist_monitors(dep);
}
for(dep = erts_hidden_dist_entries; dep; dep = dep->next) {
- if(dep->nlinks)
- insert_links2(dep->nlinks, dep->sysname);
- if(dep->node_links)
- insert_links(dep->node_links, dep->sysname);
- if(dep->monitors)
- insert_monitors(dep->monitors, dep->sysname);
+ insert_dist_links(dep);
+ insert_dist_monitors(dep);
+ }
+
+ for(dep = erts_pending_dist_entries; dep; dep = dep->next) {
+ insert_dist_links(dep);
+ insert_dist_monitors(dep);
}
/* Not connected dist entries should not have any links,
but inspect them anyway */
for(dep = erts_not_connected_dist_entries; dep; dep = dep->next) {
- if(dep->nlinks)
- insert_links2(dep->nlinks, dep->sysname);
- if(dep->node_links)
- insert_links(dep->node_links, dep->sysname);
- if(dep->monitors)
- insert_monitors(dep->monitors, dep->sysname);
+ insert_dist_links(dep);
+ insert_dist_monitors(dep);
}
/* Insert all ets tables */
@@ -1779,6 +2009,10 @@ reference_table_term(Uint **hpp, ErlOffHeap *ohp, Uint *szp)
tup = MK_2TUP(AM_system, MK_UINT(nrp->system_ref));
nrl = MK_CONS(tup, nrl);
}
+ if(nrp->signal_ref) {
+ tup = MK_2TUP(AM_signal, MK_UINT(nrp->signal_ref));
+ nrl = MK_CONS(tup, nrl);
+ }
nrid = nrp->id;
if (!IS_CONST(nrp->id)) {
@@ -1802,24 +2036,25 @@ reference_table_term(Uint **hpp, ErlOffHeap *ohp, Uint *szp)
}
else if(is_internal_port(nrid)) {
ASSERT(!nrp->heap_ref && !nrp->ets_ref && !nrp->bin_ref
- && !nrp->timer_ref && !nrp->system_ref);
+ && !nrp->timer_ref && !nrp->system_ref && !nrp->signal_ref);
tup = MK_2TUP(AM_port, nrid);
}
else if(nrp->ets_ref) {
ASSERT(!nrp->heap_ref && !nrp->link_ref &&
!nrp->monitor_ref && !nrp->bin_ref
- && !nrp->timer_ref && !nrp->system_ref);
+ && !nrp->timer_ref && !nrp->system_ref && !nrp->signal_ref);
tup = MK_2TUP(AM_ets, nrid);
}
else if(nrp->bin_ref) {
ASSERT(is_small(nrid) || is_big(nrid));
ASSERT(!nrp->heap_ref && !nrp->ets_ref && !nrp->link_ref &&
!nrp->monitor_ref && !nrp->timer_ref
- && !nrp->system_ref);
+ && !nrp->system_ref && !nrp->signal_ref);
tup = MK_2TUP(AM_match_spec, nrid);
}
else {
- ASSERT(!nrp->heap_ref && !nrp->ets_ref && !nrp->bin_ref);
+ ASSERT(!nrp->heap_ref && !nrp->ets_ref && !nrp->bin_ref
+ && !nrp->signal_ref);
ASSERT(is_atom(nrid));
tup = MK_2TUP(AM_dist, nrid);
}
@@ -1855,29 +2090,44 @@ reference_table_term(Uint **hpp, ErlOffHeap *ohp, Uint *szp)
tup = MK_2TUP(AM_heap, MK_UINT(drp->heap_ref));
drl = MK_CONS(tup, drl);
}
+ if(drp->ets_ref) {
+ tup = MK_2TUP(AM_ets, MK_UINT(drp->ets_ref));
+ drl = MK_CONS(tup, drl);
+ }
if(drp->system_ref) {
tup = MK_2TUP(AM_system, MK_UINT(drp->system_ref));
drl = MK_CONS(tup, drl);
}
+ if(drp->signal_ref) {
+ tup = MK_2TUP(AM_signal, MK_UINT(drp->signal_ref));
+ drl = MK_CONS(tup, drl);
+ }
if (is_internal_pid(drp->id)) {
ASSERT(!drp->node_ref);
tup = MK_2TUP(AM_process, drp->id);
}
else if(is_internal_port(drp->id)) {
- ASSERT(drp->ctrl_ref && !drp->node_ref);
+ ASSERT(drp->ctrl_ref && !drp->node_ref && !drp->signal_ref);
tup = MK_2TUP(AM_port, drp->id);
}
else if (is_tuple(drp->id)) {
Eterm *t;
ASSERT(drp->system_ref && !drp->node_ref
- && !drp->ctrl_ref && !drp->heap_ref);
+ && !drp->ctrl_ref && !drp->heap_ref && !drp->ets_ref
+ && !drp->signal_ref);
t = tuple_val(drp->id);
ASSERT(2 == arityval(t[0]));
tup = MK_2TUP(t[1], t[2]);
}
+ else if (drp->ets_ref) {
+ ASSERT(!drp->heap_ref && !drp->node_ref &&
+ !drp->ctrl_ref && !drp->system_ref
+ && !drp->signal_ref);
+ tup = MK_2TUP(AM_ets, drp->id);
+ }
else {
- ASSERT(!drp->ctrl_ref && drp->node_ref);
+ ASSERT(!drp->ctrl_ref && drp->node_ref && !drp->signal_ref);
ASSERT(is_atom(drp->id));
tup = MK_2TUP(drp->id, MK_UINT(drp->creation));
tup = MK_2TUP(AM_node, tup);
@@ -1912,10 +2162,21 @@ reference_table_term(Uint **hpp, ErlOffHeap *ohp, Uint *szp)
}
+static void noop_sig_msg(ErtsMessage *msg, void *arg)
+{
+
+}
+
+static void noop_sig_offheap(ErlOffHeap *oh, void *arg)
+{
+
+}
+
static void
delete_reference_table(void)
{
- Uint i;
+ DistEntry *dep;
+ int i, max;
for(i = 0; i < no_referred_nodes; i++) {
NodeReferrer *nrp;
NodeReferrer *tnrp;
@@ -1947,6 +2208,60 @@ delete_reference_table(void)
inserted_bins = inserted_bins->next;
erts_free(ERTS_ALC_T_NC_TMP, (void *)ib);
}
+
+ /* Cleanup... */
+
+ max = erts_ptab_max(&erts_proc);
+ for (i = 0; i < max; i++) {
+ Process *proc = erts_pix2proc(i);
+ if (proc) {
+ clear_visited_p_links(&proc->common);
+ clear_visited_p_monitors(&proc->common);
+ erts_proc_sig_debug_foreach_sig(proc,
+ noop_sig_msg,
+ noop_sig_offheap,
+ clear_visited_monitor,
+ clear_visited_link,
+ (void *) proc);
+ }
+ }
+
+ max = erts_ptab_max(&erts_port);
+ for (i = 0; i < max; i++) {
+ erts_aint32_t state;
+ Port *prt;
+
+ prt = erts_pix2port(i);
+ if (!prt)
+ continue;
+
+ state = erts_atomic32_read_nob(&prt->state);
+ if (state & ERTS_PORT_SFLGS_DEAD)
+ continue;
+
+ clear_visited_p_links(&prt->common);
+ clear_visited_p_monitors(&prt->common);
+ }
+
+ for(dep = erts_visible_dist_entries; dep; dep = dep->next) {
+ clear_visited_dist_links(dep);
+ clear_visited_dist_monitors(dep);
+ }
+
+ for(dep = erts_hidden_dist_entries; dep; dep = dep->next) {
+ clear_visited_dist_links(dep);
+ clear_visited_dist_monitors(dep);
+ }
+
+ for(dep = erts_pending_dist_entries; dep; dep = dep->next) {
+ clear_visited_dist_links(dep);
+ clear_visited_dist_monitors(dep);
+ }
+
+ for(dep = erts_not_connected_dist_entries; dep; dep = dep->next) {
+ clear_visited_dist_links(dep);
+ clear_visited_dist_monitors(dep);
+ }
}
void
diff --git a/erts/emulator/beam/erl_node_tables.h b/erts/emulator/beam/erl_node_tables.h
index 3bba673435..9a792b10b1 100644
--- a/erts/emulator/beam/erl_node_tables.h
+++ b/erts/emulator/beam/erl_node_tables.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2001-2017. All Rights Reserved.
+ * Copyright Ericsson AB 2001-2018. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,7 +18,17 @@
* %CopyrightEnd%
*/
-#ifndef ERL_NODE_TABLES_H__
+#ifndef ERL_NODE_TABLES_BASIC__
+#define ERL_NODE_TABLES_BASIC__
+
+typedef struct dist_entry_ DistEntry;
+typedef struct ErtsDistOutputBuf_ ErtsDistOutputBuf;
+void erts_ref_dist_entry(DistEntry *dep);
+void erts_deref_dist_entry(DistEntry *dep);
+
+#endif
+
+#if !defined(ERL_NODE_TABLES_BASIC_ONLY) && !defined(ERL_NODE_TABLES_H__)
#define ERL_NODE_TABLES_H__
/*
@@ -42,14 +52,14 @@
#include "sys.h"
#include "hash.h"
#include "erl_alloc.h"
-#include "erl_process.h"
-#include "erl_monitors.h"
#define ERTS_PORT_TASK_ONLY_BASIC_TYPES__
#include "erl_port_task.h"
#undef ERTS_PORT_TASK_ONLY_BASIC_TYPES__
+#include "erl_process.h"
#define ERTS_BINARY_TYPES_ONLY__
#include "erl_binary.h"
#undef ERTS_BINARY_TYPES_ONLY__
+#include "erl_monitor_link.h"
#define ERTS_NODE_TAB_DELAY_GC_DEFAULT (60)
#define ERTS_NODE_TAB_DELAY_GC_MAX (100*1000*1000)
@@ -57,11 +67,12 @@
#define ERST_INTERNAL_CHANNEL_NO 0
-#define ERTS_DE_SFLG_CONNECTED (((Uint32) 1) << 0)
-#define ERTS_DE_SFLG_EXITING (((Uint32) 1) << 1)
-
-#define ERTS_DE_SFLGS_ALL (ERTS_DE_SFLG_CONNECTED \
- | ERTS_DE_SFLG_EXITING)
+enum dist_entry_state {
+ ERTS_DE_STATE_IDLE,
+ ERTS_DE_STATE_PENDING,
+ ERTS_DE_STATE_CONNECTED,
+ ERTS_DE_STATE_EXITING
+};
#define ERTS_DE_QFLG_BUSY (((erts_aint32_t) 1) << 0)
#define ERTS_DE_QFLG_EXIT (((erts_aint32_t) 1) << 1)
@@ -81,14 +92,16 @@
#define ERTS_DIST_OUTPUT_BUF_DBG_PATTERN ((Uint) 0xf713f713)
#endif
-typedef struct ErtsDistOutputBuf_ ErtsDistOutputBuf;
struct ErtsDistOutputBuf_ {
#ifdef DEBUG
Uint dbg_pattern;
+ byte *alloc_endp;
#endif
ErtsDistOutputBuf *next;
+ Uint hopefull_flags;
byte *extp;
byte *ext_endp;
+ byte *msg_start;
byte data[1];
};
@@ -109,9 +122,7 @@ struct ErtsProcList_;
* unlock mutexes with higher numbers before mutexes with higher numbers.
*/
-struct erl_link;
-
-typedef struct dist_entry_ {
+struct dist_entry_ {
HashBucket hash_bucket; /* Hash bucket */
struct dist_entry_ *next; /* Next entry in dist_table (not sorted) */
struct dist_entry_ *prev; /* Previous entry in dist_table (not sorted) */
@@ -123,23 +134,12 @@ typedef struct dist_entry_ {
Eterm cid; /* connection handler (pid or port),
NIL == free */
Uint32 connection_id; /* Connection id incremented on connect */
- Uint32 status; /* Slot status, like exiting reserved etc */
+ enum dist_entry_state state;
Uint32 flags; /* Distribution flags, like hidden,
atom cache etc. */
unsigned long version; /* Protocol version */
-
- erts_mtx_t lnk_mtx; /* Protects node_links, nlinks, and
- monitors. */
- ErtsLink *node_links; /* In a dist entry, node links are kept
- in a separate tree, while they are
- colocted with the ordinary link tree
- for processes. It's not due to confusion,
- it's because the link tree for the dist
- entry is in two levels, see erl_monitors.h
- */
- ErtsLink *nlinks; /* Link tree with subtrees */
- ErtsMonitor *monitors; /* Monitor tree */
+ ErtsMonLnkDist *mld; /* Monitors and links */
erts_mtx_t qlock; /* Protects qflgs and out_queue */
erts_atomic32_t qflgs;
@@ -159,7 +159,9 @@ typedef struct dist_entry_ {
struct cache* cache; /* The atom cache */
ErtsThrPrgrLaterOp later_op;
-} DistEntry;
+
+ struct transcode_context* transcode_ctx;
+};
typedef struct erl_node_ {
HashBucket hash_bucket; /* Hash bucket */
@@ -177,9 +179,11 @@ extern erts_rwmtx_t erts_node_table_rwmtx;
extern DistEntry *erts_hidden_dist_entries;
extern DistEntry *erts_visible_dist_entries;
+extern DistEntry *erts_pending_dist_entries;
extern DistEntry *erts_not_connected_dist_entries;
extern Sint erts_no_of_hidden_dist_entries;
extern Sint erts_no_of_visible_dist_entries;
+extern Sint erts_no_of_pending_dist_entries;
extern Sint erts_no_of_not_connected_dist_entries;
extern DistEntry *erts_this_dist_entry;
@@ -195,6 +199,7 @@ void erts_schedule_delete_dist_entry(DistEntry *);
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);
ErlNode *erts_find_or_insert_node(Eterm, Uint32);
void erts_schedule_delete_node(ErlNode *);
@@ -210,17 +215,14 @@ int erts_lc_is_de_rlocked(DistEntry *);
#endif
int erts_dist_entry_destructor(Binary *bin);
DistEntry *erts_dhandle_to_dist_entry(Eterm dhandle);
+Eterm erts_build_dhandle(Eterm **hpp, ErlOffHeap*, DistEntry*);
Eterm erts_make_dhandle(Process *c_p, DistEntry *dep);
-void erts_ref_dist_entry(DistEntry *dep);
-void erts_deref_dist_entry(DistEntry *dep);
ERTS_GLB_INLINE void erts_deref_node_entry(ErlNode *np);
ERTS_GLB_INLINE void erts_de_rlock(DistEntry *dep);
ERTS_GLB_INLINE void erts_de_runlock(DistEntry *dep);
ERTS_GLB_INLINE void erts_de_rwlock(DistEntry *dep);
ERTS_GLB_INLINE void erts_de_rwunlock(DistEntry *dep);
-ERTS_GLB_INLINE void erts_de_links_lock(DistEntry *dep);
-ERTS_GLB_INLINE void erts_de_links_unlock(DistEntry *dep);
#if ERTS_GLB_INLINE_INCL_FUNC_DEF
@@ -256,20 +258,9 @@ erts_de_rwunlock(DistEntry *dep)
erts_rwmtx_rwunlock(&dep->rwmtx);
}
-ERTS_GLB_INLINE void
-erts_de_links_lock(DistEntry *dep)
-{
- erts_mtx_lock(&dep->lnk_mtx);
-}
-
-ERTS_GLB_INLINE void
-erts_de_links_unlock(DistEntry *dep)
-{
- erts_mtx_unlock(&dep->lnk_mtx);
-}
-
#endif /* #if ERTS_GLB_INLINE_INCL_FUNC_DEF */
void erts_debug_test_node_tab_delayed_delete(Sint64 millisecs);
+void erts_lcnt_update_distribution_locks(int enable);
#endif
diff --git a/erts/emulator/beam/erl_port.h b/erts/emulator/beam/erl_port.h
index 9117eb1f72..2a98a6f00b 100644
--- a/erts/emulator/beam/erl_port.h
+++ b/erts/emulator/beam/erl_port.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2012-2017. All Rights Reserved.
+ * Copyright Ericsson AB 2012-2018. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -180,6 +180,7 @@ void erts_init_port_data(Port *);
void erts_cleanup_port_data(Port *);
Uint erts_port_data_size(Port *);
ErlOffHeap *erts_port_data_offheap(Port *);
+Eterm erts_port_data_read(Port* prt);
#define ERTS_PORT_GET_CONNECTED(PRT) \
((Eterm) erts_atomic_read_nob(&(PRT)->connected))
@@ -195,26 +196,52 @@ struct erl_drv_port_data_lock {
Port *prt;
};
+ERTS_GLB_INLINE void erts_init_runq_port(Port *prt, ErtsRunQueue *runq);
+ERTS_GLB_INLINE void erts_set_runq_port(Port *prt, ErtsRunQueue *runq);
+ERTS_GLB_INLINE ErtsRunQueue *erts_get_runq_port(Port *prt);
ERTS_GLB_INLINE ErtsRunQueue *erts_port_runq(Port *prt);
#if ERTS_GLB_INLINE_INCL_FUNC_DEF
+ERTS_GLB_INLINE void
+erts_init_runq_port(Port *prt, ErtsRunQueue *runq)
+{
+ if (!runq)
+ ERTS_INTERNAL_ERROR("Missing run-queue");
+ erts_atomic_init_nob(&prt->run_queue, (erts_aint_t) runq);
+}
+
+ERTS_GLB_INLINE void
+erts_set_runq_port(Port *prt, ErtsRunQueue *runq)
+{
+ if (!runq)
+ ERTS_INTERNAL_ERROR("Missing run-queue");
+ erts_atomic_set_nob(&prt->run_queue, (erts_aint_t) runq);
+}
+
+ERTS_GLB_INLINE ErtsRunQueue *
+erts_get_runq_port(Port *prt)
+{
+ ErtsRunQueue *runq;
+ runq = (ErtsRunQueue *) erts_atomic_read_nob(&prt->run_queue);
+ if (!runq)
+ ERTS_INTERNAL_ERROR("Missing run-queue");
+ return runq;
+}
+
+
ERTS_GLB_INLINE ErtsRunQueue *
erts_port_runq(Port *prt)
{
ErtsRunQueue *rq1, *rq2;
- rq1 = (ErtsRunQueue *) erts_atomic_read_nob(&prt->run_queue);
- if (!rq1)
- return NULL;
+ rq1 = erts_get_runq_port(prt);
while (1) {
erts_runq_lock(rq1);
- rq2 = (ErtsRunQueue *) erts_atomic_read_nob(&prt->run_queue);
+ rq2 = erts_get_runq_port(prt);
if (rq1 == rq2)
return rq1;
erts_runq_unlock(rq1);
rq1 = rq2;
- if (!rq1)
- return NULL;
}
}
@@ -346,7 +373,7 @@ Eterm erts_request_io_bytes(Process *c_p);
void print_port_info(Port *, fmtfn_t, void *);
void erts_port_free(Port *);
-void erts_fire_port_monitor(Port *prt, Eterm ref);
+void erts_fire_port_monitor(Port *prt, ErtsMonitor *tmon);
int erts_port_handle_xports(Port *);
#if defined(ERTS_ENABLE_LOCK_CHECK)
@@ -860,20 +887,20 @@ struct ErtsProc2PortSigData_ {
Eterm item;
} info;
struct {
- Eterm port;
- Eterm to;
+ Eterm port_id;
+ ErtsLink *lnk;
} link;
struct {
- Eterm from;
+ Eterm port_id;
+ ErtsLink *lnk;
} unlink;
struct {
- Eterm origin; /* who receives monitor event, pid */
- Eterm name; /* either name for named monitor, or port id */
+ Eterm port_id;
+ ErtsMonitor *mon;
} monitor;
struct {
- Eterm origin; /* who is at the other end of the monitor, pid */
- Eterm name; /* port id */
- Uint32 ref[ERTS_MAX_REF_NUMBERS]; /* box contents of a ref */
+ Eterm port_id;
+ ErtsMonitor *mon;
} demonitor;
} u;
} ;
@@ -919,7 +946,6 @@ typedef int (*ErtsProc2PortSigCallback)(Port *,
typedef enum {
ERTS_PORT_OP_BADARG,
- ERTS_PORT_OP_CALLER_EXIT,
ERTS_PORT_OP_BUSY,
ERTS_PORT_OP_BUSY_SCHEDULED,
ERTS_PORT_OP_SCHEDULED,
@@ -955,32 +981,13 @@ ErtsPortOpResult erts_port_command(Process *, int, Port *, Eterm, Eterm *);
ErtsPortOpResult erts_port_output(Process *, int, Port *, Eterm, Eterm, Eterm *);
ErtsPortOpResult erts_port_exit(Process *, int, Port *, Eterm, Eterm, Eterm *);
ErtsPortOpResult erts_port_connect(Process *, int, Port *, Eterm, Eterm, Eterm *);
-ErtsPortOpResult erts_port_link(Process *, Port *, Eterm, Eterm *);
-ErtsPortOpResult erts_port_unlink(Process *, Port *, Eterm, Eterm *);
+ErtsPortOpResult erts_port_link(Process *, Port *, ErtsLink *, Eterm *);
+ErtsPortOpResult erts_port_unlink(Process *, Port *, ErtsLink *, Eterm *);
ErtsPortOpResult erts_port_control(Process *, Port *, unsigned int, Eterm, Eterm *);
ErtsPortOpResult erts_port_call(Process *, Port *, unsigned int, Eterm, Eterm *);
ErtsPortOpResult erts_port_info(Process *, Port *, Eterm, Eterm *);
-
-/* Creates monitor between Origin and Target. Ref must be initialized to
- * a reference (ref may be rewritten to be used to serve additionally as a
- * signal id). Name is atom if user monitors port by name or NIL */
-ErtsPortOpResult erts_port_monitor(Process *origin, Port *target, Eterm name,
- Eterm *ref);
-
-typedef enum {
- /* Normal demonitor rules apply with locking and reductions bump */
- ERTS_PORT_DEMONITOR_NORMAL = 1,
- /* Relaxed demonitor rules when process is about to die, which means that
- * pid lookup won't work, locks won't work, no reductions bump. */
- ERTS_PORT_DEMONITOR_ORIGIN_ON_DEATHBED = 2,
-} ErtsDemonitorMode;
-
-/* Removes monitor between origin and target, identified by ref.
- * origin_is_dying can be 0 (false, normal locking rules and reductions bump
- * apply) or 1 (true, in case when we avoid origin locking) */
-ErtsPortOpResult erts_port_demonitor(Process *origin, ErtsDemonitorMode mode,
- Port *target, Eterm ref,
- Eterm *trap_ref);
+ErtsPortOpResult erts_port_monitor(Process *, Port *, ErtsMonitor *);
+ErtsPortOpResult erts_port_demonitor(Process *, Port *, ErtsMonitor *);
/* defined in erl_bif_port.c */
Port *erts_sig_lookup_port(Process *c_p, Eterm id_or_name);
diff --git a/erts/emulator/beam/erl_port_task.c b/erts/emulator/beam/erl_port_task.c
index a588477320..4a3671df0c 100644
--- a/erts/emulator/beam/erl_port_task.c
+++ b/erts/emulator/beam/erl_port_task.c
@@ -84,11 +84,10 @@ static void chk_task_queues(Port *pp, ErtsPortTask *execq, int processing_busy_q
#define LTTNG_DRIVER(TRACEPOINT, PP) do {} while(0)
#endif
-#define ERTS_LC_VERIFY_RQ(RQ, PP) \
- do { \
+#define ERTS_LC_VERIFY_RQ(RQ, PP) \
+ do { \
ERTS_LC_ASSERT(erts_lc_runq_is_locked(runq)); \
- ERTS_LC_ASSERT((RQ) == ((ErtsRunQueue *) \
- erts_atomic_read_nob(&(PP)->run_queue))); \
+ ERTS_LC_ASSERT((RQ) == erts_get_runq_port((PP))); \
} while (0)
#define ERTS_PT_STATE_SCHEDULED 0
@@ -1520,19 +1519,15 @@ erts_port_task_schedule(Eterm id,
/* Enqueue port on run-queue */
runq = erts_port_runq(pp);
- if (!runq)
- ERTS_INTERNAL_ERROR("Missing run-queue");
xrunq = erts_check_emigration_need(runq, ERTS_PORT_PRIO_LEVEL);
ERTS_LC_ASSERT(runq != xrunq);
ERTS_LC_VERIFY_RQ(runq, pp);
if (xrunq) {
/* Emigrate port ... */
- erts_atomic_set_nob(&pp->run_queue, (erts_aint_t) xrunq);
+ erts_set_runq_port(pp, xrunq);
erts_runq_unlock(runq);
runq = erts_port_runq(pp);
- if (!runq)
- ERTS_INTERNAL_ERROR("Missing run-queue");
}
enqueue_port(runq, pp);
@@ -1593,8 +1588,6 @@ erts_port_task_free_port(Port *pp)
ASSERT(!(erts_atomic32_read_nob(&pp->state) & ERTS_PORT_SFLGS_DEAD));
runq = erts_port_runq(pp);
- if (!runq)
- ERTS_INTERNAL_ERROR("Missing run-queue");
erts_port_task_sched_lock(&pp->sched);
flags = erts_atomic32_read_bor_relb(&pp->sched.flags,
ERTS_PTS_FLG_EXIT);
@@ -1805,7 +1798,7 @@ erts_port_task_execute(ErtsRunQueue *runq, Port **curr_port_pp)
erts_unblock_fpe(fpe_was_unmasked);
ERTS_MSACC_POP_STATE_M();
- ASSERT(runq == (ErtsRunQueue *) erts_atomic_read_nob(&pp->run_queue));
+ ASSERT(runq == erts_get_runq_port(pp));
active = finalize_exec(pp, &execq, processing_busy_q);
@@ -1831,11 +1824,10 @@ erts_port_task_execute(ErtsRunQueue *runq, Port **curr_port_pp)
}
else {
/* Emigrate port... */
- erts_atomic_set_nob(&pp->run_queue, (erts_aint_t) xrunq);
+ erts_set_runq_port(pp, xrunq);
erts_runq_unlock(runq);
xrunq = erts_port_runq(pp);
- ASSERT(xrunq);
enqueue_port(xrunq, pp);
erts_runq_unlock(xrunq);
erts_notify_inc_runq(xrunq);
@@ -2069,7 +2061,7 @@ void
erts_enqueue_port(ErtsRunQueue *rq, Port *pp)
{
ERTS_LC_ASSERT(erts_lc_runq_is_locked(rq));
- ASSERT(rq == (ErtsRunQueue *) erts_atomic_read_nob(&pp->run_queue));
+ ASSERT(rq == erts_get_runq_port(pp));
ASSERT(erts_atomic32_read_nob(&pp->sched.flags) & ERTS_PTS_FLG_IN_RUNQ);
enqueue_port(rq, pp);
}
@@ -2080,8 +2072,7 @@ erts_dequeue_port(ErtsRunQueue *rq)
Port *pp;
ERTS_LC_ASSERT(erts_lc_runq_is_locked(rq));
pp = pop_port(rq);
- ASSERT(!pp
- || rq == (ErtsRunQueue *) erts_atomic_read_nob(&pp->run_queue));
+ ASSERT(!pp || rq == erts_get_runq_port(pp));
ASSERT(!pp || (erts_atomic32_read_nob(&pp->sched.flags)
& ERTS_PTS_FLG_IN_RUNQ));
return pp;
diff --git a/erts/emulator/beam/erl_posix_str.c b/erts/emulator/beam/erl_posix_str.c
index deb7e3e173..7b3e640d3f 100644
--- a/erts/emulator/beam/erl_posix_str.c
+++ b/erts/emulator/beam/erl_posix_str.c
@@ -156,6 +156,9 @@ erl_errno_id(error)
#ifdef EFAULT
case EFAULT: return "efault";
#endif
+#ifdef EFTYPE
+ case EFTYPE: return "eftype";
+#endif
#ifdef EFBIG
case EFBIG: return "efbig";
#endif
@@ -351,6 +354,9 @@ erl_errno_id(error)
#if defined(EOPNOTSUPP) && (!defined(ENOTSUP) || (EOPNOTSUPP != ENOTSUP))
case EOPNOTSUPP: return "eopnotsupp";
#endif
+#ifdef EOVERFLOW
+ case EOVERFLOW: return "eoverflow";
+#endif
#ifdef EPERM
case EPERM: return "eperm";
#endif
diff --git a/erts/emulator/beam/erl_printf_term.c b/erts/emulator/beam/erl_printf_term.c
index e6f8460164..910f241a3a 100644
--- a/erts/emulator/beam/erl_printf_term.c
+++ b/erts/emulator/beam/erl_printf_term.c
@@ -532,14 +532,13 @@ print_term(fmtfn_t fn, void* arg, Eterm obj, long *dcount) {
Atom* module = atom_tab(atom_val(ep->info.mfa.module));
Atom* name = atom_tab(atom_val(ep->info.mfa.function));
- PRINT_STRING(res, fn, arg, "#Fun<");
+ PRINT_STRING(res, fn, arg, "fun ");
PRINT_BUF(res, fn, arg, module->name, module->len);
- PRINT_CHAR(res, fn, arg, '.');
+ PRINT_CHAR(res, fn, arg, ':');
PRINT_BUF(res, fn, arg, name->name, name->len);
- PRINT_CHAR(res, fn, arg, '.');
+ PRINT_CHAR(res, fn, arg, '/');
PRINT_SWORD(res, fn, arg, 'd', 0, 1,
(ErlPfSWord) ep->info.mfa.arity);
- PRINT_CHAR(res, fn, arg, '>');
}
break;
case FUN_DEF:
diff --git a/erts/emulator/beam/erl_proc_sig_queue.c b/erts/emulator/beam/erl_proc_sig_queue.c
new file mode 100644
index 0000000000..bcc4fc6d9b
--- /dev/null
+++ b/erts/emulator/beam/erl_proc_sig_queue.c
@@ -0,0 +1,4274 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2018. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * %CopyrightEnd%
+ */
+
+/*
+ * Description: Process signal queue implementation.
+ *
+ * Author: Rickard Green
+ */
+
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "sys.h"
+#include "global.h"
+#include "dist.h"
+#include "erl_process.h"
+#include "erl_port_task.h"
+#include "erl_trace.h"
+#include "beam_bp.h"
+#include "big.h"
+#include "erl_gc.h"
+#include "bif.h"
+#include "erl_proc_sig_queue.h"
+#include "dtrace-wrapper.h"
+
+#define ERTS_SIG_REDS_CNT_FACTOR 4
+#define ERTS_PROC_SIG_TRACE_COUNT_LIMIT 200
+
+/*
+ * Note that not all signal are handled using this functionality!
+ */
+
+#define ERTS_SIG_Q_OP_MAX 11
+
+#define ERTS_SIG_Q_OP_EXIT 0
+#define ERTS_SIG_Q_OP_EXIT_LINKED 1
+#define ERTS_SIG_Q_OP_MONITOR_DOWN 2
+#define ERTS_SIG_Q_OP_MONITOR 3
+#define ERTS_SIG_Q_OP_DEMONITOR 4
+#define ERTS_SIG_Q_OP_LINK 5
+#define ERTS_SIG_Q_OP_UNLINK 6
+#define ERTS_SIG_Q_OP_GROUP_LEADER 7
+#define ERTS_SIG_Q_OP_TRACE_CHANGE_STATE 8
+#define ERTS_SIG_Q_OP_PERSISTENT_MON_MSG 9
+#define ERTS_SIG_Q_OP_IS_ALIVE 10
+#define ERTS_SIG_Q_OP_PROCESS_INFO ERTS_SIG_Q_OP_MAX
+
+#define ERTS_SIG_Q_TYPE_MAX (ERTS_MON_LNK_TYPE_MAX + 5)
+
+#define ERTS_SIG_Q_TYPE_UNDEFINED \
+ (ERTS_MON_LNK_TYPE_MAX + 1)
+#define ERTS_SIG_Q_TYPE_DIST_LINK \
+ (ERTS_MON_LNK_TYPE_MAX + 2)
+#define ERTS_SIG_Q_TYPE_GEN_EXIT \
+ (ERTS_MON_LNK_TYPE_MAX + 3)
+#define ERTS_SIG_Q_TYPE_DIST_PROC_DEMONITOR \
+ (ERTS_MON_LNK_TYPE_MAX + 4)
+#define ERTS_SIG_Q_TYPE_ADJUST_TRACE_INFO \
+ ERTS_SIG_Q_TYPE_MAX
+
+Process *ERTS_WRITE_UNLIKELY(erts_dirty_process_signal_handler);
+Process *ERTS_WRITE_UNLIKELY(erts_dirty_process_signal_handler_high);
+Process *ERTS_WRITE_UNLIKELY(erts_dirty_process_signal_handler_max);
+
+void
+erts_proc_sig_queue_init(void)
+{
+ ERTS_CT_ASSERT(ERTS_SIG_Q_OP_MASK > ERTS_SIG_Q_OP_MAX);
+ ERTS_CT_ASSERT(ERTS_SIG_Q_OP_MSGQ_LEN_OFFS_MARK > ERTS_SIG_Q_OP_MAX);
+ ERTS_CT_ASSERT(ERTS_SIG_Q_TYPE_MASK >= ERTS_SIG_Q_TYPE_MAX);
+}
+
+typedef struct {
+ int active;
+ int procs;
+ struct {
+ int active;
+#if defined(USE_VM_PROBES)
+ int vm_probes;
+ char receiver_name[DTRACE_TERM_BUF_SIZE];
+#endif
+ int receive_trace;
+ int bp_ix;
+ ErtsMessage **next;
+ ErtsTracingEvent *event;
+ } messages;
+} ErtsSigRecvTracing;
+
+typedef struct {
+ Eterm message;
+ Eterm from;
+ Eterm reason;
+ union {
+ Eterm ref;
+ int normal_kills;
+ } u;
+} ErtsExitSignalData;
+
+typedef struct {
+ Eterm message;
+ Eterm key;
+} ErtsPersistMonMsg;
+
+typedef struct {
+ ErtsSignalCommon common;
+ Eterm local; /* internal pid (immediate) */
+ Eterm remote; /* external pid (heap for it follow) */
+ Eterm heap[EXTERNAL_THING_HEAD_SIZE + 1];
+} ErtsSigDistLinkOp;
+
+typedef struct {
+ ErtsSignalCommon common;
+ Uint flags_on;
+ Uint flags_off;
+ Eterm tracer;
+} ErtsSigTraceInfo;
+
+#define ERTS_SIG_GL_FLG_ACTIVE (((erts_aint_t) 1) << 0)
+#define ERTS_SIG_GL_FLG_RECEIVER (((erts_aint_t) 1) << 1)
+#define ERTS_SIG_GL_FLG_SENDER (((erts_aint_t) 1) << 2)
+
+typedef struct {
+ ErtsSignalCommon common;
+ erts_atomic_t flags;
+ Eterm group_leader;
+ Eterm reply_to;
+ Eterm ref;
+ ErlOffHeap oh;
+ Eterm heap[1];
+} ErtsSigGroupLeader;
+
+typedef struct {
+ Eterm message;
+ Eterm requester;
+} ErtsIsAliveRequest;
+
+typedef struct {
+ ErtsSignalCommon common;
+ Sint refc;
+ Sint delayed_len;
+ Sint len_offset;
+} ErtsProcSigMsgQLenOffsetMarker;
+
+typedef struct {
+ ErtsSignalCommon common;
+ ErtsProcSigMsgQLenOffsetMarker marker;
+ Sint msgq_len_offset;
+ Eterm requester;
+ Eterm ref;
+ ErtsORefThing oref_thing;
+ Uint reserve_size;
+ Uint len;
+ int flags;
+ int item_ix[1]; /* of len size in reality... */
+} ErtsProcessInfoSig;
+
+#define ERTS_PROC_SIG_PI_MSGQ_LEN_IGNORE ((Sint) -1)
+#define ERTS_PROC_SIG_PI_MSGQ_LEN_SYNC ((Sint) -2)
+
+static int handle_msg_tracing(Process *c_p,
+ ErtsSigRecvTracing *tracing,
+ ErtsMessage ***next_nm_sig);
+static int handle_trace_change_state(Process *c_p,
+ ErtsSigRecvTracing *tracing,
+ Uint16 type,
+ ErtsMessage *sig,
+ ErtsMessage ***next_nm_sig);
+static void getting_unlinked(Process *c_p, Eterm unlinker);
+static void getting_linked(Process *c_p, Eterm linker);
+static void group_leader_reply(Process *c_p, Eterm to,
+ Eterm ref, int success);
+static int stretch_limit(Process *c_p, ErtsSigRecvTracing *tp,
+ int abs_lim, int *limp);
+
+#ifdef ERTS_PROC_SIG_HARD_DEBUG
+#define ERTS_PROC_SIG_HDBG_PRIV_CHKQ(P, T, NMN) \
+ do { \
+ ErtsMessage **nm_next__ = *(NMN); \
+ ErtsMessage **nm_last__ = (P)->sig_qs.nmsigs.last; \
+ if (!nm_next__ || !*nm_next__) { \
+ nm_next__ = NULL; \
+ nm_last__ = NULL; \
+ } \
+ proc_sig_hdbg_check_queue((P), \
+ 1, \
+ &(P)->sig_qs.cont, \
+ (P)->sig_qs.cont_last, \
+ nm_next__, \
+ nm_last__, \
+ (T), \
+ NULL, \
+ ERTS_PSFLG_FREE); \
+ } while (0);
+static Sint
+proc_sig_hdbg_check_queue(Process *c_p,
+ int privq,
+ ErtsMessage **sig_next,
+ ErtsMessage **sig_last,
+ ErtsMessage **sig_nm_next,
+ ErtsMessage **sig_nm_last,
+ ErtsSigRecvTracing *tracing,
+ int *found_saved_last_p,
+ erts_aint32_t sig_psflg);
+#else
+#define ERTS_PROC_SIG_HDBG_PRIV_CHKQ(P, T, NMN)
+#endif
+
+typedef struct {
+ ErtsSignalCommon common;
+ Eterm ref;
+ Eterm heap[1];
+} ErtsSigDistProcDemonitor;
+
+static void
+destroy_dist_proc_demonitor(ErtsSigDistProcDemonitor *dmon)
+{
+ Eterm ref = dmon->ref;
+ if (is_external(ref)) {
+ ExternalThing *etp = external_thing_ptr(ref);
+ erts_deref_node_entry(etp->node);
+ }
+ erts_free(ERTS_ALC_T_DIST_DEMONITOR, dmon);
+}
+
+static ERTS_INLINE ErtsSigDistLinkOp *
+make_sig_dist_link_op(int op, Eterm local, Eterm remote)
+{
+ Eterm *hp;
+ ErlOffHeap oh = {0};
+ ErtsSigDistLinkOp *sdlnk = erts_alloc(ERTS_ALC_T_SIG_DATA,
+ sizeof(ErtsSigDistLinkOp));
+ ASSERT(is_internal_pid(local));
+ ASSERT(is_external_pid(remote));
+
+ hp = &sdlnk->heap[0];
+
+ sdlnk->common.tag = ERTS_PROC_SIG_MAKE_TAG(op,
+ ERTS_SIG_Q_TYPE_DIST_LINK,
+ 0);
+ sdlnk->local = local;
+ sdlnk->remote = STORE_NC(&hp, &oh, remote);
+
+ ASSERT(&sdlnk->heap[0] < hp);
+ ASSERT(hp <= &sdlnk->heap[0] + sizeof(sdlnk->heap)/sizeof(sdlnk->heap[0]));
+ ASSERT(boxed_val(sdlnk->remote) == &sdlnk->heap[0]);
+
+ return sdlnk;
+}
+
+static ERTS_INLINE void
+destroy_sig_dist_link_op(ErtsSigDistLinkOp *sdlnk)
+{
+ ASSERT(is_external_pid(sdlnk->remote));
+ ASSERT(boxed_val(sdlnk->remote) == &sdlnk->heap[0]);
+ erts_deref_node_entry(((ExternalThing *) &sdlnk->heap[0])->node);
+ erts_free(ERTS_ALC_T_SIG_DATA, sdlnk);
+}
+
+static ERTS_INLINE ErtsExitSignalData *
+get_exit_signal_data(ErtsMessage *xsig)
+{
+ ASSERT(ERTS_SIG_IS_NON_MSG(xsig));
+ ASSERT((ERTS_PROC_SIG_OP(((ErtsSignal *) xsig)->common.tag)
+ == ERTS_SIG_Q_OP_EXIT)
+ || (ERTS_PROC_SIG_OP(((ErtsSignal *) xsig)->common.tag)
+ == ERTS_SIG_Q_OP_EXIT_LINKED)
+ || (ERTS_PROC_SIG_OP(((ErtsSignal *) xsig)->common.tag)
+ == ERTS_SIG_Q_OP_MONITOR_DOWN));
+ ASSERT(xsig->hfrag.alloc_size > xsig->hfrag.used_size);
+ ASSERT((xsig->hfrag.alloc_size - xsig->hfrag.used_size)*sizeof(UWord)
+ >= sizeof(ErtsExitSignalData));
+ return (ErtsExitSignalData *) (char *) (&xsig->hfrag.mem[0]
+ + xsig->hfrag.used_size);
+}
+
+static ERTS_INLINE void
+destroy_trace_info(ErtsSigTraceInfo *ti)
+{
+ if (is_value(ti->tracer))
+ erts_tracer_update(&ti->tracer, NIL);
+ erts_free(ERTS_ALC_T_SIG_DATA, ti);
+}
+
+static void
+destroy_sig_group_leader(ErtsSigGroupLeader *sgl)
+{
+ erts_cleanup_offheap(&sgl->oh);
+ erts_free(ERTS_ALC_T_SIG_DATA, sgl);
+}
+
+static ERTS_INLINE void
+sig_enqueue_trace(Process *c_p, ErtsMessage *sig, int op,
+ Process *rp, ErtsMessage **first,
+ ErtsMessage **last, ErtsMessage ***last_next)
+{
+ switch (op) {
+ case ERTS_SIG_Q_OP_LINK:
+ if (c_p
+ && ((!!IS_TRACED(c_p))
+ & (ERTS_TRACE_FLAGS(c_p) & (F_TRACE_SOL
+ | F_TRACE_SOL1)))) {
+ ErtsSigTraceInfo *ti;
+ Eterm tag;
+ /*
+ * Set on link enabled.
+ *
+ * Prepend a trace-change-state signal before the
+ * link signal...
+ */
+
+ tag = ERTS_PROC_SIG_MAKE_TAG(ERTS_SIG_Q_OP_TRACE_CHANGE_STATE,
+ ERTS_SIG_Q_TYPE_ADJUST_TRACE_INFO,
+ 0);
+ ti = erts_alloc(ERTS_ALC_T_SIG_DATA, sizeof(ErtsSigTraceInfo));
+ ti->common.next = *last;
+ ti->common.specific.next = &ti->common.next;
+ ti->common.tag = tag;
+ ti->flags_on = ERTS_TRACE_FLAGS(c_p) & TRACEE_FLAGS;
+ if (!(ti->flags_on & F_TRACE_SOL1))
+ ti->flags_off = 0;
+ else {
+ ti->flags_off = F_TRACE_SOL1|F_TRACE_SOL;
+ erts_proc_lock(c_p, ERTS_PROC_LOCKS_ALL_MINOR);
+ ERTS_TRACE_FLAGS(c_p) &= ~(F_TRACE_SOL1|F_TRACE_SOL);
+ erts_proc_unlock(c_p, ERTS_PROC_LOCKS_ALL_MINOR);
+ }
+ erts_tracer_update(&ti->tracer, ERTS_TRACER(c_p));
+ *first = (ErtsMessage *) ti;
+ *last_next = &ti->common.next;
+ }
+ break;
+
+#ifdef USE_VM_PROBES
+ case ERTS_SIG_Q_OP_EXIT:
+ case ERTS_SIG_Q_OP_EXIT_LINKED:
+
+ if (DTRACE_ENABLED(process_exit_signal)) {
+ Uint16 type = ERTS_PROC_SIG_TYPE(((ErtsSignal *) sig)->common.tag);
+ Eterm reason, from;
+
+ if (type == ERTS_SIG_Q_TYPE_GEN_EXIT) {
+ ErtsExitSignalData *xsigd = get_exit_signal_data(sig);
+ reason = xsigd->reason;
+ from = xsigd->from;
+ }
+ else {
+ ErtsLink *lnk = (ErtsLink *) sig, *olnk;
+
+ ASSERT(type == ERTS_LNK_TYPE_PROC
+ || type == ERTS_LNK_TYPE_PORT
+ || type == ERTS_LNK_TYPE_DIST_PROC);
+
+ olnk = erts_link_to_other(lnk, NULL);
+ reason = lnk->other.item;
+ from = olnk->other.item;
+ }
+
+ if (is_pid(from)) {
+
+ DTRACE_CHARBUF(sender_str, DTRACE_TERM_BUF_SIZE);
+ DTRACE_CHARBUF(receiver_str, DTRACE_TERM_BUF_SIZE);
+ DTRACE_CHARBUF(reason_buf, DTRACE_TERM_BUF_SIZE);
+
+ if (reason == am_kill) {
+ reason = am_killed;
+ }
+
+ dtrace_pid_str(from, sender_str);
+ dtrace_proc_str(rp, receiver_str);
+ erts_snprintf(reason_buf, sizeof(DTRACE_CHARBUF_NAME(reason_buf)) - 1, "%T", reason);
+ DTRACE3(process_exit_signal, sender_str, receiver_str, reason_buf);
+ }
+ }
+ break;
+
+#endif
+
+ default:
+ break;
+ }
+}
+
+static void
+sig_enqueue_trace_cleanup(ErtsMessage *first, ErtsSignal *sig, ErtsMessage *last)
+{
+ ErtsMessage *tmp;
+
+ /* The usual case; no tracing signals... */
+ if (sig == (ErtsSignal *) first && sig == (ErtsSignal *) last) {
+ sig->common.next = NULL;
+ return;
+ }
+
+ /* Got trace signals to clean up... */
+
+ tmp = first;
+
+ while (tmp) {
+ ErtsMessage *tmp_free = tmp;
+ tmp = tmp->next;
+ if (sig != (ErtsSignal *) tmp_free) {
+ switch (ERTS_PROC_SIG_OP(((ErtsSignal *) tmp_free)->common.tag)) {
+ case ERTS_SIG_Q_OP_TRACE_CHANGE_STATE:
+ destroy_trace_info((ErtsSigTraceInfo *) tmp_free);
+ break;
+ default:
+ ERTS_INTERNAL_ERROR("Unexpected signal op");
+ break;
+ }
+ }
+ }
+}
+
+static ERTS_INLINE erts_aint32_t
+enqueue_signals(Process *rp, ErtsMessage *first,
+ ErtsMessage *last, ErtsMessage **last_next,
+ erts_aint32_t in_state)
+{
+ erts_aint32_t state = in_state;
+ ErtsMessage **this = rp->sig_inq.last;
+
+ ERTS_HDBG_CHECK_SIGNAL_IN_QUEUE(rp);
+
+ ASSERT(!*this);
+ *this = first;
+ rp->sig_inq.last = &last->next;
+
+ if (!rp->sig_inq.nmsigs.next) {
+ ASSERT(!rp->sig_inq.nmsigs.last);
+ rp->sig_inq.nmsigs.next = this;
+ state = erts_atomic32_read_bor_nob(&rp->state,
+ ERTS_PSFLG_SIG_IN_Q);
+ ASSERT(!(state & ERTS_PSFLG_SIG_IN_Q));
+ }
+ else {
+ ErtsSignal *sig;
+ ASSERT(rp->sig_inq.nmsigs.last);
+
+ sig = (ErtsSignal *) *rp->sig_inq.nmsigs.last;
+
+ ASSERT(sig && !sig->common.specific.next);
+ ASSERT(state & ERTS_PSFLG_SIG_IN_Q);
+ sig->common.specific.next = this;
+ }
+
+ if (last_next) {
+ ASSERT(first != last);
+ rp->sig_inq.nmsigs.last = last_next;
+ }
+ else {
+ ASSERT(first == last);
+ rp->sig_inq.nmsigs.last = this;
+ }
+
+ ERTS_HDBG_CHECK_SIGNAL_IN_QUEUE(rp);
+
+ return state;
+}
+
+static ERTS_INLINE void
+ensure_dirty_proc_handled(Eterm pid,
+ erts_aint32_t state,
+ erts_aint32_t prio)
+{
+ if (state & (ERTS_PSFLG_DIRTY_RUNNING
+ | ERTS_PSFLG_DIRTY_RUNNING_SYS)) {
+ Eterm *hp;
+ ErtsMessage *mp;
+ Process *sig_handler;
+
+ if (prio < 0)
+ prio = (int) ERTS_PSFLGS_GET_USR_PRIO(state);
+
+ switch (prio) {
+ case PRIORITY_MAX:
+ sig_handler = erts_dirty_process_signal_handler_max;
+ break;
+ case PRIORITY_HIGH:
+ sig_handler = erts_dirty_process_signal_handler_high;
+ break;
+ default:
+ sig_handler = erts_dirty_process_signal_handler;
+ break;
+ }
+
+ /* Make sure signals are handled... */
+ mp = erts_alloc_message(0, &hp);
+ erts_queue_message(sig_handler, 0, mp, pid, am_system);
+ }
+}
+
+static void
+check_push_msgq_len_offs_marker(Process *rp, ErtsSignal *sig);
+
+static int
+proc_queue_signal(Process *c_p, Eterm pid, ErtsSignal *sig, int op)
+{
+ int res;
+ Process *rp;
+ ErtsMessage *first, *last, **last_next;
+ ErtsSchedulerData *esdp = erts_get_scheduler_data();
+ int is_normal_sched = !!esdp && esdp->type == ERTS_SCHED_NORMAL;
+ erts_aint32_t state;
+
+ if (is_normal_sched)
+ rp = erts_proc_lookup_raw(pid);
+ else
+ rp = erts_proc_lookup_raw_inc_refc(pid);
+
+ if (!rp)
+ return 0;
+
+ sig->common.specific.next = NULL;
+ first = last = (ErtsMessage *) sig;
+ last_next = NULL;
+
+ /* may add signals before and/or after sig */
+ sig_enqueue_trace(c_p, first, op, rp,
+ &first, &last, &last_next);
+
+ last->next = NULL;
+
+ erts_proc_lock(rp, ERTS_PROC_LOCK_MSGQ);
+
+ state = erts_atomic32_read_nob(&rp->state);
+
+ if (ERTS_PSFLG_FREE & state)
+ res = 0;
+ else {
+ state = enqueue_signals(rp, first, last, last_next, state);
+ if (ERTS_UNLIKELY(op == ERTS_SIG_Q_OP_PROCESS_INFO))
+ check_push_msgq_len_offs_marker(rp, sig);
+ res = !0;
+ }
+
+ erts_proc_unlock(rp, ERTS_PROC_LOCK_MSGQ);
+
+ if (res == 0)
+ sig_enqueue_trace_cleanup(first, sig, last);
+
+ if (!(state & (ERTS_PSFLG_EXITING
+ | ERTS_PSFLG_ACTIVE_SYS
+ | ERTS_PSFLG_SIG_IN_Q))) {
+ /* Schedule process... */
+ state = erts_proc_sys_schedule(rp, state, 0);
+ }
+
+ ensure_dirty_proc_handled(rp->common.id, state, -1);
+
+ if (!is_normal_sched)
+ erts_proc_dec_refc(rp);
+
+ return res;
+}
+
+static int
+maybe_elevate_sig_handling_prio(Process *c_p, Eterm other)
+{
+ /*
+ * returns:
+ * > 0 -> elevated prio; process alive or exiting
+ * < 0 -> no elevation needed; process alive or exiting
+ * 0 -> process terminated (free)
+ */
+ int res;
+ Process *rp;
+ erts_aint32_t state, my_prio, other_prio;
+
+ rp = erts_proc_lookup_raw(other);
+ if (!rp)
+ res = 0;
+ else {
+ res = -1;
+ state = erts_atomic32_read_nob(&c_p->state);
+ my_prio = ERTS_PSFLGS_GET_USR_PRIO(state);
+
+ state = erts_atomic32_read_nob(&rp->state);
+ other_prio = ERTS_PSFLGS_GET_USR_PRIO(state);
+
+ if (other_prio > my_prio) {
+ /* Others prio is lower than mine; elevate it... */
+ res = !!erts_sig_prio(other, my_prio);
+ if (res) {
+ /* ensure handled if dirty executing... */
+ state = erts_atomic32_read_nob(&rp->state);
+ ensure_dirty_proc_handled(other, state, my_prio);
+ }
+ }
+ }
+ return res;
+}
+
+void
+erts_proc_sig_fetch__(Process *proc)
+{
+#ifdef ERTS_PROC_SIG_HARD_DEBUG
+ ErtsSignalPrivQueues sig_qs = proc->sig_qs;
+ ErtsSignalInQueue sig_inq = proc->sig_inq;
+#endif
+
+ ASSERT(proc->sig_inq.first);
+
+ if (!proc->sig_inq.nmsigs.next) {
+ ASSERT(!(ERTS_PSFLG_SIG_IN_Q
+ & erts_atomic32_read_nob(&proc->state)));
+ ASSERT(!proc->sig_inq.nmsigs.last);
+
+ if (proc->sig_qs.cont || ERTS_MSG_RECV_TRACED(proc)) {
+ *proc->sig_qs.cont_last = proc->sig_inq.first;
+ proc->sig_qs.cont_last = proc->sig_inq.last;
+ }
+ else {
+ *proc->sig_qs.last = proc->sig_inq.first;
+ proc->sig_qs.last = proc->sig_inq.last;
+ }
+ }
+ else {
+#ifdef DEBUG
+ erts_aint32_t s;
+#endif
+ ASSERT(proc->sig_inq.nmsigs.last);
+ if (!proc->sig_qs.nmsigs.last) {
+ ASSERT(!proc->sig_qs.nmsigs.next);
+ if (proc->sig_inq.nmsigs.next == &proc->sig_inq.first)
+ proc->sig_qs.nmsigs.next = proc->sig_qs.cont_last;
+ else
+ proc->sig_qs.nmsigs.next = proc->sig_inq.nmsigs.next;
+
+#ifdef DEBUG
+ s =
+#endif
+ erts_atomic32_read_bset_nob(&proc->state,
+ (ERTS_PSFLG_SIG_Q
+ | ERTS_PSFLG_SIG_IN_Q),
+ ERTS_PSFLG_SIG_Q);
+
+ ASSERT((s & (ERTS_PSFLG_SIG_Q|ERTS_PSFLG_SIG_IN_Q))
+ == ERTS_PSFLG_SIG_IN_Q);
+ }
+ else {
+ ErtsSignal *sig;
+ ASSERT(proc->sig_qs.nmsigs.next);
+ sig = ((ErtsSignal *) *proc->sig_qs.nmsigs.last);
+ ASSERT(ERTS_SIG_IS_NON_MSG(sig));
+ ASSERT(!sig->common.specific.next);
+ if (proc->sig_inq.nmsigs.next == &proc->sig_inq.first)
+ sig->common.specific.next = proc->sig_qs.cont_last;
+ else
+ sig->common.specific.next = proc->sig_inq.nmsigs.next;
+
+#ifdef DEBUG
+ s =
+#endif
+ erts_atomic32_read_band_nob(&proc->state,
+ ~ERTS_PSFLG_SIG_IN_Q);
+
+ ASSERT((s & (ERTS_PSFLG_SIG_Q|ERTS_PSFLG_SIG_IN_Q))
+ == (ERTS_PSFLG_SIG_Q|ERTS_PSFLG_SIG_IN_Q));
+ }
+ if (proc->sig_inq.nmsigs.last == &proc->sig_inq.first)
+ proc->sig_qs.nmsigs.last = proc->sig_qs.cont_last;
+ else
+ proc->sig_qs.nmsigs.last = proc->sig_inq.nmsigs.last;
+ proc->sig_inq.nmsigs.next = NULL;
+ proc->sig_inq.nmsigs.last = NULL;
+
+ *proc->sig_qs.cont_last = proc->sig_inq.first;
+ proc->sig_qs.cont_last = proc->sig_inq.last;
+ }
+
+ proc->sig_qs.len += proc->sig_inq.len;
+
+ proc->sig_inq.first = NULL;
+ proc->sig_inq.last = &proc->sig_inq.first;
+ proc->sig_inq.len = 0;
+
+}
+
+Sint
+erts_proc_sig_fetch_msgq_len_offs__(Process *proc)
+{
+ ErtsProcSigMsgQLenOffsetMarker *marker
+ = (ErtsProcSigMsgQLenOffsetMarker *) proc->sig_inq.first;
+
+ ASSERT(marker->common.tag == ERTS_PROC_SIG_MSGQ_LEN_OFFS_MARK);
+
+ if (marker->common.next) {
+ Sint len;
+
+ proc->flags |= F_DELAYED_PSIGQS_LEN;
+
+ /*
+ * Prevent update of sig_qs.len in fetch. These
+ * updates are done via process-info signal(s)
+ * instead...
+ */
+ len = proc->sig_inq.len;
+ marker->delayed_len += len;
+ marker->len_offset -= len;
+ proc->sig_inq.len = 0;
+
+ /*
+ * Temorarily remove marker during fetch...
+ */
+
+ proc->sig_inq.first = marker->common.next;
+ if (proc->sig_inq.last == &marker->common.next)
+ proc->sig_inq.last = &proc->sig_inq.first;
+ if (proc->sig_inq.nmsigs.next == &marker->common.next)
+ proc->sig_inq.nmsigs.next = &proc->sig_inq.first;
+ if (proc->sig_inq.nmsigs.last == &marker->common.next)
+ proc->sig_inq.nmsigs.last = &proc->sig_inq.first;
+
+ erts_proc_sig_fetch__(proc);
+
+ marker->common.next = NULL;
+ proc->sig_inq.first = (ErtsMessage *) marker;
+ proc->sig_inq.last = &marker->common.next;
+
+ }
+
+ return marker->delayed_len;
+}
+
+static ERTS_INLINE Sint
+proc_sig_privqs_len(Process *c_p, int have_qlock)
+{
+ Sint res = c_p->sig_qs.len;
+
+ ERTS_LC_ASSERT(!have_qlock
+ ? (ERTS_PROC_LOCK_MAIN
+ == erts_proc_lc_my_proc_locks(c_p))
+ : ((ERTS_PROC_LOCK_MSGQ|ERTS_PROC_LOCK_MAIN)
+ == ((ERTS_PROC_LOCK_MSGQ|ERTS_PROC_LOCK_MAIN)
+ & erts_proc_lc_my_proc_locks(c_p))));
+
+ if (c_p->flags & F_DELAYED_PSIGQS_LEN) {
+ ErtsProcSigMsgQLenOffsetMarker *marker;
+
+ if (!have_qlock)
+ erts_proc_lock(c_p, ERTS_PROC_LOCK_MSGQ);
+
+ marker = (ErtsProcSigMsgQLenOffsetMarker *) c_p->sig_inq.first;
+ ASSERT(marker);
+ ASSERT(marker->common.tag == ERTS_PROC_SIG_MSGQ_LEN_OFFS_MARK);
+
+ res += marker->delayed_len;
+
+ if (!have_qlock)
+ erts_proc_unlock(c_p, ERTS_PROC_LOCK_MSGQ);
+ }
+
+#ifdef ERTS_PROC_SIG_HARD_DEBUG_SIGQ_MSG_LEN
+ {
+ Sint len = 0;
+ ERTS_FOREACH_SIG_PRIVQS(
+ c_p, mp,
+ {
+ if (ERTS_SIG_IS_MSG(mp))
+ len++;
+ });
+ ERTS_ASSERT(res == len);
+ }
+#endif
+
+ return res;
+}
+
+Sint
+erts_proc_sig_privqs_len(Process *c_p)
+{
+ return proc_sig_privqs_len(c_p, 0);
+}
+
+static void do_seq_trace_output(Eterm to, Eterm token, Eterm msg);
+
+static void
+send_gen_exit_signal(Process *c_p, Eterm from_tag,
+ Eterm from, Eterm to,
+ Sint16 op, Eterm reason, Eterm ref,
+ Eterm token, int normal_kills)
+{
+ ErtsExitSignalData *xsigd;
+ Eterm *hp, *start_hp, s_reason, s_ref, s_message, s_token, s_from;
+ ErtsMessage *mp;
+ ErlHeapFragment *hfrag;
+ ErlOffHeap *ohp;
+ Uint hsz, from_sz, reason_sz, ref_sz, token_sz;
+ int seq_trace;
+#ifdef USE_VM_PROBES
+ Eterm s_utag, utag;
+ Uint utag_sz;
+#endif
+
+ ASSERT(is_immed(from_tag));
+
+ hsz = sizeof(ErtsExitSignalData)/sizeof(Uint);
+
+ seq_trace = c_p && have_seqtrace(token);
+ if (seq_trace)
+ seq_trace_update_send(c_p);
+
+#ifdef USE_VM_PROBES
+ utag_sz = 0;
+ utag = NIL;
+ if (c_p && token != NIL && (DT_UTAG_FLAGS(c_p) & DT_UTAG_SPREADING)) {
+ utag_sz = size_object(DT_UTAG(c_p));
+ utag = DT_UTAG(c_p);
+ }
+ else if (token == am_have_dt_utag) {
+ token = NIL;
+ }
+ hsz += utag_sz;
+#endif
+
+ token_sz = is_immed(token) ? 0 : size_object(token);
+ hsz += token_sz;
+
+ from_sz = is_immed(from) ? 0 : size_object(from);
+ hsz += from_sz;
+
+ reason_sz = is_immed(reason) ? 0 : size_object(reason);
+ hsz += reason_sz;
+
+ switch (op) {
+ case ERTS_SIG_Q_OP_EXIT:
+ case ERTS_SIG_Q_OP_EXIT_LINKED: {
+ /* {'EXIT', From, Reason} */
+ hsz += 4; /* 3-tuple */
+ ref_sz = 0;
+ break;
+ }
+ case ERTS_SIG_Q_OP_MONITOR_DOWN: {
+ /* {'DOWN', Ref, process, From, Reason} */
+ hsz += 6; /* 5-tuple */
+ ref_sz = NC_HEAP_SIZE(ref);
+ hsz += ref_sz;
+ break;
+ }
+ default:
+ ERTS_INTERNAL_ERROR("Invalid exit signal op");
+ break;
+ }
+
+ /*
+ * Allocate message combined with heap fragment...
+ */
+ mp = erts_alloc_message(hsz, &hp);
+ hfrag = &mp->hfrag;
+ mp->next = NULL;
+ ohp = &hfrag->off_heap;
+ start_hp = hp;
+
+ s_token = (is_immed(token)
+ ? token
+ : copy_struct(token, token_sz, &hp, ohp));
+
+ s_reason = (is_immed(reason)
+ ? reason
+ : copy_struct(reason, reason_sz, &hp, ohp));
+
+ s_from = (is_immed(from)
+ ? from
+ : copy_struct(from, from_sz, &hp, ohp));
+
+ if (!ref_sz)
+ s_ref = NIL;
+ else
+ s_ref = STORE_NC(&hp, ohp, ref);
+
+ switch (op) {
+ case ERTS_SIG_Q_OP_EXIT:
+ case ERTS_SIG_Q_OP_EXIT_LINKED:
+ /* {'EXIT', From, Reason} */
+ s_message = TUPLE3(hp, am_EXIT, s_from, s_reason);
+ hp += 4;
+ break;
+ case ERTS_SIG_Q_OP_MONITOR_DOWN:
+ /* {'DOWN', Ref, process, From, Reason} */
+ s_message = TUPLE5(hp, am_DOWN, s_ref, am_process, s_from, s_reason);
+ hp += 6;
+ break;
+ }
+
+#ifdef USE_VM_PROBES
+ s_utag = (is_immed(utag)
+ ? utag
+ : copy_struct(utag, utag_sz, &hp, ohp));
+ ERL_MESSAGE_DT_UTAG(mp) = s_utag;
+#endif
+
+ ERL_MESSAGE_TERM(mp) = ERTS_PROC_SIG_MAKE_TAG(op,
+ ERTS_SIG_Q_TYPE_GEN_EXIT,
+ 0);
+ ERL_MESSAGE_TOKEN(mp) = s_token;
+ ERL_MESSAGE_FROM(mp) = from_tag; /* immediate... */
+
+ hfrag->used_size = hp - start_hp;
+
+ xsigd = (ErtsExitSignalData *) (char *) hp;
+
+ xsigd->message = s_message;
+ xsigd->from = s_from;
+ xsigd->reason = s_reason;
+ if (is_nil(s_ref))
+ xsigd->u.normal_kills = normal_kills;
+ else {
+ ASSERT(is_ref(s_ref));
+ xsigd->u.ref = s_ref;
+ }
+
+ if (seq_trace)
+ do_seq_trace_output(to, s_token, s_message);
+
+ if (!proc_queue_signal(c_p, to, (ErtsSignal *) mp, op)) {
+ mp->next = NULL;
+ erts_cleanup_messages(mp);
+ }
+}
+
+static void
+do_seq_trace_output(Eterm to, Eterm token, Eterm msg)
+{
+ /*
+ * We could do this when enqueuing the signal and avoid some
+ * locking. However, the enqueuing code would then always
+ * have the penalty of this seq-tracing code which we do not
+ * want...
+ */
+ ErtsSchedulerData *esdp = erts_get_scheduler_data();
+ int is_normal_sched = !!esdp && esdp->type == ERTS_SCHED_NORMAL;
+ Process *rp;
+
+ if (is_normal_sched)
+ rp = erts_proc_lookup_raw(to);
+ else
+ rp = erts_proc_lookup_raw_inc_refc(to);
+
+ if (rp) {
+ erts_proc_lock(rp, ERTS_PROC_LOCK_MSGQ);
+
+ if (!ERTS_PROC_IS_EXITING(rp))
+ seq_trace_output(token, msg, SEQ_TRACE_SEND, to, rp);
+
+ erts_proc_unlock(rp, ERTS_PROC_LOCK_MSGQ);
+
+ if (!is_normal_sched)
+ erts_proc_dec_refc(rp);
+ }
+}
+
+void
+erts_proc_sig_send_persistent_monitor_msg(Uint16 type, Eterm key,
+ Eterm from, Eterm to,
+ Eterm msg, Uint msg_sz)
+{
+ ErtsPersistMonMsg *prst_mon;
+ ErtsMessage *mp;
+ ErlHeapFragment *hfrag;
+ Eterm *hp, *start_hp, message;
+ ErlOffHeap *ohp;
+ Uint hsz = sizeof(ErtsPersistMonMsg) + msg_sz;
+
+ /*
+ * Allocate message combined with heap fragment...
+ */
+ mp = erts_alloc_message(hsz, &hp);
+ hfrag = &mp->hfrag;
+ mp->next = NULL;
+ ohp = &hfrag->off_heap;
+ start_hp = hp;
+
+ ASSERT(msg_sz == size_object(msg));
+ message = copy_struct(msg, msg_sz, &hp, ohp);
+ hfrag->used_size = hp - start_hp;
+
+ prst_mon = (ErtsPersistMonMsg *) (char *) hp;
+ prst_mon->message = message;
+
+ switch (type) {
+ case ERTS_MON_TYPE_NODES:
+ ASSERT(is_small(key));
+ prst_mon->key = key;
+ break;
+
+ case ERTS_MON_TYPE_TIME_OFFSET:
+ ASSERT(is_internal_ref(key));
+ ASSERT(is_tuple_arity(message, 5));
+
+ prst_mon->key = tuple_val(message)[2];
+
+ ASSERT(eq(prst_mon->key, key));
+ break;
+
+ default:
+ ERTS_INTERNAL_ERROR("Invalid persistent monitor type");
+ prst_mon->key = key;
+ break;
+ }
+
+ ASSERT(is_immed(from));
+
+ ERL_MESSAGE_TERM(mp) = ERTS_PROC_SIG_MAKE_TAG(ERTS_SIG_Q_OP_PERSISTENT_MON_MSG,
+ type, 0);
+ ERL_MESSAGE_FROM(mp) = from;
+ ERL_MESSAGE_TOKEN(mp) = NIL;
+#ifdef USE_VM_PROBES
+ ERL_MESSAGE_DT_UTAG(mp) = NIL;
+#endif
+
+ if (!proc_queue_signal(NULL, to, (ErtsSignal *) mp,
+ ERTS_SIG_Q_OP_PERSISTENT_MON_MSG)) {
+ mp->next = NULL;
+ erts_cleanup_messages(mp);
+ }
+}
+
+static ERTS_INLINE Eterm
+get_persist_mon_msg(ErtsMessage *sig, Eterm *msg)
+{
+ ErtsPersistMonMsg *prst_mon;
+ prst_mon = ((ErtsPersistMonMsg *)
+ (char *) (&sig->hfrag.mem[0]
+ + sig->hfrag.used_size));
+ *msg = prst_mon->message;
+ return prst_mon->key;
+}
+
+void
+erts_proc_sig_send_exit(Process *c_p, Eterm from, Eterm to,
+ Eterm reason, Eterm token,
+ int normal_kills)
+{
+ Eterm from_tag;
+ ASSERT(!c_p || c_p->common.id == from);
+ if (is_immed(from)) {
+ ASSERT(is_internal_pid(from) || is_internal_port(from));
+ from_tag = from;
+ }
+ else {
+ DistEntry *dep;
+ ASSERT(is_external_pid(from));
+ dep = external_pid_dist_entry(from);
+ from_tag = dep->sysname;
+ }
+ send_gen_exit_signal(c_p, from_tag, from, to, ERTS_SIG_Q_OP_EXIT,
+ reason, NIL, token, normal_kills);
+}
+
+void
+erts_proc_sig_send_link_exit(Process *c_p, Eterm from, ErtsLink *lnk,
+ Eterm reason, Eterm token)
+{
+ Eterm to;
+ ASSERT(!c_p || c_p->common.id == from);
+ ASSERT(lnk);
+ to = lnk->other.item;
+ if (is_not_immed(reason) || is_not_nil(token)) {
+ ASSERT(is_internal_pid(from) || is_internal_port(from));
+ send_gen_exit_signal(c_p, from, from, to, ERTS_SIG_Q_OP_EXIT_LINKED,
+ reason, NIL, token, 0);
+ }
+ else {
+ /* Pass signal using old link structure... */
+ ErtsSignal *sig = (ErtsSignal *) lnk;
+ lnk->other.item = reason; /* pass reason via this other.item */
+ sig->common.tag = ERTS_PROC_SIG_MAKE_TAG(ERTS_SIG_Q_OP_EXIT_LINKED,
+ lnk->type, 0);
+ if (proc_queue_signal(c_p, to, sig, ERTS_SIG_Q_OP_EXIT_LINKED))
+ return; /* receiver will destroy lnk structure */
+ }
+ if (lnk)
+ erts_link_release(lnk);
+}
+
+int
+erts_proc_sig_send_link(Process *c_p, Eterm to, ErtsLink *lnk)
+{
+ ErtsSignal *sig;
+ Uint16 type = lnk->type;
+
+ ASSERT(!c_p || c_p->common.id == lnk->other.item);
+ ASSERT(lnk);
+ ASSERT(is_internal_pid(to));
+
+ sig = (ErtsSignal *) lnk;
+ sig->common.tag = ERTS_PROC_SIG_MAKE_TAG(ERTS_SIG_Q_OP_LINK,
+ type, 0);
+
+ return proc_queue_signal(c_p, to, sig, ERTS_SIG_Q_OP_LINK);
+}
+
+void
+erts_proc_sig_send_unlink(Process *c_p, ErtsLink *lnk)
+{
+ ErtsSignal *sig;
+ Eterm to;
+
+ ASSERT(lnk);
+
+ sig = (ErtsSignal *) lnk;
+ to = lnk->other.item;
+
+ ASSERT(is_internal_pid(to));
+
+ sig->common.tag = ERTS_PROC_SIG_MAKE_TAG(ERTS_SIG_Q_OP_UNLINK,
+ lnk->type, 0);
+
+ if (!proc_queue_signal(c_p, to, sig, ERTS_SIG_Q_OP_UNLINK))
+ erts_link_release(lnk);
+}
+
+void
+erts_proc_sig_send_dist_link_exit(DistEntry *dep,
+ Eterm from, Eterm to,
+ Eterm reason, Eterm token)
+{
+ send_gen_exit_signal(NULL, dep->sysname, from, to, ERTS_SIG_Q_OP_EXIT_LINKED,
+ reason, NIL, token, 0);
+}
+
+void
+erts_proc_sig_send_dist_unlink(DistEntry *dep, Eterm from, Eterm to)
+{
+ ErtsSignal *sig;
+
+ ASSERT(is_internal_pid(to));
+ ASSERT(is_external_pid(from));
+ ASSERT(dep == external_pid_dist_entry(from));
+
+ sig = (ErtsSignal *) make_sig_dist_link_op(ERTS_SIG_Q_OP_UNLINK,
+ to, from);
+
+ if (!proc_queue_signal(NULL, to, sig, ERTS_SIG_Q_OP_UNLINK))
+ destroy_sig_dist_link_op((ErtsSigDistLinkOp *) sig);
+}
+
+void
+erts_proc_sig_send_dist_monitor_down(DistEntry *dep, Eterm ref,
+ Eterm from, Eterm to,
+ Eterm reason)
+{
+ Eterm monitored, heap[3];
+ if (is_atom(from))
+ monitored = TUPLE2(&heap[0], from, dep->sysname);
+ else
+ monitored = from;
+ send_gen_exit_signal(NULL, dep->sysname, monitored,
+ to, ERTS_SIG_Q_OP_MONITOR_DOWN,
+ reason, ref, NIL, 0);
+}
+
+void
+erts_proc_sig_send_monitor_down(ErtsMonitor *mon, Eterm reason)
+{
+ Eterm to;
+
+ ASSERT(erts_monitor_is_target(mon));
+ ASSERT(!erts_monitor_is_in_table(mon));
+
+ to = mon->other.item;
+ ASSERT(is_internal_pid(to));
+
+ if (is_immed(reason)) {
+ /* Pass signal using old monitor structure... */
+ ErtsSignal *sig;
+
+ mon->other.item = reason; /* Pass immed reason via other.item... */
+ sig = (ErtsSignal *) mon;
+ sig->common.tag = ERTS_PROC_SIG_MAKE_TAG(ERTS_SIG_Q_OP_MONITOR_DOWN,
+ mon->type, 0);
+ if (proc_queue_signal(NULL, to, sig, ERTS_SIG_Q_OP_MONITOR_DOWN))
+ return; /* receiver will destroy mon structure */
+ }
+ else {
+ ErtsMonitorData *mdp = erts_monitor_to_data(mon);
+ Eterm from_tag, monitored, heap[3];
+
+ if (!(mon->flags & ERTS_ML_FLG_NAME)) {
+ from_tag = monitored = mdp->origin.other.item;
+ if (is_external_pid(from_tag)) {
+ DistEntry *dep = external_pid_dist_entry(from_tag);
+ from_tag = dep->sysname;
+ }
+ }
+ else {
+ ErtsMonitorDataExtended *mdep;
+ Eterm name, node;
+ mdep = (ErtsMonitorDataExtended *) mdp;
+ name = mdep->u.name;
+ ASSERT(is_atom(name));
+ if (mdep->dist) {
+ node = mdep->dist->nodename;
+ from_tag = node;
+ }
+ else {
+ node = erts_this_dist_entry->sysname;
+ from_tag = mdp->origin.other.item;
+ }
+ ASSERT(is_internal_port(from_tag)
+ || is_internal_pid(from_tag)
+ || is_atom(from_tag));
+ monitored = TUPLE2(&heap[0], name, node);
+ }
+ send_gen_exit_signal(NULL, from_tag, monitored,
+ to, ERTS_SIG_Q_OP_MONITOR_DOWN,
+ reason, mdp->ref, NIL, 0);
+ }
+ erts_monitor_release(mon);
+}
+
+void
+erts_proc_sig_send_dist_demonitor(Eterm to, Eterm ref)
+{
+ ErtsSigDistProcDemonitor *dmon;
+ ErtsSignal *sig;
+ Eterm *hp;
+ ErlOffHeap oh;
+ size_t size;
+
+ ERTS_INIT_OFF_HEAP(&oh);
+
+ ASSERT(is_internal_pid(to));
+
+ size = sizeof(ErtsSigDistProcDemonitor) - sizeof(Eterm);
+ ASSERT(is_ref(ref));
+ size += NC_HEAP_SIZE(ref)*sizeof(Eterm);
+
+ dmon = erts_alloc(ERTS_ALC_T_DIST_DEMONITOR, size);
+
+ hp = &dmon->heap[0];
+ dmon->ref = STORE_NC(&hp, &oh, ref);
+ sig = (ErtsSignal *) dmon;
+
+ sig->common.tag = ERTS_PROC_SIG_MAKE_TAG(ERTS_SIG_Q_OP_DEMONITOR,
+ ERTS_SIG_Q_TYPE_DIST_PROC_DEMONITOR,
+ 0);
+
+ if (!proc_queue_signal(NULL, to, sig, ERTS_SIG_Q_OP_DEMONITOR))
+ destroy_dist_proc_demonitor(dmon);
+}
+
+void
+erts_proc_sig_send_demonitor(ErtsMonitor *mon)
+{
+ ErtsSignal *sig = (ErtsSignal *) mon;
+ Uint16 type = mon->type;
+ Eterm to = mon->other.item;
+
+ ASSERT(is_internal_pid(to));
+ ASSERT(erts_monitor_is_origin(mon));
+ ASSERT(!erts_monitor_is_in_table(mon));
+
+ sig->common.tag = ERTS_PROC_SIG_MAKE_TAG(ERTS_SIG_Q_OP_DEMONITOR,
+ type, 0);
+
+ if (!proc_queue_signal(NULL, to, sig, ERTS_SIG_Q_OP_DEMONITOR))
+ erts_monitor_release(mon);
+}
+
+int
+erts_proc_sig_send_monitor(ErtsMonitor *mon, Eterm to)
+{
+ ErtsSignal *sig = (ErtsSignal *) mon;
+ Uint16 type = mon->type;
+
+ ASSERT(is_internal_pid(to) || to == am_undefined);
+ ASSERT(erts_monitor_is_target(mon));
+
+ sig->common.tag = ERTS_PROC_SIG_MAKE_TAG(ERTS_SIG_Q_OP_MONITOR,
+ type, 0);
+
+ return proc_queue_signal(NULL, to, sig, ERTS_SIG_Q_OP_MONITOR);
+}
+
+void
+erts_proc_sig_send_trace_change(Eterm to, Uint on, Uint off, Eterm tracer)
+{
+ ErtsSigTraceInfo *ti;
+ Eterm tag;
+
+ ti = erts_alloc(ERTS_ALC_T_SIG_DATA, sizeof(ErtsSigTraceInfo));
+ tag = ERTS_PROC_SIG_MAKE_TAG(ERTS_SIG_Q_OP_TRACE_CHANGE_STATE,
+ ERTS_SIG_Q_TYPE_ADJUST_TRACE_INFO,
+ 0);
+
+ ti->common.tag = tag;
+ ti->flags_off = off;
+ ti->flags_on = on;
+ ti->tracer = NIL;
+ if (is_not_nil(tracer))
+ erts_tracer_update(&ti->tracer, tracer);
+
+ if (!proc_queue_signal(NULL, to, (ErtsSignal *) ti,
+ ERTS_SIG_Q_OP_TRACE_CHANGE_STATE))
+ destroy_trace_info(ti);
+}
+
+void
+erts_proc_sig_send_group_leader(Process *c_p, Eterm to, Eterm gl, Eterm ref)
+{
+ int res;
+ ErtsSigGroupLeader *sgl;
+ Eterm *hp;
+ Uint gl_sz, ref_sz, size;
+ erts_aint_t init_flags = ERTS_SIG_GL_FLG_ACTIVE|ERTS_SIG_GL_FLG_RECEIVER;
+ if (c_p)
+ init_flags |= ERTS_SIG_GL_FLG_SENDER;
+
+ ASSERT(c_p ? is_internal_ref(ref) : ref == NIL);
+
+ gl_sz = is_immed(gl) ? 0 : size_object(gl);
+ ref_sz = is_immed(ref) ? 0 : size_object(ref);
+
+ size = sizeof(ErtsSigGroupLeader);
+
+ size += (gl_sz + ref_sz - 1) * sizeof(Eterm);
+
+ sgl = erts_alloc(ERTS_ALC_T_SIG_DATA, size);
+
+ erts_atomic_init_nob(&sgl->flags, init_flags);
+
+ ERTS_INIT_OFF_HEAP(&sgl->oh);
+
+ hp = &sgl->heap[0];
+
+ sgl->group_leader = is_immed(gl) ? gl : copy_struct(gl, gl_sz, &hp, &sgl->oh);
+ sgl->reply_to = c_p ? c_p->common.id : NIL;
+ sgl->ref = is_immed(ref) ? ref : copy_struct(ref, ref_sz, &hp, &sgl->oh);
+
+ sgl->common.tag = ERTS_PROC_SIG_MAKE_TAG(ERTS_SIG_Q_OP_GROUP_LEADER,
+ ERTS_SIG_Q_TYPE_UNDEFINED,
+ 0);
+
+ res = proc_queue_signal(c_p, to, (ErtsSignal *) sgl,
+ ERTS_SIG_Q_OP_GROUP_LEADER);
+
+ if (!res)
+ destroy_sig_group_leader(sgl);
+ else if (c_p) {
+ erts_aint_t flags, rm_flags = ERTS_SIG_GL_FLG_SENDER;
+ int prio_res = maybe_elevate_sig_handling_prio(c_p, to);
+ if (!prio_res)
+ rm_flags |= ERTS_SIG_GL_FLG_ACTIVE;
+ flags = erts_atomic_read_band_nob(&sgl->flags, ~rm_flags);
+ if (!prio_res && (flags & ERTS_SIG_GL_FLG_ACTIVE))
+ res = 0; /* We deactivated signal... */
+ if ((flags & ~rm_flags) == 0)
+ destroy_sig_group_leader(sgl);
+ }
+
+ if (!res && c_p)
+ group_leader_reply(c_p, c_p->common.id, ref, 0);
+}
+
+void
+erts_proc_sig_send_is_alive_request(Process *c_p, Eterm to, Eterm ref)
+{
+ ErlHeapFragment *hfrag;
+ Uint hsz;
+ Eterm *hp, *start_hp, ref_cpy, msg;
+ ErlOffHeap *ohp;
+ ErtsMessage *mp;
+ ErtsIsAliveRequest *alive_req;
+
+ ASSERT(is_internal_ordinary_ref(ref));
+
+ hsz = ERTS_REF_THING_SIZE + 3 + sizeof(ErtsIsAliveRequest)/sizeof(Eterm);
+
+ mp = erts_alloc_message(hsz, &hp);
+ hfrag = &mp->hfrag;
+ mp->next = NULL;
+ ohp = &hfrag->off_heap;
+ start_hp = hp;
+
+ ref_cpy = STORE_NC(&hp, ohp, ref);
+ msg = TUPLE2(hp, ref_cpy, am_false); /* default res 'false' */
+ hp += 3;
+
+ hfrag->used_size = hp - start_hp;
+
+ alive_req = (ErtsIsAliveRequest *) (char *) hp;
+ alive_req->message = msg;
+ alive_req->requester = c_p->common.id;
+
+ ERL_MESSAGE_TERM(mp) = ERTS_PROC_SIG_MAKE_TAG(ERTS_SIG_Q_OP_IS_ALIVE,
+ ERTS_SIG_Q_TYPE_UNDEFINED,
+ 0);
+ ERL_MESSAGE_TOKEN(mp) = NIL;
+ ERL_MESSAGE_FROM(mp) = am_system;
+#ifdef USE_VM_PROBES
+ ERL_MESSAGE_DT_UTAG(mp) = NIL;
+#endif
+
+ if (proc_queue_signal(c_p, to, (ErtsSignal *) mp, ERTS_SIG_Q_OP_IS_ALIVE))
+ (void) maybe_elevate_sig_handling_prio(c_p, to);
+ else {
+ /* It wasn't alive; reply to ourselves... */
+ mp->next = NULL;
+ mp->data.attached = ERTS_MSG_COMBINED_HFRAG;
+ erts_queue_message(c_p, ERTS_PROC_LOCK_MAIN,
+ mp, msg, am_system);
+ }
+}
+
+int
+erts_proc_sig_send_process_info_request(Process *c_p,
+ Eterm to,
+ int *item_ix,
+ int len,
+ int need_msgq_len,
+ int flags,
+ Uint reserve_size,
+ Eterm ref)
+{
+ Uint size = sizeof(ErtsProcessInfoSig) + (len - 1) * sizeof(int);
+ ErtsProcessInfoSig *pis = erts_alloc(ERTS_ALC_T_SIG_DATA, size);
+ int res;
+
+ ASSERT(c_p);
+ ASSERT(item_ix);
+ ASSERT(len > 0);
+ ASSERT(is_internal_ordinary_ref(ref));
+
+ pis->common.tag = ERTS_PROC_SIG_MAKE_TAG(ERTS_SIG_Q_OP_PROCESS_INFO,
+ 0, 0);
+
+ if (!need_msgq_len)
+ pis->msgq_len_offset = ERTS_PROC_SIG_PI_MSGQ_LEN_IGNORE;
+ else {
+ pis->msgq_len_offset = ERTS_PROC_SIG_PI_MSGQ_LEN_SYNC;
+ pis->marker.common.next = NULL;
+ pis->marker.common.specific.next = NULL;
+ pis->marker.common.tag = ERTS_PROC_SIG_MSGQ_LEN_OFFS_MARK;
+ pis->marker.refc = 0;
+ pis->marker.delayed_len = 0;
+ pis->marker.len_offset = 0;
+ }
+ pis->requester = c_p->common.id;
+ sys_memcpy((void *) &pis->oref_thing,
+ (void *) internal_ref_val(ref),
+ sizeof(ErtsORefThing));
+ pis->ref = make_internal_ref((char *) &pis->oref_thing);
+ pis->reserve_size = reserve_size;
+ pis->len = len;
+ pis->flags = flags;
+ sys_memcpy((void *) &pis->item_ix[0],
+ (void *) item_ix,
+ sizeof(int)*len);
+ res = proc_queue_signal(c_p, to, (ErtsSignal *) pis,
+ ERTS_SIG_Q_OP_PROCESS_INFO);
+ if (res)
+ (void) maybe_elevate_sig_handling_prio(c_p, to);
+ else
+ erts_free(ERTS_ALC_T_SIG_DATA, pis);
+ return res;
+}
+
+static void
+is_alive_response(Process *c_p, ErtsMessage *mp, int is_alive)
+{
+ /*
+ * Sender prepared the message for us. Just patch
+ * the result if necessary. The default prepared
+ * result is 'false'.
+ */
+ Process *rp;
+ ErtsIsAliveRequest *alive_req;
+
+ alive_req = (ErtsIsAliveRequest *) (char *) (&mp->hfrag.mem[0]
+ + mp->hfrag.used_size);
+
+
+ ASSERT(ERTS_SIG_IS_NON_MSG(mp));
+ ASSERT(ERTS_PROC_SIG_OP(((ErtsSignal *) mp)->common.tag)
+ == ERTS_SIG_Q_OP_IS_ALIVE);
+ ASSERT(mp->hfrag.alloc_size > mp->hfrag.used_size);
+ ASSERT((mp->hfrag.alloc_size - mp->hfrag.used_size)*sizeof(UWord)
+ >= sizeof(ErtsIsAliveRequest));
+ ASSERT(is_internal_pid(alive_req->requester));
+ ASSERT(alive_req->requester != c_p->common.id);
+ ASSERT(is_tuple_arity(alive_req->message, 2));
+ ASSERT(is_internal_ordinary_ref(tuple_val(alive_req->message)[1]));
+ ASSERT(tuple_val(alive_req->message)[2] == am_false);
+
+ ERL_MESSAGE_TERM(mp) = alive_req->message;
+ mp->data.attached = ERTS_MSG_COMBINED_HFRAG;
+ mp->next = NULL;
+
+ rp = erts_proc_lookup(alive_req->requester);
+ if (!rp)
+ erts_cleanup_messages(mp);
+ else {
+ if (is_alive) { /* patch result... */
+ Eterm *tp = tuple_val(alive_req->message);
+ tp[2] = am_true;
+ }
+ erts_queue_message(rp, 0, mp, alive_req->message, am_system);
+ }
+}
+
+static ERTS_INLINE void
+adjust_tracing_state(Process *c_p, ErtsSigRecvTracing *tracing, int setup)
+{
+ if (!IS_TRACED(c_p) || (ERTS_TRACE_FLAGS(c_p) & F_SENSITIVE)) {
+ tracing->messages.active = 0;
+ tracing->messages.receive_trace = 0;
+ tracing->messages.event = NULL;
+ tracing->messages.next = NULL;
+ tracing->procs = 0;
+ tracing->active = 0;
+ }
+ else {
+ Uint flgs = ERTS_TRACE_FLAGS(c_p);
+ int procs_trace = !!(flgs & F_TRACE_PROCS);
+ int recv_trace = !!(flgs & F_TRACE_RECEIVE);
+ /* procs tracing enabled? */
+
+ tracing->procs = procs_trace;
+
+ /* message receive tracing enabled? */
+ tracing->messages.receive_trace = recv_trace;
+ if (!recv_trace)
+ tracing->messages.event = NULL;
+ else {
+ if (tracing->messages.bp_ix < 0)
+ tracing->messages.bp_ix = erts_active_bp_ix();
+ tracing->messages.event = &erts_receive_tracing[tracing->messages.bp_ix];
+ }
+ if (setup) {
+ if (recv_trace)
+ tracing->messages.next = &c_p->sig_qs.cont;
+ else
+ tracing->messages.next = NULL;
+ }
+ tracing->messages.active = recv_trace;
+ tracing->active = recv_trace | procs_trace;
+ }
+
+#if defined(USE_VM_PROBES)
+ /* vm probe message_queued enabled? */
+
+ tracing->messages.vm_probes = DTRACE_ENABLED(message_queued);
+ if (tracing->messages.vm_probes) {
+ dtrace_proc_str(c_p, tracing->messages.receiver_name);
+ tracing->messages.active = !0;
+ tracing->active = !0;
+ if (setup && !tracing->messages.next)
+ tracing->messages.next = &c_p->sig_qs.cont;
+ }
+
+#endif
+}
+
+static ERTS_INLINE void
+setup_tracing_state(Process *c_p, ErtsSigRecvTracing *tracing)
+{
+ tracing->messages.bp_ix = -1;
+ adjust_tracing_state(c_p, tracing, !0);
+}
+
+static ERTS_INLINE void
+remove_iq_sig(Process *c_p, ErtsMessage *sig, ErtsMessage **next_sig)
+{
+ /*
+ * Remove signal from inner queue.
+ */
+ ASSERT(c_p->sig_qs.cont_last != &sig->next);
+ ASSERT(c_p->sig_qs.nmsigs.next != &sig->next);
+ ASSERT(c_p->sig_qs.nmsigs.last != &sig->next);
+
+ if (c_p->sig_qs.save == &sig->next)
+ c_p->sig_qs.save = next_sig;
+ if (c_p->sig_qs.last == &sig->next)
+ c_p->sig_qs.last = next_sig;
+ if (c_p->sig_qs.saved_last == &sig->next)
+ c_p->sig_qs.saved_last = next_sig;
+
+ *next_sig = sig->next;
+}
+
+static ERTS_INLINE void
+remove_mq_sig(Process *c_p, ErtsMessage *sig,
+ ErtsMessage **next_sig, ErtsMessage ***next_nm_sig)
+{
+ /*
+ * Remove signal from middle queue.
+ */
+ ASSERT(c_p->sig_qs.save != &sig->next);
+ ASSERT(c_p->sig_qs.last != &sig->next);
+
+ if (c_p->sig_qs.cont_last == &sig->next)
+ c_p->sig_qs.cont_last = next_sig;
+ if (c_p->sig_qs.saved_last == &sig->next)
+ c_p->sig_qs.saved_last = next_sig;
+ if (*next_nm_sig == &sig->next)
+ *next_nm_sig = next_sig;
+ if (c_p->sig_qs.nmsigs.last == &sig->next)
+ c_p->sig_qs.nmsigs.last = next_sig;
+
+ *next_sig = sig->next;
+}
+
+static ERTS_INLINE void
+remove_nm_sig(Process *c_p, ErtsMessage *sig, ErtsMessage ***next_nm_sig)
+{
+ ErtsMessage **next_sig = *next_nm_sig;
+ ASSERT(ERTS_SIG_IS_NON_MSG(sig));
+ ASSERT(*next_sig == sig);
+ *next_nm_sig = ((ErtsSignal *) sig)->common.specific.next;
+ remove_mq_sig(c_p, sig, next_sig, next_nm_sig);
+}
+
+static ERTS_INLINE void
+convert_to_msg(Process *c_p, ErtsMessage *sig, ErtsMessage *msg,
+ ErtsMessage ***next_nm_sig)
+{
+ ErtsMessage **next_sig = *next_nm_sig;
+ ASSERT(ERTS_SIG_IS_NON_MSG(sig));
+ *next_nm_sig = ((ErtsSignal *) sig)->common.specific.next;
+ c_p->sig_qs.len++;
+ *next_sig = msg;
+ remove_mq_sig(c_p, sig, &msg->next, next_nm_sig);
+}
+
+static ERTS_INLINE void
+convert_to_msgs(Process *c_p, ErtsMessage *sig, Uint no_msgs,
+ ErtsMessage *first_msg, ErtsMessage *last_msg,
+ ErtsMessage ***next_nm_sig)
+{
+ ErtsMessage **next_sig = *next_nm_sig;
+ ASSERT(ERTS_SIG_IS_NON_MSG(sig));
+ *next_nm_sig = ((ErtsSignal *) sig)->common.specific.next;
+ c_p->sig_qs.len += no_msgs;
+ *next_sig = first_msg;
+ remove_mq_sig(c_p, sig, &last_msg->next, next_nm_sig);
+}
+
+static ERTS_INLINE void
+insert_messages(Process *c_p, ErtsMessage **next, ErtsMessage *first,
+ ErtsMessage *last, Uint no_msgs, ErtsMessage ***next_nm_sig)
+{
+ last->next = *next;
+ if (c_p->sig_qs.cont_last == next)
+ c_p->sig_qs.cont_last = &last->next;
+ if (*next_nm_sig == next)
+ *next_nm_sig = &last->next;
+ if (c_p->sig_qs.nmsigs.last == next)
+ c_p->sig_qs.nmsigs.last = &last->next;
+ c_p->sig_qs.len += no_msgs;
+ *next = first;
+}
+
+static ERTS_INLINE void
+remove_mq_m_sig(Process *c_p, ErtsMessage *sig, ErtsMessage **next_sig, ErtsMessage ***next_nm_sig)
+{
+ /* Removing message... */
+ ASSERT(!ERTS_SIG_IS_NON_MSG(sig));
+ c_p->sig_qs.len--;
+ remove_mq_sig(c_p, sig, next_sig, next_nm_sig);
+}
+
+static ERTS_INLINE void
+remove_iq_m_sig(Process *c_p, ErtsMessage *sig, ErtsMessage **next_sig)
+{
+ /* Removing message... */
+ ASSERT(!ERTS_SIG_IS_NON_MSG(sig));
+ c_p->sig_qs.len--;
+ remove_iq_sig(c_p, sig, next_sig);
+}
+
+static ERTS_INLINE void
+convert_prepared_sig_to_msg(Process *c_p, ErtsMessage *sig, Eterm msg,
+ ErtsMessage ***next_nm_sig)
+{
+ /*
+ * Everything is already there except for the reference to
+ * the message and the combined hfrag marker that needs to be
+ * restored...
+ */
+ *next_nm_sig = ((ErtsSignal *) sig)->common.specific.next;
+ sig->data.attached = ERTS_MSG_COMBINED_HFRAG;
+ ERL_MESSAGE_TERM(sig) = msg;
+ c_p->sig_qs.len++;
+}
+
+static ERTS_INLINE int
+handle_exit_signal(Process *c_p, ErtsSigRecvTracing *tracing,
+ ErtsMessage *sig, ErtsMessage ***next_nm_sig,
+ int *exited)
+{
+ ErtsMessage *conv_msg = NULL;
+ ErtsExitSignalData *xsigd = NULL;
+ ErtsLinkData *ldp = NULL; /* Avoid erroneous warning... */
+ ErtsLink *dlnk = NULL; /* Avoid erroneous warning... */
+ Eterm tag = ((ErtsSignal *) sig)->common.tag;
+ Uint16 type = ERTS_PROC_SIG_TYPE(tag);
+ int op = ERTS_PROC_SIG_OP(tag);
+ int destroy = 0;
+ int ignore = 0;
+ int save = 0;
+ int exit = 0;
+ int cnt = 1;
+ Eterm reason;
+ Eterm from;
+
+ if (type == ERTS_SIG_Q_TYPE_GEN_EXIT) {
+ xsigd = get_exit_signal_data(sig);
+ from = xsigd->from;
+ reason = xsigd->reason;
+ if (op != ERTS_SIG_Q_OP_EXIT_LINKED)
+ ignore = 0;
+ else {
+ ErtsLink *llnk = erts_link_tree_lookup(ERTS_P_LINKS(c_p), from);
+ if (!llnk) {
+ /* Link no longer active; ignore... */
+ ignore = !0;
+ destroy = !0;
+ }
+ else {
+ ignore = 0;
+ erts_link_tree_delete(&ERTS_P_LINKS(c_p), llnk);
+ if (llnk->type != ERTS_LNK_TYPE_DIST_PROC)
+ erts_link_release(llnk);
+ else {
+ dlnk = erts_link_to_other(llnk, &ldp);
+ if (erts_link_dist_delete(dlnk))
+ erts_link_release_both(ldp);
+ else
+ erts_link_release(llnk);
+ }
+ }
+ }
+
+ if (!ignore) {
+
+ if ((op != ERTS_SIG_Q_OP_EXIT || reason != am_kill)
+ && (c_p->flags & F_TRAP_EXIT)) {
+ convert_prepared_sig_to_msg(c_p, sig,
+ xsigd->message, next_nm_sig);
+ conv_msg = sig;
+ }
+ else if (reason == am_normal && !xsigd->u.normal_kills) {
+ /* Ignore it... */
+ destroy = !0;
+ ignore = !0;
+ }
+ else {
+ /* Terminate... */
+ save = !0;
+ exit = !0;
+ if (op == ERTS_SIG_Q_OP_EXIT && reason == am_kill)
+ reason = am_killed;
+ }
+ }
+ }
+ else { /* Link exit */
+ ErtsLink *slnk = (ErtsLink *) sig;
+ ErtsLink *llnk = erts_link_to_other(slnk, &ldp);
+
+ ASSERT(type == ERTS_LNK_TYPE_PROC
+ || type == ERTS_LNK_TYPE_PORT
+ || type == ERTS_LNK_TYPE_DIST_PROC);
+
+ from = llnk->other.item;
+ reason = slnk->other.item; /* reason in other.item ... */
+ ASSERT(is_pid(from) || is_internal_port(from));
+ ASSERT(is_immed(reason));
+ ASSERT(op == ERTS_SIG_Q_OP_EXIT_LINKED);
+ dlnk = erts_link_tree_key_delete(&ERTS_P_LINKS(c_p), llnk);
+ if (!dlnk) {
+ ignore = !0; /* Link no longer active; ignore... */
+ ldp = NULL;
+ }
+ else {
+ Eterm pid;
+ ErtsMessage *mp;
+ ErtsProcLocks locks;
+ Uint hsz;
+ Eterm *hp;
+ ErlOffHeap *ohp;
+ ignore = 0;
+ if (dlnk == llnk)
+ dlnk = NULL;
+ else
+ ldp = NULL;
+
+ ASSERT(is_immed(reason));
+
+ if (!(c_p->flags & F_TRAP_EXIT)) {
+ if (reason == am_normal)
+ ignore = !0; /* Ignore it... */
+ else
+ exit = !0; /* Terminate... */
+ }
+ else {
+
+ /*
+ * Create and EXIT message and replace
+ * the original signal with the message...
+ */
+
+ locks = ERTS_PROC_LOCK_MAIN;
+
+ hsz = 4 + NC_HEAP_SIZE(from);
+
+ mp = erts_alloc_message_heap(c_p, &locks, hsz, &hp, &ohp);
+
+ if (locks != ERTS_PROC_LOCK_MAIN)
+ erts_proc_unlock(c_p, locks & ~ERTS_PROC_LOCK_MAIN);
+
+ pid = STORE_NC(&hp, ohp, from);
+
+ ERL_MESSAGE_TERM(mp) = TUPLE3(hp, am_EXIT, pid, reason);
+ ERL_MESSAGE_TOKEN(mp) = NIL;
+#ifdef USE_VM_PROBES
+ ERL_MESSAGE_DT_UTAG(mp) = NIL;
+#endif
+ if (is_immed(pid))
+ ERL_MESSAGE_FROM(mp) = pid;
+ else {
+ DistEntry *dep;
+ ASSERT(is_external_pid(pid));
+ dep = external_pid_dist_entry(pid);
+ ERL_MESSAGE_FROM(mp) = dep->sysname;
+ }
+
+ /* Replace original signal with the exit message... */
+ convert_to_msg(c_p, sig, mp, next_nm_sig);
+
+ cnt += 4;
+
+ conv_msg = mp;
+ }
+ }
+ destroy = !0;
+ }
+
+ if (ignore|exit) {
+ remove_nm_sig(c_p, sig, next_nm_sig);
+ if (exit) {
+ if (save) {
+ sig->data.attached = ERTS_MSG_COMBINED_HFRAG;
+ ERL_MESSAGE_TERM(sig) = xsigd->message;
+ erts_save_message_in_proc(c_p, sig);
+ }
+ /* Exit process... */
+ erts_set_self_exiting(c_p, reason);
+
+ cnt++;
+ }
+ }
+
+ if (!exit) {
+ if (conv_msg)
+ erts_proc_notify_new_message(c_p, ERTS_PROC_LOCK_MAIN);
+ if (op == ERTS_SIG_Q_OP_EXIT_LINKED && tracing->procs)
+ getting_unlinked(c_p, from);
+ }
+
+ if (destroy) {
+ cnt++;
+ if (type == ERTS_SIG_Q_TYPE_GEN_EXIT) {
+ sig->next = NULL;
+ erts_cleanup_messages(sig);
+ }
+ else {
+ if (ldp)
+ erts_link_release_both(ldp);
+ else {
+ if (dlnk)
+ erts_link_release(dlnk);
+ erts_link_release((ErtsLink *) sig);
+ }
+ }
+ }
+
+ *exited = exit;
+
+ return cnt;
+}
+
+static ERTS_INLINE int
+convert_prepared_down_message(Process *c_p, ErtsMessage *sig,
+ Eterm msg, ErtsMessage ***next_nm_sig)
+{
+ convert_prepared_sig_to_msg(c_p, sig, msg, next_nm_sig);
+ erts_proc_notify_new_message(c_p, ERTS_PROC_LOCK_MAIN);
+ return 1;
+}
+
+static int
+convert_to_down_message(Process *c_p,
+ ErtsMessage *sig,
+ ErtsMonitorData *mdp,
+ Uint16 mon_type,
+ ErtsMessage ***next_nm_sig)
+{
+ /*
+ * Create a 'DOWN' message and replace the signal
+ * with it...
+ */
+ int cnt = 0;
+ Eterm node = am_undefined;
+ ErtsMessage *mp;
+ ErtsProcLocks locks;
+ Uint hsz;
+ Eterm *hp, ref, from, type, reason;
+ ErlOffHeap *ohp;
+
+ ASSERT(mdp);
+ ASSERT((mdp->origin.flags & ERTS_ML_FLGS_SAME)
+ == (mdp->target.flags & ERTS_ML_FLGS_SAME));
+
+ hsz = 6; /* 5-tuple */
+
+ if (mdp->origin.flags & ERTS_ML_FLG_NAME)
+ hsz += 3; /* reg name 2-tuple */
+ else {
+ ASSERT(is_pid(mdp->origin.other.item)
+ || is_internal_port(mdp->origin.other.item));
+ hsz += NC_HEAP_SIZE(mdp->origin.other.item);
+ }
+
+ ASSERT(is_ref(mdp->ref));
+ hsz += NC_HEAP_SIZE(mdp->ref);
+
+ locks = ERTS_PROC_LOCK_MAIN;
+
+ /* reason is mdp->target.other.item */
+ reason = mdp->target.other.item;
+ ASSERT(is_immed(reason));
+
+ mp = erts_alloc_message_heap(c_p, &locks, hsz, &hp, &ohp);
+
+ if (locks != ERTS_PROC_LOCK_MAIN)
+ erts_proc_unlock(c_p, locks & ~ERTS_PROC_LOCK_MAIN);
+
+ cnt += 4;
+
+ ref = STORE_NC(&hp, ohp, mdp->ref);
+
+ if (!(mdp->origin.flags & ERTS_ML_FLG_NAME)) {
+ from = STORE_NC(&hp, ohp, mdp->origin.other.item);
+ }
+ else {
+ ErtsMonitorDataExtended *mdep;
+ ASSERT(mdp->origin.flags & ERTS_ML_FLG_EXTENDED);
+ mdep = (ErtsMonitorDataExtended *) mdp;
+ ASSERT(is_atom(mdep->u.name));
+ if (mdep->dist)
+ node = mdep->dist->nodename;
+ else
+ node = erts_this_dist_entry->sysname;
+ from = TUPLE2(hp, mdep->u.name, node);
+ hp += 3;
+ }
+
+ ASSERT(mdp->origin.type == mon_type);
+ switch (mon_type) {
+ case ERTS_MON_TYPE_PORT:
+ type = am_port;
+ if (mdp->origin.other.item == am_undefined) {
+ /* failed by name... */
+ ERL_MESSAGE_FROM(mp) = am_system;
+ }
+ else {
+ ASSERT(is_internal_port(mdp->origin.other.item));
+ ERL_MESSAGE_FROM(mp) = mdp->origin.other.item;
+ }
+ break;
+ case ERTS_MON_TYPE_PROC:
+ type = am_process;
+ if (mdp->origin.other.item == am_undefined) {
+ /* failed by name... */
+ ERL_MESSAGE_FROM(mp) = am_system;
+ }
+ else {
+ ASSERT(is_internal_pid(mdp->origin.other.item));
+ ERL_MESSAGE_FROM(mp) = mdp->origin.other.item;
+ }
+ break;
+ case ERTS_MON_TYPE_DIST_PROC:
+ type = am_process;
+ if (node == am_undefined) {
+ ErtsMonitorDataExtended *mdep;
+ ASSERT(mdp->origin.flags & ERTS_ML_FLG_EXTENDED);
+ mdep = (ErtsMonitorDataExtended *) mdp;
+ ASSERT(mdep->dist);
+ node = mdep->dist->nodename;
+ }
+ ASSERT(is_atom(node) && node != am_undefined);
+ ERL_MESSAGE_FROM(mp) = node;
+ break;
+ default:
+ ERTS_INTERNAL_ERROR("Unexpected monitor type");
+ type = am_undefined;
+ ERL_MESSAGE_FROM(mp) = am_undefined;
+ break;
+ }
+
+ ERL_MESSAGE_TERM(mp) = TUPLE5(hp, am_DOWN, ref,
+ type, from, reason);
+ hp += 6;
+
+ ERL_MESSAGE_TOKEN(mp) = NIL;
+#ifdef USE_VM_PROBES
+ ERL_MESSAGE_DT_UTAG(mp) = NIL;
+#endif
+ /* Replace original signal with the exit message... */
+ convert_to_msg(c_p, sig, mp, next_nm_sig);
+
+ cnt += 4;
+
+ erts_proc_notify_new_message(c_p, ERTS_PROC_LOCK_MAIN);
+
+ return cnt;
+}
+
+static ERTS_INLINE int
+convert_to_nodedown_messages(Process *c_p,
+ ErtsMessage *sig,
+ ErtsMonitorData *mdp,
+ ErtsMessage ***next_nm_sig)
+{
+ int cnt = 1;
+ Uint n;
+ ErtsMonitorDataExtended *mdep = (ErtsMonitorDataExtended *) mdp;
+
+ ASSERT((mdp->origin.flags & ERTS_ML_FLGS_SAME)
+ == (mdp->target.flags & ERTS_ML_FLGS_SAME));
+ ASSERT(mdp->origin.flags & ERTS_ML_FLG_EXTENDED);
+
+ n = mdep->u.refc;
+
+ if (n == 0)
+ remove_nm_sig(c_p, sig, next_nm_sig);
+ else {
+ Uint i;
+ ErtsMessage *nd_first = NULL;
+ ErtsMessage *nd_last = NULL;
+ ErtsProcLocks locks = ERTS_PROC_LOCK_MAIN;
+ Eterm node = mdep->dist->nodename;
+
+ ASSERT(is_atom(node));
+ ASSERT(n > 0);
+
+ for (i = 0; i < n; i++) {
+ ErtsMessage *mp;
+ ErlOffHeap *ohp;
+ Eterm *hp;
+
+ mp = erts_alloc_message_heap(c_p, &locks, 3, &hp, &ohp);
+
+ ERL_MESSAGE_TERM(mp) = TUPLE2(hp, am_nodedown, node);
+ ERL_MESSAGE_FROM(mp) = am_system;
+ ERL_MESSAGE_TOKEN(mp) = NIL;
+#ifdef USE_VM_PROBES
+ ERL_MESSAGE_DT_UTAG(mp) = NIL;
+#endif
+ mp->next = nd_first;
+ nd_first = mp;
+ if (!nd_last)
+ nd_last = mp;
+ cnt++;
+ }
+
+ if (locks != ERTS_PROC_LOCK_MAIN)
+ erts_proc_unlock(c_p, locks & ~ERTS_PROC_LOCK_MAIN);
+
+ /* Replace signal with 'nodedown' messages */
+ convert_to_msgs(c_p, sig, n, nd_first, nd_last, next_nm_sig);
+
+ erts_proc_notify_new_message(c_p, ERTS_PROC_LOCK_MAIN);
+ }
+ return cnt;
+}
+
+static int
+handle_nodedown(Process *c_p,
+ ErtsMessage *sig,
+ ErtsMonitorData *mdp,
+ ErtsMessage ***next_nm_sig)
+{
+ ErtsMonitorDataExtended *mdep = (ErtsMonitorDataExtended *) mdp;
+ ErtsMonitor *omon = &mdp->origin;
+ int not_in_subtab = !(omon->flags & ERTS_ML_FLG_IN_SUBTABLE);
+ int cnt = 1;
+
+ ASSERT(erts_monitor_is_in_table(omon));
+
+ if (not_in_subtab & !mdep->uptr.node_monitors)
+ erts_monitor_tree_delete(&ERTS_P_MONITORS(c_p), omon);
+ else if (not_in_subtab) {
+ ErtsMonitor *sub_mon;
+ ErtsMonitorDataExtended *sub_mdep;
+ sub_mon = erts_monitor_list_last(mdep->uptr.node_monitors);
+ ASSERT(sub_mon);
+ erts_monitor_list_delete(&mdep->uptr.node_monitors, sub_mon);
+ sub_mon->flags &= ~ERTS_ML_FLG_IN_SUBTABLE;
+ sub_mdep = (ErtsMonitorDataExtended *) erts_monitor_to_data(sub_mon);
+ ASSERT(!sub_mdep->uptr.node_monitors);
+ sub_mdep->uptr.node_monitors = mdep->uptr.node_monitors;
+ mdep->uptr.node_monitors = NULL;
+ erts_monitor_tree_replace(&ERTS_P_MONITORS(c_p), omon, sub_mon);
+ cnt += 2;
+ }
+ else {
+ ErtsMonitorDataExtended *top_mdep;
+ ErtsMonitor *top_mon;
+ ASSERT(is_atom(omon->other.item));
+ ASSERT(!mdep->uptr.node_monitors);
+ top_mon = erts_monitor_tree_lookup(ERTS_P_MONITORS(c_p),
+ omon->other.item);
+ ASSERT(top_mon);
+ top_mdep = (ErtsMonitorDataExtended *) erts_monitor_to_data(top_mon);
+ ASSERT(top_mdep->uptr.node_monitors);
+ erts_monitor_list_delete(&top_mdep->uptr.node_monitors, omon);
+ omon->flags &= ~ERTS_ML_FLG_IN_SUBTABLE;
+ cnt += 3;
+ }
+
+ return cnt + convert_to_nodedown_messages(c_p, sig, mdp, next_nm_sig);
+}
+
+static void
+handle_persistent_mon_msg(Process *c_p, Uint16 type,
+ ErtsMonitor *mon, ErtsMessage *sig,
+ Eterm msg, ErtsMessage ***next_nm_sig)
+{
+ convert_prepared_sig_to_msg(c_p, sig, msg, next_nm_sig);
+
+ switch (type) {
+
+ case ERTS_MON_TYPE_TIME_OFFSET:
+ ASSERT(mon->type == ERTS_MON_TYPE_TIME_OFFSET);
+ break;
+
+ case ERTS_MON_TYPE_NODES: {
+ ErtsMonitorDataExtended *mdep;
+ Uint n;
+ ASSERT(mon->type == ERTS_MON_TYPE_NODES);
+ mdep = (ErtsMonitorDataExtended *) erts_monitor_to_data(mon);
+ ERTS_ML_ASSERT(mdep->u.refc > 0);
+ n = mdep->u.refc;
+ n--;
+ if (n > 0) {
+ ErtsProcLocks locks = ERTS_PROC_LOCK_MAIN;
+ ErtsMessage *first = NULL, *prev, *last;
+ Uint hsz = size_object(msg);
+ Uint i;
+
+ for (i = 0; i < n; i++) {
+ Eterm *hp;
+ ErlOffHeap *ohp;
+
+ last = erts_alloc_message_heap(c_p, &locks, hsz, &hp, &ohp);
+
+ if (!first)
+ first = last;
+ else
+ prev->next = last;
+ prev = last;
+
+ ERL_MESSAGE_TERM(last) = copy_struct(msg, hsz, &hp, ohp);
+
+#ifdef USE_VM_PROBES
+ ASSERT(is_immed(ERL_MESSAGE_DT_UTAG(sig)));
+ ERL_MESSAGE_DT_UTAG(last) = ERL_MESSAGE_DT_UTAG(sig);
+#endif
+ ASSERT(is_immed(ERL_MESSAGE_TOKEN(sig)));
+ ERL_MESSAGE_TOKEN(last) = ERL_MESSAGE_TOKEN(sig);
+ ASSERT(is_immed(ERL_MESSAGE_FROM(sig)));
+ ERL_MESSAGE_FROM(last) = ERL_MESSAGE_FROM(sig);
+
+ }
+ if (locks != ERTS_PROC_LOCK_MAIN)
+ erts_proc_unlock(c_p, locks & ~ERTS_PROC_LOCK_MAIN);
+ insert_messages(c_p, &sig->next, first, last, n, next_nm_sig);
+ }
+ break;
+ }
+
+ default:
+ ERTS_INTERNAL_ERROR("Invalid type");
+ break;
+ }
+
+ erts_proc_notify_new_message(c_p, ERTS_PROC_LOCK_MAIN);
+}
+
+static void
+group_leader_reply(Process *c_p, Eterm to, Eterm ref, int success)
+{
+ Process *rp = erts_proc_lookup(to);
+
+ if (rp) {
+ ErtsProcLocks locks;
+ Uint sz;
+ Eterm *hp, msg, ref_cpy, result;
+ ErlOffHeap *ohp;
+ ErtsMessage *mp;
+
+ ASSERT(is_internal_ref(ref));
+
+ locks = c_p == rp ? ERTS_PROC_LOCK_MAIN : 0;
+ sz = size_object(ref);
+
+ mp = erts_alloc_message_heap(rp, &locks, sz+3,
+ &hp, &ohp);
+
+ ref_cpy = copy_struct(ref, sz, &hp, ohp);
+ result = success ? am_true : am_badarg;
+ msg = TUPLE2(hp, ref_cpy, result);
+
+ erts_queue_message(rp, locks, mp, msg, am_system);
+
+ if (c_p == rp)
+ locks &= ~ERTS_PROC_LOCK_MAIN;
+
+ if (locks)
+ erts_proc_unlock(rp, locks);
+ }
+}
+
+static void
+handle_group_leader(Process *c_p, ErtsSigGroupLeader *sgl)
+{
+ erts_aint_t flags;
+
+ flags = erts_atomic_read_band_nob(&sgl->flags, ~ERTS_SIG_GL_FLG_ACTIVE);
+ if (flags & ERTS_SIG_GL_FLG_ACTIVE) {
+ int res = erts_set_group_leader(c_p, sgl->group_leader);
+ if (is_internal_pid(sgl->reply_to))
+ group_leader_reply(c_p, sgl->reply_to, sgl->ref, res);
+ }
+
+ flags = erts_atomic_read_band_nob(&sgl->flags, ~ERTS_SIG_GL_FLG_RECEIVER);
+ if ((flags & ~ERTS_SIG_GL_FLG_RECEIVER) == 0)
+ destroy_sig_group_leader(sgl);
+}
+
+static void
+check_push_msgq_len_offs_marker(Process *rp, ErtsSignal *sig)
+{
+ ErtsProcessInfoSig *pisig = (ErtsProcessInfoSig *) sig;
+
+ ASSERT(ERTS_PROC_SIG_OP(sig->common.tag) == ERTS_SIG_Q_OP_PROCESS_INFO);
+
+ if (pisig->msgq_len_offset == ERTS_PROC_SIG_PI_MSGQ_LEN_SYNC) {
+ ErtsProcSigMsgQLenOffsetMarker *mrkr;
+ Sint len, msgq_len_offset;
+ ErtsMessage *first = rp->sig_inq.first;
+ ASSERT(first);
+ if (((ErtsSignal *) first)->common.tag == ERTS_PROC_SIG_MSGQ_LEN_OFFS_MARK)
+ mrkr = (ErtsProcSigMsgQLenOffsetMarker *) first;
+ else {
+ mrkr = &pisig->marker;
+
+ ASSERT(mrkr->common.tag == ERTS_PROC_SIG_MSGQ_LEN_OFFS_MARK);
+
+ mrkr->common.next = first;
+ ASSERT(rp->sig_inq.last != &rp->sig_inq.first);
+ if (rp->sig_inq.nmsigs.next == &rp->sig_inq.first)
+ rp->sig_inq.nmsigs.next = &mrkr->common.next;
+ if (rp->sig_inq.nmsigs.last == &rp->sig_inq.first)
+ rp->sig_inq.nmsigs.last = &mrkr->common.next;
+ rp->sig_inq.first = (ErtsMessage *) mrkr;
+ }
+
+ len = rp->sig_inq.len;
+ msgq_len_offset = len - mrkr->len_offset;
+
+ mrkr->len_offset = len;
+ mrkr->refc++;
+
+ pisig->msgq_len_offset = msgq_len_offset;
+
+#ifdef DEBUG
+ /* save pointer to used marker... */
+ pisig->marker.common.specific.attachment = (void *) mrkr;
+#endif
+
+ }
+}
+
+static void
+destroy_process_info_request(Process *c_p, ErtsProcessInfoSig *pisig)
+{
+ int dealloc_pisig = !0;
+
+ if (pisig->msgq_len_offset != ERTS_PROC_SIG_PI_MSGQ_LEN_IGNORE) {
+ Sint refc;
+ int dealloc_marker = 0;
+ ErtsProcSigMsgQLenOffsetMarker *marker;
+#ifdef ERTS_PROC_SIG_HARD_DEBUG_SIGQ_MSG_LEN
+ Sint delayed_len;
+#endif
+
+ ASSERT(pisig->msgq_len_offset >= 0);
+
+ erts_proc_lock(c_p, ERTS_PROC_LOCK_MSGQ);
+ marker = (ErtsProcSigMsgQLenOffsetMarker *) c_p->sig_inq.first;
+ ASSERT(marker);
+ ASSERT(marker->refc > 0);
+ ASSERT(pisig->marker.common.specific.attachment == (void *) marker);
+
+ marker->delayed_len -= pisig->msgq_len_offset;
+#ifdef ERTS_PROC_SIG_HARD_DEBUG_SIGQ_MSG_LEN
+ delayed_len = marker->delayed_len;
+#endif
+
+ refc = --marker->refc;
+ if (refc) {
+ if (marker == &pisig->marker) {
+ /* Another signal using our marker... */
+ dealloc_pisig = 0;
+ }
+ }
+ else {
+ /* Marker unused; remove it... */
+ ASSERT(marker->delayed_len + marker->len_offset == 0);
+#ifdef ERTS_PROC_SIG_HARD_DEBUG_SIGQ_MSG_LEN
+ delayed_len += marker->len_offset;
+#endif
+ if (marker != &pisig->marker)
+ dealloc_marker = !0; /* used another signals marker... */
+ c_p->sig_inq.first = marker->common.next;
+ if (c_p->sig_inq.last == &marker->common.next)
+ c_p->sig_inq.last = &c_p->sig_inq.first;
+ if (c_p->sig_inq.nmsigs.next == &marker->common.next)
+ c_p->sig_inq.nmsigs.next = &c_p->sig_inq.first;
+ if (c_p->sig_inq.nmsigs.last == &marker->common.next)
+ c_p->sig_inq.nmsigs.last = &c_p->sig_inq.first;
+ }
+ erts_proc_unlock(c_p, ERTS_PROC_LOCK_MSGQ);
+
+ if (!refc) {
+ c_p->flags &= ~F_DELAYED_PSIGQS_LEN;
+ /* Adjust msg len of inner+middle queue */
+ ASSERT(marker->len_offset <= 0);
+ c_p->sig_qs.len -= marker->len_offset;
+
+ ASSERT(c_p->sig_qs.len >= 0);
+ }
+
+#ifdef ERTS_PROC_SIG_HARD_DEBUG_SIGQ_MSG_LEN
+ {
+ Sint len = 0;
+ ERTS_FOREACH_SIG_PRIVQS(
+ c_p, mp,
+ {
+ if (ERTS_SIG_IS_MSG(mp))
+ len++;
+ });
+ ERTS_ASSERT(c_p->sig_qs.len + delayed_len == len);
+ }
+#endif
+
+
+ if (dealloc_marker) {
+ ErtsProcessInfoSig *pisig2
+ = (ErtsProcessInfoSig *) (((char *) marker)
+ - offsetof(ErtsProcessInfoSig,
+ marker));
+ erts_free(ERTS_ALC_T_SIG_DATA, pisig2);
+ }
+ }
+
+ if (dealloc_pisig)
+ erts_free(ERTS_ALC_T_SIG_DATA, pisig);
+}
+
+static int
+handle_process_info(Process *c_p, ErtsMessage *sig,
+ ErtsMessage ***next_nm_sig, int is_alive)
+{
+ ErtsProcessInfoSig *pisig = (ErtsProcessInfoSig *) sig;
+ Uint reds = 0;
+ Process *rp;
+
+ if (pisig->msgq_len_offset != ERTS_PROC_SIG_PI_MSGQ_LEN_IGNORE) {
+ /*
+ * Request requires message queue data to be updated
+ * before inspection...
+ */
+
+ ASSERT(pisig->msgq_len_offset >= 0);
+ /*
+ * Update sig_qs.len to reflect the length
+ * of the message queue...
+ */
+ c_p->sig_qs.len += pisig->msgq_len_offset;
+
+ if (is_alive) {
+ /*
+ * Move messages part of message queue into inner
+ * signal queue...
+ */
+ if (*next_nm_sig != &c_p->sig_qs.cont) {
+ *c_p->sig_qs.last = c_p->sig_qs.cont;
+ c_p->sig_qs.last = *next_nm_sig;
+
+ c_p->sig_qs.cont = **next_nm_sig;
+ if (c_p->sig_qs.nmsigs.last == *next_nm_sig)
+ c_p->sig_qs.nmsigs.last = &c_p->sig_qs.cont;
+ *next_nm_sig = &c_p->sig_qs.cont;
+ *c_p->sig_qs.last = NULL;
+ }
+
+ if (!pisig->common.specific.next) {
+ /*
+ * No more signals in middle queue...
+ *
+ * Process-info 'status' needs sig-q
+ * process flag to be updated in order
+ * to show accurate result...
+ */
+ erts_atomic32_read_band_nob(&c_p->state,
+ ~ERTS_PSFLG_SIG_Q);
+ }
+
+#ifdef ERTS_PROC_SIG_HARD_DEBUG_SIGQ_MSG_LEN
+ {
+ Sint len;
+ ErtsMessage *mp;
+ for (mp = c_p->sig_qs.first, len = 0; mp; mp = mp->next) {
+ ERTS_ASSERT(ERTS_SIG_IS_MSG(mp));
+ len++;
+ }
+ ERTS_ASSERT(c_p->sig_qs.len == len);
+ }
+#endif
+ }
+ }
+ if (is_alive)
+ remove_nm_sig(c_p, sig, next_nm_sig);
+
+ rp = erts_proc_lookup(pisig->requester);
+ ASSERT(c_p != rp);
+ if (rp) {
+ Eterm msg, res, ref, *hp;
+ ErtsProcLocks locks = 0;
+ ErtsHeapFactory hfact;
+ ErtsMessage *mp;
+ Uint reserve_size = 3 + sizeof(pisig->oref_thing)/sizeof(Eterm);
+
+ if (!is_alive) {
+ ErlOffHeap *ohp;
+ mp = erts_alloc_message_heap(rp, &locks, reserve_size, &hp, &ohp);
+ res = am_undefined;
+ }
+ else {
+ ErlHeapFragment *hfrag;
+
+ reserve_size += pisig->reserve_size;
+
+ mp = erts_alloc_message(0, NULL);
+ hfrag = new_message_buffer(reserve_size);
+ mp->data.heap_frag = hfrag;
+ erts_factory_selfcontained_message_init(&hfact, mp, &hfrag->mem[0]);
+
+ res = erts_process_info(c_p, &hfact, c_p, ERTS_PROC_LOCK_MAIN,
+ pisig->item_ix, pisig->len,
+ pisig->flags, reserve_size, &reds);
+
+ hp = erts_produce_heap(&hfact,
+ 3 + sizeof(pisig->oref_thing)/sizeof(Eterm),
+ 0);
+ }
+
+ sys_memcpy((void *) hp, (void *) &pisig->oref_thing,
+ sizeof(pisig->oref_thing));
+ ref = make_internal_ref(hp);
+ hp += sizeof(pisig->oref_thing)/sizeof(Eterm);
+
+ msg = TUPLE2(hp, ref, res);
+
+ if (is_alive)
+ erts_factory_trim_and_close(&hfact, &msg, 1);
+
+ erts_queue_message(rp, locks, mp, msg, c_p->common.id);
+
+ if (!is_alive && locks)
+ erts_proc_unlock(rp, locks);
+ }
+
+ destroy_process_info_request(c_p, pisig);
+
+ if (reds > INT_MAX/8)
+ reds = INT_MAX/8;
+
+ return ((int) reds)*4 + 8;
+}
+
+/*
+ * Called in order to handle incoming signals.
+ */
+
+int
+erts_proc_sig_handle_incoming(Process *c_p, erts_aint32_t *statep,
+ int *redsp, int max_reds, int local_only)
+{
+ Eterm tag;
+ erts_aint32_t state;
+ int cnt, limit, abs_lim, msg_tracing;
+ ErtsMessage *sig, ***next_nm_sig;
+ ErtsSigRecvTracing tracing;
+
+ ERTS_HDBG_CHECK_SIGNAL_PRIV_QUEUE(c_p, 0);
+ ERTS_LC_ASSERT(ERTS_PROC_LOCK_MAIN == erts_proc_lc_my_proc_locks(c_p));
+
+ if (local_only)
+ state = -1; /* can never be a valid state... */
+ else {
+ state = erts_atomic32_read_nob(&c_p->state);
+ if (ERTS_PSFLG_SIG_IN_Q & state) {
+ erts_proc_lock(c_p, ERTS_PROC_LOCK_MSGQ);
+ erts_proc_sig_fetch(c_p);
+ erts_proc_unlock(c_p, ERTS_PROC_LOCK_MSGQ);
+ }
+ }
+
+ limit = *redsp;
+ *redsp = 0;
+
+ if (!c_p->sig_qs.cont) {
+ if (state == -1)
+ *statep = erts_atomic32_read_nob(&c_p->state);
+ else
+ *statep = state;
+ return !0;
+ }
+
+ next_nm_sig = &c_p->sig_qs.nmsigs.next;
+
+ setup_tracing_state(c_p, &tracing);
+ msg_tracing = tracing.messages.active;
+
+ limit *= ERTS_SIG_REDS_CNT_FACTOR;
+ abs_lim = ERTS_SIG_REDS_CNT_FACTOR*max_reds;
+ if (limit > abs_lim)
+ limit = abs_lim;
+
+ cnt = 0;
+
+ do {
+
+ if (msg_tracing) {
+ ERTS_PROC_SIG_HDBG_PRIV_CHKQ(c_p, &tracing, next_nm_sig);
+ if (handle_msg_tracing(c_p, &tracing, next_nm_sig) != 0) {
+ ERTS_PROC_SIG_HDBG_PRIV_CHKQ(c_p, &tracing, next_nm_sig);
+ break; /* tracing limit or end... */
+ }
+ ERTS_PROC_SIG_HDBG_PRIV_CHKQ(c_p, &tracing, next_nm_sig);
+ }
+
+ if (!*next_nm_sig)
+ break;
+
+ sig = **next_nm_sig;
+
+ ASSERT(sig);
+ ASSERT(ERTS_SIG_IS_NON_MSG(sig));
+
+ tag = ((ErtsSignal *) sig)->common.tag;
+
+ switch (ERTS_PROC_SIG_OP(tag)) {
+
+ case ERTS_SIG_Q_OP_EXIT:
+ case ERTS_SIG_Q_OP_EXIT_LINKED: {
+ int exited;
+
+ ERTS_PROC_SIG_HDBG_PRIV_CHKQ(c_p, &tracing, next_nm_sig);
+
+ cnt += handle_exit_signal(c_p, &tracing, sig,
+ next_nm_sig, &exited);
+
+ ERTS_PROC_SIG_HDBG_PRIV_CHKQ(c_p, &tracing, next_nm_sig);
+
+ if (exited)
+ goto stop; /* terminated by signal */
+ /* ignored or converted to exit message... */
+ break;
+ }
+
+ case ERTS_SIG_Q_OP_MONITOR_DOWN: {
+ Uint16 type = ERTS_PROC_SIG_TYPE(tag);
+ ErtsExitSignalData *xsigd = NULL;
+ ErtsMonitorData *mdp = NULL;
+ ErtsMonitor *omon = NULL, *tmon = NULL;
+
+ ERTS_PROC_SIG_HDBG_PRIV_CHKQ(c_p, &tracing, next_nm_sig);
+
+ switch (type) {
+ case ERTS_MON_TYPE_DIST_PROC:
+ case ERTS_MON_TYPE_PROC:
+ case ERTS_MON_TYPE_PORT:
+ tmon = (ErtsMonitor *) sig;
+ ASSERT(erts_monitor_is_target(tmon));
+ ASSERT(!erts_monitor_is_in_table(tmon));
+ mdp = erts_monitor_to_data(tmon);
+ if (erts_monitor_is_in_table(&mdp->origin)) {
+ omon = &mdp->origin;
+ erts_monitor_tree_delete(&ERTS_P_MONITORS(c_p),
+ omon);
+ cnt += convert_to_down_message(c_p, sig, mdp,
+ type, next_nm_sig);
+ }
+ break;
+ case ERTS_SIG_Q_TYPE_GEN_EXIT:
+ xsigd = get_exit_signal_data(sig);
+ omon = erts_monitor_tree_lookup(ERTS_P_MONITORS(c_p),
+ xsigd->u.ref);
+ if (omon) {
+ ASSERT(erts_monitor_is_origin(omon));
+ if (omon->type == ERTS_MON_TYPE_DIST_PROC) {
+ mdp = erts_monitor_to_data(omon);
+ if (erts_monitor_dist_delete(&mdp->target))
+ tmon = &mdp->target;
+ }
+ erts_monitor_tree_delete(&ERTS_P_MONITORS(c_p),
+ omon);
+ cnt += convert_prepared_down_message(c_p, sig,
+ xsigd->message,
+ next_nm_sig);
+ }
+ break;
+ case ERTS_MON_TYPE_NODE:
+ tmon = (ErtsMonitor *) sig;
+ ASSERT(erts_monitor_is_target(tmon));
+ ASSERT(!erts_monitor_is_in_table(tmon));
+ mdp = erts_monitor_to_data(tmon);
+ if (erts_monitor_is_in_table(&mdp->origin)) {
+ omon = &mdp->origin;
+ cnt += handle_nodedown(c_p, sig, mdp, next_nm_sig);
+ }
+ break;
+ default:
+ ERTS_INTERNAL_ERROR("invalid monitor type");
+ break;
+ }
+
+ if (omon) {
+ if (tmon)
+ erts_monitor_release_both(mdp);
+ else
+ erts_monitor_release(omon);
+ }
+ else {
+ remove_nm_sig(c_p, sig, next_nm_sig);
+ if (xsigd) {
+ sig->next = NULL;
+ erts_cleanup_messages(sig);
+ }
+ if (tmon)
+ erts_monitor_release(tmon);
+ }
+
+ ERTS_PROC_SIG_HDBG_PRIV_CHKQ(c_p, &tracing, next_nm_sig);
+ break;
+ }
+
+ case ERTS_SIG_Q_OP_PERSISTENT_MON_MSG: {
+ Uint16 type = ERTS_PROC_SIG_TYPE(tag);
+ ErtsMonitor *mon;
+ Eterm msg;
+ Eterm key;
+
+ ERTS_PROC_SIG_HDBG_PRIV_CHKQ(c_p, &tracing, next_nm_sig);
+
+ key = get_persist_mon_msg(sig, &msg);
+
+ cnt++;
+ mon = erts_monitor_tree_lookup(ERTS_P_MONITORS(c_p), key);
+ if (mon) {
+ ASSERT(erts_monitor_is_origin(mon));
+ handle_persistent_mon_msg(c_p, type, mon, sig,
+ msg, next_nm_sig);
+ }
+ else {
+ cnt++;
+ remove_nm_sig(c_p, sig, next_nm_sig);
+ sig->next = NULL;
+ erts_cleanup_messages(sig);
+ }
+
+ ERTS_PROC_SIG_HDBG_PRIV_CHKQ(c_p, &tracing, next_nm_sig);
+ break;
+ }
+
+ case ERTS_SIG_Q_OP_MONITOR: {
+ ErtsMonitor *mon = (ErtsMonitor *) sig;
+
+ ASSERT(erts_monitor_is_target(mon));
+ ERTS_PROC_SIG_HDBG_PRIV_CHKQ(c_p, &tracing, next_nm_sig);
+
+ remove_nm_sig(c_p, sig, next_nm_sig);
+
+ if (mon->type == ERTS_MON_TYPE_DIST_PROC)
+ erts_monitor_tree_insert(&ERTS_P_MONITORS(c_p), mon);
+ else
+ erts_monitor_list_insert(&ERTS_P_LT_MONITORS(c_p), mon);
+ ERTS_PROC_SIG_HDBG_PRIV_CHKQ(c_p, &tracing, next_nm_sig);
+ break;
+ }
+
+ case ERTS_SIG_Q_OP_DEMONITOR: {
+ Uint16 type = ERTS_PROC_SIG_TYPE(tag);
+
+ ERTS_PROC_SIG_HDBG_PRIV_CHKQ(c_p, &tracing, next_nm_sig);
+
+ remove_nm_sig(c_p, sig, next_nm_sig);
+
+ if (type == ERTS_SIG_Q_TYPE_DIST_PROC_DEMONITOR) {
+ ErtsMonitor *tmon;
+ ErtsSigDistProcDemonitor *dmon;
+ dmon = (ErtsSigDistProcDemonitor *) sig;
+ tmon = erts_monitor_tree_lookup(ERTS_P_MONITORS(c_p), dmon->ref);
+ destroy_dist_proc_demonitor(dmon);
+ cnt++;
+ if (tmon) {
+ ErtsMonitorData *mdp = erts_monitor_to_data(tmon);
+ ASSERT(erts_monitor_is_target(tmon));
+ erts_monitor_tree_delete(&ERTS_P_MONITORS(c_p), tmon);
+ if (!erts_monitor_dist_delete(&mdp->origin))
+ erts_monitor_release(tmon);
+ else
+ erts_monitor_release_both(mdp);
+ cnt += 2;
+ }
+ }
+ else {
+ ErtsMonitor *omon = (ErtsMonitor *) sig;
+ ErtsMonitorData *mdp = erts_monitor_to_data(omon);
+ ASSERT(omon->type == type);
+ ASSERT(erts_monitor_is_origin(omon));
+ ASSERT(!erts_monitor_is_in_table(omon));
+ if (!erts_monitor_is_in_table(&mdp->target))
+ erts_monitor_release(omon);
+ else {
+ ErtsMonitor *tmon = &mdp->target;
+ ASSERT(tmon->type == type);
+ if (type == ERTS_MON_TYPE_DIST_PROC)
+ erts_monitor_tree_delete(&ERTS_P_MONITORS(c_p), tmon);
+ else {
+ erts_monitor_list_delete(&ERTS_P_LT_MONITORS(c_p), tmon);
+ if (type == ERTS_MON_TYPE_RESOURCE) {
+ erts_nif_demonitored((ErtsResource *) tmon->other.ptr);
+ cnt++;
+ }
+ }
+ erts_monitor_release_both(mdp);
+ cnt++;
+ }
+ cnt++;
+ }
+ ERTS_PROC_SIG_HDBG_PRIV_CHKQ(c_p, &tracing, next_nm_sig);
+ break;
+ }
+
+ case ERTS_SIG_Q_OP_LINK: {
+ ErtsLink *rlnk, *lnk = (ErtsLink *) sig;
+
+ ERTS_PROC_SIG_HDBG_PRIV_CHKQ(c_p, &tracing, next_nm_sig);
+
+ remove_nm_sig(c_p, sig, next_nm_sig);
+ rlnk = erts_link_tree_insert_addr_replace(&ERTS_P_LINKS(c_p),
+ lnk);
+ if (!rlnk) {
+ if (tracing.procs)
+ getting_linked(c_p, lnk->other.item);
+ }
+ else {
+ if (rlnk->type != ERTS_LNK_TYPE_DIST_PROC)
+ erts_link_release(rlnk);
+ else {
+ ErtsLinkData *ldp;
+ ErtsLink *dlnk = erts_link_to_other(rlnk, &ldp);
+ if (erts_link_dist_delete(dlnk))
+ erts_link_release_both(ldp);
+ else
+ erts_link_release(rlnk);
+ }
+ }
+
+ ERTS_PROC_SIG_HDBG_PRIV_CHKQ(c_p, &tracing, next_nm_sig);
+ break;
+ }
+
+ case ERTS_SIG_Q_OP_UNLINK: {
+ Uint16 type = ERTS_PROC_SIG_TYPE(tag);
+ ErtsLinkData *ldp;
+ ErtsLink *llnk;
+
+ ERTS_PROC_SIG_HDBG_PRIV_CHKQ(c_p, &tracing, next_nm_sig);
+
+ remove_nm_sig(c_p, sig, next_nm_sig);
+ if (type == ERTS_SIG_Q_TYPE_DIST_LINK) {
+ ErtsSigDistLinkOp *sdlnk = (ErtsSigDistLinkOp *) sig;
+ ASSERT(type == ERTS_SIG_Q_TYPE_DIST_LINK);
+ ASSERT(is_external_pid(sdlnk->remote));
+ llnk = erts_link_tree_lookup(ERTS_P_LINKS(c_p), sdlnk->remote);
+ if (llnk) {
+ ErtsLink *dlnk = erts_link_to_other(llnk, &ldp);
+ erts_link_tree_delete(&ERTS_P_LINKS(c_p), llnk);
+ if (erts_link_dist_delete(dlnk))
+ erts_link_release_both(ldp);
+ else
+ erts_link_release(llnk);
+ cnt += 8;
+ if (tracing.procs)
+ getting_unlinked(c_p, sdlnk->remote);
+ }
+ destroy_sig_dist_link_op(sdlnk);
+ cnt++;
+ }
+ else {
+ ErtsLinkData *ldp;
+ ErtsLink *dlnk, *slnk;
+ slnk = (ErtsLink *) sig;
+ llnk = erts_link_to_other(slnk, &ldp);
+ dlnk = erts_link_tree_key_delete(&ERTS_P_LINKS(c_p), llnk);
+ if (!dlnk)
+ erts_link_release(slnk);
+ else {
+ if (tracing.procs)
+ getting_unlinked(c_p, llnk->other.item);
+ if (dlnk == llnk)
+ erts_link_release_both(ldp);
+ else {
+ erts_link_release(slnk);
+ erts_link_release(dlnk);
+ }
+ }
+ cnt += 2;
+ }
+
+ ERTS_PROC_SIG_HDBG_PRIV_CHKQ(c_p, &tracing, next_nm_sig);
+ break;
+ }
+
+ case ERTS_SIG_Q_OP_GROUP_LEADER: {
+ ErtsSigGroupLeader *sgl = (ErtsSigGroupLeader *) sig;
+ ERTS_PROC_SIG_HDBG_PRIV_CHKQ(c_p, &tracing, next_nm_sig);
+ remove_nm_sig(c_p, sig, next_nm_sig);
+ handle_group_leader(c_p, sgl);
+ ERTS_PROC_SIG_HDBG_PRIV_CHKQ(c_p, &tracing, next_nm_sig);
+ break;
+ }
+
+ case ERTS_SIG_Q_OP_IS_ALIVE:
+ ERTS_PROC_SIG_HDBG_PRIV_CHKQ(c_p, &tracing, next_nm_sig);
+ remove_nm_sig(c_p, sig, next_nm_sig);
+ is_alive_response(c_p, sig, !0);
+ ERTS_PROC_SIG_HDBG_PRIV_CHKQ(c_p, &tracing, next_nm_sig);
+ break;
+
+ case ERTS_SIG_Q_OP_PROCESS_INFO:
+ ERTS_PROC_SIG_HDBG_PRIV_CHKQ(c_p, &tracing, next_nm_sig);
+ handle_process_info(c_p, sig, next_nm_sig, !0);
+ ERTS_PROC_SIG_HDBG_PRIV_CHKQ(c_p, &tracing, next_nm_sig);
+ break;
+
+ case ERTS_SIG_Q_OP_TRACE_CHANGE_STATE: {
+ Uint16 type = ERTS_PROC_SIG_TYPE(tag);
+
+ ERTS_PROC_SIG_HDBG_PRIV_CHKQ(c_p, &tracing, next_nm_sig);
+ msg_tracing = handle_trace_change_state(c_p, &tracing,
+ type, sig,
+ next_nm_sig);
+ ERTS_PROC_SIG_HDBG_PRIV_CHKQ(c_p, &tracing, next_nm_sig);
+
+ break;
+ }
+
+ default:
+ ERTS_INTERNAL_ERROR("Unknown signal");
+ break;
+ }
+
+ cnt++;
+
+ } while (cnt <= limit || stretch_limit(c_p, &tracing, abs_lim, &limit));
+
+stop: {
+ int deferred_save, deferred_saved_last, res;
+
+ deferred_saved_last = !!(c_p->flags & F_DEFERRED_SAVED_LAST);
+ deferred_save = 0;
+
+ if (!deferred_saved_last)
+ deferred_save = 0;
+ else {
+ if (c_p->sig_qs.saved_last == &c_p->sig_qs.cont) {
+ c_p->sig_qs.saved_last = c_p->sig_qs.last;
+ c_p->flags &= ~F_DEFERRED_SAVED_LAST;
+ deferred_saved_last = deferred_save = 0;
+ }
+ else {
+ if (c_p->sig_qs.save == c_p->sig_qs.last)
+ deferred_save = !0;
+ else
+ deferred_save = 0;
+ }
+ }
+
+ ASSERT(c_p->sig_qs.saved_last != &c_p->sig_qs.cont);
+
+ if (ERTS_UNLIKELY(msg_tracing != 0)) {
+ /*
+ * All messages that has been traced should
+ * be moved to inner queue. Next signal in
+ * middle queue should either be next message
+ * to trace or next non-message signal.
+ */
+ ASSERT(tracing.messages.next);
+ if (*next_nm_sig) {
+ if (*next_nm_sig == tracing.messages.next)
+ *next_nm_sig = &c_p->sig_qs.cont;
+ if (c_p->sig_qs.nmsigs.last == tracing.messages.next)
+ c_p->sig_qs.nmsigs.last = &c_p->sig_qs.cont;
+ *statep = erts_atomic32_read_nob(&c_p->state);
+ }
+ else {
+ ASSERT(!c_p->sig_qs.nmsigs.next);
+ c_p->sig_qs.nmsigs.last = NULL;
+ state = erts_atomic32_read_band_nob(&c_p->state,
+ ~ERTS_PSFLG_SIG_Q);
+ state &= ~ERTS_PSFLG_SIG_Q;
+ *statep = state;
+ }
+
+ if (tracing.messages.next != &c_p->sig_qs.cont) {
+ *c_p->sig_qs.last = c_p->sig_qs.cont;
+ c_p->sig_qs.last = tracing.messages.next;
+
+ c_p->sig_qs.cont = *tracing.messages.next;
+ if (!c_p->sig_qs.cont)
+ c_p->sig_qs.cont_last = &c_p->sig_qs.cont;
+ *c_p->sig_qs.last = NULL;
+ }
+
+ res = !c_p->sig_qs.cont;
+ }
+ else if (*next_nm_sig) {
+ /*
+ * All messages prior to next non-message
+ * signal should be moved to inner queue.
+ * Next non-message signal to handle should
+ * be first in middle queue.
+ */
+ ASSERT(**next_nm_sig);
+ if (*next_nm_sig != &c_p->sig_qs.cont) {
+ *c_p->sig_qs.last = c_p->sig_qs.cont;
+ c_p->sig_qs.last = *next_nm_sig;
+
+ c_p->sig_qs.cont = **next_nm_sig;
+ if (c_p->sig_qs.nmsigs.last == *next_nm_sig)
+ c_p->sig_qs.nmsigs.last = &c_p->sig_qs.cont;
+ *next_nm_sig = &c_p->sig_qs.cont;
+ *c_p->sig_qs.last = NULL;
+ }
+
+ ASSERT(c_p->sig_qs.cont);
+
+ *statep = erts_atomic32_read_nob(&c_p->state);
+
+ res = 0;
+ }
+ else {
+ /*
+ * All non-message signals handled. All
+ * messages should be moved to inner queue.
+ * Middle queue should be empty.
+ */
+ ASSERT(!c_p->sig_qs.nmsigs.next);
+ c_p->sig_qs.nmsigs.last = NULL;
+
+ if (c_p->sig_qs.cont_last != &c_p->sig_qs.cont) {
+ ASSERT(!*c_p->sig_qs.last);
+ *c_p->sig_qs.last = c_p->sig_qs.cont;
+ c_p->sig_qs.last = c_p->sig_qs.cont_last;
+ ASSERT(!*c_p->sig_qs.last);
+
+ c_p->sig_qs.cont_last = &c_p->sig_qs.cont;
+ c_p->sig_qs.cont = NULL;
+ }
+
+ ASSERT(!c_p->sig_qs.cont);
+
+ state = erts_atomic32_read_band_nob(&c_p->state,
+ ~ERTS_PSFLG_SIG_Q);
+ state &= ~ERTS_PSFLG_SIG_Q;
+ *statep = state;
+ res = !0;
+ }
+
+ if (deferred_saved_last
+ && (c_p->sig_qs.saved_last == &c_p->sig_qs.cont)) {
+ c_p->sig_qs.saved_last = c_p->sig_qs.last;
+ c_p->flags &= ~F_DEFERRED_SAVED_LAST;
+ if (deferred_save)
+ c_p->sig_qs.save = c_p->sig_qs.saved_last;
+ }
+ else if (!res) {
+ if (deferred_save) {
+ c_p->sig_qs.save = c_p->sig_qs.last;
+ ASSERT(!PEEK_MESSAGE(c_p));
+ }
+ }
+ else {
+ c_p->flags &= ~F_DEFERRED_SAVED_LAST;
+ if (deferred_save)
+ c_p->sig_qs.save = c_p->sig_qs.saved_last;
+ }
+
+ ERTS_HDBG_CHECK_SIGNAL_PRIV_QUEUE(c_p, 0);
+
+ *redsp = cnt/4 + 1;
+
+ return res;
+ }
+}
+
+static int
+stretch_limit(Process *c_p, ErtsSigRecvTracing *tp,
+ int abs_lim, int *limp)
+{
+ int lim;
+ /*
+ * Stretch limit up to a maximum of 'abs_lim' if
+ * there currently are no messages available to
+ * inspect by 'receive' and it might be possible
+ * to get messages available by processing
+ * signals (or trace messages).
+ */
+
+ lim = *limp;
+ ASSERT(abs_lim >= lim);
+ if (abs_lim == lim)
+ return 0;
+
+ if (!(c_p->flags & F_DEFERRED_SAVED_LAST)) {
+ ErtsSignal *sig;
+
+ if (PEEK_MESSAGE(c_p))
+ return 0;
+ sig = (ErtsSignal *) c_p->sig_qs.cont;
+ if (!sig)
+ return 0; /* No signals to process available... */
+ if (ERTS_SIG_IS_MSG(sig) && tp->messages.next != &c_p->sig_qs.cont)
+ return 0;
+ }
+
+ lim += ERTS_SIG_REDS_CNT_FACTOR*100;
+ if (lim > abs_lim)
+ lim = abs_lim;
+ *limp = lim;
+ return !0;
+}
+
+
+int
+erts_proc_sig_handle_exit(Process *c_p, int *redsp)
+{
+ int cnt, limit;
+ ErtsMessage *sig, ***next_nm_sig;
+
+ ERTS_HDBG_CHECK_SIGNAL_PRIV_QUEUE(c_p, 0);
+ ERTS_LC_ASSERT(erts_proc_lc_my_proc_locks(c_p) == ERTS_PROC_LOCK_MAIN);
+
+ ASSERT(!(ERTS_PSFLG_SIG_IN_Q & erts_atomic32_read_nob(&c_p->state)));
+
+ limit = *redsp;
+ limit *= ERTS_SIG_REDS_CNT_FACTOR;
+
+ *redsp = 1;
+
+ next_nm_sig = &c_p->sig_qs.nmsigs.next;
+
+ if (!*next_nm_sig) {
+ ASSERT(!c_p->sig_qs.nmsigs.last);
+ return !0; /* done... */
+ }
+
+ cnt = 0;
+
+ ERTS_PROC_SIG_HDBG_PRIV_CHKQ(c_p, NULL, next_nm_sig);
+
+ do {
+ Eterm tag;
+ Uint16 type;
+ int op;
+
+ sig = **next_nm_sig;
+
+ ASSERT(sig);
+ ASSERT(ERTS_SIG_IS_NON_MSG(sig));
+
+ tag = ((ErtsSignal *) sig)->common.tag;
+ type = ERTS_PROC_SIG_TYPE(tag);
+ op = ERTS_PROC_SIG_OP(tag);
+
+ remove_nm_sig(c_p, sig, next_nm_sig);
+
+ ERTS_PROC_SIG_HDBG_PRIV_CHKQ(c_p, NULL, next_nm_sig);
+
+ cnt++;
+
+ switch (op) {
+
+ case ERTS_SIG_Q_OP_EXIT:
+ case ERTS_SIG_Q_OP_EXIT_LINKED:
+ case ERTS_SIG_Q_OP_MONITOR_DOWN:
+ switch (type) {
+ case ERTS_SIG_Q_TYPE_GEN_EXIT:
+ sig->next = NULL;
+ erts_cleanup_messages(sig);
+ break;
+ case ERTS_LNK_TYPE_PORT:
+ case ERTS_LNK_TYPE_PROC:
+ case ERTS_LNK_TYPE_DIST_PROC:
+ erts_link_release((ErtsLink *) sig);
+ break;
+ case ERTS_MON_TYPE_PORT:
+ case ERTS_MON_TYPE_PROC:
+ case ERTS_MON_TYPE_DIST_PROC:
+ case ERTS_MON_TYPE_NODE:
+ erts_monitor_release((ErtsMonitor *) sig);
+ break;
+ default:
+ ERTS_INTERNAL_ERROR("Unexpected sig type");
+ break;
+ }
+ break;
+
+ case ERTS_SIG_Q_OP_PERSISTENT_MON_MSG:
+ sig->next = NULL;
+ erts_cleanup_messages(sig);
+ break;
+
+ case ERTS_SIG_Q_OP_MONITOR: {
+ ErtsProcExitContext pectxt = {c_p, am_noproc};
+ erts_proc_exit_handle_monitor((ErtsMonitor *) sig,
+ (void *) &pectxt);
+ cnt += 4;
+ break;
+ }
+
+ case ERTS_SIG_Q_OP_DEMONITOR:
+ if (type == ERTS_SIG_Q_TYPE_DIST_PROC_DEMONITOR)
+ destroy_dist_proc_demonitor((ErtsSigDistProcDemonitor *) sig);
+ else
+ erts_monitor_release((ErtsMonitor *) sig);
+ break;
+
+ case ERTS_SIG_Q_OP_LINK: {
+ ErtsProcExitContext pectxt = {c_p, am_noproc};
+ erts_proc_exit_handle_link((ErtsLink *) sig, (void *) &pectxt);
+ break;
+ }
+
+ case ERTS_SIG_Q_OP_UNLINK:
+ if (type == ERTS_SIG_Q_TYPE_DIST_LINK)
+ destroy_sig_dist_link_op((ErtsSigDistLinkOp *) sig);
+ else
+ erts_link_release((ErtsLink *) sig);
+ break;
+
+ case ERTS_SIG_Q_OP_GROUP_LEADER: {
+ ErtsSigGroupLeader *sgl = (ErtsSigGroupLeader *) sig;
+ handle_group_leader(c_p, sgl);
+ break;
+ }
+
+ case ERTS_SIG_Q_OP_IS_ALIVE:
+ is_alive_response(c_p, sig, 0);
+ break;
+
+ case ERTS_SIG_Q_OP_PROCESS_INFO:
+ handle_process_info(c_p, sig, next_nm_sig, 0);
+ break;
+
+ case ERTS_SIG_Q_OP_TRACE_CHANGE_STATE:
+ destroy_trace_info((ErtsSigTraceInfo *) sig);
+ break;
+
+ default:
+ ERTS_INTERNAL_ERROR("Unknown signal");
+ break;
+ }
+
+ } while (cnt >= limit && *next_nm_sig);
+
+ *redsp += cnt / ERTS_SIG_REDS_CNT_FACTOR;
+
+ if (*next_nm_sig)
+ return 0;
+
+ ASSERT(!c_p->sig_qs.nmsigs.next);
+ c_p->sig_qs.nmsigs.last = NULL;
+ (void) erts_atomic32_read_band_nob(&c_p->state,
+ ~ERTS_PSFLG_SIG_Q);
+ return !0;
+}
+
+#ifdef USE_VM_PROBES
+# define ERTS_CLEAR_SEQ_TOKEN(MP) \
+ ERL_MESSAGE_TOKEN((MP)) = ((ERL_MESSAGE_DT_UTAG((MP)) != NIL) \
+ ? am_have_dt_utag : NIL)
+#else
+# define ERTS_CLEAR_SEQ_TOKEN(MP) \
+ ERL_MESSAGE_TOKEN((MP)) = NIL
+#endif
+
+static ERTS_INLINE void
+clear_seq_trace_token(ErtsMessage *sig)
+{
+ if (ERTS_SIG_IS_MSG((ErtsSignal *) sig))
+ ERTS_CLEAR_SEQ_TOKEN(sig);
+ else {
+ Uint tag;
+ Uint16 op, type;
+
+ tag = ((ErtsSignal *) sig)->common.tag;
+ type = ERTS_PROC_SIG_TYPE(tag);
+ op = ERTS_PROC_SIG_OP(tag);
+
+ switch (op) {
+
+ case ERTS_SIG_Q_OP_EXIT:
+ case ERTS_SIG_Q_OP_EXIT_LINKED:
+ case ERTS_SIG_Q_OP_MONITOR_DOWN:
+ switch (type) {
+ case ERTS_SIG_Q_TYPE_GEN_EXIT:
+ ERTS_CLEAR_SEQ_TOKEN(sig);
+ break;
+ case ERTS_LNK_TYPE_PORT:
+ case ERTS_LNK_TYPE_PROC:
+ case ERTS_LNK_TYPE_DIST_PROC:
+ case ERTS_MON_TYPE_PORT:
+ case ERTS_MON_TYPE_PROC:
+ case ERTS_MON_TYPE_DIST_PROC:
+ case ERTS_MON_TYPE_NODE:
+ break;
+ default:
+ ERTS_INTERNAL_ERROR("Unexpected sig type");
+ break;
+ }
+ break;
+
+ case ERTS_SIG_Q_OP_PERSISTENT_MON_MSG:
+ ERTS_CLEAR_SEQ_TOKEN(sig);
+ break;
+
+ case ERTS_SIG_Q_OP_MONITOR:
+ case ERTS_SIG_Q_OP_DEMONITOR:
+ case ERTS_SIG_Q_OP_LINK:
+ case ERTS_SIG_Q_OP_UNLINK:
+ case ERTS_SIG_Q_OP_TRACE_CHANGE_STATE:
+ break;
+
+ default:
+ ERTS_INTERNAL_ERROR("Unknown signal");
+ break;
+ }
+ }
+}
+
+void
+erts_proc_sig_clear_seq_trace_tokens(Process *c_p)
+{
+ ASSERT(erts_thr_progress_is_blocking());
+ erts_proc_sig_fetch(c_p);
+ ERTS_FOREACH_SIG_PRIVQS(c_p, sig, clear_seq_trace_token(sig));
+}
+
+Uint
+erts_proc_sig_signal_size(ErtsSignal *sig)
+{
+ Eterm tag;
+ Uint16 type;
+ int op;
+ Uint size = 0;
+
+ ASSERT(sig);
+ ASSERT(ERTS_SIG_IS_NON_MSG(sig));
+
+ tag = sig->common.tag;
+ type = ERTS_PROC_SIG_TYPE(tag);
+ op = ERTS_PROC_SIG_OP(tag);
+
+ switch (op) {
+ case ERTS_SIG_Q_OP_EXIT:
+ case ERTS_SIG_Q_OP_EXIT_LINKED:
+ case ERTS_SIG_Q_OP_MONITOR_DOWN:
+ switch (type) {
+ case ERTS_SIG_Q_TYPE_GEN_EXIT:
+ size = ((ErtsMessage *) sig)->hfrag.alloc_size;
+ size *= sizeof(Eterm);
+ size += sizeof(ErtsMessage) - sizeof(Eterm);
+ break;
+ case ERTS_LNK_TYPE_PORT:
+ case ERTS_LNK_TYPE_PROC:
+ case ERTS_LNK_TYPE_DIST_PROC:
+ size = erts_link_size((ErtsLink *) sig);
+ break;
+ case ERTS_MON_TYPE_PORT:
+ case ERTS_MON_TYPE_PROC:
+ case ERTS_MON_TYPE_DIST_PROC:
+ case ERTS_MON_TYPE_NODE:
+ size = erts_monitor_size((ErtsMonitor *) sig);
+ default:
+ ERTS_INTERNAL_ERROR("Unexpected sig type");
+ break;
+ }
+ break;
+
+ case ERTS_SIG_Q_OP_PERSISTENT_MON_MSG:
+ case ERTS_SIG_Q_OP_IS_ALIVE:
+ size = ((ErtsMessage *) sig)->hfrag.alloc_size;
+ size *= sizeof(Eterm);
+ size += sizeof(ErtsMessage) - sizeof(Eterm);
+ break;
+
+ case ERTS_SIG_Q_OP_DEMONITOR:
+ if (type == ERTS_SIG_Q_TYPE_DIST_PROC_DEMONITOR) {
+ size = NC_HEAP_SIZE(((ErtsSigDistProcDemonitor *) sig)->ref);
+ size--;
+ size *= sizeof(Eterm);
+ size += sizeof(ErtsSigDistProcDemonitor);
+ break;
+ }
+ /* Fall through... */
+
+ case ERTS_SIG_Q_OP_MONITOR:
+ size = erts_monitor_size((ErtsMonitor *) sig);
+ break;
+
+ case ERTS_SIG_Q_OP_UNLINK:
+ if (type == ERTS_SIG_Q_TYPE_DIST_LINK) {
+ size = NC_HEAP_SIZE(((ErtsSigDistLinkOp *) sig)->remote);
+ size--;
+ size *= sizeof(Eterm);
+ size += sizeof(ErtsSigDistLinkOp);
+ break;
+ }
+ /* Fall through... */
+
+ case ERTS_SIG_Q_OP_LINK:
+ size = erts_link_size((ErtsLink *) sig);
+ break;
+
+ case ERTS_SIG_Q_OP_GROUP_LEADER: {
+ ErtsSigGroupLeader *sgl = (ErtsSigGroupLeader *) sig;
+ size = size_object(sgl->group_leader);
+ size += size_object(sgl->ref);
+ size *= sizeof(Eterm);
+ size += sizeof(ErtsSigGroupLeader) - sizeof(Eterm);
+ break;
+ }
+
+ case ERTS_SIG_Q_OP_TRACE_CHANGE_STATE:
+ size = sizeof(ErtsSigTraceInfo);
+ break;
+
+ case ERTS_SIG_Q_OP_PROCESS_INFO: {
+ ErtsProcessInfoSig *pisig = (ErtsProcessInfoSig *) sig;
+ size = sizeof(ErtsProcessInfoSig);
+ size += (pisig->len - 1) * sizeof(int);
+ break;
+ }
+
+ default:
+ ERTS_INTERNAL_ERROR("Unknown signal");
+ break;
+ }
+
+ return size;
+}
+
+int
+erts_proc_sig_receive_helper(Process *c_p,
+ int fcalls,
+ int neg_o_reds,
+ ErtsMessage **msgpp,
+ int *get_outp)
+{
+ ErtsMessage *msgp;
+ int reds, consumed_reds, left_reds, max_reds;
+
+ /*
+ * Called from the loop-rec instruction when receive
+ * has reached end of inner (private) queue. This function
+ * tries to move more messages into the inner queue
+ * for the receive to handle. This by, processing the
+ * middle (private) queue and/or moving signals from
+ * the outer (public) queue into the middle queue.
+ *
+ * If this function succeeds in making more messages
+ * available in the inner queue, *msgpp points to next
+ * message. If *msgpp is set to NULL when:
+ * -- process became exiting. *get_outp is set to a
+ * value greater than zero.
+ * -- process needs to yield. *get_outp is set to a
+ * value less than zero.
+ * -- no more messages exist in any of the queues.
+ * *get_outp is set to zero and the message queue
+ * lock remains locked. This so the process can
+ * make its way to the appropriate wait instruction
+ * without racing with new incoming messages.
+ */
+
+ ASSERT(erts_proc_lc_my_proc_locks(c_p) == ERTS_PROC_LOCK_MAIN);
+ ASSERT(!ERTS_PROC_IS_EXITING(c_p));
+ ASSERT(!*msgpp);
+
+ left_reds = fcalls - neg_o_reds;
+ consumed_reds = 0;
+
+ while (!0) {
+ erts_aint32_t state;
+
+ if (!c_p->sig_qs.cont) {
+
+ consumed_reds += 4;
+ left_reds -= 4;
+ erts_proc_lock(c_p, ERTS_PROC_LOCK_MSGQ);
+ erts_proc_sig_fetch(c_p);
+ /*
+ * Messages may have been moved directly to
+ * inner queue...
+ */
+ msgp = PEEK_MESSAGE(c_p);
+ if (msgp) {
+ erts_proc_unlock(c_p, ERTS_PROC_LOCK_MSGQ);
+ *get_outp = 0;
+ *msgpp = msgp;
+ return consumed_reds;
+ }
+
+ if (!c_p->sig_qs.cont) {
+ /*
+ * No messages! Return with message queue
+ * locked and let the process continue
+ * to wait instruction...
+ */
+ *get_outp = 0;
+ *msgpp = NULL;
+ return consumed_reds;
+ }
+ erts_proc_unlock(c_p, ERTS_PROC_LOCK_MSGQ);
+
+ if (left_reds <= 0) {
+ *get_outp = -1; /* yield */
+ *msgpp = NULL;
+
+ ASSERT(consumed_reds >= (fcalls - neg_o_reds));
+ return consumed_reds;
+ }
+
+ /* handle newly arrived signals... */
+ }
+
+ reds = ERTS_SIG_HANDLE_REDS_MAX_PREFERED;
+#ifdef DEBUG
+ /* test that it works also with very few reds */
+ max_reds = left_reds;
+ if (reds > left_reds)
+ reds = left_reds;
+#else
+ /* At least work preferred amount of reds... */
+ max_reds = left_reds;
+ if (max_reds < reds)
+ max_reds = reds;
+#endif
+ (void) erts_proc_sig_handle_incoming(c_p, &state, &reds,
+ max_reds, !0);
+ consumed_reds += reds;
+ left_reds -= reds;
+ /* we may have exited by an incoming signal... */
+ if (state & ERTS_PSFLG_EXITING) {
+ /*
+ * Process need to schedule out in order
+ * to terminate. Prepare this a bit...
+ */
+ ASSERT(c_p->flags & F_DELAY_GC);
+
+ c_p->flags &= ~F_DELAY_GC;
+ c_p->arity = 0;
+ c_p->current = NULL;
+ *get_outp = 1;
+ *msgpp = NULL;
+ return consumed_reds;
+ }
+
+ msgp = PEEK_MESSAGE(c_p);
+ if (msgp) {
+ *get_outp = 0;
+ *msgpp = msgp;
+ return consumed_reds;
+ }
+
+ if (left_reds <= 0) {
+ *get_outp = -1; /* yield */
+ *msgpp = NULL;
+
+ ASSERT(consumed_reds >= (fcalls - neg_o_reds));
+ return consumed_reds;
+ }
+
+ ASSERT(!c_p->sig_qs.cont);
+ /* Go fetch again... */
+ }
+}
+
+static int
+handle_trace_change_state(Process *c_p,
+ ErtsSigRecvTracing *tracing,
+ Uint16 type,
+ ErtsMessage *sig,
+ ErtsMessage ***next_nm_sig)
+{
+ ErtsSigTraceInfo *trace_info = (ErtsSigTraceInfo *) sig;
+ ErtsMessage **next = *next_nm_sig;
+ int msgs_active, old_msgs_active = !!tracing->messages.active;
+
+ ASSERT(sig == *next);
+
+ erts_proc_lock(c_p, ERTS_PROC_LOCKS_ALL_MINOR);
+
+ ERTS_TRACE_FLAGS(c_p) |= trace_info->flags_on;
+ ERTS_TRACE_FLAGS(c_p) &= ~trace_info->flags_off;
+ if (is_value(trace_info->tracer))
+ erts_tracer_replace(&c_p->common, trace_info->tracer);
+
+ erts_proc_unlock(c_p, ERTS_PROC_LOCKS_ALL_MINOR);
+
+ remove_nm_sig(c_p, sig, next_nm_sig);
+ destroy_trace_info(trace_info);
+ /*
+ * Adjust tracing state according to modifications made by
+ * the trace info signal...
+ */
+ adjust_tracing_state(c_p, tracing, 0);
+ msgs_active = !!tracing->messages.active;
+
+ if (old_msgs_active ^ msgs_active) {
+ if (msgs_active) {
+ ASSERT(!tracing->messages.next);
+ tracing->messages.next = next;
+ }
+ else {
+ ASSERT(tracing->messages.next);
+ tracing->messages.next = NULL;
+ }
+ }
+
+ ASSERT(!msgs_active || tracing->messages.next);
+
+ return msgs_active;
+}
+
+static void
+getting_unlinked(Process *c_p, Eterm unlinker)
+{
+ trace_proc(c_p, ERTS_PROC_LOCK_MAIN, c_p,
+ am_getting_unlinked, unlinker);
+}
+
+static void
+getting_linked(Process *c_p, Eterm linker)
+{
+ trace_proc(c_p, ERTS_PROC_LOCK_MAIN, c_p,
+ am_getting_linked, linker);
+}
+
+static ERTS_INLINE void
+handle_message_enqueued_tracing(Process *c_p,
+ ErtsSigRecvTracing *tracing,
+ ErtsMessage *msg)
+{
+ ASSERT(ERTS_SIG_IS_INTERNAL_MSG(msg));
+
+#if defined(USE_VM_PROBES)
+ if (tracing->messages.vm_probes && DTRACE_ENABLED(message_queued)) {
+ Sint tok_label = 0;
+ Sint tok_lastcnt = 0;
+ Sint tok_serial = 0;
+ Sint len = erts_proc_sig_privqs_len(c_p);
+ Eterm seq_trace_token = ERL_MESSAGE_TOKEN(msg);
+
+ if (seq_trace_token != NIL && is_tuple(seq_trace_token)) {
+ tok_label = SEQ_TRACE_T_DTRACE_LABEL(seq_trace_token);
+ tok_lastcnt = signed_val(SEQ_TRACE_T_LASTCNT(seq_trace_token));
+ tok_serial = signed_val(SEQ_TRACE_T_SERIAL(seq_trace_token));
+ }
+ /* Message intentionally not passed... */
+ DTRACE6(message_queued,
+ tracing->messages.receiver_name,
+ size_object(ERL_MESSAGE_TERM(msg)),
+ len, /* This is NOT message queue len, but its something... */
+ tok_label, tok_lastcnt, tok_serial);
+ }
+#endif
+
+ if (tracing->messages.receive_trace && tracing->messages.event->on) {
+ ASSERT(IS_TRACED(c_p));
+ trace_receive(c_p,
+ ERL_MESSAGE_FROM(msg),
+ ERL_MESSAGE_TERM(msg),
+ tracing->messages.event);
+ }
+}
+
+static int
+handle_msg_tracing(Process *c_p, ErtsSigRecvTracing *tracing,
+ ErtsMessage ***next_nm_sig)
+{
+ ErtsMessage **next_sig, *sig;
+ int cnt = 0, limit = ERTS_PROC_SIG_TRACE_COUNT_LIMIT;
+
+ ASSERT(tracing->messages.next);
+ next_sig = tracing->messages.next;
+ sig = *next_sig;
+
+ /*
+ * Receive tracing active. Handle all messages
+ * until next non-message signal...
+ */
+
+ while (sig && ERTS_SIG_IS_MSG(sig)) {
+ if (cnt > limit) {
+ tracing->messages.next = next_sig;
+ return -1; /* Yield... */
+ }
+ if (ERTS_SIG_IS_EXTERNAL_MSG(sig)) {
+ cnt++;
+ if (!erts_decode_dist_message(c_p, ERTS_PROC_LOCK_MAIN,
+ sig, 0)) {
+ /* Bad dist message; remove it... */
+ remove_mq_m_sig(c_p, sig, next_sig, next_nm_sig);
+ sig = *next_sig;
+ continue;
+ }
+ }
+ handle_message_enqueued_tracing(c_p, tracing, sig);
+ cnt++;
+
+ next_sig = &(*next_sig)->next;
+ sig = *next_sig;
+ }
+
+ tracing->messages.next = next_sig;
+
+ if (!sig) {
+ ASSERT(!*next_nm_sig);
+ return 1; /* end... */
+ }
+
+ ASSERT(*next_nm_sig);
+ ASSERT(**next_nm_sig == sig);
+
+ /* Next signal a non-message signal... */
+ ASSERT(ERTS_SIG_IS_NON_MSG(sig));
+
+ /*
+ * Return and handle the non-message signal...
+ */
+
+ return 0;
+}
+
+Uint
+erts_proc_sig_prep_msgq_for_inspection(Process *c_p,
+ Process *rp,
+ ErtsProcLocks rp_locks,
+ int info_on_self,
+ ErtsMessageInfo *mip)
+{
+ Uint tot_heap_size;
+ ErtsMessage *mp, **mpp;
+ Sint i;
+ int self_on_heap;
+
+ /*
+ * Prepare the message queue (inner signal queue)
+ * for inspection by process_info().
+ *
+ * - Decode all messages on external format
+ * - Remove all corrupt dist messages from queue
+ * - Save pointer to, and heap size need of each
+ * message in the mip array.
+ * - Return total heap size need for all messages
+ * that needs to be copied.
+ *
+ */
+
+ ASSERT(!info_on_self || c_p == rp);
+
+ self_on_heap = info_on_self && !(c_p->flags & F_OFF_HEAP_MSGQ);
+
+ tot_heap_size = 0;
+ i = 0;
+ mpp = &rp->sig_qs.first;
+ mp = rp->sig_qs.first;
+ while (mp) {
+ Eterm msg = ERL_MESSAGE_TERM(mp);
+
+ mip[i].size = 0;
+
+ if (ERTS_SIG_IS_EXTERNAL_MSG(mp)) {
+ /* decode it... */
+ if (mp->data.attached)
+ erts_decode_dist_message(rp, rp_locks, mp, !0);
+
+ msg = ERL_MESSAGE_TERM(mp);
+
+ if (is_non_value(msg)) {
+ ErtsMessage *bad_mp = mp;
+ /*
+ * Bad distribution message; remove
+ * it from the queue...
+ */
+ ASSERT(!mp->data.attached);
+
+ ASSERT(*mpp == bad_mp);
+
+ remove_iq_m_sig(rp, mp, mpp);
+
+ mp = *mpp;
+
+ bad_mp->next = NULL;
+ erts_cleanup_messages(bad_mp);
+ continue;
+ }
+ }
+
+ ASSERT(is_value(msg));
+
+ if (is_not_immed(msg) && (!self_on_heap || mp->data.attached)) {
+ Uint sz = size_object(msg);
+ mip[i].size = sz;
+ tot_heap_size += sz;
+ }
+
+ mip[i].msgp = mp;
+ i++;
+ mpp = &mp->next;
+ mp = mp->next;
+ }
+
+ ASSERT(c_p->sig_qs.len == i);
+
+ return tot_heap_size;
+}
+
+static ERTS_INLINE void
+move_msg_to_heap(Process *c_p, ErtsMessage *mp)
+{
+ /*
+ * We leave not yet decoded distribution messages
+ * as they are in the queue since it is not
+ * possible to determine a maximum size until
+ * actual decoding...
+ *
+ * We also leave combined messages as they are...
+ */
+ if (ERTS_SIG_IS_INTERNAL_MSG(mp)
+ && mp->data.attached
+ && mp->data.attached != ERTS_MSG_COMBINED_HFRAG) {
+ ErlHeapFragment *bp;
+
+ bp = erts_message_to_heap_frag(mp);
+
+ if (bp->next)
+ erts_move_multi_frags(&c_p->htop, &c_p->off_heap, bp,
+ mp->m, ERL_MESSAGE_REF_ARRAY_SZ, 0);
+ else
+ erts_copy_one_frag(&c_p->htop, &c_p->off_heap, bp,
+ mp->m, ERL_MESSAGE_REF_ARRAY_SZ);
+
+ mp->data.heap_frag = NULL;
+ free_message_buffer(bp);
+ }
+}
+
+void
+erts_proc_sig_move_msgs_to_heap(Process *c_p)
+{
+ ERTS_HDBG_CHECK_SIGNAL_PRIV_QUEUE(c_p, 0);
+
+ ERTS_FOREACH_SIG_PRIVQS(c_p, sig, move_msg_to_heap(c_p, sig));
+
+ ERTS_HDBG_CHECK_SIGNAL_PRIV_QUEUE(c_p, 0);
+}
+
+
+BIF_RETTYPE
+erts_internal_dirty_process_handle_signals_1(BIF_ALIST_1)
+{
+ erts_aint32_t state, dirty, noproc;
+ int busy;
+ Process *rp;
+
+ if (BIF_P != erts_dirty_process_signal_handler
+ && BIF_P != erts_dirty_process_signal_handler_high
+ && BIF_P != erts_dirty_process_signal_handler_max)
+ BIF_ERROR(BIF_P, EXC_NOTSUP);
+
+ if (is_not_internal_pid(BIF_ARG_1))
+ BIF_RET(am_false);
+
+ rp = erts_proc_lookup_raw(BIF_ARG_1);
+ if (!rp)
+ BIF_RET(am_noproc);
+
+ state = erts_atomic32_read_nob(&rp->state);
+ dirty = (state & (ERTS_PSFLG_DIRTY_RUNNING
+ | ERTS_PSFLG_DIRTY_RUNNING_SYS));
+ if (!dirty)
+ BIF_RET(am_normal);
+
+ busy = erts_proc_trylock(rp, ERTS_PROC_LOCK_MAIN) == EBUSY;
+
+ state = erts_atomic32_read_mb(&rp->state);
+ noproc = (state & ERTS_PSFLG_FREE);
+ dirty = (state & (ERTS_PSFLG_DIRTY_RUNNING
+ | ERTS_PSFLG_DIRTY_RUNNING_SYS));
+
+ if (busy) {
+ if (noproc)
+ BIF_RET(am_noproc);
+ if (dirty)
+ BIF_RET(am_more); /* try again... */
+ BIF_RET(am_normal); /* will handle signals itself... */
+ }
+ else {
+ erts_aint32_t state;
+ int done;
+ Eterm res = am_false;
+ int reds = 0;
+
+ if (noproc)
+ res = am_noproc;
+ else if (!dirty)
+ res = am_normal; /* will handle signals itself... */
+ else {
+ reds = ERTS_BIF_REDS_LEFT(BIF_P);
+ done = erts_proc_sig_handle_incoming(rp, &state, &reds,
+ reds, 0);
+ if (done || (state & ERTS_PSFLG_EXITING))
+ res = am_ok;
+ else
+ res = am_more;
+ }
+
+ erts_proc_unlock(rp, ERTS_PROC_LOCK_MAIN);
+
+ if (reds)
+ BUMP_REDS(BIF_P, reds);
+
+ BIF_RET(res);
+ }
+}
+
+static void
+debug_foreach_sig_heap_frags(ErlHeapFragment *hfrag,
+ void (*oh_func)(ErlOffHeap *, void *),
+ void *arg)
+{
+ ErlHeapFragment *hf = hfrag;
+ while (hf) {
+ oh_func(&(hf->off_heap), arg);
+ hf = hf->next;
+ }
+}
+
+static void
+debug_foreach_sig_fake_oh(Eterm term,
+ void (*oh_func)(ErlOffHeap *, void *),
+ void *arg)
+{
+ if (is_external(term)) {
+ ErlOffHeap oh;
+ oh.overhead = 0;
+ oh.first = ((struct erl_off_heap_header *)
+ (char *) external_thing_ptr(term));
+ ASSERT(!oh.first->next);
+ oh_func(&oh, arg);
+ }
+
+}
+
+void
+erts_proc_sig_debug_foreach_sig(Process *c_p,
+ void (*msg_func)(ErtsMessage *, void *),
+ void (*oh_func)(ErlOffHeap *, void *),
+ void (*mon_func)(ErtsMonitor *, void *),
+ void (*lnk_func)(ErtsLink *, void *),
+ void *arg)
+{
+ ErtsMessage *queue[] = {c_p->sig_qs.first, c_p->sig_qs.cont, c_p->sig_inq.first};
+ int qix;
+
+ for (qix = 0; qix < sizeof(queue)/sizeof(queue[0]); qix++) {
+ ErtsMessage *sig;
+ for (sig = queue[qix]; sig; sig = sig->next) {
+
+ if (ERTS_SIG_IS_MSG(sig))
+ msg_func(sig, arg);
+ else {
+ Eterm tag;
+ Uint16 type;
+ int op;
+
+ ASSERT(sig);
+ ASSERT(ERTS_SIG_IS_NON_MSG(sig));
+
+ tag = ((ErtsSignal *) sig)->common.tag;
+ type = ERTS_PROC_SIG_TYPE(tag);
+ op = ERTS_PROC_SIG_OP(tag);
+
+ switch (op) {
+
+ case ERTS_SIG_Q_OP_EXIT:
+ case ERTS_SIG_Q_OP_EXIT_LINKED:
+ case ERTS_SIG_Q_OP_MONITOR_DOWN:
+ switch (type) {
+ case ERTS_SIG_Q_TYPE_GEN_EXIT:
+ debug_foreach_sig_heap_frags(&sig->hfrag, oh_func, arg);
+ break;
+ case ERTS_LNK_TYPE_PORT:
+ case ERTS_LNK_TYPE_PROC:
+ case ERTS_LNK_TYPE_DIST_PROC:
+ lnk_func((ErtsLink *) sig, arg);
+ break;
+ case ERTS_MON_TYPE_PORT:
+ case ERTS_MON_TYPE_PROC:
+ case ERTS_MON_TYPE_DIST_PROC:
+ case ERTS_MON_TYPE_NODE:
+ mon_func((ErtsMonitor *) sig, arg);
+ break;
+ default:
+ ERTS_INTERNAL_ERROR("Unexpected sig type");
+ break;
+ }
+ break;
+
+ case ERTS_SIG_Q_OP_PERSISTENT_MON_MSG:
+ debug_foreach_sig_heap_frags(&sig->hfrag, oh_func, arg);
+ break;
+
+ case ERTS_SIG_Q_OP_DEMONITOR:
+ if (type == ERTS_SIG_Q_TYPE_DIST_PROC_DEMONITOR) {
+ debug_foreach_sig_fake_oh(((ErtsSigDistProcDemonitor *) sig)->ref,
+ oh_func, arg);
+ break;
+ }
+ /* Fall through... */
+
+ case ERTS_SIG_Q_OP_MONITOR:
+ mon_func((ErtsMonitor *) sig, arg);
+ break;
+
+ case ERTS_SIG_Q_OP_UNLINK:
+ if (type == ERTS_SIG_Q_TYPE_DIST_LINK) {
+ debug_foreach_sig_fake_oh(((ErtsSigDistLinkOp *) sig)->remote,
+ oh_func, arg);
+ break;
+ }
+ /* Fall through... */
+
+ case ERTS_SIG_Q_OP_LINK:
+ lnk_func((ErtsLink *) sig, arg);
+ break;
+
+ case ERTS_SIG_Q_OP_GROUP_LEADER: {
+ ErtsSigGroupLeader *sgl = (ErtsSigGroupLeader *) sig;
+ oh_func(&sgl->oh, arg);
+ break;
+ }
+
+ case ERTS_SIG_Q_OP_IS_ALIVE:
+ case ERTS_SIG_Q_OP_TRACE_CHANGE_STATE:
+ case ERTS_SIG_Q_OP_PROCESS_INFO:
+ break;
+
+ default:
+ ERTS_INTERNAL_ERROR("Unknown signal");
+ break;
+ }
+
+ }
+ }
+ }
+}
+
+#ifdef ERTS_PROC_SIG_HARD_DEBUG
+
+static void
+chk_eterm(Process *c_p, int privq, ErtsMessage *mp, Eterm term)
+{
+ ErlHeapFragment *bp;
+ Eterm *ptr = NULL;
+
+ switch (primary_tag(term)) {
+ case TAG_PRIMARY_IMMED1:
+ return;
+ case TAG_PRIMARY_LIST:
+ ptr = list_val(term);
+ ERTS_ASSERT(!is_header(CAR(ptr)));
+ ERTS_ASSERT(!is_header(CDR(ptr)));
+ break;
+ case TAG_PRIMARY_BOXED:
+ ptr = boxed_val(term);
+ ERTS_ASSERT(is_header(*ptr));
+ break;
+ case TAG_PRIMARY_HEADER:
+ default:
+ ERTS_INTERNAL_ERROR("Not valid term");
+ break;
+ }
+
+ if (erts_is_literal(term, ptr))
+ return;
+
+ for (bp = erts_message_to_heap_frag(mp); bp; bp = bp->next) {
+ if (bp->mem <= ptr && ptr < bp->mem + bp->used_size)
+ return;
+ }
+
+ ASSERT(erts_dbg_within_proc(ptr, c_p, NULL));
+}
+
+static Sint
+proc_sig_hdbg_check_queue(Process *proc,
+ int privq,
+ ErtsMessage **sig_next,
+ ErtsMessage **sig_last,
+ ErtsMessage **sig_nm_next,
+ ErtsMessage **sig_nm_last,
+ ErtsSigRecvTracing *tracing,
+ int *found_saved_last_p,
+ erts_aint32_t sig_psflg)
+{
+ ErtsMessage **next, *sig, **nm_next, **nm_last;
+ int last_nm_sig_found, nm_sigs = 0, found_next_trace = 0,
+ found_save = 0, last_sig_found = 0, found_saved_last = 0;
+ Sint msg_len = 0;
+ ErtsMessage **next_trace = tracing ? tracing->messages.next : NULL;
+ ErtsMessage **save = proc->sig_qs.save;
+ ErtsMessage **saved_last = proc->sig_qs.saved_last;
+
+ if (!privq) {
+ ErtsSignal *sig = (ErtsSignal *) *sig_next;
+ if (sig->common.tag == ERTS_PROC_SIG_MSGQ_LEN_OFFS_MARK) {
+
+ }
+ }
+
+ nm_next = sig_nm_next;
+ nm_last = sig_nm_last;
+ next = sig_next;
+ sig = *sig_next;
+
+ last_nm_sig_found = !nm_last;
+ if (last_nm_sig_found)
+ ERTS_ASSERT(!nm_next);
+ else
+ ERTS_ASSERT(nm_next);
+
+ while (1) {
+ ErtsSignal *nm_sig;
+
+ if (next == sig_last) {
+ ASSERT(!*next);
+ last_sig_found = 1;
+ }
+
+ if (next == save)
+ found_save = 1;
+
+ if (next == saved_last)
+ found_saved_last = 1;
+
+ if (next == next_trace) {
+ found_next_trace = 1;
+ ERTS_ASSERT(nm_sigs == 0);
+ }
+
+ while (sig && ERTS_SIG_IS_MSG(sig)) {
+ int i;
+ if (ERTS_SIG_IS_EXTERNAL_MSG(sig))
+ i = 1;
+ else
+ i = 0;
+ for (; i < ERL_MESSAGE_REF_ARRAY_SZ; i++)
+ chk_eterm(proc, privq, sig, sig->m[i]);
+
+ msg_len++;
+ next = &sig->next;
+ sig = sig->next;
+
+ if (next == sig_last) {
+ ASSERT(!*next);
+ last_sig_found = 1;
+ }
+
+ if (next == save)
+ found_save = 1;
+
+ if (next == saved_last)
+ found_saved_last = 1;
+
+ if (next == next_trace) {
+ found_next_trace = 1;
+ ERTS_ASSERT(nm_sigs == 0);
+ }
+ }
+
+ if (!sig)
+ break;
+
+ nm_sig = (ErtsSignal *) sig;
+
+ if (nm_sig->common.tag == ERTS_PROC_SIG_MSGQ_LEN_OFFS_MARK) {
+ ERTS_ASSERT(!privq);
+ ERTS_ASSERT(sig == *sig_next);
+ }
+ else {
+ nm_sigs++;
+
+ ERTS_ASSERT(!last_nm_sig_found);
+ ERTS_ASSERT(ERTS_SIG_IS_NON_MSG(sig));
+
+ ERTS_ASSERT(nm_next == next);
+
+ if (nm_last == next) {
+ ASSERT(!nm_sig->common.specific.next);
+ last_nm_sig_found = 1;
+ }
+
+ nm_next = nm_sig->common.specific.next;
+
+ }
+
+ next = &nm_sig->common.next;
+ sig = nm_sig->common.next;
+
+ }
+
+ if (!privq) {
+ /* outer queue */
+ ERTS_ASSERT(!found_save);
+ ERTS_ASSERT(!found_saved_last);
+ }
+ else if (privq > 0) {
+ /* middle queue */
+ ERTS_ASSERT(!next_trace || found_next_trace);
+ ERTS_ASSERT(!found_save);
+ if (!found_saved_last_p) {
+ ERTS_ASSERT(!found_saved_last
+ || (proc->flags & F_DEFERRED_SAVED_LAST));
+ }
+ else {
+ if (*found_saved_last_p) {
+ ERTS_ASSERT(!found_saved_last);
+ ERTS_ASSERT(!(proc->flags & F_DEFERRED_SAVED_LAST));
+ }
+ else if (saved_last) {
+ ERTS_ASSERT(found_saved_last);
+ ERTS_ASSERT(proc->flags & F_DEFERRED_SAVED_LAST);
+ }
+ *found_saved_last_p |= found_saved_last;
+ }
+ }
+ else {
+ /* inner queue */
+ ERTS_ASSERT(!found_next_trace);
+ ERTS_ASSERT(nm_sigs == 0);
+ ERTS_ASSERT(found_save);
+ ERTS_ASSERT(!saved_last
+ || (found_saved_last
+ || (proc->flags & F_DEFERRED_SAVED_LAST)));
+ if (found_saved_last_p)
+ *found_saved_last_p |= found_saved_last;
+ }
+
+ ERTS_ASSERT(last_nm_sig_found);
+ ERTS_ASSERT(last_sig_found);
+
+ if (sig_psflg != ERTS_PSFLG_FREE) {
+ erts_aint32_t state = erts_atomic32_read_nob(&proc->state);
+ ERTS_ASSERT(nm_sigs ? !!(state & sig_psflg) : !(state & sig_psflg));
+ }
+
+ return msg_len;
+}
+
+void
+erts_proc_sig_hdbg_check_priv_queue(Process *p, int qlock, char *what, char *file, int line)
+{
+ int found_saved_last = 0;
+ Sint len, len1, len2;
+ ERTS_LC_ASSERT(erts_thr_progress_is_blocking()
+ || ERTS_PROC_IS_EXITING(p)
+ || (ERTS_PROC_LOCK_MAIN
+ & erts_proc_lc_my_proc_locks(p)));
+ len1 = proc_sig_hdbg_check_queue(p,
+ -1,
+ &p->sig_qs.first,
+ p->sig_qs.last,
+ NULL,
+ NULL,
+ NULL,
+ &found_saved_last,
+ ERTS_PSFLG_FREE);
+ len2 = proc_sig_hdbg_check_queue(p,
+ 1,
+ &p->sig_qs.cont,
+ p->sig_qs.cont_last,
+ p->sig_qs.nmsigs.next,
+ p->sig_qs.nmsigs.last,
+ NULL,
+ &found_saved_last,
+ ERTS_PSFLG_SIG_Q);
+ if (p->sig_qs.saved_last)
+ ERTS_ASSERT(found_saved_last);
+ len = proc_sig_privqs_len(p, qlock);
+ ERTS_ASSERT(len == len1 + len2);
+}
+
+void
+erts_proc_sig_hdbg_check_in_queue(Process *p, char *what, char *file, int line)
+{
+ Sint len;
+ ERTS_LC_ASSERT(erts_thr_progress_is_blocking()
+ || ERTS_PROC_IS_EXITING(p)
+ || (ERTS_PROC_LOCK_MSGQ
+ & erts_proc_lc_my_proc_locks(p)));
+ len = proc_sig_hdbg_check_queue(p,
+ 0,
+ &p->sig_inq.first,
+ p->sig_inq.last,
+ p->sig_inq.nmsigs.next,
+ p->sig_inq.nmsigs.last,
+ NULL,
+ NULL,
+ ERTS_PSFLG_SIG_IN_Q);
+ ASSERT(p->sig_inq.len == len);
+}
+
+#endif
diff --git a/erts/emulator/beam/erl_proc_sig_queue.h b/erts/emulator/beam/erl_proc_sig_queue.h
new file mode 100644
index 0000000000..d250ad820f
--- /dev/null
+++ b/erts/emulator/beam/erl_proc_sig_queue.h
@@ -0,0 +1,872 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2018. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * %CopyrightEnd%
+ */
+
+/*
+ * Description: Process signal queue implementation.
+ *
+ * Currently the following signals are handled:
+ * - Messages
+ * - Exit
+ * - Monitor
+ * - Demonitor
+ * - Monitor down
+ * - Persistent monitor message
+ * - Link
+ * - Unlink
+ * - Group leader
+ * - Is process alive
+ * - Process info request
+ * - Trace change
+ *
+ * The signal queue consists of three parts:
+ * - Outer queue (sig_inq field in process struct)
+ * - Middle queue (sig_qs field in process struct)
+ * - Inner queue (sig_qs field in process struct)
+ *
+ * Incoming signals are placed in the outer queue
+ * by other processes, ports, or by the runtime system
+ * itself. This queue is protected by the msgq process
+ * lock and may be accessed by any other entity. While
+ * a signal is located in the outer queue, it is still
+ * in transit between sender and receiver.
+ *
+ * The middle and the inner queues are private to the
+ * receiving process and can only be accessed while
+ * holding the main process lock. The signal changes
+ * from being in transit to being received while in
+ * the middle queue. Non-message signals are handled
+ * immediately upon reception while message signals
+ * are moved into the inner queue.
+ *
+ * In the outer and middle queues both message signals
+ * and non-message signals are mixed. Signals in these
+ * queues are referenced using two single linked lists.
+ * One single linked list that go through all signals
+ * in the queue and another single linked list that
+ * goes through only non-message signals. The list
+ * through the non-message signals is used for fast
+ * access to these signals in the middle queue, since
+ * these should be handled immediately upon reception.
+ *
+ * The inner queue consists only of one single linked
+ * list through the message signals. A receive
+ * expression can only operate on messages once they
+ * have entered the inner queue.
+ *
+ * Author: Rickard Green
+ */
+
+#ifndef ERTS_PROC_SIG_QUEUE_H_TYPE__
+#define ERTS_PROC_SIG_QUEUE_H_TYPE__
+
+#if 0
+# define ERTS_PROC_SIG_HARD_DEBUG
+#endif
+#if 0
+# define ERTS_PROC_SIG_HARD_DEBUG_SIGQ_MSG_LEN
+#endif
+
+struct erl_mesg;
+
+typedef struct {
+ struct erl_mesg *next;
+ union {
+ struct erl_mesg **next;
+ void *attachment;
+ } specific;
+ Eterm tag;
+} ErtsSignalCommon;
+
+#define ERTS_SIG_HANDLE_REDS_MAX_PREFERED (CONTEXT_REDS/40)
+
+#ifdef ERTS_PROC_SIG_HARD_DEBUG
+# define ERTS_HDBG_CHECK_SIGNAL_IN_QUEUE(P) \
+ ERTS_HDBG_CHECK_SIGNAL_IN_QUEUE__((P), "")
+# define ERTS_HDBG_CHECK_SIGNAL_PRIV_QUEUE(P, QL) \
+ ERTS_HDBG_CHECK_SIGNAL_PRIV_QUEUE__((P), (QL), "")
+# define ERTS_HDBG_CHECK_SIGNAL_IN_QUEUE__(P, What) \
+ erts_proc_sig_hdbg_check_in_queue((P), (What), __FILE__, __LINE__)
+# define ERTS_HDBG_CHECK_SIGNAL_PRIV_QUEUE__(P, QL, What) \
+ erts_proc_sig_hdbg_check_priv_queue((P), (QL), (What), __FILE__, __LINE__)
+struct process;
+void erts_proc_sig_hdbg_check_priv_queue(struct process *c_p, int qlock,
+ char *what, char *file, int line);
+void erts_proc_sig_hdbg_check_in_queue(struct process *c_p, char *what,
+ char *file, int line);
+#else
+# define ERTS_HDBG_CHECK_SIGNAL_IN_QUEUE(P)
+# define ERTS_HDBG_CHECK_SIGNAL_PRIV_QUEUE(P, QL)
+# define ERTS_HDBG_CHECK_SIGNAL_IN_QUEUE__(P, What)
+#define ERTS_HDBG_CHECK_SIGNAL_PRIV_QUEUE__(P, QL, What)
+#endif
+
+#endif
+
+#if !defined(ERTS_PROC_SIG_QUEUE_H__) && !defined(ERTS_PROC_SIG_QUEUE_TYPE_ONLY)
+#define ERTS_PROC_SIG_QUEUE_H__
+
+#define ERTS_SIG_Q_OP_BITS 8
+#define ERTS_SIG_Q_OP_SHIFT 0
+#define ERTS_SIG_Q_OP_MASK ((1 << ERTS_SIG_Q_OP_BITS) - 1)
+
+#define ERTS_SIG_Q_TYPE_BITS 8
+#define ERTS_SIG_Q_TYPE_SHIFT ERTS_SIG_Q_OP_BITS
+#define ERTS_SIG_Q_TYPE_MASK ((1 << ERTS_SIG_Q_TYPE_BITS) - 1)
+
+#define ERTS_SIG_Q_NON_X_BITS__ (_HEADER_ARITY_OFFS \
+ + ERTS_SIG_Q_OP_BITS \
+ + ERTS_SIG_Q_TYPE_BITS)
+
+#define ERTS_SIG_Q_XTRA_BITS (32 - ERTS_SIG_Q_NON_X_BITS__)
+#define ERTS_SIG_Q_XTRA_SHIFT (ERTS_SIG_Q_OP_BITS \
+ + ERTS_SIG_Q_TYPE_BITS)
+#define ERTS_SIG_Q_XTRA_MASK ((1 << ERTS_SIG_Q_XTRA_BITS) - 1)
+
+
+#define ERTS_PROC_SIG_OP(Tag) \
+ ((int) (_unchecked_thing_arityval((Tag)) \
+ >> ERTS_SIG_Q_OP_SHIFT) & ERTS_SIG_Q_OP_MASK)
+
+#define ERTS_PROC_SIG_TYPE(Tag) \
+ ((Uint16) (_unchecked_thing_arityval((Tag)) \
+ >> ERTS_SIG_Q_TYPE_SHIFT) & ERTS_SIG_Q_TYPE_MASK)
+
+#define ERTS_PROC_SIG_XTRA(Tag) \
+ ((Uint32) (_unchecked_thing_arityval((Tag)) \
+ >> ERTS_SIG_Q_XTRA_SHIFT) & ERTS_SIG_Q_XTRA_MASK)
+
+#define ERTS_PROC_SIG_MAKE_TAG(Op, Type, Xtra) \
+ (ASSERT(0 <= (Xtra) && (Xtra) <= ERTS_SIG_Q_XTRA_MASK), \
+ _make_header((((Type) & ERTS_SIG_Q_TYPE_MASK) \
+ << ERTS_SIG_Q_TYPE_SHIFT) \
+ | (((Op) & ERTS_SIG_Q_OP_MASK) \
+ << ERTS_SIG_Q_OP_SHIFT) \
+ | (((Xtra) & ERTS_SIG_Q_XTRA_MASK) \
+ << ERTS_SIG_Q_XTRA_SHIFT), \
+ _TAG_HEADER_EXTERNAL_PID))
+
+
+/*
+ * ERTS_SIG_Q_OP_MSGQ_LEN_OFFS_MARK is not an actual
+ * operation. We keep it at the top of the OP range,
+ * larger than ERTS_SIG_Q_OP_MAX.
+ */
+#define ERTS_SIG_Q_OP_MSGQ_LEN_OFFS_MARK ERTS_SIG_Q_OP_MASK
+
+#define ERTS_PROC_SIG_MSGQ_LEN_OFFS_MARK \
+ ERTS_PROC_SIG_MAKE_TAG(ERTS_SIG_Q_OP_MSGQ_LEN_OFFS_MARK,0,0)
+
+struct dist_entry_;
+
+/*
+ * Send operations of currently supported process signals follow...
+ */
+
+/**
+ *
+ * @brief Send an exit signal to a process.
+ *
+ *
+ * @param[in] c_p Pointer to process struct of
+ * currently executing process.
+ *
+ * @param[in] from Identifier of sender.
+ *
+ * @param[in] to Identifier of local process
+ * to send signal to.
+ *
+ * @param[in] reason Exit reason.
+ *
+ * @param[in] token Seq trace token.
+ *
+ * @param[in] normal_kills If non-zero, also normal exit
+ * reason will kill the receiver
+ * if it is not trapping exit.
+ *
+ */
+void
+erts_proc_sig_send_exit(Process *c_p, Eterm from, Eterm to,
+ Eterm reason, Eterm token, int normal_kills);
+
+/**
+ *
+ * @brief Send an exit signal due to broken link to a process.
+ *
+ *
+ * @param[in] c_p Pointer to process struct of
+ * currently executing process.
+ *
+ * @param[in] from Identifier of sender.
+ *
+ * @param[in] lnk Pointer to link structure
+ * from the sending side. It
+ * should contain information
+ * about receiver.
+ *
+ * @param[in] reason Exit reason.
+ *
+ * @param[in] token Seq trace token.
+ *
+ */
+void
+erts_proc_sig_send_link_exit(Process *c_p, Eterm from, ErtsLink *lnk,
+ Eterm reason, Eterm token);
+
+/**
+ *
+ * @brief Send an link signal to a process.
+ *
+ *
+ * @param[in] c_p Pointer to process struct of
+ * currently executing process.
+ *
+ * @param[in] to Identifier of receiver.
+ *
+ * @param[in] lnk Pointer to link structure to
+ * insert on receiver side.
+ *
+ * @return A non-zero value if
+ * signal was successfully
+ * sent. If a zero, value
+ * the signal was not sent
+ * due to the receiver not
+ * existing. The sender
+ * needs to deallocate the
+ * link structure.
+ *
+ */
+int
+erts_proc_sig_send_link(Process *c_p, Eterm to, ErtsLink *lnk);
+
+/**
+ *
+ * @brief Send an unlink signal to a process.
+ *
+ *
+ * @param[in] c_p Pointer to process struct of
+ * currently executing process.
+ *
+ * @param[in] lnk Pointer to link structure from
+ * the sending side. It should
+ * contain information about
+ * receiver.
+ */
+void
+erts_proc_sig_send_unlink(Process *c_p, ErtsLink *lnk);
+
+/**
+ *
+ * @brief Send an exit signal due to broken link to a process.
+ *
+ * This function is used instead of erts_proc_sig_send_link_exit()
+ * when the signal arrives via the distribution and
+ * no link structure is available.
+ *
+ * @param[in] dep Distribution entry of channel
+ * that the signal arrived on.
+ *
+ * @param[in] from Identifier of sender.
+ *
+ * @param[in] to Identifier of receiver.
+ *
+ * @param[in] reason Exit reason.
+ *
+ * @param[in] token Seq trace token.
+ *
+ */
+void
+erts_proc_sig_send_dist_link_exit(struct dist_entry_ *dep,
+ Eterm from, Eterm to,
+ Eterm reason, Eterm token);
+
+/**
+ *
+ * @brief Send an unlink signal to a process.
+ *
+ * This function is used instead of erts_proc_sig_send_unlink()
+ * when the signal arrives via the distribution and
+ * no link structure is available.
+ *
+ * @param[in] dep Distribution entry of channel
+ * that the signal arrived on.
+ *
+ * @param[in] from Identifier of sender.
+ *
+ * @param[in] to Identifier of receiver.
+ *
+ */
+void
+erts_proc_sig_send_dist_unlink(struct dist_entry_ *dep,
+ Eterm from, Eterm to);
+
+/**
+ *
+ * @brief Send a monitor down signal to a process.
+ *
+ * @param[in] mon Pointer to target monitor
+ * structure from the sending
+ * side. It should contain
+ * information about receiver.
+ *
+ * @param[in] reason Exit reason.
+ *
+ */
+void
+erts_proc_sig_send_monitor_down(ErtsMonitor *mon, Eterm reason);
+
+/**
+ *
+ * @brief Send a demonitor signal to a process.
+ *
+ * @param[in] mon Pointer to origin monitor
+ * structure from the sending
+ * side. It should contain
+ * information about receiver.
+ *
+ * @param[in] reason Exit reason.
+ *
+ */
+void
+erts_proc_sig_send_demonitor(ErtsMonitor *mon);
+
+/**
+ *
+ * @brief Send a monitor signal to a process.
+ *
+ * @param[in] mon Pointer to target monitor
+ * structure to insert on
+ * receiver side.
+ *
+ * @param[in] to Identifier of receiver.
+ *
+ * @return A non-zero value if
+ * signal was successfully
+ * sent. If a zero, value
+ * the signal was not sent
+ * due to the receiver not
+ * existing. The sender
+ * needs to deallocate the
+ * monitor structure.
+ *
+ */
+int
+erts_proc_sig_send_monitor(ErtsMonitor *mon, Eterm to);
+
+/**
+ *
+ * @brief Send a monitor down signal to a process.
+ *
+ * This function is used instead of erts_proc_sig_send_monitor_down()
+ * when the signal arrives via the distribution and
+ * no link structure is available.
+ *
+ * @param[in] dep Pointer to distribution entry
+ * of channel that the signal
+ * arrived on.
+ *
+ * @param[in] ref Reference identifying the monitor.
+ *
+ * @param[in] from Identifier of sender.
+ *
+ * @param[in] to Identifier of receiver.
+ *
+ * @param[in] reason Exit reason.
+ *
+ */
+void
+erts_proc_sig_send_dist_monitor_down(DistEntry *dep, Eterm ref,
+ Eterm from, Eterm to,
+ Eterm reason);
+
+/**
+ *
+ * @brief Send a demonitor signal to a process.
+ *
+ * This function is used instead of erts_proc_sig_send_demonitor()
+ * when the signal arrives via the distribution and
+ * no monitor structure is available.
+ *
+ * @param[in] to Identifier of receiver.
+ *
+ * @param[in] ref Reference identifying the monitor.
+ *
+ */
+void
+erts_proc_sig_send_dist_demonitor(Eterm to, Eterm ref);
+
+/**
+ *
+ * @brief Send a persistent monitor triggered signal to a process.
+ *
+ * Used by monitors that are not auto disabled such as for
+ * example 'time_offset' monitors.
+ *
+ * @param[in] type Monitor type.
+ *
+ * @param[in] key Monitor key.
+ *
+ * @param[in] from Identifier of sender.
+ *
+ * @param[in] to Identifier of receiver.
+ *
+ * @param[in] msg Message template.
+ *
+ * @param[in] msg_sz Heap size of message template.
+ *
+ */
+void
+erts_proc_sig_send_persistent_monitor_msg(Uint16 type, Eterm key,
+ Eterm from, Eterm to,
+ Eterm msg, Uint msg_sz);
+
+/**
+ *
+ * @brief Send a trace change signal to a process.
+ *
+ * @param[in] to Identifier of receiver.
+ *
+ * @param[in] on Trace flags to enable.
+ *
+ * @param[in] off Trace flags to disable.
+ *
+ * @param[in] tracer Tracer to set. If the non-value,
+ * tracer will not be changed.
+ *
+ */
+void
+erts_proc_sig_send_trace_change(Eterm to, Uint on, Uint off,
+ Eterm tracer);
+
+/**
+ *
+ * @brief Send a group leader signal to a process.
+ *
+ * Set group-leader of receiving process. If sent locally,
+ * a response message '{Ref, Result}' is sent to the original
+ * sender when performed where Ref is the reference passed
+ * as 'ref' argument, and Result is either 'true' or 'badarg'.
+ *
+ * @param[in] c_p Pointer to process struct of
+ * currently executing process.
+ * NULL if signal arrived via
+ * distribution.
+ *
+ * @param[in] to Identifier of receiver.
+ *
+ * @param[in] gl Identifier of new group leader.
+ *
+ * @param[in] ref Reference to use in response
+ * message to locally sending
+ * process (i.e., c_p when c_p
+ * is non-null).
+ *
+ */
+void
+erts_proc_sig_send_group_leader(Process *c_p, Eterm to, Eterm gl,
+ Eterm ref);
+
+/**
+ *
+ * @brief Send an 'is process alive' signal to a process.
+ *
+ * A response message '{Ref, Result}' is sent to the
+ * sender when performed where Ref is the reference passed
+ * as 'ref' argument, and Result is either 'true' or 'false'.
+ *
+ * @param[in] c_p Pointer to process struct of
+ * currently executing process.
+ * NULL if signal arrived via
+ * distribution.
+ *
+ * @param[in] to Identifier of receiver.
+ *
+ * @param[in] ref Reference to use in response
+ * message to the sending
+ * process (i.e., c_p).
+ *
+ */
+void
+erts_proc_sig_send_is_alive_request(Process *c_p, Eterm to,
+ Eterm ref);
+
+/**
+ *
+ * @brief Send a 'process info request' signal to a process.
+ *
+ * A response message '{Ref, Result}' is sent to the
+ * sender when performed where Ref is the reference passed
+ * as 'ref' argument, and Result corresponds to return result
+ * from erlang:process_info/[1,2].
+ *
+ * @param[in] c_p Pointer to process struct of
+ * currently executing process.
+ * NULL if signal arrived via
+ * distribution.
+ *
+ * @param[in] to Identifier of receiver.
+ *
+ * @param[in] item_ix Info index array to pass to
+ * erts_process_info()
+ *
+ * @param[in] len Lenght of info index array
+ *
+ * @param[in] need_msgq_len Non-zero if message queue
+ * length is needed; otherwise,
+ * zero. If non-zero, sig_qs.len
+ * will be set to correspond
+ * to the message queue length
+ * before call to
+ * erts_process_info()
+ *
+ * @param[in] flags Flags to pass to
+ * erts_process_info()
+ *
+ * @param[in] reserve_size Heap size that is known to
+ * be needed. May not be correct
+ * though.
+ *
+ * @param[in] ref Reference to use in response
+ * message to the sending
+ * process (i.e., c_p).
+ *
+ */
+int
+erts_proc_sig_send_process_info_request(Process *c_p,
+ Eterm to,
+ int *item_ix,
+ int len,
+ int need_msgq_len,
+ int flags,
+ Uint reserve_size,
+ Eterm ref);
+
+
+/*
+ * End of send operations of currently supported process signals.
+ */
+
+
+/**
+ *
+ * @brief Handle incoming signals.
+ *
+ * Called by an ordinary scheduler in order to handle incoming
+ * signals for a process. The work is done on the middle part
+ * of the signal queue. The maximum amount of signals handled
+ * is limited by the amount of reductions given when calling.
+ * Note that a reduction does not necessarily map to a signal.
+ *
+ * @param[in] c_p Pointer to process struct of
+ * currently executing process.
+ *
+ * @param[out] statep Pointer to process state after
+ * signal handling. May not be NULL.
+ *
+ * @param[in,out] redsp Pointer to an integer containing
+ * reductions. On input, the amount
+ * of preferred reductions to be
+ * used by the call. On output, the
+ * amount of reductions consumed.
+ *
+ * @param[in] max_reds Absolute maximum of reductions
+ * to use. If the process cannot
+ * make progress after the preferred
+ * amount of reductions has been
+ * consumed, signal handling may
+ * proceed up to a maximum of
+ * 'max_reds' in order to make
+ * the process able to proceed
+ * with other tasks after handling
+ * has finished.
+ *
+ * @param[in] local_only If is zero, new signals may be
+ * fetched from the outer queue and
+ * put in the middle queue before
+ * signal handling is performed. If
+ * non-zero, no new signals will be
+ * fetched before handling begins.
+ *
+ * @return Returns a non-zero value, when
+ * no more signals to handle in the
+ * middle queue remain. A zero
+ * return value means that there
+ * remains signals in the middle
+ * queue.
+ */
+int
+erts_proc_sig_handle_incoming(Process *c_p, erts_aint32_t *statep,
+ int *redsp, int max_reds,
+ int local_only);
+
+/**
+ *
+ * @brief Handle remaining signals for an exiting process
+ *
+ * Called as part of termination of a process. It will handle
+ * remaining signals.
+ *
+ * @param[in] c_p Pointer to process struct of
+ * currently executing process.
+ *
+ * @param[in,out] redsp Pointer to an integer containing
+ * reductions. On input, the amount
+ * of maximum reductions to be
+ * used by the call. On output, the
+ * amount of reductions consumed.
+ *
+ * @return Returns a non-zero value, when
+ * no more signals to handle in the
+ * middle queue remain. A zero
+ * return value means that there
+ * remains signals in the middle
+ * queue.
+ */
+int
+erts_proc_sig_handle_exit(Process *c_p, int *redsp);
+
+/**
+ *
+ * @brief Helper for loop_rec instruction.
+ *
+ * This function should only be called from the loop_rec
+ * instruction (or equivalents). It is called when loop_rec
+ * reach the end of the inner queue (which is the only
+ * part of the signal queue that receive is allowed to
+ * operate on). When called, this function tries to make
+ * more messages available in the inner queue. This by
+ * fetching signals from the outer queue to the middle
+ * queue and/or processing signals in the middle queue.
+ *
+ * @param[in] c_p Pointer to process struct of
+ * currently executing process.
+ *
+ * @param[in] fcalls Content of FCALLS in
+ * process_main()
+ *
+ * @param[in] neg_o_reds Content of neg_o_reds in
+ * process_main()
+ *
+ * @param[out] msgpp Pointer to pointer to next
+ * available message to process.
+ * If *msgpp == NULL, no more
+ * messages are available.
+ *
+ * @param[out] get_outp Pointer to an integer
+ * indicating how to respond
+ * if no more messages are
+ * available (msgpp). If integer
+ * is set to zero, loop_rec
+ * should jump to an appropriate
+ * wait instruction. If zero,
+ * the message queue lock remain
+ * locked since the test for
+ * more messages was done.
+ * If the integer is set to a
+ * value larger that zero, the
+ * process exited. If the integer
+ * is set to a value less than
+ * zero, the process is required
+ * to yield.
+ *
+ *
+ * @return The amount of reductions
+ * consumed.
+ *
+ */
+int
+erts_proc_sig_receive_helper(Process *c_p, int fcalls,
+ int neg_o_reds, ErtsMessage **msgpp,
+ int *get_outp);
+
+/**
+ *
+ * @brief Fetch signals from the outer queue
+ *
+ * Fetches signals from outer queue and places them in the
+ * middle queue ready for signal handling. If the middle
+ * queue is empty, only message signals were present in the
+ * outer queue, and no receive tracing has been enabled on
+ * the process, the middle queue is bypassed and messages
+ * are delivered directly to the inner queue instead.
+ *
+ * @param[in] c_p Pointer to process struct of
+ * currently executing process.
+ * @returns Amount of message signals in
+ * inner plus middle signal
+ * queues after fetch completed
+ * (NOT the message queue
+ * length).
+ */
+ERTS_GLB_INLINE Sint erts_proc_sig_fetch(Process *p);
+
+/**
+ *
+ * @brief Get amount of messages in private queues
+ *
+ * @param[in] c_p Pointer to process struct of
+ * currently executing process.
+ *
+ * @returns Amount of message signals in
+ * inner plus middle signal
+ * queues after fetch completed
+ * (NOT the message queue
+ * length).
+ */
+Sint
+erts_proc_sig_privqs_len(Process *c_p);
+
+
+typedef struct {
+ Uint size;
+ ErtsMessage *msgp;
+} ErtsMessageInfo;
+
+/**
+ *
+ * @brief Prepare signal queue for inspection by process_info()
+ *
+ *
+ * @param[in] c_p Pointer to process struct of
+ * currently executing process.
+ *
+ * @param[in] rp Pointer to process struct of
+ * process to inspect.
+ *
+ * @param[in] rp_locks Process locks held on 'rp'.
+ *
+ * @param[in] info_on_self Integer set to non-zero value
+ * if caller is inspecting itself;
+ * otherwise, zero.
+ *
+ * @param[in] mip Pointer to array of
+ * ErtsMessageInfo structures.
+ */
+Uint erts_proc_sig_prep_msgq_for_inspection(Process *c_p,
+ Process *rp,
+ ErtsProcLocks rp_locks,
+ int info_on_self,
+ ErtsMessageInfo *mip);
+
+/**
+ *
+ * @brief Move message data of messages in private queues to heap
+ *
+ * Move message data of messages in private queues to the heap.
+ * This is part of GC of processes that uses on-heap message
+ * data.
+ *
+ * @param[in] c_p Pointer to process struct of
+ * currently executing process.
+ *
+ */
+void erts_proc_sig_move_msgs_to_heap(Process *c_p);
+
+/**
+ *
+ * @brief Size of signal in bytes.
+ *
+ * @param[in] sig Signal to inspect.
+ *
+ */
+Uint erts_proc_sig_signal_size(ErtsSignal *sig);
+
+
+/**
+ *
+ * @brief Clear seq trace tokens on all signals
+ *
+ * Assumes thread progress has been blocked!
+ *
+ * @param[in] c_p Pointer to process
+ *
+ */
+void
+erts_proc_sig_clear_seq_trace_tokens(Process *c_p);
+
+/**
+ * @brief Initialize this functionality
+ */
+void erts_proc_sig_queue_init(void);
+
+void
+erts_proc_sig_debug_foreach_sig(Process *c_p,
+ void (*msg_func)(ErtsMessage *, void *),
+ void (*oh_func)(ErlOffHeap *, void *),
+ void (*mon_func)(ErtsMonitor *, void *),
+ void (*lnk_func)(ErtsLink *, void *),
+ void *arg);
+
+extern Process *erts_dirty_process_signal_handler;
+extern Process *erts_dirty_process_signal_handler_high;
+extern Process *erts_dirty_process_signal_handler_max;
+
+void erts_proc_sig_fetch__(Process *proc);
+Sint erts_proc_sig_fetch_msgq_len_offs__(Process *proc);
+
+#if ERTS_GLB_INLINE_INCL_FUNC_DEF
+
+ERTS_GLB_INLINE Sint
+erts_proc_sig_fetch(Process *proc)
+{
+ Sint res = 0;
+ ErtsSignal *sig;
+
+ ERTS_LC_ASSERT(erts_thr_progress_is_blocking()
+ || ERTS_PROC_IS_EXITING(proc)
+ || ((erts_proc_lc_my_proc_locks(proc)
+ & (ERTS_PROC_LOCK_MAIN
+ | ERTS_PROC_LOCK_MSGQ))
+ == (ERTS_PROC_LOCK_MAIN
+ | ERTS_PROC_LOCK_MSGQ)));
+
+ ERTS_HDBG_CHECK_SIGNAL_IN_QUEUE(proc);
+ ERTS_HDBG_CHECK_SIGNAL_PRIV_QUEUE(proc, !0);
+
+ sig = (ErtsSignal *) proc->sig_inq.first;
+ if (sig) {
+ if (ERTS_LIKELY(sig->common.tag != ERTS_PROC_SIG_MSGQ_LEN_OFFS_MARK))
+ erts_proc_sig_fetch__(proc);
+ else
+ res = erts_proc_sig_fetch_msgq_len_offs__(proc);
+ }
+
+ res += proc->sig_qs.len;
+
+ ERTS_HDBG_CHECK_SIGNAL_PRIV_QUEUE(proc, !0);
+
+#ifdef ERTS_PROC_SIG_HARD_DEBUG_SIGQ_MSG_LEN
+ {
+ Sint len = 0;
+ ERTS_FOREACH_SIG_PRIVQS(
+ proc, mp,
+ {
+ if (ERTS_SIG_IS_MSG(mp))
+ len++;
+ });
+ ERTS_ASSERT(res == len);
+ }
+#endif
+
+ return res;
+}
+
+#endif /* ERTS_GLB_INLINE_INCL_FUNC_DEF */
+
+#endif /* ERTS_PROC_SIG_QUEUE_H__ */
diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c
index 938dd1b9ac..650ec0958c 100644
--- a/erts/emulator/beam/erl_process.c
+++ b/erts/emulator/beam/erl_process.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1996-2017. All Rights Reserved.
+ * Copyright Ericsson AB 1996-2018. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -36,7 +36,6 @@
#include "erl_db.h"
#include "dist.h"
#include "beam_catches.h"
-#include "erl_instrument.h"
#include "erl_threads.h"
#include "erl_binary.h"
#include "beam_bp.h"
@@ -53,6 +52,7 @@
#include "erl_nfunc_sched.h"
#include "erl_check_io.h"
#include "erl_poll.h"
+#include "erl_proc_sig_queue.h"
#define ERTS_CHECK_TIME_REDS CONTEXT_REDS
#define ERTS_DELAYED_WAKEUP_INFINITY (~(Uint64) 0)
@@ -137,36 +137,6 @@ runq_got_work_to_execute(ErtsRunQueue *rq)
return runq_got_work_to_execute_flags(ERTS_RUNQ_FLGS_GET_NOB(rq));
}
-#undef RUNQ_READ_RQ
-#undef RUNQ_SET_RQ
-#define RUNQ_READ_RQ(X) ((ErtsRunQueue *) erts_atomic_read_nob((X)))
-#define RUNQ_SET_RQ(X, RQ) erts_atomic_set_nob((X), (erts_aint_t) (RQ))
-
-#ifdef DEBUG
-# if defined(ARCH_64)
-# define ERTS_DBG_SET_INVALID_RUNQP(RQP, N) \
- (RUNQ_SET_RQ((RQP), (0xdeadbeefdead0003LL | ((N) << 4)))
-# define ERTS_DBG_VERIFY_VALID_RUNQP(RQP) \
-do { \
- ASSERT((RQP) != NULL); \
- ASSERT(((((Uint) (RQP)) & ((Uint) 0x3))) == ((Uint) 0)); \
- ASSERT((((Uint) (RQP)) & ~((Uint) 0xffff)) != ((Uint) 0xdeadbeefdead0000LL));\
-} while (0)
-# else
-# define ERTS_DBG_SET_INVALID_RUNQP(RQP, N) \
- (RUNQ_SET_RQ((RQP), (0xdead0003 | ((N) << 4))))
-# define ERTS_DBG_VERIFY_VALID_RUNQP(RQP) \
-do { \
- ASSERT((RQP) != NULL); \
- ASSERT(((((UWord) (RQP)) & ((UWord) 1))) == ((UWord) 0)); \
- ASSERT((((UWord) (RQP)) & ~((UWord) 0xffff)) != ((UWord) 0xdead0000)); \
-} while (0)
-# endif
-#else
-# define ERTS_DBG_SET_INVALID_RUNQP(RQP, N)
-# define ERTS_DBG_VERIFY_VALID_RUNQP(RQP)
-#endif
-
const Process erts_invalid_process = {{ERTS_INVALID_PID}};
extern BeamInstr beam_apply[];
@@ -201,11 +171,19 @@ int erts_dio_sched_thread_suggested_stack_size = -1;
ErtsLcPSDLocks erts_psd_required_locks[ERTS_PSD_SIZE];
#endif
-static struct ErtsSchedBusyWait_ {
+typedef struct {
int aux_work;
int tse;
int sys_schedule;
-} sched_busy_wait;
+} ErtsBusyWaitParams;
+
+static ErtsBusyWaitParams sched_busy_wait_params[ERTS_SCHED_TYPE_LAST + 1];
+
+static ERTS_INLINE ErtsBusyWaitParams *
+sched_get_busy_wait_params(ErtsSchedulerData *esdp)
+{
+ return &sched_busy_wait_params[esdp->type];
+}
int erts_disable_proc_not_running_opt;
@@ -461,7 +439,8 @@ typedef enum {
ERTS_PSTT_CLA, /* Copy Literal Area */
ERTS_PSTT_COHMQ, /* Change off heap message queue */
ERTS_PSTT_FTMQ, /* Flush trace msg queue */
- ERTS_PSTT_ETS_FREE_FIXATION
+ ERTS_PSTT_ETS_FREE_FIXATION,
+ ERTS_PSTT_PRIO_SIG /* Elevate prio on signal management */
} ErtsProcSysTaskType;
#define ERTS_MAX_PROC_SYS_TASK_ARGS 2
@@ -609,7 +588,6 @@ dbg_chk_aux_work_val(erts_aint32_t value)
valid |= ERTS_SSI_AUX_WORK_CNCLD_TMRS;
valid |= ERTS_SSI_AUX_WORK_CNCLD_TMRS_THR_PRGR;
valid |= ERTS_SSI_AUX_WORK_THR_PRGR_LATER_OP;
- valid |= ERTS_SSI_AUX_WORK_PENDING_EXITERS;
#if HAVE_ERTS_MSEG
valid |= ERTS_SSI_AUX_WORK_MSEG_CACHE_CHECK;
#endif
@@ -631,7 +609,6 @@ dbg_chk_aux_work_val(erts_aint32_t value)
#define ERTS_DBG_CHK_SSI_AUX_WORK(SSI)
#endif
-static void do_handle_pending_exiters(ErtsProcList *);
static void wake_scheduler(ErtsRunQueue *rq);
#if defined(ERTS_ENABLE_LOCK_CHECK)
@@ -696,8 +673,6 @@ erts_pre_init_process(void)
= "MISC_THR_PRGR";
erts_aux_work_flag_descr[ERTS_SSI_AUX_WORK_MISC_IX]
= "MISC";
- erts_aux_work_flag_descr[ERTS_SSI_AUX_WORK_PENDING_EXITERS_IX]
- = "PENDING_EXITERS";
erts_aux_work_flag_descr[ERTS_SSI_AUX_WORK_SET_TMO_IX]
= "SET_TMO";
erts_aux_work_flag_descr[ERTS_SSI_AUX_WORK_MSEG_CACHE_CHECK_IX]
@@ -1538,6 +1513,20 @@ erts_proclist_destroy(ErtsProcList *plp)
proclist_destroy(plp);
}
+void
+erts_proclist_dump(fmtfn_t to, void *to_arg, ErtsProcList *plp)
+{
+ ErtsProcList *first = plp;
+
+ while (plp) {
+ erts_print(to, to_arg, "%T", plp->pid);
+ plp = plp->next;
+ if (plp == first)
+ break;
+ }
+ erts_print(to, to_arg, "\n");
+}
+
void *
erts_psd_set_init(Process *p, int ix, void *data)
{
@@ -2518,6 +2507,8 @@ handle_yield(ErtsAuxWorkData *awdp, erts_aint32_t aux_work, int waiting)
yield |= erts_handle_yielded_ets_all_request(awdp->esdp,
&awdp->yield.ets_all);
+ yield |= erts_handle_yielded_alcu_blockscan(awdp->esdp,
+ &awdp->yield.alcu_blockscan);
/*
* Other yielding operations...
@@ -2547,27 +2538,6 @@ handle_mseg_cache_check(ErtsAuxWorkData *awdp, erts_aint32_t aux_work, int waiti
static ERTS_INLINE erts_aint32_t
-handle_pending_exiters(ErtsAuxWorkData *awdp, erts_aint32_t aux_work, int waiting)
-{
- ErtsProcList *pnd_xtrs;
- ErtsRunQueue *rq;
-
- rq = awdp->esdp->run_queue;
- unset_aux_work_flags(awdp->ssi, ERTS_SSI_AUX_WORK_PENDING_EXITERS);
-
- erts_runq_lock(rq);
- pnd_xtrs = rq->procs.pending_exiters;
- rq->procs.pending_exiters = NULL;
- erts_runq_unlock(rq);
-
- if (erts_proclist_fetch(&pnd_xtrs, NULL))
- do_handle_pending_exiters(pnd_xtrs);
-
- return aux_work & ~ERTS_SSI_AUX_WORK_PENDING_EXITERS;
-}
-
-
-static ERTS_INLINE erts_aint32_t
handle_setup_aux_work_timer(ErtsAuxWorkData *awdp, erts_aint32_t aux_work, int waiting)
{
unset_aux_work_flags(awdp->ssi, ERTS_SSI_AUX_WORK_SET_TMO);
@@ -2648,9 +2618,6 @@ handle_aux_work(ErtsAuxWorkData *awdp, erts_aint32_t orig_aux_work, int waiting)
HANDLE_AUX_WORK(ERTS_SSI_AUX_WORK_MISC,
handle_misc_aux_work);
- HANDLE_AUX_WORK(ERTS_SSI_AUX_WORK_PENDING_EXITERS,
- handle_pending_exiters);
-
HANDLE_AUX_WORK(ERTS_SSI_AUX_WORK_SET_TMO,
handle_setup_aux_work_timer);
@@ -3276,7 +3243,7 @@ scheduler_wait(int *fcalls, ErtsSchedulerData *esdp, ErtsRunQueue *rq)
erts_aint32_t aux_work = 0;
int thr_prgr_active = 1;
erts_aint32_t flgs;
- ERTS_MSACC_PUSH_STATE_M();
+ ERTS_MSACC_PUSH_STATE();
ERTS_LC_ASSERT(erts_lc_runq_is_locked(rq));
@@ -3304,7 +3271,7 @@ scheduler_wait(int *fcalls, ErtsSchedulerData *esdp, ErtsRunQueue *rq)
erts_runq_unlock(rq);
- spincount = sched_busy_wait.tse;
+ spincount = sched_get_busy_wait_params(esdp)->tse;
if (ERTS_SCHEDULER_IS_DIRTY(esdp))
dirty_sched_wall_time_change(esdp, working = 0);
@@ -3385,9 +3352,9 @@ scheduler_wait(int *fcalls, ErtsSchedulerData *esdp, ErtsRunQueue *rq)
- 1) + 1;
} else
timeout = -1;
- ERTS_MSACC_SET_STATE_CACHED_M(ERTS_MSACC_STATE_SLEEP);
+ ERTS_MSACC_SET_STATE_CACHED(ERTS_MSACC_STATE_SLEEP);
res = erts_tse_twait(ssi->event, timeout);
- ERTS_MSACC_POP_STATE_M();
+ ERTS_MSACC_POP_STATE();
current_time = ERTS_SCHEDULER_IS_DIRTY(esdp) ? 0 :
erts_get_monotonic_time(esdp);
} while (res == EINTR);
@@ -3406,7 +3373,7 @@ scheduler_wait(int *fcalls, ErtsSchedulerData *esdp, ErtsRunQueue *rq)
}
flgs = sched_prep_cont_spin_wait(ssi);
- spincount = sched_busy_wait.aux_work;
+ spincount = sched_get_busy_wait_params(esdp)->aux_work;
if (!(flgs & ERTS_SSI_FLG_WAITING)) {
ASSERT(!(flgs & ERTS_SSI_FLG_SLEEPING));
@@ -3926,21 +3893,16 @@ immigrate(ErtsRunQueue *c_rq, ErtsMigrationPath *mp)
Port *prt;
prt = erts_dequeue_port(rq);
if (prt)
- RUNQ_SET_RQ(&prt->run_queue, c_rq);
+ erts_set_runq_port(prt, c_rq);
erts_runq_unlock(rq);
if (prt) {
- /* port might terminate while we have no lock... */
rq = erts_port_runq(prt);
- if (rq) {
- if (rq != c_rq)
- erts_exit(ERTS_ABORT_EXIT,
- "%s:%d:%s(): Internal error",
- __FILE__, __LINE__, __func__);
- erts_enqueue_port(c_rq, prt);
- if (!iflag)
- return; /* done */
- erts_runq_unlock(c_rq);
- }
+ if (rq != c_rq)
+ ERTS_INTERNAL_ERROR("Unexpected run-queue");
+ erts_enqueue_port(c_rq, prt);
+ if (!iflag)
+ return; /* done */
+ erts_runq_unlock(c_rq);
}
}
else {
@@ -3954,12 +3916,11 @@ immigrate(ErtsRunQueue *c_rq, ErtsMigrationPath *mp)
while (proc) {
erts_aint32_t state;
state = erts_atomic32_read_acqb(&proc->state);
- if (!(ERTS_PSFLG_BOUND & state)
- && (prio == (int) ERTS_PSFLGS_GET_PRQ_PRIO(state))) {
+ if (prio == (int) ERTS_PSFLGS_GET_PRQ_PRIO(state)
+ && erts_try_change_runq_proc(proc, c_rq)) {
ErtsRunQueueInfo *rqi = &rq->procs.prio_info[prio];
unqueue_process(rq, rpq, rqi, prio, prev_proc, proc);
erts_runq_unlock(rq);
- RUNQ_SET_RQ(&proc->run_queue, c_rq);
rq_locked = 0;
erts_runq_lock(c_rq);
@@ -4140,21 +4101,13 @@ evacuate_run_queue(ErtsRunQueue *rq,
while (prt) {
ErtsRunQueue *prt_rq;
prt = erts_dequeue_port(rq);
- RUNQ_SET_RQ(&prt->run_queue, to_rq);
+ erts_set_runq_port(prt, to_rq);
erts_runq_unlock(rq);
- /*
- * The port might terminate while
- * we have no lock on it...
- */
prt_rq = erts_port_runq(prt);
- if (prt_rq) {
- if (prt_rq != to_rq)
- erts_exit(ERTS_ABORT_EXIT,
- "%s:%d:%s() internal error\n",
- __FILE__, __LINE__, __func__);
- erts_enqueue_port(to_rq, prt);
- erts_runq_unlock(to_rq);
- }
+ if (prt_rq != to_rq)
+ ERTS_INTERNAL_ERROR("Unexpected run-queue");
+ erts_enqueue_port(to_rq, prt);
+ erts_runq_unlock(to_rq);
erts_runq_lock(rq);
prt = rq->ports.start;
}
@@ -4165,8 +4118,6 @@ evacuate_run_queue(ErtsRunQueue *rq,
for (prio_q = 0; prio_q < ERTS_NO_PROC_PRIO_QUEUES; prio_q++) {
erts_aint32_t state;
Process *proc;
- int notify = 0;
- to_rq = NULL;
if (!mp->prio[prio_q].runq)
return;
@@ -4177,14 +4128,13 @@ evacuate_run_queue(ErtsRunQueue *rq,
while (proc) {
Process *real_proc;
int prio;
- erts_aint32_t max_qbit, qbit, real_state;
+ erts_aint32_t max_qbit, qbit;
prio = ERTS_PSFLGS_GET_PRQ_PRIO(state);
qbit = ((erts_aint32_t) 1) << prio;
if (!(state & ERTS_PSFLG_PROXY)) {
real_proc = proc;
- real_state = state;
}
else {
real_proc = erts_proc_lookup_raw(proc->common.id);
@@ -4192,7 +4142,6 @@ evacuate_run_queue(ErtsRunQueue *rq,
free_proxy_proc(proc);
goto handle_next_proc;
}
- real_state = erts_atomic32_read_acqb(&real_proc->state);
}
max_qbit = (state >> ERTS_PSFLGS_IN_PRQ_MASK_OFFSET);
@@ -4227,7 +4176,13 @@ evacuate_run_queue(ErtsRunQueue *rq,
goto handle_next_proc;
}
- if (ERTS_PSFLG_BOUND & real_state) {
+ prio = (int) ERTS_PSFLGS_GET_PRQ_PRIO(state);
+ to_rq = mp->prio[prio].runq;
+
+ if (!to_rq)
+ goto handle_next_proc;
+
+ if (!erts_try_change_runq_proc(proc, to_rq)) {
/* Bound processes get stuck here... */
proc->next = NULL;
if (sbpp->last)
@@ -4237,16 +4192,13 @@ evacuate_run_queue(ErtsRunQueue *rq,
sbpp->last = proc;
}
else {
- int prio = (int) ERTS_PSFLGS_GET_PRQ_PRIO(state);
erts_runq_unlock(rq);
- to_rq = mp->prio[prio].runq;
- RUNQ_SET_RQ(&proc->run_queue, to_rq);
-
erts_runq_lock(to_rq);
enqueue_process(to_rq, prio, proc);
erts_runq_unlock(to_rq);
- notify = 1;
+
+ smp_notify_inc_runq(to_rq);
erts_runq_lock(rq);
}
@@ -4254,8 +4206,7 @@ evacuate_run_queue(ErtsRunQueue *rq,
handle_next_proc:
proc = dequeue_process(rq, prio_q, &state);
}
- if (notify)
- smp_notify_inc_runq(to_rq);
+
}
}
@@ -4309,14 +4260,13 @@ try_steal_task_from_victim(ErtsRunQueue *rq, int *rq_lockedp, ErtsRunQueue *vrq,
proc = rpq->first;
while (proc) {
- erts_aint32_t state = erts_atomic32_read_acqb(&proc->state);
- if (!(ERTS_PSFLG_BOUND & state)) {
+ if (erts_try_change_runq_proc(proc, rq)) {
+ erts_aint32_t state = erts_atomic32_read_acqb(&proc->state);
/* Steal process */
int prio = (int) ERTS_PSFLGS_GET_PRQ_PRIO(state);
ErtsRunQueueInfo *rqi = &vrq->procs.prio_info[prio];
unqueue_process(vrq, rpq, rqi, prio, prev_proc, proc);
erts_runq_unlock(vrq);
- RUNQ_SET_RQ(&proc->run_queue, rq);
erts_runq_lock(rq);
*rq_lockedp = 1;
@@ -4341,26 +4291,14 @@ no_procs:
if (vrq->ports.start) {
ErtsRunQueue *prt_rq;
Port *prt = erts_dequeue_port(vrq);
- RUNQ_SET_RQ(&prt->run_queue, rq);
+ erts_set_runq_port(prt, rq);
erts_runq_unlock(vrq);
-
- /*
- * The port might terminate while
- * we have no lock on it...
- */
-
prt_rq = erts_port_runq(prt);
- if (!prt_rq)
- return 0;
- else {
- if (prt_rq != rq)
- erts_exit(ERTS_ABORT_EXIT,
- "%s:%d:%s() internal error\n",
- __FILE__, __LINE__, __func__);
- *rq_lockedp = 1;
- erts_enqueue_port(rq, prt);
- return !0;
- }
+ if (prt_rq != rq)
+ ERTS_INTERNAL_ERROR("Unexpected run-queue");
+ *rq_lockedp = 1;
+ erts_enqueue_port(rq, prt);
+ return !0;
}
erts_runq_unlock(vrq);
@@ -5321,24 +5259,35 @@ typedef enum {
#define ERTS_WAKEUP_OTHER_FIXED_INC_LEGACY (CONTEXT_REDS/10)
-static struct {
+typedef struct {
ErtsSchedWakeupOtherThreshold threshold;
ErtsSchedWakeupOtherType type;
int limit;
int dec_shift;
int dec_mask;
void (*check)(ErtsRunQueue *rq, Uint32 flags);
-} wakeup_other;
+} ErtsWakeupOtherParams;
+
+static ErtsWakeupOtherParams sched_wakeup_other_params[ERTS_SCHED_TYPE_LAST + 1];
+
+static ERTS_INLINE ErtsWakeupOtherParams *
+runq_get_wakeup_other_params(ErtsRunQueue *rq)
+{
+ ErtsSchedulerData *esdp = rq->scheduler;
+ return &sched_wakeup_other_params[esdp->type];
+}
static void
wakeup_other_check(ErtsRunQueue *rq, Uint32 flags)
{
+ ErtsWakeupOtherParams *wo_params = runq_get_wakeup_other_params(rq);
int wo_reds = rq->wakeup_other_reds;
+
if (wo_reds) {
int left_len = erts_atomic32_read_dirty(&rq->len) - 1;
if (left_len < 1) {
- int wo_reduce = wo_reds << wakeup_other.dec_shift;
- wo_reduce &= wakeup_other.dec_mask;
+ int wo_reduce = wo_reds << wo_params->dec_shift;
+ wo_reduce &= wo_params->dec_mask;
rq->wakeup_other -= wo_reduce;
if (rq->wakeup_other < 0)
rq->wakeup_other = 0;
@@ -5346,7 +5295,7 @@ wakeup_other_check(ErtsRunQueue *rq, Uint32 flags)
else {
rq->wakeup_other += (left_len*wo_reds
+ ERTS_WAKEUP_OTHER_FIXED_INC);
- if (rq->wakeup_other > wakeup_other.limit) {
+ if (rq->wakeup_other > wo_params->limit) {
if (ERTS_RUNQ_IX_IS_DIRTY(rq->ix)) {
if (rq->waiting) {
wake_dirty_scheduler(rq);
@@ -5368,42 +5317,44 @@ wakeup_other_check(ErtsRunQueue *rq, Uint32 flags)
}
static void
-wakeup_other_set_limit(void)
+wakeup_other_set_limit(ErtsWakeupOtherParams *params)
{
- switch (wakeup_other.threshold) {
+ switch (params->threshold) {
case ERTS_SCHED_WAKEUP_OTHER_THRESHOLD_VERY_HIGH:
- wakeup_other.limit = ERTS_WAKEUP_OTHER_LIMIT_VERY_HIGH;
- wakeup_other.dec_shift = ERTS_WAKEUP_OTHER_DEC_SHIFT_VERY_HIGH;
+ params->limit = ERTS_WAKEUP_OTHER_LIMIT_VERY_HIGH;
+ params->dec_shift = ERTS_WAKEUP_OTHER_DEC_SHIFT_VERY_HIGH;
break;
case ERTS_SCHED_WAKEUP_OTHER_THRESHOLD_HIGH:
- wakeup_other.limit = ERTS_WAKEUP_OTHER_LIMIT_HIGH;
- wakeup_other.dec_shift = ERTS_WAKEUP_OTHER_DEC_SHIFT_HIGH;
+ params->limit = ERTS_WAKEUP_OTHER_LIMIT_HIGH;
+ params->dec_shift = ERTS_WAKEUP_OTHER_DEC_SHIFT_HIGH;
break;
case ERTS_SCHED_WAKEUP_OTHER_THRESHOLD_MEDIUM:
- wakeup_other.limit = ERTS_WAKEUP_OTHER_LIMIT_MEDIUM;
- wakeup_other.dec_shift = ERTS_WAKEUP_OTHER_DEC_SHIFT_MEDIUM;
+ params->limit = ERTS_WAKEUP_OTHER_LIMIT_MEDIUM;
+ params->dec_shift = ERTS_WAKEUP_OTHER_DEC_SHIFT_MEDIUM;
break;
case ERTS_SCHED_WAKEUP_OTHER_THRESHOLD_LOW:
- wakeup_other.limit = ERTS_WAKEUP_OTHER_LIMIT_LOW;
- wakeup_other.dec_shift = ERTS_WAKEUP_OTHER_DEC_SHIFT_LOW;
+ params->limit = ERTS_WAKEUP_OTHER_LIMIT_LOW;
+ params->dec_shift = ERTS_WAKEUP_OTHER_DEC_SHIFT_LOW;
break;
case ERTS_SCHED_WAKEUP_OTHER_THRESHOLD_VERY_LOW:
- wakeup_other.limit = ERTS_WAKEUP_OTHER_LIMIT_VERY_LOW;
- wakeup_other.dec_shift = ERTS_WAKEUP_OTHER_DEC_SHIFT_VERY_LOW;
+ params->limit = ERTS_WAKEUP_OTHER_LIMIT_VERY_LOW;
+ params->dec_shift = ERTS_WAKEUP_OTHER_DEC_SHIFT_VERY_LOW;
break;
}
- if (wakeup_other.dec_shift < 0)
- wakeup_other.dec_mask = (1 << (sizeof(wakeup_other.dec_mask)*8
- + wakeup_other.dec_shift)) - 1;
+
+ if (params->dec_shift < 0)
+ params->dec_mask = (1 << (sizeof(params->dec_mask)*8
+ + params->dec_shift)) - 1;
else {
- wakeup_other.dec_mask = 0;
- wakeup_other.dec_mask = ~wakeup_other.dec_mask;
+ params->dec_mask = 0;
+ params->dec_mask = ~params->dec_mask;
}
}
static void
wakeup_other_check_legacy(ErtsRunQueue *rq, Uint32 flags)
{
+ ErtsWakeupOtherParams *wo_params = runq_get_wakeup_other_params(rq);
int wo_reds = rq->wakeup_other_reds;
if (wo_reds) {
erts_aint32_t len = erts_atomic32_read_dirty(&rq->len);
@@ -5412,7 +5363,7 @@ wakeup_other_check_legacy(ErtsRunQueue *rq, Uint32 flags)
if (rq->wakeup_other < 0)
rq->wakeup_other = 0;
}
- else if (rq->wakeup_other < wakeup_other.limit)
+ else if (rq->wakeup_other < wo_params->limit)
rq->wakeup_other += len*wo_reds + ERTS_WAKEUP_OTHER_FIXED_INC_LEGACY;
else {
if (flags & ERTS_RUNQ_FLG_PROTECTED)
@@ -5428,23 +5379,23 @@ wakeup_other_check_legacy(ErtsRunQueue *rq, Uint32 flags)
}
static void
-wakeup_other_set_limit_legacy(void)
+wakeup_other_set_limit_legacy(ErtsWakeupOtherParams *params)
{
- switch (wakeup_other.threshold) {
+ switch (params->threshold) {
case ERTS_SCHED_WAKEUP_OTHER_THRESHOLD_VERY_HIGH:
- wakeup_other.limit = ERTS_WAKEUP_OTHER_LIMIT_VERY_HIGH_LEGACY;
+ params->limit = ERTS_WAKEUP_OTHER_LIMIT_VERY_HIGH_LEGACY;
break;
case ERTS_SCHED_WAKEUP_OTHER_THRESHOLD_HIGH:
- wakeup_other.limit = ERTS_WAKEUP_OTHER_LIMIT_HIGH_LEGACY;
+ params->limit = ERTS_WAKEUP_OTHER_LIMIT_HIGH_LEGACY;
break;
case ERTS_SCHED_WAKEUP_OTHER_THRESHOLD_MEDIUM:
- wakeup_other.limit = ERTS_WAKEUP_OTHER_LIMIT_MEDIUM_LEGACY;
+ params->limit = ERTS_WAKEUP_OTHER_LIMIT_MEDIUM_LEGACY;
break;
case ERTS_SCHED_WAKEUP_OTHER_THRESHOLD_LOW:
- wakeup_other.limit = ERTS_WAKEUP_OTHER_LIMIT_LOW_LEGACY;
+ params->limit = ERTS_WAKEUP_OTHER_LIMIT_LOW_LEGACY;
break;
case ERTS_SCHED_WAKEUP_OTHER_THRESHOLD_VERY_LOW:
- wakeup_other.limit = ERTS_WAKEUP_OTHER_LIMIT_VERY_LOW_LEGACY;
+ params->limit = ERTS_WAKEUP_OTHER_LIMIT_VERY_LOW_LEGACY;
break;
}
}
@@ -5452,15 +5403,21 @@ wakeup_other_set_limit_legacy(void)
static void
set_wakeup_other_data(void)
{
- switch (wakeup_other.type) {
- case ERTS_SCHED_WAKEUP_OTHER_TYPE_DEFAULT:
- wakeup_other.check = wakeup_other_check;
- wakeup_other_set_limit();
- break;
- case ERTS_SCHED_WAKEUP_OTHER_TYPE_LEGACY:
- wakeup_other.check = wakeup_other_check_legacy;
- wakeup_other_set_limit_legacy();
- break;
+ ErtsSchedType type;
+
+ for (type = ERTS_SCHED_TYPE_FIRST; type <= ERTS_SCHED_TYPE_LAST; type++) {
+ ErtsWakeupOtherParams *params = &sched_wakeup_other_params[type];
+
+ switch (params->type) {
+ case ERTS_SCHED_WAKEUP_OTHER_TYPE_DEFAULT:
+ params->check = wakeup_other_check;
+ wakeup_other_set_limit(params);
+ break;
+ case ERTS_SCHED_WAKEUP_OTHER_TYPE_LEGACY:
+ params->check = wakeup_other_check_legacy;
+ wakeup_other_set_limit_legacy(params);
+ break;
+ }
}
}
@@ -5515,56 +5472,64 @@ runq_supervisor(void *unused)
void
erts_early_init_scheduling(int no_schedulers)
{
+ ErtsSchedType type;
+
aux_work_timeout_early_init(no_schedulers);
- wakeup_other.threshold = ERTS_SCHED_WAKEUP_OTHER_THRESHOLD_MEDIUM;
- wakeup_other.type = ERTS_SCHED_WAKEUP_OTHER_TYPE_DEFAULT;
- sched_busy_wait.sys_schedule = ERTS_SCHED_SYS_SLEEP_SPINCOUNT_MEDIUM;
- sched_busy_wait.tse = (ERTS_SCHED_SYS_SLEEP_SPINCOUNT_MEDIUM
- * ERTS_SCHED_TSE_SLEEP_SPINCOUNT_FACT);
- sched_busy_wait.aux_work = (ERTS_SCHED_SYS_SLEEP_SPINCOUNT_MEDIUM
- * ERTS_SCHED_AUX_WORK_SLEEP_SPINCOUNT_FACT_MEDIUM);
+
+ for (type = ERTS_SCHED_TYPE_FIRST; type <= ERTS_SCHED_TYPE_LAST; type++) {
+ erts_sched_set_wakeup_other_threshold(type, "medium");
+ erts_sched_set_wakeup_other_type(type, "default");
+
+ erts_sched_set_busy_wait_threshold(type, "medium");
+ }
+
+ erts_sched_set_busy_wait_threshold(ERTS_SCHED_DIRTY_CPU, "short");
+ erts_sched_set_busy_wait_threshold(ERTS_SCHED_DIRTY_IO, "short");
}
int
-erts_sched_set_wakeup_other_thresold(char *str)
-{
- ErtsSchedWakeupOtherThreshold threshold;
- if (sys_strcmp(str, "very_high") == 0)
- threshold = ERTS_SCHED_WAKEUP_OTHER_THRESHOLD_VERY_HIGH;
- else if (sys_strcmp(str, "high") == 0)
- threshold = ERTS_SCHED_WAKEUP_OTHER_THRESHOLD_HIGH;
- else if (sys_strcmp(str, "medium") == 0)
- threshold = ERTS_SCHED_WAKEUP_OTHER_THRESHOLD_MEDIUM;
- else if (sys_strcmp(str, "low") == 0)
- threshold = ERTS_SCHED_WAKEUP_OTHER_THRESHOLD_LOW;
- else if (sys_strcmp(str, "very_low") == 0)
- threshold = ERTS_SCHED_WAKEUP_OTHER_THRESHOLD_VERY_LOW;
- else
- return EINVAL;
- wakeup_other.threshold = threshold;
- set_wakeup_other_data();
+erts_sched_set_wakeup_other_threshold(ErtsSchedType sched_type, char *str)
+{
+ ErtsWakeupOtherParams *params = &sched_wakeup_other_params[sched_type];
+
+ if (sys_strcmp(str, "very_high") == 0) {
+ params->threshold = ERTS_SCHED_WAKEUP_OTHER_THRESHOLD_VERY_HIGH;
+ } else if (sys_strcmp(str, "high") == 0) {
+ params->threshold = ERTS_SCHED_WAKEUP_OTHER_THRESHOLD_HIGH;
+ } else if (sys_strcmp(str, "medium") == 0) {
+ params->threshold = ERTS_SCHED_WAKEUP_OTHER_THRESHOLD_MEDIUM;
+ } else if (sys_strcmp(str, "low") == 0) {
+ params->threshold = ERTS_SCHED_WAKEUP_OTHER_THRESHOLD_LOW;
+ } else if (sys_strcmp(str, "very_low") == 0) {
+ params->threshold = ERTS_SCHED_WAKEUP_OTHER_THRESHOLD_VERY_LOW;
+ } else {
+ return EINVAL;
+ }
+
return 0;
}
int
-erts_sched_set_wakeup_other_type(char *str)
+erts_sched_set_wakeup_other_type(ErtsSchedType sched_type, char *str)
{
- ErtsSchedWakeupOtherType type;
- if (sys_strcmp(str, "default") == 0)
- type = ERTS_SCHED_WAKEUP_OTHER_TYPE_DEFAULT;
- else if (sys_strcmp(str, "legacy") == 0)
- type = ERTS_SCHED_WAKEUP_OTHER_TYPE_LEGACY;
- else
- return EINVAL;
- wakeup_other.type = type;
+ ErtsWakeupOtherParams *params = &sched_wakeup_other_params[sched_type];
+
+ if (sys_strcmp(str, "default") == 0) {
+ params->type = ERTS_SCHED_WAKEUP_OTHER_TYPE_DEFAULT;
+ } else if (sys_strcmp(str, "legacy") == 0) {
+ params->type = ERTS_SCHED_WAKEUP_OTHER_TYPE_LEGACY;
+ } else {
+ return EINVAL;
+ }
+
return 0;
}
int
-erts_sched_set_busy_wait_threshold(char *str)
+erts_sched_set_busy_wait_threshold(ErtsSchedType sched_type, char *str)
{
- int sys_sched;
- int aux_work_fact;
+ ErtsBusyWaitParams *params = &sched_busy_wait_params[sched_type];
+ int aux_work_fact, sys_sched;
if (sys_strcmp(str, "very_long") == 0) {
sys_sched = ERTS_SCHED_SYS_SLEEP_SPINCOUNT_VERY_LONG;
@@ -5594,9 +5559,9 @@ erts_sched_set_busy_wait_threshold(char *str)
return EINVAL;
}
- sched_busy_wait.sys_schedule = sys_sched;
- sched_busy_wait.tse = sys_sched*ERTS_SCHED_TSE_SLEEP_SPINCOUNT_FACT;
- sched_busy_wait.aux_work = sys_sched*aux_work_fact;
+ params->sys_schedule = sys_sched;
+ params->tse = sys_sched * ERTS_SCHED_TSE_SLEEP_SPINCOUNT_FACT;
+ params->aux_work = sys_sched * aux_work_fact;
return 0;
}
@@ -5734,6 +5699,7 @@ init_scheduler_data(ErtsSchedulerData* esdp, int num,
esdp->ssi = ssi;
esdp->current_process = NULL;
esdp->current_port = NULL;
+ esdp->current_nif = NULL;
esdp->virtual_reds = 0;
esdp->cpu_id = -1;
@@ -5838,7 +5804,6 @@ erts_init_scheduling(int no_schedulers, int no_schedulers_online, int no_poll_th
rq->wakeup_other = 0;
rq->wakeup_other_reds = 0;
- rq->procs.pending_exiters = NULL;
rq->procs.context_switches = 0;
rq->procs.reductions = 0;
@@ -6116,7 +6081,8 @@ make_proxy_proc(Process *prev_proxy, Process *proc, erts_aint32_t prio)
{
erts_aint32_t state;
Process *proxy;
- ErtsRunQueue *rq = RUNQ_READ_RQ(&proc->run_queue);
+ int bound;
+ ErtsRunQueue *rq = erts_get_runq_proc(proc, &bound);
state = (ERTS_PSFLG_PROXY
| ERTS_PSFLG_IN_RUNQ
@@ -6129,7 +6095,7 @@ make_proxy_proc(Process *prev_proxy, Process *proc, erts_aint32_t prio)
proxy = prev_proxy;
ASSERT(erts_atomic32_read_nob(&proxy->state) & ERTS_PSFLG_PROXY);
erts_atomic32_set_nob(&proxy->state, state);
- RUNQ_SET_RQ(&proc->run_queue, rq);
+ (void) erts_set_runq_proc(proxy, rq, &bound);
}
else {
proxy = erts_alloc(ERTS_ALC_T_PROC, sizeof(Process));
@@ -6142,8 +6108,7 @@ make_proxy_proc(Process *prev_proxy, Process *proc, erts_aint32_t prio)
}
#endif
erts_atomic32_init_nob(&proxy->state, state);
- erts_atomic_init_nob(&proxy->run_queue,
- erts_atomic_read_nob(&proc->run_queue));
+ erts_init_runq_proc(proxy, rq, bound);
}
proxy->common.id = proc->common.id;
@@ -6334,18 +6299,21 @@ select_enqueue_run_queue(int enqueue, int enq_prio, Process *p, erts_aint32_t st
default: {
ErtsRunQueue* runq;
+ int bound;
ASSERT(enqueue == ERTS_ENQUEUE_NORMAL_QUEUE
|| enqueue == -ERTS_ENQUEUE_NORMAL_QUEUE);
- runq = erts_get_runq_proc(p);
+ runq = erts_get_runq_proc(p, &bound);
- if (!(ERTS_PSFLG_BOUND & state)) {
+ if (!bound) {
ErtsRunQueue *new_runq = erts_check_emigration_need(runq, enq_prio);
- if (new_runq) {
- RUNQ_SET_RQ(&p->run_queue, new_runq);
- runq = new_runq;
- }
+ if (new_runq) {
+ if (erts_try_change_runq_proc(p, new_runq))
+ runq = new_runq;
+ else
+ runq = erts_get_runq_proc(p, NULL);
+ }
}
ASSERT(runq);
@@ -6370,11 +6338,36 @@ schedule_out_process(ErtsRunQueue *c_rq, erts_aint32_t state, Process *p,
int enqueue; /* < 0 -> use proxy */
ErtsRunQueue* runq;
+ ASSERT(!(state & (ERTS_PSFLG_DIRTY_IO_PROC
+ |ERTS_PSFLG_DIRTY_CPU_PROC))
+ || (BeamIsOpCode(*p->i, op_call_nif)
+ || BeamIsOpCode(*p->i, op_apply_bif)));
+
+ a = state;
+
+ /* Clear activ-sys if needed... */
+ while (1) {
+ n = e = a;
+ if (a & ERTS_PSFLG_ACTIVE_SYS) {
+ if (a & (ERTS_PSFLG_SIG_Q
+ | ERTS_PSFLG_SIG_IN_Q
+ | ERTS_PSFLG_SYS_TASKS))
+ break;
+ /* Clear active-sys */
+ n &= ~ERTS_PSFLG_ACTIVE_SYS;
+ }
+ a = erts_atomic32_cmpxchg_nob(&p->state, n, e);
+ if (a == e) {
+ a = n;
+ break;
+ }
+ }
+
if (!is_normal_sched)
running_flgs = ERTS_PSFLG_DIRTY_RUNNING|ERTS_PSFLG_DIRTY_RUNNING_SYS;
else {
running_flgs = ERTS_PSFLG_RUNNING|ERTS_PSFLG_RUNNING_SYS;
- if (state & ERTS_PSFLG_DIRTY_ACTIVE_SYS
+ if ((a & ERTS_PSFLG_DIRTY_ACTIVE_SYS)
&& (p->flags & (F_DELAY_GC|F_DISABLE_GC))) {
/*
* Delay dirty GC; will be enabled automatically
@@ -6387,14 +6380,12 @@ schedule_out_process(ErtsRunQueue *c_rq, erts_aint32_t state, Process *p,
*/
ASSERT(!(p->flags & (F_DIRTY_CLA | F_DIRTY_GC_HIBERNATE)));
- state = erts_atomic32_read_band_nob(&p->state,
- ~ERTS_PSFLG_DIRTY_ACTIVE_SYS);
- state &= ~ERTS_PSFLG_DIRTY_ACTIVE_SYS;
+ a = erts_atomic32_read_band_nob(&p->state,
+ ~ERTS_PSFLG_DIRTY_ACTIVE_SYS);
+ a &= ~ERTS_PSFLG_DIRTY_ACTIVE_SYS;
}
}
- a = state;
-
while (1) {
n = e = a;
@@ -6402,6 +6393,11 @@ schedule_out_process(ErtsRunQueue *c_rq, erts_aint32_t state, Process *p,
enqueue = ERTS_ENQUEUE_NOT;
+ ASSERT(((a & (ERTS_PSFLG_EXITING|ERTS_PSFLG_FREE))
+ != ERTS_PSFLG_EXITING)
+ || ((a & (ERTS_PSFLG_ACTIVE|ERTS_PSFLG_SUSPENDED))
+ == ERTS_PSFLG_ACTIVE));
+
n &= ~running_flgs;
if ((a & (ERTS_PSFLG_ACTIVE_SYS|ERTS_PSFLG_DIRTY_ACTIVE_SYS))
|| (a & (ERTS_PSFLG_ACTIVE|ERTS_PSFLG_SUSPENDED)) == ERTS_PSFLG_ACTIVE) {
@@ -6626,7 +6622,7 @@ change_proc_schedule_state(Process *p,
}
- *statep = a;
+ *statep = n;
return enqueue;
}
@@ -6651,6 +6647,95 @@ erts_schedule_process(Process *p, erts_aint32_t state, ErtsProcLocks locks)
schedule_process(p, state, locks);
}
+static ERTS_INLINE erts_aint32_t
+active_sys_enqueue(Process *p, erts_aint32_t state,
+ erts_aint32_t enable_flags, int status_locked)
+{
+ /*
+ * This function may or may not be called with status locke held.
+ * It always returns without the status lock held!
+ */
+ unsigned int prof_runnable_procs = erts_system_profile_flags.runnable_procs;
+ erts_aint32_t n, a = state, enq_prio = -1;
+ int slocked = status_locked;
+ int enqueue; /* < 0 -> use proxy */
+
+ /* Status lock prevents out of order "runnable proc" trace msgs */
+ ERTS_LC_ASSERT(slocked || !(ERTS_PROC_LOCK_STATUS & erts_proc_lc_my_proc_locks(p)));
+ ERTS_LC_ASSERT(!slocked || (ERTS_PROC_LOCK_STATUS & erts_proc_lc_my_proc_locks(p)));
+
+ if (!prof_runnable_procs) {
+ if (slocked) {
+ erts_proc_unlock(p, ERTS_PROC_LOCK_STATUS);
+ slocked = 0;
+ }
+ }
+ else {
+ if (!slocked) {
+ erts_proc_lock(p, ERTS_PROC_LOCK_STATUS);
+ slocked = !0;
+ }
+ }
+
+ ASSERT(!(state & ERTS_PSFLG_PROXY));
+
+ while (1) {
+ erts_aint32_t e;
+ n = e = a;
+
+ if (a & ERTS_PSFLG_FREE)
+ goto cleanup; /* We don't want to schedule free processes... */
+
+ enqueue = ERTS_ENQUEUE_NOT;
+ n |= enable_flags;
+ n |= ERTS_PSFLG_ACTIVE_SYS;
+ if (!(a & (ERTS_PSFLG_RUNNING
+ | ERTS_PSFLG_RUNNING_SYS
+ | ERTS_PSFLG_DIRTY_RUNNING
+ | ERTS_PSFLG_DIRTY_RUNNING_SYS)))
+ enqueue = check_enqueue_in_prio_queue(p, &enq_prio, &n, a);
+ a = erts_atomic32_cmpxchg_mb(&p->state, n, e);
+ if (a == e)
+ break;
+ if (a == n && enqueue == ERTS_ENQUEUE_NOT)
+ goto cleanup;
+ }
+
+ if (prof_runnable_procs) {
+
+ if (!(a & (ERTS_PSFLG_ACTIVE_SYS
+ | ERTS_PSFLG_RUNNING
+ | ERTS_PSFLG_RUNNING_SYS
+ | ERTS_PSFLG_DIRTY_RUNNING
+ | ERTS_PSFLG_DIRTY_RUNNING_SYS))
+ && (!(a & ERTS_PSFLG_ACTIVE) || (a & ERTS_PSFLG_SUSPENDED))) {
+ /* We activated a prevously inactive process */
+ profile_runnable_proc(p, am_active);
+ }
+
+ erts_proc_unlock(p, ERTS_PROC_LOCK_STATUS);
+ slocked = 0;
+ }
+
+ add2runq(enqueue, enq_prio, p, n, NULL);
+
+cleanup:
+
+ if (slocked)
+ erts_proc_unlock(p, ERTS_PROC_LOCK_STATUS);
+
+ ERTS_LC_ASSERT(!(ERTS_PROC_LOCK_STATUS & erts_proc_lc_my_proc_locks(p)));
+
+ return n;
+}
+
+erts_aint32_t
+erts_proc_sys_schedule(Process *p, erts_aint32_t state, erts_aint32_t enable_flag)
+{
+ /* We are not allowed to call this function with status lock held... */
+ return active_sys_enqueue(p, state, enable_flag, 0);
+}
+
static int
schedule_process_sys_task(Process *p, erts_aint32_t prio, ErtsProcSysTask *st,
erts_aint32_t *fail_state_p)
@@ -6658,19 +6743,16 @@ schedule_process_sys_task(Process *p, erts_aint32_t prio, ErtsProcSysTask *st,
int res;
int locked;
ErtsProcSysTaskQs *stqs, *free_stqs;
- erts_aint32_t fail_state, state, a, n, enq_prio;
- int enqueue; /* < 0 -> use proxy */
- unsigned int prof_runnable_procs;
+ erts_aint32_t fail_state, state;
fail_state = *fail_state_p;
res = 1; /* prepare for success */
st->next = st->prev = st; /* Prep for empty prio queue */
state = erts_atomic32_read_nob(&p->state);
- prof_runnable_procs = erts_system_profile_flags.runnable_procs;
locked = 0;
free_stqs = NULL;
- if (state & ERTS_PSFLG_ACTIVE_SYS)
+ if (state & ERTS_PSFLG_SYS_TASKS)
stqs = NULL;
else {
alloc_qs:
@@ -6690,6 +6772,7 @@ schedule_process_sys_task(Process *p, erts_aint32_t prio, ErtsProcSysTask *st,
state = erts_atomic32_read_nob(&p->state);
if (state & fail_state) {
+ erts_proc_unlock(p, ERTS_PROC_LOCK_STATUS);
*fail_state_p = (state & fail_state);
free_stqs = stqs;
res = 0;
@@ -6737,69 +6820,14 @@ schedule_process_sys_task(Process *p, erts_aint32_t prio, ErtsProcSysTask *st,
state = n;
}
-
- a = state;
- enq_prio = -1;
-
- /* Status lock prevents out of order "runnable proc" trace msgs */
- ERTS_LC_ASSERT(ERTS_PROC_LOCK_STATUS & erts_proc_lc_my_proc_locks(p));
-
- if (!prof_runnable_procs) {
- erts_proc_unlock(p, ERTS_PROC_LOCK_STATUS);
- locked = 0;
- }
-
- ASSERT(!(state & ERTS_PSFLG_PROXY));
-
- while (1) {
- erts_aint32_t e;
- n = e = a;
-
- if (a & ERTS_PSFLG_FREE)
- goto cleanup; /* We don't want to schedule free processes... */
-
- enqueue = ERTS_ENQUEUE_NOT;
- n |= ERTS_PSFLG_ACTIVE_SYS;
- if (!(a & (ERTS_PSFLG_RUNNING
- | ERTS_PSFLG_RUNNING_SYS
- | ERTS_PSFLG_DIRTY_RUNNING
- | ERTS_PSFLG_DIRTY_RUNNING_SYS)))
- enqueue = check_enqueue_in_prio_queue(p, &enq_prio, &n, a);
- a = erts_atomic32_cmpxchg_mb(&p->state, n, e);
- if (a == e)
- break;
- if (a == n && enqueue == ERTS_ENQUEUE_NOT)
- goto cleanup;
- }
-
- if (prof_runnable_procs) {
-
- if (!(a & (ERTS_PSFLG_ACTIVE_SYS
- | ERTS_PSFLG_RUNNING
- | ERTS_PSFLG_RUNNING_SYS
- | ERTS_PSFLG_DIRTY_RUNNING
- | ERTS_PSFLG_DIRTY_RUNNING_SYS))
- && (!(a & ERTS_PSFLG_ACTIVE) || (a & ERTS_PSFLG_SUSPENDED))) {
- /* We activated a prevously inactive process */
- profile_runnable_proc(p, am_active);
- }
-
- erts_proc_unlock(p, ERTS_PROC_LOCK_STATUS);
- locked = 0;
- }
-
- add2runq(enqueue, enq_prio, p, n, NULL);
+ /* active_sys_enqueue() always return with status lock unlocked */
+ (void) active_sys_enqueue(p, state, ERTS_PSFLG_SYS_TASKS, locked);
cleanup:
- if (locked)
- erts_proc_unlock(p, ERTS_PROC_LOCK_STATUS);
-
if (free_stqs)
proc_sys_task_queues_free(free_stqs);
- ERTS_LC_ASSERT(!(ERTS_PROC_LOCK_STATUS & erts_proc_lc_my_proc_locks(p)));
-
return res;
}
@@ -8307,6 +8335,7 @@ sched_thread_func(void *vesdp)
ERTS_VERIFY_UNUSED_TEMP_ALLOC(NULL);
#endif
+ erts_alcu_sched_spec_data_init(esdp);
erts_ets_sched_spec_data_init(esdp);
process_main(esdp->x_reg_array, esdp->f_reg_array);
@@ -8432,11 +8461,13 @@ erts_start_schedulers(void)
erts_atomic_init_nob(&runq_supervisor_sleeping, 0);
if (0 != ethr_event_init(&runq_supervision_event))
erts_exit(ERTS_ERROR_EXIT, "Failed to create run-queue supervision event\n");
- if (0 != ethr_thr_create(&runq_supervisor_tid,
- runq_supervisor,
- NULL,
- &opts))
- erts_exit(ERTS_ERROR_EXIT, "Failed to create run-queue supervision thread\n");
+ res = ethr_thr_create(&runq_supervisor_tid,
+ runq_supervisor,
+ NULL,
+ &opts);
+ if (0 != res)
+ erts_exit(ERTS_ERROR_EXIT, "Failed to create run-queue supervision thread, "
+ "error = %d\n", res);
}
@@ -8471,7 +8502,7 @@ erts_start_schedulers(void)
opts.suggested_stack_size = erts_dcpu_sched_thread_suggested_stack_size;
res = ethr_thr_create(&esdp->tid,sched_dirty_cpu_thread_func,(void*)esdp,&opts);
if (res != 0)
- erts_exit(ERTS_ERROR_EXIT, "Failed to create dirty cpu scheduler thread %d\n", ix);
+ erts_exit(ERTS_ERROR_EXIT, "Failed to create dirty cpu scheduler thread %d, error = %d\n", ix, res);
}
for (ix = 0; ix < erts_no_dirty_io_schedulers; ix++) {
ErtsSchedulerData *esdp = ERTS_DIRTY_IO_SCHEDULER_IX(ix);
@@ -8479,7 +8510,7 @@ erts_start_schedulers(void)
opts.suggested_stack_size = erts_dio_sched_thread_suggested_stack_size;
res = ethr_thr_create(&esdp->tid,sched_dirty_io_thread_func,(void*)esdp,&opts);
if (res != 0)
- erts_exit(ERTS_ERROR_EXIT, "Failed to create dirty io scheduler thread %d\n", ix);
+ erts_exit(ERTS_ERROR_EXIT, "Failed to create dirty io scheduler thread %d, error = %d\n", ix, res);
}
}
@@ -8489,7 +8520,7 @@ erts_start_schedulers(void)
res = ethr_thr_create(&tid, aux_thread, NULL, &opts);
if (res != 0)
- erts_exit(ERTS_ERROR_EXIT, "Failed to create aux thread\n");
+ erts_exit(ERTS_ERROR_EXIT, "Failed to create aux thread, error = %d\n", res);
for (ix = 0; ix < erts_no_poll_threads; ix++) {
erts_snprintf(opts.name, 16, "%d_poller", ix);
@@ -8576,11 +8607,13 @@ static ERTS_INLINE void
cancel_suspend_of_suspendee(Process *p, ErtsProcLocks p_locks)
{
if (is_not_nil(p->suspendee)) {
+ ErtsMonitor *mon;
+ Eterm suspendee = p->suspendee;
Process *rp;
if (!(p_locks & ERTS_PROC_LOCK_STATUS))
erts_proc_lock(p, ERTS_PROC_LOCK_STATUS);
rp = erts_pid2proc(p, p_locks|ERTS_PROC_LOCK_STATUS,
- p->suspendee, ERTS_PROC_LOCK_STATUS);
+ suspendee, ERTS_PROC_LOCK_STATUS);
if (rp) {
erts_resume(rp, ERTS_PROC_LOCK_STATUS);
erts_proc_unlock(rp, ERTS_PROC_LOCK_STATUS);
@@ -8588,6 +8621,14 @@ cancel_suspend_of_suspendee(Process *p, ErtsProcLocks p_locks)
if (!(p_locks & ERTS_PROC_LOCK_STATUS))
erts_proc_unlock(p, ERTS_PROC_LOCK_STATUS);
p->suspendee = NIL;
+
+ mon = erts_monitor_tree_lookup(p->suspend_monitors,
+ suspendee);
+ if (mon) {
+ erts_monitor_tree_delete(&p->suspend_monitors,
+ mon);
+ erts_monitor_suspend_destroy(erts_monitor_suspend(mon));
+ }
}
}
@@ -8746,8 +8787,7 @@ pid2proc_not_running(Process *c_p, ErtsProcLocks c_p_locks,
{
erts_aint32_t state;
state = erts_atomic32_read_nob(&rp->state);
- ASSERT((state & ERTS_PSFLG_PENDING_EXIT)
- || !(state & ERTS_PSFLG_RUNNING));
+ ASSERT(!(state & ERTS_PSFLG_RUNNING));
}
#endif
@@ -8802,7 +8842,7 @@ erts_pid2proc_nropt(Process *c_p, ErtsProcLocks c_p_locks,
static ERTS_INLINE int
do_bif_suspend_process(Process *c_p,
- ErtsSuspendMonitor *smon,
+ ErtsMonitorSuspend *smon,
Process *suspendee)
{
ASSERT(suspendee);
@@ -8835,21 +8875,25 @@ handle_pend_bif_sync_suspend(Process *suspendee,
suspender = erts_pid2proc(suspendee,
suspendee_locks,
suspender_pid,
- ERTS_PROC_LOCK_LINK|ERTS_PROC_LOCK_STATUS);
+ ERTS_PROC_LOCK_STATUS);
if (suspender) {
+ ErtsMonitorSuspend *smon;
+ ErtsMonitor *mon;
+ mon = erts_monitor_tree_lookup(suspender->suspend_monitors,
+ suspendee->common.id);
+ smon = erts_monitor_suspend(mon);
+
ASSERT(is_nil(suspender->suspendee));
- if (!suspendee_alive)
- erts_delete_suspend_monitor(&suspender->suspend_monitors,
- suspendee->common.id);
+ if (!suspendee_alive) {
+ if (mon) {
+ erts_monitor_tree_delete(&suspender->suspend_monitors,
+ mon);
+ erts_monitor_suspend_destroy(smon);
+ }
+ }
else {
#ifdef DEBUG
- int res;
-#endif
- ErtsSuspendMonitor *smon;
- smon = erts_lookup_suspend_monitor(suspender->suspend_monitors,
- suspendee->common.id);
-#ifdef DEBUG
- res =
+ int res =
#endif
do_bif_suspend_process(suspendee, smon, suspendee);
ASSERT(!smon || res != 0);
@@ -8858,9 +8902,8 @@ handle_pend_bif_sync_suspend(Process *suspendee,
/* suspender is suspended waiting for suspendee to suspend;
resume suspender */
ASSERT(suspender != suspendee);
- resume_process(suspender, ERTS_PROC_LOCK_LINK|ERTS_PROC_LOCK_STATUS);
- erts_proc_unlock(suspender,
- ERTS_PROC_LOCK_LINK|ERTS_PROC_LOCK_STATUS);
+ resume_process(suspender, ERTS_PROC_LOCK_STATUS);
+ erts_proc_unlock(suspender, ERTS_PROC_LOCK_STATUS);
}
}
@@ -8878,26 +8921,29 @@ handle_pend_bif_async_suspend(Process *suspendee,
suspender = erts_pid2proc(suspendee,
suspendee_locks,
suspender_pid,
- ERTS_PROC_LOCK_LINK);
+ ERTS_PROC_LOCK_STATUS);
if (suspender) {
+ ErtsMonitorSuspend *smon;
+ ErtsMonitor *mon;
+ mon = erts_monitor_tree_lookup(suspender->suspend_monitors,
+ suspendee->common.id);
+ smon = erts_monitor_suspend(mon);
ASSERT(is_nil(suspender->suspendee));
- if (!suspendee_alive)
- erts_delete_suspend_monitor(&suspender->suspend_monitors,
- suspendee->common.id);
+ if (!suspendee_alive) {
+ if (mon) {
+ erts_monitor_tree_delete(&suspender->suspend_monitors,
+ mon);
+ erts_monitor_suspend_destroy(smon);
+ }
+ }
else {
#ifdef DEBUG
- int res;
+ int res =
#endif
- ErtsSuspendMonitor *smon;
- smon = erts_lookup_suspend_monitor(suspender->suspend_monitors,
- suspendee->common.id);
-#ifdef DEBUG
- res =
-#endif
- do_bif_suspend_process(suspendee, smon, suspendee);
+ do_bif_suspend_process(suspendee, smon, suspendee);
ASSERT(!smon || res != 0);
}
- erts_proc_unlock(suspender, ERTS_PROC_LOCK_LINK);
+ erts_proc_unlock(suspender, ERTS_PROC_LOCK_STATUS);
}
}
@@ -8911,8 +8957,9 @@ suspend_process_2(BIF_ALIST_2)
{
Eterm res;
Process* suspendee = NULL;
- ErtsSuspendMonitor *smon;
+ ErtsMonitorSuspend *smon;
ErtsProcLocks xlocks = (ErtsProcLocks) 0;
+ int created;
/* Options and default values: */
int asynchronous = 0;
@@ -8945,9 +8992,7 @@ suspend_process_2(BIF_ALIST_2)
goto badarg;
}
- xlocks = ERTS_PROC_LOCK_LINK | (asynchronous
- ? (ErtsProcLocks) 0
- : ERTS_PROC_LOCK_STATUS);
+ xlocks = ERTS_PROC_LOCK_STATUS;
erts_proc_lock(BIF_P, xlocks);
@@ -8958,15 +9003,14 @@ suspend_process_2(BIF_ALIST_2)
if (!suspendee)
goto no_suspendee;
- smon = erts_add_or_lookup_suspend_monitor(&BIF_P->suspend_monitors,
- BIF_ARG_1);
-
- /* ... but a little trickier with SMP support ... */
+ smon = erts_monitor_suspend_tree_lookup_create(&BIF_P->suspend_monitors,
+ &created,
+ BIF_ARG_1);
if (asynchronous) {
/* --- Asynchronous suspend begin ---------------------------------- */
- ERTS_LC_ASSERT(ERTS_PROC_LOCK_LINK
+ ERTS_LC_ASSERT(ERTS_PROC_LOCK_STATUS
& erts_proc_lc_my_proc_locks(BIF_P));
ERTS_LC_ASSERT(ERTS_PROC_LOCK_STATUS
== erts_proc_lc_my_proc_locks(suspendee));
@@ -9008,9 +9052,9 @@ suspend_process_2(BIF_ALIST_2)
else /* if (!asynchronous) */ {
/* --- Synchronous suspend begin ----------------------------------- */
- ERTS_LC_ASSERT(((ERTS_PROC_LOCK_LINK|ERTS_PROC_LOCK_STATUS)
+ ERTS_LC_ASSERT(((ERTS_PROC_LOCK_STATUS|ERTS_PROC_LOCK_STATUS)
& erts_proc_lc_my_proc_locks(BIF_P))
- == (ERTS_PROC_LOCK_LINK|ERTS_PROC_LOCK_STATUS));
+ == (ERTS_PROC_LOCK_STATUS|ERTS_PROC_LOCK_STATUS));
ERTS_LC_ASSERT(ERTS_PROC_LOCK_STATUS
== erts_proc_lc_my_proc_locks(suspendee));
@@ -9095,9 +9139,15 @@ suspend_process_2(BIF_ALIST_2)
ERTS_BIF_PREP_ERROR(res, BIF_P, SYSTEM_LIMIT);
goto do_return;
- no_suspendee:
- BIF_P->suspendee = NIL;
- erts_delete_suspend_monitor(&BIF_P->suspend_monitors, BIF_ARG_1);
+ no_suspendee: {
+ ErtsMonitor *mon;
+ BIF_P->suspendee = NIL;
+ mon = erts_monitor_tree_lookup(BIF_P->suspend_monitors, BIF_ARG_1);
+ if (mon) {
+ erts_monitor_tree_delete(&BIF_P->suspend_monitors, mon);
+ erts_monitor_suspend_destroy(erts_monitor_suspend(mon));
+ }
+ }
badarg:
ERTS_BIF_PREP_ERROR(res, BIF_P, BADARG);
@@ -9124,15 +9174,17 @@ suspend_process_2(BIF_ALIST_2)
BIF_RETTYPE
resume_process_1(BIF_ALIST_1)
{
- ErtsSuspendMonitor *smon;
+ ErtsMonitor *mon;
+ ErtsMonitorSuspend *smon;
Process *suspendee;
int is_active;
if (BIF_P->common.id == BIF_ARG_1)
BIF_ERROR(BIF_P, BADARG);
- erts_proc_lock(BIF_P, ERTS_PROC_LOCK_LINK);
- smon = erts_lookup_suspend_monitor(BIF_P->suspend_monitors, BIF_ARG_1);
+ erts_proc_lock(BIF_P, ERTS_PROC_LOCK_STATUS);
+ mon = erts_monitor_tree_lookup(BIF_P->suspend_monitors, BIF_ARG_1);
+ smon = erts_monitor_suspend(mon);
if (!smon) {
/* No previous suspend or dead suspendee */
@@ -9149,20 +9201,17 @@ resume_process_1(BIF_ALIST_1)
}
else if (smon->active) {
smon->active--;
- ASSERT(smon->pending >= 0);
+ ASSERT(smon->pending == 0);
is_active = 1;
}
else {
/* No previous suspend or dead suspendee */
- goto error;
+ goto no_suspendee;
}
if (smon->active || smon->pending || !is_active) {
/* Leave the suspendee as it is; just verify that it is still alive */
- suspendee = erts_pid2proc(BIF_P,
- ERTS_PROC_LOCK_MAIN|ERTS_PROC_LOCK_LINK,
- BIF_ARG_1,
- 0);
+ suspendee = erts_proc_lookup(BIF_ARG_1);
if (!suspendee)
goto no_suspendee;
@@ -9170,11 +9219,18 @@ resume_process_1(BIF_ALIST_1)
else {
/* Resume */
suspendee = erts_pid2proc(BIF_P,
- ERTS_PROC_LOCK_MAIN|ERTS_PROC_LOCK_LINK,
+ ERTS_PROC_LOCK_MAIN|ERTS_PROC_LOCK_STATUS,
BIF_ARG_1,
ERTS_PROC_LOCK_STATUS);
- if (!suspendee)
+ if (!suspendee) {
+ mon = erts_monitor_tree_lookup(BIF_P->suspend_monitors, BIF_ARG_1);
+ smon = erts_monitor_suspend(mon);
+ if (!mon)
+ goto error;
goto no_suspendee;
+ }
+
+ ASSERT(mon == erts_monitor_tree_lookup(BIF_P->suspend_monitors, BIF_ARG_1));
ASSERT(ERTS_PSFLG_SUSPENDED
& erts_atomic32_read_nob(&suspendee->state));
@@ -9184,19 +9240,24 @@ resume_process_1(BIF_ALIST_1)
erts_proc_unlock(suspendee, ERTS_PROC_LOCK_STATUS);
}
- if (!smon->active && !smon->pending)
- erts_delete_suspend_monitor(&BIF_P->suspend_monitors, BIF_ARG_1);
+ if (!smon->active && !smon->pending) {
+ ASSERT(mon);
+ erts_monitor_tree_delete(&BIF_P->suspend_monitors, mon);
+ erts_monitor_suspend_destroy(smon);
+ }
- erts_proc_unlock(BIF_P, ERTS_PROC_LOCK_LINK);
+ erts_proc_unlock(BIF_P, ERTS_PROC_LOCK_STATUS);
BIF_RET(am_true);
no_suspendee:
/* cleanup */
- erts_delete_suspend_monitor(&BIF_P->suspend_monitors, BIF_ARG_1);
+ ASSERT(mon);
+ erts_monitor_tree_delete(&BIF_P->suspend_monitors, mon);
+ erts_monitor_suspend_destroy(smon);
error:
- erts_proc_unlock(BIF_P, ERTS_PROC_LOCK_LINK);
+ erts_proc_unlock(BIF_P, ERTS_PROC_LOCK_STATUS);
BIF_ERROR(BIF_P, BADARG);
}
@@ -9415,9 +9476,8 @@ erts_resume_processes(ErtsProcList *list)
}
Eterm
-erts_get_process_priority(Process *p)
+erts_get_process_priority(erts_aint32_t state)
{
- erts_aint32_t state = erts_atomic32_read_nob(&p->state);
switch (ERTS_PSFLGS_GET_USR_PRIO(state)) {
case PRIORITY_MAX: return am_max;
case PRIORITY_HIGH: return am_high;
@@ -9467,7 +9527,10 @@ erts_set_process_priority(Process *p, Eterm value)
}
max_qbit = 0;
- if (a & ERTS_PSFLG_ACTIVE_SYS)
+ ASSERT((a & ERTS_PSFLG_SYS_TASKS)
+ ? !!p->sys_task_qs
+ : !p->sys_task_qs);
+ if (a & ERTS_PSFLG_SYS_TASKS)
max_qbit |= p->sys_task_qs->qmask;
if (a & ERTS_PSFLG_DELAYED_SYS) {
ErtsProcSysTaskQs *qs;
@@ -9490,8 +9553,8 @@ erts_set_process_priority(Process *p, Eterm value)
aprio = PRIORITY_LOW;
break;
default:
- ERTS_INTERNAL_ERROR("Invalid qmask");
- aprio = -1;
+ aprio = nprio;
+ break;
}
if (aprio > nprio) /* low value -> high prio */
@@ -9667,10 +9730,6 @@ Process *erts_schedule(ErtsSchedulerData *esdp, Process *p, int calls)
/* have to re-read state after taking lock */
state = erts_atomic32_read_nob(&p->state);
- if (is_normal_sched && (state & ERTS_PSFLG_PENDING_EXIT))
- erts_handle_pending_exit(p, (ERTS_PROC_LOCK_MAIN
- | ERTS_PROC_LOCK_TRACE
- | ERTS_PROC_LOCK_STATUS));
if (p->pending_suspenders)
handle_pending_suspend(p, (ERTS_PROC_LOCK_MAIN
| ERTS_PROC_LOCK_TRACE
@@ -9699,7 +9758,7 @@ Process *erts_schedule(ErtsSchedulerData *esdp, Process *p, int calls)
| ERTS_PROC_LOCK_STATUS
| ERTS_PROC_LOCK_TRACE));
- ERTS_MSACC_SET_STATE_CACHED_M(ERTS_MSACC_STATE_OTHER);
+ ERTS_MSACC_SET_STATE_CACHED(ERTS_MSACC_STATE_OTHER);
if (state & ERTS_PSFLG_FREE) {
if (!is_normal_sched) {
@@ -9873,7 +9932,7 @@ Process *erts_schedule(ErtsSchedulerData *esdp, Process *p, int calls)
if (flags & ERTS_RUNQ_FLG_MISC_OP)
exec_misc_ops(rq);
- wakeup_other.check(rq, flags);
+ runq_get_wakeup_other_params(rq)->check(rq, flags);
/*
* Find a new port to run.
@@ -9914,7 +9973,7 @@ Process *erts_schedule(ErtsSchedulerData *esdp, Process *p, int calls)
case 0: /* No process at all */
default:
ASSERT(qmask == 0);
- ERTS_MSACC_SET_STATE_CACHED_M(ERTS_MSACC_STATE_OTHER);
+ ERTS_MSACC_SET_STATE_CACHED(ERTS_MSACC_STATE_OTHER);
goto check_activities_to_run;
}
@@ -9985,12 +10044,10 @@ Process *erts_schedule(ErtsSchedulerData *esdp, Process *p, int calls)
& ((state & (ERTS_PSFLG_SUSPENDED
| ERTS_PSFLG_EXITING
| ERTS_PSFLG_FREE
- | ERTS_PSFLG_PENDING_EXIT
| ERTS_PSFLG_ACTIVE_SYS
| ERTS_PSFLG_DIRTY_ACTIVE_SYS))
!= ERTS_PSFLG_SUSPENDED)
- & (!(state & (ERTS_PSFLG_EXITING
- | ERTS_PSFLG_PENDING_EXIT))
+ & (!(state & ERTS_PSFLG_EXITING)
| (!!is_normal_sched))
);
@@ -10032,7 +10089,7 @@ Process *erts_schedule(ErtsSchedulerData *esdp, Process *p, int calls)
}
- ERTS_MSACC_SET_STATE_CACHED_M(ERTS_MSACC_STATE_EMULATOR);
+ ERTS_MSACC_SET_STATE_CACHED(ERTS_MSACC_STATE_EMULATOR);
if (flags & ERTS_RUNQ_FLG_PROTECTED)
@@ -10077,8 +10134,11 @@ Process *erts_schedule(ErtsSchedulerData *esdp, Process *p, int calls)
p->scheduler_data = esdp;
}
else {
+ if (!(state & ERTS_PSFLGS_DIRTY_WORK)) {
+ /* Dirty work completed... */
+ goto sunlock_sched_out_proc;
+ }
if (state & (ERTS_PSFLG_ACTIVE_SYS
- | ERTS_PSFLG_PENDING_EXIT
| ERTS_PSFLG_EXITING)) {
/*
* IMPORTANT! We need to take care of
@@ -10101,13 +10161,6 @@ Process *erts_schedule(ErtsSchedulerData *esdp, Process *p, int calls)
&& (state & ERTS_PSFLG_DIRTY_IO_PROC)));
}
- if (state & ERTS_PSFLG_PENDING_EXIT) {
- erts_handle_pending_exit(p,
- ERTS_PROC_LOCK_MAIN|ERTS_PROC_LOCK_STATUS);
- state = erts_atomic32_read_nob(&p->state);
- }
-
-
erts_proc_unlock(p, ERTS_PROC_LOCK_STATUS);
/* Clear tracer if it has been removed */
@@ -10129,25 +10182,48 @@ Process *erts_schedule(ErtsSchedulerData *esdp, Process *p, int calls)
}
if (is_normal_sched) {
-
if (state & ERTS_PSFLG_RUNNING_SYS) {
- /*
- * GC is normally never delayed when a process
- * is scheduled out, but might be when executing
- * hand written beam assembly in
- * prim_eval:'receive'. If GC is delayed we are
- * not allowed to execute system tasks.
- */
- if (!(p->flags & F_DELAY_GC)) {
- int cost = execute_sys_tasks(p, &state, reds);
- calls += cost;
- reds -= cost;
- if (reds <= 0)
- goto sched_out_proc;
- if (state & ERTS_PSFLGS_DIRTY_WORK)
- goto sched_out_proc;
+ if (state & (ERTS_PSFLG_SIG_Q|ERTS_PSFLG_SIG_IN_Q)) {
+ int local_only = !!(p->flags & F_LOCAL_SIGS_ONLY);
+ if (!local_only || (state & ERTS_PSFLG_SIG_Q)) {
+ int sig_reds;
+ /*
+ * If we have dirty work scheduled we allow
+ * usage of all reductions since we need to
+ * handle all signals before doing dirty
+ * work...
+ */
+ if (state & ERTS_PSFLGS_DIRTY_WORK)
+ sig_reds = reds;
+ else
+ sig_reds = ERTS_SIG_HANDLE_REDS_MAX_PREFERED;
+ (void) erts_proc_sig_handle_incoming(p,
+ &state,
+ &sig_reds,
+ sig_reds,
+ local_only);
+ reds -= sig_reds;
+ }
+ }
+ if ((state & (ERTS_PSFLG_SYS_TASKS
+ | ERTS_PSFLG_EXITING)) == ERTS_PSFLG_SYS_TASKS) {
+ /*
+ * GC is normally never delayed when a process
+ * is scheduled out, but might be when executing
+ * hand written beam assembly in
+ * prim_eval:'receive'. If GC is delayed we are
+ * not allowed to execute system tasks.
+ */
+ if (!(p->flags & F_DELAY_GC)) {
+ int cost = execute_sys_tasks(p, &state, reds);
+ calls += cost;
+ reds -= cost;
+ }
}
+ if (reds <= 0 || (state & ERTS_PSFLGS_DIRTY_WORK))
+ goto sched_out_proc;
+
ASSERT(state & psflg_running_sys);
ASSERT(!(state & psflg_running));
@@ -10156,7 +10232,7 @@ Process *erts_schedule(ErtsSchedulerData *esdp, Process *p, int calls)
if (((state & (ERTS_PSFLG_SUSPENDED
| ERTS_PSFLG_ACTIVE)) != ERTS_PSFLG_ACTIVE)
- && !(state & ERTS_PSFLG_EXITING)) {
+ & !(state & ERTS_PSFLG_EXITING)) {
goto sched_out_proc;
}
@@ -10198,15 +10274,6 @@ Process *erts_schedule(ErtsSchedulerData *esdp, Process *p, int calls)
ERTS_CHK_HAVE_ONLY_MAIN_PROC_LOCK(p);
- /* Never run a suspended process */
-#ifdef DEBUG
- {
- erts_aint32_t dstate = erts_atomic32_read_nob(&p->state);
- ASSERT(!(ERTS_PSFLG_SUSPENDED & dstate)
- || (ERTS_PSFLG_DIRTY_RUNNING_SYS & dstate));
- }
-#endif
-
ASSERT(erts_proc_read_refc(p) > 0);
if (!(state & ERTS_PSFLG_EXITING) && ERTS_PTMR_IS_TIMED_OUT(p)) {
@@ -10219,6 +10286,40 @@ Process *erts_schedule(ErtsSchedulerData *esdp, Process *p, int calls)
ERTS_PTMR_CLEAR(p);
}
+ /* if exiting, we *shall* exit... */
+ ASSERT(!(state & ERTS_PSFLG_EXITING)
+ || p->i == (BeamInstr *) beam_exit
+ || p->i == (BeamInstr *) beam_continue_exit);
+
+#ifdef DEBUG
+ if (is_normal_sched) {
+ if (state & ERTS_PSFLGS_DIRTY_WORK)
+ ERTS_INTERNAL_ERROR("Executing dirty code on normal scheduler");
+ }
+ else {
+ if (!(state & ERTS_PSFLGS_DIRTY_WORK)) {
+ if (esdp->type == ERTS_SCHED_DIRTY_CPU)
+ ERTS_INTERNAL_ERROR("Executing normal code on dirty CPU scheduler");
+ else if (esdp->type == ERTS_SCHED_DIRTY_IO)
+ ERTS_INTERNAL_ERROR("Executing normal code on dirty IO scheduler");
+ else
+ ERTS_INTERNAL_ERROR("Executing normal code on dirty UNKNOWN scheduler");
+ }
+ }
+ {
+ erts_aint32_t dstate = erts_atomic32_read_nob(&p->state);
+
+ /* Never run a suspended process */
+ ASSERT(!(ERTS_PSFLG_SUSPENDED & dstate)
+ || (ERTS_PSFLG_DIRTY_RUNNING_SYS & dstate));
+
+ /* Do not execute on the wrong type of scheduler... */
+ ASSERT(is_normal_sched
+ ? !(dstate & ERTS_PSFLGS_DIRTY_WORK)
+ : !!(dstate & ERTS_PSFLGS_DIRTY_WORK));
+ }
+#endif
+
return p;
}
}
@@ -10418,7 +10519,7 @@ fetch_sys_task(Process *c_p, erts_aint32_t state, int *qmaskp, int *priop)
n |= (prio << ERTS_PSFLGS_ACT_PRIO_OFFSET);
if (!qmask)
- n &= ~ERTS_PSFLG_ACTIVE_SYS;
+ n &= ~ERTS_PSFLG_SYS_TASKS;
if (a == n)
break;
@@ -10438,6 +10539,8 @@ done:
return st;
}
+
+static void exit_permanent_prio_elevation(Process *c_p, erts_aint32_t state);
static void save_gc_task(Process *c_p, ErtsProcSysTask *st, int prio);
static void save_dirty_task(Process *c_p, ErtsProcSysTask *st);
@@ -10458,12 +10561,8 @@ execute_sys_tasks(Process *c_p, erts_aint32_t *statep, int in_reds)
int st_prio;
Eterm st_res;
- if (state & (ERTS_PSFLG_EXITING|ERTS_PSFLG_PENDING_EXIT)) {
- if (state & ERTS_PSFLG_PENDING_EXIT)
- erts_handle_pending_exit(c_p, ERTS_PROC_LOCK_MAIN);
- ASSERT(ERTS_PROC_IS_EXITING(c_p));
+ if (state & ERTS_PSFLG_EXITING)
break;
- }
st = fetch_sys_task(c_p, state, &qmask, &st_prio);
if (!st)
@@ -10559,6 +10658,43 @@ execute_sys_tasks(Process *c_p, erts_aint32_t *statep, int in_reds)
reds -= erts_db_execute_free_fixation(c_p, (DbFixation*)st->arg[0]);
st_res = am_true;
break;
+ case ERTS_PSTT_PRIO_SIG: {
+ erts_aint32_t fail_state, state;
+ int local_only, sig_res, sig_reds = reds;
+ st_res = am_false;
+
+ if (st->arg[0] == am_true)
+ local_only = !0;
+ else
+ local_only = 0;
+
+ sig_reds = reds;
+ sig_res = erts_proc_sig_handle_incoming(c_p, &state, &sig_reds,
+ reds, local_only);
+ reds -= sig_reds;
+
+ if (state & ERTS_PSFLG_EXITING) {
+ exit_permanent_prio_elevation(c_p, state);
+ break;
+ }
+
+ if (sig_res)
+ break;
+
+ st->arg[0] = am_true;
+
+ fail_state = ERTS_PSFLG_EXITING;
+
+ if (schedule_process_sys_task(c_p, st_prio, st, &fail_state)) {
+ /* Successfully rescheduled task... */
+ st = NULL;
+ }
+ else {
+ state = erts_atomic32_read_nob(&c_p->state);
+ exit_permanent_prio_elevation(c_p, state);
+ }
+ break;
+ }
default:
ERTS_INTERNAL_ERROR("Invalid process sys task type");
st_res = am_false;
@@ -10607,6 +10743,10 @@ cleanup_sys_tasks(Process *c_p, erts_aint32_t in_state, int in_reds)
}
switch (st->type) {
+ case ERTS_PSTT_PRIO_SIG:
+ state = erts_atomic32_read_nob(&c_p->state);
+ exit_permanent_prio_elevation(c_p, state);
+ /* fall through... */
case ERTS_PSTT_GC_MAJOR:
case ERTS_PSTT_GC_MINOR:
case ERTS_PSTT_CPC:
@@ -10635,6 +10775,35 @@ cleanup_sys_tasks(Process *c_p, erts_aint32_t in_state, int in_reds)
return reds;
}
+static void
+exit_permanent_prio_elevation(Process *c_p, erts_aint32_t state)
+{
+ erts_aint32_t a;
+ /*
+ * we are about to terminate; permanently elevate
+ * prio in order to ensure high prio signal
+ * handling...
+ */
+ a = state;
+ while (1) {
+ erts_aint32_t aprio, uprio, n, e;
+ ASSERT(a & ERTS_PSFLG_EXITING);
+ aprio = ERTS_PSFLGS_GET_ACT_PRIO(a);
+ uprio = ERTS_PSFLGS_GET_USR_PRIO(a);
+ if (aprio >= uprio)
+ break; /* user prio >= actual prio */
+ /*
+ * actual prio is higher than user prio; raise
+ * user prio to actual prio...
+ */
+ n = e = a;
+ n &= ~ERTS_PSFLGS_USR_PRIO_MASK;
+ n |= aprio << ERTS_PSFLGS_USR_PRIO_OFFSET;
+ a = erts_atomic32_cmpxchg_mb(&c_p->state, n, e);
+ if (a == e)
+ break;
+ }
+}
void
erts_execute_dirty_system_task(Process *c_p)
@@ -10664,8 +10833,7 @@ erts_execute_dirty_system_task(Process *c_p)
if (c_p->flags & F_DIRTY_GC_HIBERNATE) {
erts_proc_lock(c_p, ERTS_PROC_LOCK_MSGQ|ERTS_PROC_LOCK_STATUS);
- ERTS_MSGQ_MV_INQ2PRIVQ(c_p);
- if (c_p->msg.len)
+ if (erts_proc_sig_fetch(c_p))
c_p->flags &= ~F_DIRTY_GC_HIBERNATE; /* operation aborted... */
else {
erts_proc_unlock(c_p, ERTS_PROC_LOCK_MSGQ|ERTS_PROC_LOCK_STATUS);
@@ -10737,7 +10905,7 @@ dispatch_system_task(Process *c_p, erts_aint_t fail_state,
switch (st->type) {
case ERTS_PSTT_CPC:
- rp = erts_dirty_process_code_checker;
+ rp = erts_dirty_process_signal_handler;
ASSERT(fail_state & (ERTS_PSFLG_DIRTY_RUNNING
| ERTS_PSFLG_DIRTY_RUNNING_SYS));
if (c_p == rp) {
@@ -10822,7 +10990,7 @@ request_system_task(Process *c_p, Eterm requester, Eterm target,
goto badarg;
req_type = tp[1];
req_id = tp[2];
- req_id_sz = is_immed(req_id) ? req_id : size_object(req_id);
+ req_id_sz = is_immed(req_id) ? 0 : size_object(req_id);
tot_sz = req_id_sz;
for (i = 0; i < ERTS_MAX_PROC_SYS_TASK_ARGS; i++) {
int tix = 3 + i;
@@ -10954,13 +11122,15 @@ erts_internal_request_system_task_4(BIF_ALIST_4)
BIF_ARG_2, BIF_ARG_3, BIF_ARG_4);
}
-static void
-erts_schedule_generic_sys_task(Eterm pid, ErtsProcSysTaskType type, void* arg)
+static int
+schedule_generic_sys_task(Eterm pid, ErtsProcSysTaskType type,
+ int prio, Eterm arg0, Eterm arg1)
{
- Process *rp = erts_proc_lookup(pid);
+ int res = 0;
+ Process *rp = erts_proc_lookup_raw(pid);
if (rp) {
ErtsProcSysTask *st;
- erts_aint32_t state, fail_state;
+ erts_aint32_t st_prio, fail_state;
st = erts_alloc(ERTS_ALC_T_PROC_SYS_TSK,
ERTS_PROC_SYS_TASK_SIZE(0));
@@ -10969,31 +11139,46 @@ erts_schedule_generic_sys_task(Eterm pid, ErtsProcSysTaskType type, void* arg)
st->reply_tag = NIL;
st->req_id = NIL;
st->req_id_sz = 0;
- st->arg[0] = (Eterm)arg;
+ st->arg[0] = arg0;
+ st->arg[1] = arg1;
ERTS_INIT_OFF_HEAP(&st->off_heap);
- state = erts_atomic32_read_nob(&rp->state);
-
- fail_state = ERTS_PSFLG_EXITING;
- if (!schedule_process_sys_task(rp, ERTS_PSFLGS_GET_USR_PRIO(state),
- st, &fail_state))
+ if (prio >= 0) {
+ st_prio = (erts_aint32_t) prio;
+ fail_state = ERTS_PSFLG_FREE;
+ }
+ else {
+ erts_aint32_t state = erts_atomic32_read_nob(&rp->state);
+ st_prio = ERTS_PSFLGS_GET_USR_PRIO(state);
+ fail_state = ERTS_PSFLG_EXITING;
+ }
+ res = schedule_process_sys_task(rp, st_prio, st, &fail_state);
+ if (!res)
erts_free(ERTS_ALC_T_PROC_SYS_TSK, st);
}
+ return res;
}
-
void
erts_schedule_complete_off_heap_message_queue_change(Eterm pid)
{
- erts_schedule_generic_sys_task(pid, ERTS_PSTT_COHMQ, NULL);
+ schedule_generic_sys_task(pid, ERTS_PSTT_COHMQ,
+ -1, NIL, NIL);
}
void
erts_schedule_ets_free_fixation(Eterm pid, DbFixation* fix)
{
- erts_schedule_generic_sys_task(pid, ERTS_PSTT_ETS_FREE_FIXATION, fix);
+ schedule_generic_sys_task(pid, ERTS_PSTT_ETS_FREE_FIXATION,
+ -1, (Eterm) fix, NIL);
}
+int
+erts_sig_prio(Eterm pid, int prio)
+{
+ return schedule_generic_sys_task(pid, ERTS_PSTT_PRIO_SIG,
+ prio, am_false, NIL);
+}
static void
flush_dirty_trace_messages(void *vpid)
@@ -11033,7 +11218,7 @@ erts_schedule_flush_trace_messages(Process *proc, int force_on_proc)
dhndl = erts_thr_progress_unmanaged_delay();
- erts_schedule_generic_sys_task(pid, ERTS_PSTT_FTMQ, NULL);
+ schedule_generic_sys_task(pid, ERTS_PSTT_FTMQ, -1, NIL, NIL);
erts_thr_progress_unmanaged_continue(dhndl);
@@ -11229,9 +11414,11 @@ erts_set_gc_state(Process *c_p, int enable)
#endif
erts_atomic32_read_bset_nob(&c_p->state,
- (ERTS_PSFLG_DELAYED_SYS
- | ERTS_PSFLG_ACTIVE_SYS),
- ERTS_PSFLG_ACTIVE_SYS);
+ (ERTS_PSFLG_DELAYED_SYS
+ | ERTS_PSFLG_ACTIVE_SYS
+ | ERTS_PSFLG_SYS_TASKS),
+ (ERTS_PSFLG_ACTIVE_SYS
+ | ERTS_PSFLG_SYS_TASKS));
#ifdef DEBUG
ASSERT(state & ERTS_PSFLG_DELAYED_SYS);
@@ -11460,6 +11647,7 @@ typedef struct {
Process *proc;
erts_aint32_t state;
ErtsRunQueue *run_queue;
+ int bound;
} ErtsEarlyProcInit;
static void early_init_process_struct(void *varg, Eterm data)
@@ -11470,10 +11658,9 @@ static void early_init_process_struct(void *varg, Eterm data)
proc->common.id = make_internal_pid(data);
erts_atomic32_init_nob(&proc->dirty_state, 0);
proc->dirty_sys_tasks = NULL;
+ erts_init_runq_proc(proc, arg->run_queue, arg->bound);
erts_atomic32_init_relb(&proc->state, arg->state);
- RUNQ_SET_RQ(&proc->run_queue, arg->run_queue);
-
erts_proc_lock_init(proc); /* All locks locked */
}
@@ -11482,7 +11669,7 @@ static void early_init_process_struct(void *varg, Eterm data)
** Allocate process and find out where to place next process.
*/
static Process*
-alloc_process(ErtsRunQueue *rq, erts_aint32_t state)
+alloc_process(ErtsRunQueue *rq, int bound, erts_aint32_t state)
{
ErtsEarlyProcInit init_arg;
Process *p;
@@ -11491,9 +11678,12 @@ alloc_process(ErtsRunQueue *rq, erts_aint32_t state)
if (!p)
return NULL;
+ ASSERT(rq);
+
init_arg.proc = (Process *) p;
- init_arg.run_queue = rq;
init_arg.state = state;
+ init_arg.run_queue = rq;
+ init_arg.bound = bound;
ERTS_CT_ASSERT(offsetof(Process,common) == 0);
@@ -11509,7 +11699,6 @@ alloc_process(ErtsRunQueue *rq, erts_aint32_t state)
ASSERT(internal_pid_serial(p->common.id) <= ERTS_MAX_PID_SERIAL);
- p->approx_started = erts_get_approx_time();
p->rcount = 0;
p->heap = NULL;
@@ -11528,6 +11717,7 @@ erl_create_process(Process* parent, /* Parent of process (default group leader).
Eterm args, /* Arguments for function (must be well-formed list). */
ErlSpawnOpts* so) /* Options for spawn. */
{
+ int bound = 0;
Uint flags = 0;
ErtsRunQueue *rq = NULL;
Process *p;
@@ -11564,7 +11754,7 @@ erl_create_process(Process* parent, /* Parent of process (default group leader).
ASSERT(0 <= ix && ix < erts_no_run_queues);
rq = ERTS_RUNQ_IX(ix);
/* Unsupported feature... */
- state |= ERTS_PSFLG_BOUND;
+ bound = !0;
}
prio = (erts_aint32_t) so->priority;
}
@@ -11577,17 +11767,16 @@ erl_create_process(Process* parent, /* Parent of process (default group leader).
flags |= F_OFF_HEAP_MSGQ;
}
else if (so->flags & SPO_ON_HEAP_MSGQ) {
- state |= ERTS_PSFLG_ON_HEAP_MSGQ;
flags |= F_ON_HEAP_MSGQ;
}
ASSERT((flags & F_ON_HEAP_MSGQ) || (flags & F_OFF_HEAP_MSGQ));
if (!rq)
- rq = erts_get_runq_proc(parent);
+ rq = erts_get_runq_proc(parent, NULL);
- p = alloc_process(rq, state); /* All proc locks are locked by this thread
- on success */
+ p = alloc_process(rq, bound, state); /* All proc locks are locked by this thread
+ on success */
if (!p) {
erts_send_error_to_logger_str(parent->group_leader,
"Too many processes\n");
@@ -11595,11 +11784,6 @@ erl_create_process(Process* parent, /* Parent of process (default group leader).
goto error;
}
- ASSERT((erts_atomic32_read_nob(&p->state)
- & ERTS_PSFLG_ON_HEAP_MSGQ)
- || (erts_atomic32_read_nob(&p->state)
- & ERTS_PSFLG_OFF_HEAP_MSGQ));
-
#ifdef SHCOPY_SPAWN
arg_size = copy_shared_calculate(args, &info);
#else
@@ -11697,7 +11881,7 @@ erl_create_process(Process* parent, /* Parent of process (default group leader).
p->common.u.alive.reg = NULL;
ERTS_P_LINKS(p) = NULL;
ERTS_P_MONITORS(p) = NULL;
- p->nodes_monitors = NULL;
+ ERTS_P_LT_MONITORS(p) = NULL;
p->suspend_monitors = NULL;
ASSERT(is_pid(parent->group_leader));
@@ -11714,14 +11898,20 @@ erl_create_process(Process* parent, /* Parent of process (default group leader).
erts_get_default_proc_tracing(&ERTS_TRACE_FLAGS(p), &ERTS_TRACER(p));
- p->msg.first = NULL;
- p->msg.last = &p->msg.first;
- p->msg.save = &p->msg.first;
- p->msg.saved_last = &p->msg.first;
- p->msg.len = 0;
- p->msg_inq.first = NULL;
- p->msg_inq.last = &p->msg_inq.first;
- p->msg_inq.len = 0;
+ p->sig_qs.first = NULL;
+ p->sig_qs.last = &p->sig_qs.first;
+ p->sig_qs.cont = NULL;
+ p->sig_qs.cont_last = &p->sig_qs.cont;
+ p->sig_qs.save = &p->sig_qs.first;
+ p->sig_qs.saved_last = NULL;
+ p->sig_qs.len = 0;
+ p->sig_qs.nmsigs.next = NULL;
+ p->sig_qs.nmsigs.last = NULL;
+ p->sig_inq.first = NULL;
+ p->sig_inq.last = &p->sig_inq.first;
+ p->sig_inq.len = 0;
+ p->sig_inq.nmsigs.next = NULL;
+ p->sig_inq.nmsigs.last = NULL;
p->bif_timers = NULL;
p->mbuf = NULL;
p->msg_frag = NULL;
@@ -11748,8 +11938,6 @@ erl_create_process(Process* parent, /* Parent of process (default group leader).
p->scheduler_data = NULL;
p->suspendee = NIL;
p->pending_suspenders = NULL;
- p->pending_exit.reason = THE_NON_VALUE;
- p->pending_exit.bp = NULL;
#if !defined(NO_FPE_SIGNALS) || defined(HIPE)
p->fp_exception = 0;
@@ -11803,30 +11991,33 @@ erl_create_process(Process* parent, /* Parent of process (default group leader).
*/
if (so->flags & SPO_LINK) {
-#ifdef DEBUG
- int ret;
-#endif
-#ifdef DEBUG
- ret = erts_add_link(&ERTS_P_LINKS(parent), LINK_PID, p->common.id);
- ASSERT(ret == 0);
- ret = erts_add_link(&ERTS_P_LINKS(p), LINK_PID, parent->common.id);
- ASSERT(ret == 0);
-#else
- erts_add_link(&ERTS_P_LINKS(parent), LINK_PID, p->common.id);
- erts_add_link(&ERTS_P_LINKS(p), LINK_PID, parent->common.id);
-#endif
-
+ ErtsLink *lnk;
+ ErtsLinkData *ldp = erts_link_create(ERTS_LNK_TYPE_PROC,
+ parent->common.id,
+ p->common.id);
+ lnk = erts_link_tree_lookup_insert(&ERTS_P_LINKS(parent), &ldp->a);
+ if (lnk) {
+ /*
+ * This should more or less never happen, but could
+ * potentially happen if pid:s wrap...
+ */
+ erts_link_release(lnk);
+ }
+ erts_link_tree_insert(&ERTS_P_LINKS(p), &ldp->b);
}
/*
* Test whether this process should be initially monitored by its parent.
*/
if (so->flags & SPO_MONITOR) {
- Eterm mref;
-
- mref = erts_make_ref(parent);
- erts_add_monitor(&ERTS_P_MONITORS(parent), MON_ORIGIN, mref, p->common.id, NIL);
- erts_add_monitor(&ERTS_P_MONITORS(p), MON_TARGET, mref, parent->common.id, NIL);
+ Eterm mref = erts_make_ref(parent);
+ ErtsMonitorData *mdp = erts_monitor_create(ERTS_MON_TYPE_PROC,
+ mref,
+ parent->common.id,
+ p->common.id,
+ NIL);
+ erts_monitor_tree_insert(&ERTS_P_MONITORS(parent), &mdp->origin);
+ erts_monitor_list_insert(&ERTS_P_LT_MONITORS(p), &mdp->target);
so->mref = mref;
}
@@ -11911,13 +12102,23 @@ void erts_init_empty_process(Process *p)
p->mbuf_sz = 0;
erts_atomic_init_nob(&p->psd, (erts_aint_t) NULL);
ERTS_P_MONITORS(p) = NULL;
+ ERTS_P_LT_MONITORS(p) = NULL;
ERTS_P_LINKS(p) = NULL; /* List of links */
- p->nodes_monitors = NULL;
p->suspend_monitors = NULL;
- p->msg.first = NULL;
- p->msg.last = &p->msg.first;
- p->msg.save = &p->msg.first;
- p->msg.len = 0;
+ p->sig_qs.first = NULL;
+ p->sig_qs.last = &p->sig_qs.first;
+ p->sig_qs.cont = NULL;
+ p->sig_qs.cont_last = &p->sig_qs.cont;
+ p->sig_qs.save = &p->sig_qs.first;
+ p->sig_qs.saved_last = NULL;
+ p->sig_qs.len = 0;
+ p->sig_qs.nmsigs.next = NULL;
+ p->sig_qs.nmsigs.last = NULL;
+ p->sig_inq.first = NULL;
+ p->sig_inq.last = &p->sig_inq.first;
+ p->sig_inq.len = 0;
+ p->sig_inq.nmsigs.next = NULL;
+ p->sig_inq.nmsigs.last = NULL;
p->bif_timers = NULL;
p->dictionary = NULL;
p->seq_trace_clock = 0;
@@ -11945,7 +12146,6 @@ void erts_init_empty_process(Process *p)
p->def_arg_reg[5] = 0;
p->parent = NIL;
- p->approx_started = 0;
p->static_flags = 0;
p->common.u.alive.started_interval = 0;
@@ -11965,16 +12165,11 @@ void erts_init_empty_process(Process *p)
erts_atomic32_init_nob(&p->state, (erts_aint32_t) PRIORITY_NORMAL);
p->scheduler_data = NULL;
- p->msg_inq.first = NULL;
- p->msg_inq.last = &p->msg_inq.first;
- p->msg_inq.len = 0;
p->suspendee = NIL;
p->pending_suspenders = NULL;
- p->pending_exit.reason = THE_NON_VALUE;
- p->pending_exit.bp = NULL;
erts_proc_lock_init(p);
erts_proc_unlock(p, ERTS_PROC_LOCKS_ALL);
- RUNQ_SET_RQ(&p->run_queue, ERTS_RUNQ_IX(0));
+ erts_init_runq_proc(p, ERTS_RUNQ_IX(0), 0);
#if !defined(NO_FPE_SIGNALS) || defined(HIPE)
p->fp_exception = 0;
@@ -12007,11 +12202,11 @@ erts_debug_verify_clean_empty_process(Process* p)
ASSERT(p->old_heap == NULL);
ASSERT(ERTS_P_MONITORS(p) == NULL);
+ ASSERT(ERTS_P_LT_MONITORS(p) == NULL);
ASSERT(ERTS_P_LINKS(p) == NULL);
- ASSERT(p->nodes_monitors == NULL);
ASSERT(p->suspend_monitors == NULL);
- ASSERT(p->msg.first == NULL);
- ASSERT(p->msg.len == 0);
+ ASSERT(p->sig_qs.first == NULL);
+ ASSERT(p->sig_qs.len == 0);
ASSERT(p->bif_timers == NULL);
ASSERT(p->dictionary == NULL);
ASSERT(p->catches == 0);
@@ -12021,12 +12216,10 @@ erts_debug_verify_clean_empty_process(Process* p)
ASSERT(p->parent == NIL);
- ASSERT(p->msg_inq.first == NULL);
- ASSERT(p->msg_inq.len == 0);
+ ASSERT(p->sig_inq.first == NULL);
+ ASSERT(p->sig_inq.len == 0);
ASSERT(p->suspendee == NIL);
ASSERT(p->pending_suspenders == NULL);
- ASSERT(p->pending_exit.reason == THE_NON_VALUE);
- ASSERT(p->pending_exit.bp == NULL);
/* Thing that erts_cleanup_empty_process() cleans up */
@@ -12127,809 +12320,328 @@ delete_process(Process* p)
erts_erase_dicts(p);
/* free all pending messages */
- erts_cleanup_messages(p->msg.first);
- p->msg.first = NULL;
+ erts_cleanup_messages(p->sig_qs.first);
+ p->sig_qs.first = NULL;
+ erts_cleanup_messages(p->sig_qs.cont);
+ p->sig_qs.cont = NULL;
- ASSERT(!p->nodes_monitors);
ASSERT(!p->suspend_monitors);
p->fvalue = NIL;
}
static ERTS_INLINE void
-set_proc_exiting(Process *p,
- erts_aint32_t in_state,
- Eterm reason,
- ErlHeapFragment *bp)
+set_self_exiting(Process *c_p, Eterm reason, int *enqueue,
+ erts_aint32_t *prio, erts_aint32_t *state)
{
- erts_aint32_t state = in_state, enq_prio = -1;
- int enqueue;
- ERTS_LC_ASSERT(erts_proc_lc_my_proc_locks(p) == ERTS_PROC_LOCKS_ALL);
-
- enqueue = change_proc_schedule_state(p,
- (ERTS_PSFLG_SUSPENDED
- | ERTS_PSFLG_PENDING_EXIT
- | ERTS_PSFLGS_DIRTY_WORK),
- ERTS_PSFLG_EXITING|ERTS_PSFLG_ACTIVE,
- &state,
- &enq_prio,
- ERTS_PROC_LOCKS_ALL);
-
- p->fvalue = reason;
- if (bp)
- erts_link_mbuf_to_proc(p, bp);
- /*
- * We used to set freason to EXC_EXIT here, but there is no need to
- * save the stack trace since this process irreversibly is going to
- * exit.
- */
- p->freason = EXTAG_EXIT;
- KILL_CATCHES(p);
- p->i = (BeamInstr *) beam_exit;
-
-
- add2runq(enqueue, enq_prio, p, state, NULL);
-}
-
-static ERTS_INLINE erts_aint32_t
-set_proc_self_exiting(Process *c_p)
-{
-#ifdef DEBUG
- int enqueue;
-#endif
- erts_aint32_t state, enq_prio = -1;
+ erts_aint32_t st, enq_prio = -1;
+ int enq;
ERTS_LC_ASSERT(erts_proc_lc_my_proc_locks(c_p) == ERTS_PROC_LOCKS_ALL);
- state = erts_atomic32_read_nob(&c_p->state);
- ASSERT(state & (ERTS_PSFLG_RUNNING
- |ERTS_PSFLG_RUNNING_SYS
- | ERTS_PSFLG_DIRTY_RUNNING
- | ERTS_PSFLG_DIRTY_RUNNING_SYS));
-
-#ifdef DEBUG
- enqueue =
-#endif
- change_proc_schedule_state(c_p,
- ERTS_PSFLG_SUSPENDED|ERTS_PSFLG_PENDING_EXIT,
- ERTS_PSFLG_EXITING|ERTS_PSFLG_ACTIVE,
- &state,
- &enq_prio,
- ERTS_PROC_LOCKS_ALL);
-
- ASSERT(!enqueue);
- return state;
-}
+ c_p->fvalue = reason;
+ st = erts_atomic32_read_nob(&c_p->state);
+ ASSERT(enqueue || (st & (ERTS_PSFLG_RUNNING
+ |ERTS_PSFLG_RUNNING_SYS
+ | ERTS_PSFLG_DIRTY_RUNNING
+ | ERTS_PSFLG_DIRTY_RUNNING_SYS)));
-void
-erts_handle_pending_exit(Process *c_p, ErtsProcLocks locks)
-{
- ErtsProcLocks xlocks;
- ASSERT(is_value(c_p->pending_exit.reason));
- ERTS_LC_ASSERT(erts_proc_lc_my_proc_locks(c_p) == locks);
- ERTS_LC_ASSERT(locks & ERTS_PROC_LOCK_MAIN);
- ERTS_LC_ASSERT(!((ERTS_PSFLG_EXITING|ERTS_PSFLG_FREE)
- & erts_atomic32_read_nob(&c_p->state)));
-
- /* Ensure that all locks on c_p are locked before proceeding... */
- if (locks == ERTS_PROC_LOCKS_ALL)
- xlocks = 0;
- else {
- xlocks = ~locks & ERTS_PROC_LOCKS_ALL;
- if (erts_proc_trylock(c_p, xlocks) == EBUSY) {
- erts_proc_unlock(c_p, locks & ~ERTS_PROC_LOCK_MAIN);
- erts_proc_lock(c_p, ERTS_PROC_LOCKS_ALL_MINOR);
- }
- }
+ enq = change_proc_schedule_state(c_p,
+ (ERTS_PSFLG_SUSPENDED
+ | ERTS_PSFLGS_DIRTY_WORK),
+ ERTS_PSFLG_EXITING|ERTS_PSFLG_ACTIVE,
+ &st,
+ &enq_prio,
+ ERTS_PROC_LOCKS_ALL);
- set_proc_exiting(c_p,
- erts_atomic32_read_acqb(&c_p->state),
- c_p->pending_exit.reason,
- c_p->pending_exit.bp);
- c_p->pending_exit.reason = THE_NON_VALUE;
- c_p->pending_exit.bp = NULL;
-
- if (xlocks)
- erts_proc_unlock(c_p, xlocks);
+ ASSERT(enqueue || !enq);
+ if (enqueue)
+ *enqueue = enq;
+ if (prio)
+ *prio = enq_prio;
+ if (state)
+ *state = st;
}
-static void save_pending_exiter(Process *p, ErtsProcList *plp);
-
-static void
-do_handle_pending_exiters(ErtsProcList *pnd_xtrs)
-{
- /* 'list' is expected to have been fetched (i.e. not a ring anymore) */
- ErtsProcList *plp = pnd_xtrs;
-
- while (plp) {
- ErtsProcList *next_plp = plp->next;
- Process *p = erts_proc_lookup(plp->pid);
- if (p) {
- erts_aint32_t state;
- /*
- * If the process is running on a normal scheduler, the
- * pending exit will soon be detected and handled by the
- * scheduler running the process (at schedule in/out).
- */
- if (erts_proc_trylock(p, ERTS_PROC_LOCKS_ALL) != EBUSY) {
- if (erts_proclist_same(plp, p)) {
- state = erts_atomic32_read_acqb(&p->state);
- if (!(state & (ERTS_PSFLG_RUNNING
- | ERTS_PSFLG_RUNNING_SYS
- | ERTS_PSFLG_EXITING))) {
- ASSERT(state & ERTS_PSFLG_PENDING_EXIT);
- erts_handle_pending_exit(p, ERTS_PROC_LOCKS_ALL);
- }
- }
- erts_proc_unlock(p, ERTS_PROC_LOCKS_ALL);
- }
- else {
- erts_proc_lock(p, ERTS_PROC_LOCK_STATUS);
- if (erts_proclist_same(plp, p)) {
- state = erts_atomic32_read_acqb(&p->state);
- if (!(state & (ERTS_PSFLG_RUNNING
- | ERTS_PSFLG_RUNNING_SYS
- | ERTS_PSFLG_EXITING))) {
- /*
- * Save process and try to acquire all
- * locks at a later time...
- */
- save_pending_exiter(p, plp);
- plp = NULL;
- }
- }
- erts_proc_unlock(p, ERTS_PROC_LOCK_STATUS);
- }
- }
- if (plp)
- proclist_destroy(plp);
- plp = next_plp;
- }
-}
-
-static void
-save_pending_exiter(Process *p, ErtsProcList *plp)
+void
+erts_set_self_exiting(Process *c_p, Eterm reason)
{
- ErtsSchedulerSleepInfo *ssi;
- ErtsRunQueue *rq;
-
- ERTS_LC_ASSERT(ERTS_PROC_LOCK_STATUS & erts_proc_lc_my_proc_locks(p));
-
- rq = RUNQ_READ_RQ(&p->run_queue);
- ASSERT(rq && !ERTS_RUNQ_IX_IS_DIRTY(rq->ix));
-
- if (!plp)
- plp = proclist_create(p);
-
- erts_runq_lock(rq);
-
- erts_proclist_store_last(&rq->procs.pending_exiters, plp);
-
- non_empty_runq(rq);
-
- ssi = rq->scheduler->ssi;
+ int enqueue;
+ erts_aint32_t enq_prio, state;
+ ERTS_LC_ASSERT(erts_proc_lc_my_proc_locks(c_p) == ERTS_PROC_LOCK_MAIN);
- erts_runq_unlock(rq);
+ erts_proc_lock(c_p, ERTS_PROC_LOCKS_ALL_MINOR);
- set_aux_work_flags_wakeup_nob(ssi, ERTS_SSI_AUX_WORK_PENDING_EXITERS);
-}
+ set_self_exiting(c_p, reason, &enqueue, &enq_prio, &state);
+ c_p->freason = EXTAG_EXIT;
+ KILL_CATCHES(c_p);
+ c_p->i = (BeamInstr *) beam_exit;
+ /* Always active when exiting... */
+ ASSERT(state & ERTS_PSFLG_ACTIVE);
-/*
- * This function delivers an EXIT message to a process
- * which is trapping EXITs.
- */
-
-static ERTS_INLINE void
-send_exit_message(Process *to, ErtsProcLocks *to_locksp,
- Eterm exit_term, Uint term_size, Eterm token)
-{
- ErtsMessage *mp;
- ErlOffHeap *ohp;
- Eterm* hp;
- Eterm mess;
-#ifdef SHCOPY_SEND
- erts_shcopy_t info;
-#endif
+ /*
+ * If we are terminating a process that currently
+ * is executing on a dirty scheduler. It *should*
+ * be scheduled on a normal scheduler...
+ */
+ ASSERT(!(state & (ERTS_PSFLG_DIRTY_RUNNING
+ | ERTS_PSFLG_DIRTY_RUNNING_SYS))
+ || enqueue == ERTS_ENQUEUE_NORMAL_QUEUE
+ || enqueue == -ERTS_ENQUEUE_NORMAL_QUEUE);
- if (!have_seqtrace(token)) {
-#ifdef SHCOPY_SEND
- INITIALIZE_SHCOPY(info);
- term_size = copy_shared_calculate(exit_term, &info);
- mp = erts_alloc_message_heap(to, to_locksp, term_size, &hp, &ohp);
- mess = copy_shared_perform(exit_term, term_size, &info, &hp, ohp);
- DESTROY_SHCOPY(info);
-#else
- mp = erts_alloc_message_heap(to, to_locksp, term_size, &hp, &ohp);
- mess = copy_struct(exit_term, term_size, &hp, ohp);
-#endif
- erts_queue_message(to, *to_locksp, mp, mess, am_system);
- } else {
- Eterm temp_token;
- Uint sz_token;
-
- ASSERT(is_tuple(token));
- sz_token = size_object(token);
-#ifdef SHCOPY_SEND
- INITIALIZE_SHCOPY(info);
- term_size = copy_shared_calculate(exit_term, &info);
- mp = erts_alloc_message_heap(to, to_locksp, term_size+sz_token, &hp, &ohp);
- mess = copy_shared_perform(exit_term, term_size, &info, &hp, ohp);
- DESTROY_SHCOPY(info);
-#else
- mp = erts_alloc_message_heap(to, to_locksp, term_size+sz_token, &hp, &ohp);
- mess = copy_struct(exit_term, term_size, &hp, ohp);
-#endif
- /* the trace token must in this case be updated by the caller */
- seq_trace_output(token, mess, SEQ_TRACE_SEND, to->common.id, to);
- temp_token = copy_struct(token, sz_token, &hp, ohp);
- ERL_MESSAGE_TOKEN(mp) = temp_token;
- erts_queue_message(to, *to_locksp, mp, mess, am_system);
- }
+ erts_proc_unlock(c_p, ERTS_PROC_LOCKS_ALL_MINOR);
+ if (enqueue)
+ add2runq(enqueue, enq_prio, c_p, state, NULL);
}
-/*
- *
- * *** Exit signal behavior ***
- *
- * Exit signals are asynchronous (truly asynchronous in the
- * SMP emulator). When the signal is received the receiver receives an
- * 'EXIT' message if it is trapping exits; otherwise, it will either
- * ignore the signal if the exit reason is normal, or go into an
- * exiting state (ERTS_PSFLG_EXITING). When a process has gone into the
- * exiting state it will not execute any more Erlang code, but it might
- * take a while before it actually exits. The exit signal is being
- * received when the 'EXIT' message is put in the message queue, the
- * signal is dropped, or when it changes state into exiting. The time it
- * is in the exiting state before actually exiting is undefined (it
- * might take a really long time under certain conditions). The
- * receiver of the exit signal does not break links or trigger monitors
- * until it actually exits.
- *
- * Exit signals and other signals, e.g. messages, have to be received
- * by a receiver in the same order as sent by a sender.
- *
- *
- *
- * Exit signal implementation in the SMP emulator:
- *
- * If the receiver is trapping exits, the signal is transformed
- * into an 'EXIT' message and sent as a normal message, if the
- * reason is normal the signal is dropped; otherwise, the process
- * is determined to be exited. The interesting case is when the
- * process is to be exited and this is what is described below.
- *
- * If it is possible, the receiver is set in the exiting state straight
- * away and we are done; otherwise, the sender places the exit reason
- * in the pending_exit field of the process struct and if necessary
- * adds the receiver to the run queue. It is typically not possible
- * to set a scheduled process or a process which we cannot get all locks
- * on without releasing locks on it in an exiting state straight away.
- *
- * The receiver will poll the pending_exit field when it reach certain
- * places during it's execution. When it discovers the pending exit
- * it will change state into the exiting state. If the receiver wasn't
- * scheduled when the pending exit was set, the first scheduler that
- * schedules a new process will set the receiving process in the exiting
- * state just before it schedules next process.
- *
- * When the exit signal is placed in the pending_exit field, the signal
- * is considered as being in transit on the Erlang level. The signal is
- * actually in some kind of semi transit state, since we have already
- * determined how it should be received. It will exit the process no
- * matter what if it is received (the process may exit by itself before
- * reception of the exit signal). The signal is received when it is
- * discovered in the pending_exit field by the receiver.
- *
- * The receiver have to poll the pending_exit field at least before:
- * - moving messages from the message in queue to the private message
- * queue. This in order to preserve signal order.
- * - unlink. Otherwise the process might get exited on a link that
- * have been removed.
- * - changing the trap_exit flag to true. This in order to simplify the
- * implementation; otherwise, we would have to transform the signal
- * into an 'EXIT' message when setting the trap_exit flag to true. We
- * would also have to maintain a queue of exit signals in transit.
- * - being scheduled in or out.
- */
-
-static ERTS_INLINE int
-send_exit_signal(Process *c_p, /* current process if and only
- if reason is stored on it */
- Eterm from, /* Id of sender of signal */
- Process *rp, /* receiving process */
- ErtsProcLocks *rp_locks,/* current locks on receiver */
- Eterm reason, /* exit reason */
- Eterm exit_tuple, /* Prebuild exit tuple
- or THE_NON_VALUE */
- Uint exit_tuple_sz, /* Size of prebuilt exit tuple
- (if exit_tuple != THE_NON_VALUE) */
- Eterm token, /* token */
- Process *token_update, /* token updater */
- Uint32 flags /* flags */
- )
-{
- erts_aint32_t state = erts_atomic32_read_nob(&rp->state);
- Eterm rsn = reason == am_kill ? am_killed : reason;
-
- ERTS_LC_ASSERT(*rp_locks == erts_proc_lc_my_proc_locks(rp));
- ERTS_LC_ASSERT((*rp_locks & ERTS_PROC_LOCKS_XSIG_SEND)
- == ERTS_PROC_LOCKS_XSIG_SEND);
-
- ASSERT(reason != THE_NON_VALUE);
-
-#ifdef USE_VM_PROBES
- if(DTRACE_ENABLED(process_exit_signal) && is_pid(from)) {
- DTRACE_CHARBUF(sender_str, DTRACE_TERM_BUF_SIZE);
- DTRACE_CHARBUF(receiver_str, DTRACE_TERM_BUF_SIZE);
- DTRACE_CHARBUF(reason_buf, DTRACE_TERM_BUF_SIZE);
-
- dtrace_pid_str(from, sender_str);
- dtrace_proc_str(rp, receiver_str);
- erts_snprintf(reason_buf, sizeof(DTRACE_CHARBUF_NAME(reason_buf)) - 1, "%T", reason);
- DTRACE3(process_exit_signal, sender_str, receiver_str, reason_buf);
+void
+erts_proc_exit_handle_monitor(ErtsMonitor *mon, void *vctxt)
+{
+ Process *c_p = ((ErtsProcExitContext *) vctxt)->c_p;
+ Eterm reason = ((ErtsProcExitContext *) vctxt)->reason;
+ ErtsMonitorData *mdp = NULL;
+
+ if (erts_monitor_is_target(mon)) {
+ /* We are being watched... */
+ switch (mon->type) {
+ case ERTS_MON_TYPE_PROC:
+ erts_proc_sig_send_monitor_down(mon, reason);
+ mon = NULL;
+ break;
+ case ERTS_MON_TYPE_PORT: {
+ Port *prt;
+ ASSERT(is_internal_port(mon->other.item));
+ erts_proc_unlock(c_p, ERTS_PROC_LOCK_MAIN);
+ prt = erts_id2port(mon->other.item);
+ if (prt) {
+ erts_fire_port_monitor(prt, mon);
+ erts_port_release(prt);
+ mon = NULL;
+ }
+ erts_proc_lock(c_p, ERTS_PROC_LOCK_MAIN);
+ break;
+ }
+ case ERTS_MON_TYPE_RESOURCE:
+ erts_fire_nif_monitor(mon);
+ mon = NULL;
+ break;
+ case ERTS_MON_TYPE_DIST_PROC: {
+ ErtsMonLnkDist *dist;
+ DistEntry *dep;
+ ErtsDSigData dsd;
+ int code;
+ Eterm watcher;
+ Eterm watched;
+
+ mdp = erts_monitor_to_data(mon);
+
+ if (mon->flags & ERTS_ML_FLG_NAME)
+ watched = ((ErtsMonitorDataExtended *) mdp)->u.name;
+ else
+ watched = c_p->common.id;
+ ASSERT(is_internal_pid(watched) || is_atom(watched));
+
+ watcher = mon->other.item;
+ ASSERT(is_external_pid(watcher));
+ dep = external_pid_dist_entry(watcher);
+ ASSERT(dep);
+ dist = ((ErtsMonitorDataExtended *) mdp)->dist;
+ ASSERT(dist);
+ code = erts_dsig_prepare(&dsd, dep, NULL, 0,
+ ERTS_DSP_NO_LOCK, 0, 0);
+ switch (code) {
+ case ERTS_DSIG_PREP_CONNECTED:
+ case ERTS_DSIG_PREP_PENDING:
+ if (dist->connection_id == dsd.connection_id) {
+ code = erts_dsig_send_m_exit(&dsd,
+ watcher,
+ watched,
+ mdp->ref,
+ reason);
+ ASSERT(code == ERTS_DSIG_SEND_OK);
+ }
+ default:
+ break;
+ }
+ if (!erts_monitor_dist_delete(&mdp->origin))
+ mdp = NULL;
+ break;
+ }
+ default:
+ ERTS_INTERNAL_ERROR("Invalid target monitor type");
+ break;
+ }
}
-#endif
-
- if ((state & ERTS_PSFLG_TRAP_EXIT)
- && (reason != am_kill || (flags & ERTS_XSIG_FLG_IGN_KILL))) {
- /* have to release the status lock in order to send the exit message */
- erts_proc_unlock(rp, *rp_locks & ERTS_PROC_LOCKS_XSIG_SEND);
- *rp_locks &= ~ERTS_PROC_LOCKS_XSIG_SEND;
- if (have_seqtrace(token) && token_update)
- seq_trace_update_send(token_update);
- if (is_value(exit_tuple))
- send_exit_message(rp, rp_locks, exit_tuple, exit_tuple_sz, token);
- else
- erts_deliver_exit_message(from, rp, rp_locks, rsn, token);
- return 1; /* Receiver will get a message */
- }
- else if (reason != am_normal || (flags & ERTS_XSIG_FLG_NO_IGN_NORMAL)) {
- if (!(state & (ERTS_PSFLG_EXITING|ERTS_PSFLG_PENDING_EXIT))) {
- ASSERT(!rp->pending_exit.bp);
-
- if (rp == c_p && (*rp_locks & ERTS_PROC_LOCK_MAIN)) {
- /* Ensure that all locks on c_p are locked before
- proceeding... */
- if (*rp_locks != ERTS_PROC_LOCKS_ALL) {
- ErtsProcLocks need_locks = (~(*rp_locks)
- & ERTS_PROC_LOCKS_ALL);
- if (erts_proc_trylock(c_p, need_locks) == EBUSY) {
- erts_proc_unlock(c_p,
- *rp_locks & ~ERTS_PROC_LOCK_MAIN);
- erts_proc_lock(c_p, ERTS_PROC_LOCKS_ALL_MINOR);
- }
- *rp_locks = ERTS_PROC_LOCKS_ALL;
- }
- set_proc_exiting(c_p, state, rsn, NULL);
- }
- else if (!(state & (ERTS_PSFLG_RUNNING
- | ERTS_PSFLG_RUNNING_SYS
- | ERTS_PSFLG_DIRTY_RUNNING_SYS))) {
- /* Process not running ... */
- ErtsProcLocks need_locks = ~(*rp_locks) & ERTS_PROC_LOCKS_ALL;
- ErlHeapFragment *bp = NULL;
- Eterm rsn_cpy;
- if (need_locks
- && erts_proc_trylock(rp, need_locks) == EBUSY) {
- /* ... but we havn't got all locks on it ... */
- save_pending_exiter(rp, NULL);
- /*
- * The pending exit will be discovered when next
- * process is scheduled in
- */
- goto set_pending_exit;
- }
- /* ...and we have all locks on it... */
- *rp_locks = ERTS_PROC_LOCKS_ALL;
-
- state = erts_atomic32_read_nob(&rp->state);
-
- if (is_immed(rsn))
- rsn_cpy = rsn;
- else {
- Eterm *hp;
- ErlOffHeap *ohp;
- Uint rsn_sz = size_object(rsn);
- if (state & ERTS_PSFLG_DIRTY_RUNNING) {
- bp = new_message_buffer(rsn_sz);
- ohp = &bp->off_heap;
- hp = &bp->mem[0];
- }
- else
- {
- hp = HAlloc(rp, rsn_sz);
- ohp = &rp->off_heap;
- }
- rsn_cpy = copy_struct(rsn, rsn_sz, &hp, ohp);
- }
-
- set_proc_exiting(rp, state, rsn_cpy, bp);
- }
- else { /* Process running... */
-
- /*
- * The pending exit will be discovered when the process
- * is scheduled out if not discovered earlier.
- */
-
- set_pending_exit:
- if (is_immed(rsn)) {
- rp->pending_exit.reason = rsn;
- }
- else {
- Eterm *hp;
- Uint sz = size_object(rsn);
- ErlHeapFragment *bp = new_message_buffer(sz);
-
- hp = &bp->mem[0];
- rp->pending_exit.reason = copy_struct(rsn,
- sz,
- &hp,
- &bp->off_heap);
- rp->pending_exit.bp = bp;
- }
-
- /*
- * If no dirty work has been scheduled, pending exit will
- * be discovered when the process is scheduled. If dirty work
- * has been scheduled, we may need to add it to a normal run
- * queue...
- */
- {
- erts_aint32_t a = erts_atomic32_read_nob(&rp->state);
- while (1) {
- erts_aint32_t n, e;
- int dwork;
- n = e = a;
- n |= ERTS_PSFLG_PENDING_EXIT;
- dwork = !!(n & ERTS_PSFLGS_DIRTY_WORK);
- n &= ~ERTS_PSFLGS_DIRTY_WORK;
- a = erts_atomic32_cmpxchg_mb(&rp->state, n, e);
- if (a == e) {
- if (dwork)
- erts_schedule_process(rp, n, *rp_locks);
- break;
- }
- }
+ else { /* Origin monitor */
+ /* We are watching someone else... */
+ switch (mon->type) {
+ case ERTS_MON_TYPE_PROC:
+ erts_proc_sig_send_demonitor(mon);
+ mon = NULL;
+ break;
+ case ERTS_MON_TYPE_TIME_OFFSET:
+ erts_demonitor_time_offset(mon);
+ mon = NULL;
+ break;
+ case ERTS_MON_TYPE_NODE:
+ mdp = erts_monitor_to_data(mon);
+ if (!erts_monitor_dist_delete(&mdp->target))
+ mdp = NULL;
+ break;
+ case ERTS_MON_TYPE_NODES:
+ erts_monitor_nodes_delete(mon);
+ mon = NULL;
+ break;
+ case ERTS_MON_TYPE_PORT: {
+ Port *prt;
+ ASSERT(is_internal_port(mon->other.item));
+ prt = erts_port_lookup_raw(mon->other.item);
+ if (prt) {
+ if (erts_port_demonitor(c_p, prt, mon) != ERTS_PORT_OP_DROPPED)
+ mon = NULL;
+ }
+ break;
+ }
+ case ERTS_MON_TYPE_DIST_PROC: {
+ ErtsMonLnkDist *dist;
+ DistEntry *dep;
+ ErtsDSigData dsd;
+ int code;
+ Eterm watched;
+
+ mdp = erts_monitor_to_data(mon);
+ dist = ((ErtsMonitorDataExtended *) mdp)->dist;
+ ASSERT(dist);
+ if (mon->flags & ERTS_ML_FLG_NAME) {
+ watched = ((ErtsMonitorDataExtended *) mdp)->u.name;
+ ASSERT(is_atom(watched));
+ dep = erts_sysname_to_connected_dist_entry(dist->nodename);
+ }
+ else {
+ watched = mon->other.item;
+ ASSERT(is_external_pid(watched));
+ dep = external_pid_dist_entry(watched);
+ }
+ code = erts_dsig_prepare(&dsd, dep, NULL, 0,
+ ERTS_DSP_NO_LOCK, 0, 0);
+ switch (code) {
+ case ERTS_DSIG_PREP_CONNECTED:
+ case ERTS_DSIG_PREP_PENDING:
+ if (dist->connection_id == dsd.connection_id) {
+ code = erts_dsig_send_demonitor(&dsd,
+ c_p->common.id,
+ watched,
+ mdp->ref,
+ 1);
+ ASSERT(code == ERTS_DSIG_SEND_OK);
}
- }
- }
- /* else:
- *
- * The receiver already has a pending exit (or is exiting)
- * so we drop this signal.
- *
- * NOTE: dropping this exit signal is based on the assumption
- * that the receiver *will* exit; either on the pending
- * exit or by itself before seeing the pending exit.
- */
- return -1; /* Receiver will exit */
+ default:
+ break;
+ }
+ if (!erts_monitor_dist_delete(&mdp->target))
+ mdp = NULL;
+ break;
+ }
+ default:
+ ERTS_INTERNAL_ERROR("Invalid origin monitor type");
+ break;
+ }
}
- return 0; /* Receiver unaffected */
+ if (mdp)
+ erts_monitor_release_both(mdp);
+ else if (mon)
+ erts_monitor_release(mon);
}
-
-int
-erts_send_exit_signal(Process *c_p,
- Eterm from,
- Process *rp,
- ErtsProcLocks *rp_locks,
- Eterm reason,
- Eterm token,
- Process *token_update,
- Uint32 flags)
-{
- return send_exit_signal(c_p,
- from,
- rp,
- rp_locks,
- reason,
- THE_NON_VALUE,
- 0,
- token,
- token_update,
- flags);
-}
-
-typedef struct {
- Eterm reason;
- Process *p;
-} ExitMonitorContext;
-
-static void doit_exit_monitor(ErtsMonitor *mon, void *vpcontext)
-{
- ExitMonitorContext *pcontext = vpcontext;
- DistEntry *dep;
- ErtsMonitor *rmon;
-
- switch (mon->type) {
- case MON_ORIGIN:
- /* We are monitoring someone else, we need to demonitor that one.. */
- if (is_atom(mon->u.pid)) { /* remote by name */
- ASSERT(is_node_name_atom(mon->u.pid));
- dep = erts_sysname_to_connected_dist_entry(mon->u.pid);
- if (dep) {
- erts_de_links_lock(dep);
- rmon = erts_remove_monitor(&(dep->monitors), mon->ref);
- erts_de_links_unlock(dep);
- if (rmon) {
- ErtsDSigData dsd;
- int code = erts_dsig_prepare(&dsd, dep, NULL,
- ERTS_DSP_NO_LOCK, 0);
- if (code == ERTS_DSIG_PREP_CONNECTED) {
- code = erts_dsig_send_demonitor(&dsd,
- rmon->u.pid,
- mon->name,
- mon->ref,
- 1);
- ASSERT(code == ERTS_DSIG_SEND_OK);
- }
- erts_destroy_monitor(rmon);
- }
- }
- } else {
- ASSERT(is_pid(mon->u.pid) || is_port(mon->u.pid));
- /* if is local by pid or name */
- if (is_internal_pid(mon->u.pid)) {
- Process *rp = erts_pid2proc(NULL, 0, mon->u.pid, ERTS_PROC_LOCK_LINK);
- if (!rp) {
- goto done;
- }
- rmon = erts_remove_monitor(&ERTS_P_MONITORS(rp), mon->ref);
- erts_proc_unlock(rp, ERTS_PROC_LOCK_LINK);
- if (rmon == NULL) {
- goto done;
- }
- erts_destroy_monitor(rmon);
- } else if (is_internal_port(mon->u.pid)) {
- /* Is a local port */
- Port *prt = erts_port_lookup_raw(mon->u.pid);
- if (!prt) {
- goto done;
- }
- erts_port_demonitor(pcontext->p,
- ERTS_PORT_DEMONITOR_ORIGIN_ON_DEATHBED,
- prt, mon->ref, NULL);
- } else { /* remote by pid */
- ASSERT(is_external_pid(mon->u.pid));
- dep = external_pid_dist_entry(mon->u.pid);
- ASSERT(dep != NULL);
- if (dep) {
- erts_de_links_lock(dep);
- rmon = erts_remove_monitor(&(dep->monitors), mon->ref);
- erts_de_links_unlock(dep);
- if (rmon) {
- ErtsDSigData dsd;
- int code = erts_dsig_prepare(&dsd, dep, NULL,
- ERTS_DSP_NO_LOCK, 0);
- if (code == ERTS_DSIG_PREP_CONNECTED) {
- code = erts_dsig_send_demonitor(&dsd,
- rmon->u.pid,
- mon->u.pid,
- mon->ref,
- 1);
- ASSERT(code == ERTS_DSIG_SEND_OK);
- }
- erts_destroy_monitor(rmon);
- }
- }
- }
- }
- break;
- case MON_TARGET:
- ASSERT(is_pid(mon->u.pid) || is_internal_port(mon->u.pid));
- if (is_internal_port(mon->u.pid)) {
- Port *prt = erts_id2port(mon->u.pid);
- if (prt == NULL) {
- goto done;
- }
- erts_fire_port_monitor(prt, mon->ref);
- erts_port_release(prt);
- } else if (is_internal_pid(mon->u.pid)) {/* local by name or pid */
- Eterm watched;
- Process *rp;
- DeclareTmpHeapNoproc(lhp,3);
- ErtsProcLocks rp_locks = (ERTS_PROC_LOCK_LINK
- | ERTS_PROC_LOCKS_MSG_SEND);
- rp = erts_pid2proc(NULL, 0, mon->u.pid, rp_locks);
- if (rp == NULL) {
- goto done;
- }
- UseTmpHeapNoproc(3);
- rmon = erts_remove_monitor(&ERTS_P_MONITORS(rp), mon->ref);
- if (rmon) {
- erts_destroy_monitor(rmon);
- watched = (is_atom(mon->name)
- ? TUPLE2(lhp, mon->name,
- erts_this_dist_entry->sysname)
- : pcontext->p->common.id);
- erts_queue_monitor_message(rp, &rp_locks, mon->ref, am_process,
- watched, pcontext->reason);
- }
- UnUseTmpHeapNoproc(3);
- /* else: demonitor while we exited, i.e. do nothing... */
- erts_proc_unlock(rp, rp_locks);
- } else { /* external by pid or name */
- ASSERT(is_external_pid(mon->u.pid));
- dep = external_pid_dist_entry(mon->u.pid);
- ASSERT(dep != NULL);
- if (dep) {
- erts_de_links_lock(dep);
- rmon = erts_remove_monitor(&(dep->monitors), mon->ref);
- erts_de_links_unlock(dep);
- if (rmon) {
- ErtsDSigData dsd;
- int code = erts_dsig_prepare(&dsd, dep, NULL,
- ERTS_DSP_NO_LOCK, 0);
- if (code == ERTS_DSIG_PREP_CONNECTED) {
- code = erts_dsig_send_m_exit(&dsd,
- mon->u.pid,
- (rmon->name != NIL
- ? rmon->name
- : rmon->u.pid),
- mon->ref,
- pcontext->reason);
- ASSERT(code == ERTS_DSIG_SEND_OK);
- }
- erts_destroy_monitor(rmon);
- }
- }
- }
- break;
- case MON_NIF_TARGET:
- erts_fire_nif_monitor(mon->u.resource,
- pcontext->p->common.id,
- mon->ref);
+void
+erts_proc_exit_handle_link(ErtsLink *lnk, void *vctxt)
+{
+ Process *c_p = ((ErtsProcExitContext *) vctxt)->c_p;
+ Eterm reason = ((ErtsProcExitContext *) vctxt)->reason;
+ ErtsLinkData *ldp = NULL;
+
+ switch (lnk->type) {
+ case ERTS_LNK_TYPE_PROC:
+ ASSERT(is_internal_pid(lnk->other.item));
+ erts_proc_sig_send_link_exit(c_p, c_p->common.id, lnk,
+ reason, SEQ_TRACE_TOKEN(c_p));
+ lnk = NULL;
+ break;
+ case ERTS_LNK_TYPE_PORT: {
+ Port *prt;
+ ASSERT(is_internal_port(lnk->other.item));
+ prt = erts_port_lookup(lnk->other.item,
+ ERTS_PORT_SFLGS_INVALID_LOOKUP);
+ if (prt)
+ erts_port_exit(NULL,
+ (ERTS_PORT_SIG_FLG_FORCE_SCHED
+ | ERTS_PORT_SIG_FLG_BROKEN_LINK),
+ prt,
+ c_p->common.id,
+ reason,
+ NULL);
+ break;
+ }
+ case ERTS_LNK_TYPE_DIST_PROC: {
+ DistEntry *dep;
+ ErtsMonLnkDist *dist;
+ ErtsLink *dlnk;
+ ErtsDSigData dsd;
+ int code;
+
+ dlnk = erts_link_to_other(lnk, &ldp);
+ dist = ((ErtsLinkDataExtended *) ldp)->dist;
+
+ ASSERT(is_external_pid(lnk->other.item));
+ dep = external_pid_dist_entry(lnk->other.item);
+
+ ASSERT(dep != erts_this_dist_entry);
+
+ if (!erts_link_dist_delete(dlnk))
+ ldp = NULL;
+
+ code = erts_dsig_prepare(&dsd, dep, c_p, 0, ERTS_DSP_NO_LOCK, 0, 0);
+ switch (code) {
+ case ERTS_DSIG_PREP_CONNECTED:
+ case ERTS_DSIG_PREP_PENDING:
+ if (dist->connection_id == dsd.connection_id) {
+ code = erts_dsig_send_exit_tt(&dsd,
+ c_p->common.id,
+ lnk->other.item,
+ reason,
+ SEQ_TRACE_TOKEN(c_p));
+ ASSERT(code == ERTS_DSIG_SEND_OK);
+ }
+ }
break;
- case MON_TIME_OFFSET:
- erts_demonitor_time_offset(mon->ref);
- break;
- default:
- ERTS_INTERNAL_ERROR("Invalid monitor type");
}
- done:
- /* As the monitors are previously removed from the process,
- distribution operations will not cause monitors to disappear,
- we can safely delete it. */
-
- erts_destroy_monitor(mon);
-}
-
-typedef struct {
- Process *p;
- Eterm reason;
- Eterm exit_tuple;
- Uint exit_tuple_sz;
-} ExitLinkContext;
-
-static void doit_exit_link(ErtsLink *lnk, void *vpcontext)
-{
- ExitLinkContext *pcontext = vpcontext;
- /* Unpack context, it's readonly */
- Process *p = pcontext->p;
- Eterm reason = pcontext->reason;
- Eterm exit_tuple = pcontext->exit_tuple;
- Uint exit_tuple_sz = pcontext->exit_tuple_sz;
- Eterm item = lnk->pid;
- ErtsLink *rlnk;
- DistEntry *dep;
- Process *rp;
-
-
- switch(lnk->type) {
- case LINK_PID:
- if(is_internal_port(item)) {
- Port *prt = erts_port_lookup(item, ERTS_PORT_SFLGS_INVALID_LOOKUP);
- if (prt)
- erts_port_exit(NULL,
- (ERTS_PORT_SIG_FLG_FORCE_SCHED
- | ERTS_PORT_SIG_FLG_BROKEN_LINK),
- prt,
- p->common.id,
- reason,
- NULL);
- }
- else if(is_external_port(item)) {
- erts_dsprintf_buf_t *dsbufp = erts_create_logger_dsbuf();
- erts_dsprintf(dsbufp,
- "Erroneous link between %T and external port %T "
- "found\n",
- p->common.id,
- item);
- erts_send_error_to_logger_nogl(dsbufp);
- ASSERT(0); /* It isn't possible to setup such a link... */
- }
- else if (is_internal_pid(item)) {
- ErtsProcLocks rp_locks = (ERTS_PROC_LOCK_LINK
- | ERTS_PROC_LOCKS_XSIG_SEND);
- rp = erts_pid2proc(NULL, 0, item, rp_locks);
- if (rp) {
- rlnk = erts_remove_link(&ERTS_P_LINKS(rp), p->common.id);
- /* If rlnk == NULL, we got unlinked while exiting,
- i.e., do nothing... */
- if (rlnk) {
- int xres;
- erts_destroy_link(rlnk);
- xres = send_exit_signal(NULL,
- p->common.id,
- rp,
- &rp_locks,
- reason,
- exit_tuple,
- exit_tuple_sz,
- SEQ_TRACE_TOKEN(p),
- p,
- ERTS_XSIG_FLG_IGN_KILL);
- if (xres >= 0 && IS_TRACED_FL(rp, F_TRACE_PROCS)) {
- /* We didn't exit the process and it is traced */
- if (IS_TRACED_FL(rp, F_TRACE_PROCS)) {
- if (rp_locks & ERTS_PROC_LOCKS_XSIG_SEND) {
- erts_proc_unlock(rp, ERTS_PROC_LOCKS_XSIG_SEND);
- rp_locks &= ~ERTS_PROC_LOCKS_XSIG_SEND;
- }
- trace_proc(NULL, 0, rp, am_getting_unlinked, p->common.id);
- }
- }
- }
- ASSERT(rp != p);
- erts_proc_unlock(rp, rp_locks);
- }
- }
- else if (is_external_pid(item)) {
- dep = external_pid_dist_entry(item);
- if(dep != erts_this_dist_entry) {
- ErtsDSigData dsd;
- int code;
- ErtsDistLinkData dld;
- erts_remove_dist_link(&dld, p->common.id, item, dep);
- erts_proc_lock(p, ERTS_PROC_LOCK_MAIN);
- code = erts_dsig_prepare(&dsd, dep, p, ERTS_DSP_NO_LOCK, 0);
- if (code == ERTS_DSIG_PREP_CONNECTED) {
- code = erts_dsig_send_exit_tt(&dsd, p->common.id, item,
- reason, SEQ_TRACE_TOKEN(p));
- ASSERT(code == ERTS_DSIG_SEND_OK);
- }
- erts_proc_unlock(p, ERTS_PROC_LOCK_MAIN);
- erts_destroy_dist_link(&dld);
- }
- }
- break;
- case LINK_NODE:
- ASSERT(is_node_name_atom(item));
- dep = erts_sysname_to_connected_dist_entry(item);
- if(dep) {
- /* dist entries have node links in a separate structure to
- avoid confusion */
- erts_de_links_lock(dep);
- rlnk = erts_remove_link(&(dep->node_links), p->common.id);
- erts_de_links_unlock(dep);
- if (rlnk)
- erts_destroy_link(rlnk);
- }
- break;
-
default:
- erts_exit(ERTS_ERROR_EXIT, "bad type in link list\n");
- break;
+ ERTS_INTERNAL_ERROR("Unexpected link type");
+ break;
}
- erts_destroy_link(lnk);
+
+ if (ldp)
+ erts_link_release_both(ldp);
+ else if (lnk)
+ erts_link_release(lnk);
}
static void
-resume_suspend_monitor(ErtsSuspendMonitor *smon, void *vc_p)
+resume_suspend_monitor(ErtsMonitor *mon, void *vc_p)
{
+ ErtsMonitorSuspend *smon = erts_monitor_suspend(mon);
Process *suspendee = erts_pid2proc((Process *) vc_p, ERTS_PROC_LOCK_MAIN,
- smon->pid, ERTS_PROC_LOCK_STATUS);
+ smon->mon.other.item, ERTS_PROC_LOCK_STATUS);
if (suspendee) {
ASSERT(suspendee != vc_p);
if (smon->active)
resume_process(suspendee, ERTS_PROC_LOCK_STATUS);
erts_proc_unlock(suspendee, ERTS_PROC_LOCK_STATUS);
}
- erts_destroy_suspend_monitor(smon);
+ erts_monitor_suspend_destroy(smon);
}
/* this function fishishes a process and propagates exit messages - called
@@ -12938,8 +12650,6 @@ void
erts_do_exit_process(Process* p, Eterm reason)
{
p->arity = 0; /* No live registers */
- p->fvalue = reason;
-
#ifdef USE_VM_PROBES
if (DTRACE_ENABLED(process_exit)) {
@@ -12963,19 +12673,10 @@ erts_do_exit_process(Process* p, Eterm reason)
process from exiting until the lock has been released. */
erts_proc_lock(p, ERTS_PROC_LOCKS_ALL_MINOR);
- if (ERTS_PSFLG_PENDING_EXIT & set_proc_self_exiting(p)) {
- /* Process exited before pending exit was received... */
- p->pending_exit.reason = THE_NON_VALUE;
- if (p->pending_exit.bp) {
- free_message_buffer(p->pending_exit.bp);
- p->pending_exit.bp = NULL;
- }
- }
+ set_self_exiting(p, reason, NULL, NULL, NULL);
cancel_suspend_of_suspendee(p, ERTS_PROC_LOCKS_ALL);
- ERTS_MSGQ_MV_INQ2PRIVQ(p);
-
if (IS_TRACED(p)) {
if (IS_TRACED_FL(p, F_TRACE_CALLS))
erts_schedule_time_break(p, ERTS_BP_CALL_TIME_SCHEDULE_EXITING);
@@ -13011,13 +12712,15 @@ erts_do_exit_process(Process* p, Eterm reason)
void
erts_continue_exit_process(Process *p)
{
- ErtsLink* lnk;
- ErtsMonitor *mon;
+ ErtsLink *links;
+ ErtsMonitor *monitors;
+ ErtsMonitor *lt_monitors;
ErtsProcLocks curr_locks = ERTS_PROC_LOCK_MAIN;
Eterm reason = p->fvalue;
DistEntry *dep = NULL;
erts_aint32_t state;
int delay_del_proc = 0;
+ ErtsProcExitContext pectxt;
#ifdef DEBUG
int yield_allowed = 1;
@@ -13089,16 +12792,13 @@ erts_continue_exit_process(Process *p)
erts_set_gc_state(p, 1);
state = erts_atomic32_read_acqb(&p->state);
- if (state & ERTS_PSFLG_ACTIVE_SYS
- || p->dirty_sys_tasks
- ) {
+ if ((state & ERTS_PSFLG_SYS_TASKS) || p->dirty_sys_tasks) {
if (cleanup_sys_tasks(p, state, CONTEXT_REDS) >= CONTEXT_REDS/2)
goto yield;
}
#ifdef DEBUG
erts_proc_lock(p, ERTS_PROC_LOCK_STATUS);
- ASSERT(p->sys_task_qs == NULL);
ASSERT(ERTS_PROC_GET_DELAYED_GC_TASK_QS(p) == NULL);
ASSERT(p->dirty_sys_tasks == NULL);
erts_proc_unlock(p, ERTS_PROC_LOCK_STATUS);
@@ -13109,18 +12809,10 @@ erts_continue_exit_process(Process *p)
p->flags &= ~F_USING_DDLL;
}
- if (p->nodes_monitors) {
- erts_delete_nodes_monitors(p, ERTS_PROC_LOCK_MAIN);
- p->nodes_monitors = NULL;
- }
-
-
- if (p->suspend_monitors) {
- erts_sweep_suspend_monitors(p->suspend_monitors,
- resume_suspend_monitor,
- p);
- p->suspend_monitors = NULL;
- }
+ if (p->suspend_monitors)
+ erts_monitor_tree_foreach_delete(&p->suspend_monitors,
+ resume_suspend_monitor,
+ p);
/*
* The registered name *should* be the last "erlang resource" to
@@ -13149,8 +12841,9 @@ erts_continue_exit_process(Process *p)
* Note! The monitor and link fields will be overwritten
* by erts_ptab_delete_element() below.
*/
- mon = ERTS_P_MONITORS(p);
- lnk = ERTS_P_LINKS(p);
+ links = ERTS_P_LINKS(p);
+ monitors = ERTS_P_MONITORS(p);
+ lt_monitors = ERTS_P_LT_MONITORS(p);
{
/* Do *not* use erts_get_runq_proc() */
@@ -13187,7 +12880,9 @@ erts_continue_exit_process(Process *p)
n = e = a;
ASSERT(a & ERTS_PSFLG_EXITING);
n |= ERTS_PSFLG_FREE;
- n &= ~ERTS_PSFLG_ACTIVE;
+ n &= ~(ERTS_PSFLG_ACTIVE
+ | ERTS_PSFLG_ACTIVE_SYS
+ | ERTS_PSFLG_DIRTY_ACTIVE_SYS);
if ((n & ERTS_PSFLG_IN_RUNQ) && !refc_inced) {
erts_proc_inc_refc(p);
refc_inced = 1;
@@ -13215,43 +12910,77 @@ erts_continue_exit_process(Process *p)
? ERTS_PROC_SET_DIST_ENTRY(p, NULL)
: NULL);
- erts_proc_unlock(p, ERTS_PROC_LOCKS_ALL);
+
+ /*
+ * It might show up signal prio elevation tasks until we
+ * have entered free state. Cleanup such tasks now.
+ */
+ state = erts_atomic32_read_acqb(&p->state);
+ if (!(state & ERTS_PSFLG_SYS_TASKS))
+ erts_proc_unlock(p, ERTS_PROC_LOCKS_ALL);
+ else {
+ erts_proc_unlock(p, ERTS_PROC_LOCKS_ALL_MINOR);
+
+ do {
+ (void) cleanup_sys_tasks(p, state, CONTEXT_REDS);
+ state = erts_atomic32_read_acqb(&p->state);
+ } while (state & ERTS_PSFLG_SYS_TASKS);
+
+ erts_proc_unlock(p, ERTS_PROC_LOCK_MAIN);
+ }
+
+#ifdef DEBUG
+ erts_proc_lock(p, ERTS_PROC_LOCK_STATUS);
+ ASSERT(p->sys_task_qs == NULL);
+ erts_proc_unlock(p, ERTS_PROC_LOCK_STATUS);
+#endif
if (dep) {
erts_do_net_exits(dep, (reason == am_kill) ? am_killed : reason);
erts_deref_dist_entry(dep);
}
- /*
- * Pre-build the EXIT tuple if there are any links.
- */
- if (lnk) {
- DeclareTmpHeap(tmp_heap,4,p);
- Eterm exit_tuple;
- Uint exit_tuple_sz;
- Eterm* hp;
+ pectxt.c_p = p;
+ pectxt.reason = reason;
- UseTmpHeap(4,p);
- hp = &tmp_heap[0];
+ erts_proc_lock(p, ERTS_PROC_LOCK_MAIN|ERTS_PROC_LOCK_MSGQ);
- exit_tuple = TUPLE3(hp, am_EXIT, p->common.id, reason);
+ erts_proc_sig_fetch(p);
- exit_tuple_sz = size_object(exit_tuple);
+ erts_proc_unlock(p, ERTS_PROC_LOCK_MSGQ);
- {
- ExitLinkContext context = {p, reason, exit_tuple, exit_tuple_sz};
- erts_sweep_links(lnk, &doit_exit_link, &context);
- }
- UnUseTmpHeap(4,p);
+ if (links) {
+ erts_link_tree_foreach_delete(&links,
+ erts_proc_exit_handle_link,
+ (void *) &pectxt);
+ ASSERT(!links);
}
- {
- ExitMonitorContext context = {reason, p};
- erts_sweep_monitors(mon,&doit_exit_monitor,&context); /* Allocates TmpHeap, but we
- have none here */
+ if (monitors) {
+ erts_monitor_tree_foreach_delete(&monitors,
+ erts_proc_exit_handle_monitor,
+ (void *) &pectxt);
+ ASSERT(!monitors);
+ }
+
+ if (lt_monitors) {
+ erts_monitor_list_foreach_delete(&lt_monitors,
+ erts_proc_exit_handle_monitor,
+ (void *) &pectxt);
+ ASSERT(!lt_monitors);
+ }
+
+ /*
+ * erts_proc_sig_handle_exit() implements yielding.
+ * However, this function cannot handle it yet... loop
+ * until done...
+ */
+ while (!0) {
+ int reds = CONTEXT_REDS;
+ if (erts_proc_sig_handle_exit(p, &reds))
+ break;
}
- erts_proc_lock(p, ERTS_PROC_LOCK_MAIN);
ERTS_CHK_HAVE_ONLY_MAIN_PROC_LOCK(p);
erts_flush_trace_messages(p, ERTS_PROC_LOCK_MAIN);
@@ -13287,6 +13016,54 @@ erts_continue_exit_process(Process *p)
BUMP_ALL_REDS(p);
}
+Process *
+erts_try_lock_sig_free_proc(Eterm pid, ErtsProcLocks locks,
+ erts_aint32_t *statep)
+{
+ Process *rp = erts_proc_lookup_raw(pid);
+ erts_aint32_t state;
+
+ if (!rp) {
+ if (statep)
+ *statep = ERTS_PSFLG_EXITING|ERTS_PSFLG_FREE;
+ return NULL;
+ }
+
+ ERTS_LC_ASSERT(!erts_proc_lc_my_proc_locks(rp));
+
+ state = erts_atomic32_read_nob(&rp->state);
+ if (statep)
+ *statep = state;
+
+ if (state & ERTS_PSFLG_FREE)
+ return NULL;
+
+ if (state & (ERTS_PSFLG_SIG_IN_Q|ERTS_PSFLG_SIG_Q))
+ return ERTS_PROC_LOCK_BUSY;
+
+ if (!locks)
+ return rp;
+
+ if (erts_proc_trylock(rp, locks) == EBUSY)
+ return ERTS_PROC_LOCK_BUSY;
+
+ state = erts_atomic32_read_nob(&rp->state);
+ if (statep)
+ *statep = state;
+
+ if (state & ERTS_PSFLG_FREE) {
+ erts_proc_unlock(rp, locks);
+ return NULL;
+ }
+
+ if (state & (ERTS_PSFLG_SIG_IN_Q|ERTS_PSFLG_SIG_Q)) {
+ erts_proc_unlock(rp, locks);
+ return ERTS_PROC_LOCK_BUSY;
+ }
+
+ return rp;
+}
+
/*
* Stack dump functions follow.
*/
@@ -13388,16 +13165,33 @@ stack_element_dump(fmtfn_t to, void *to_arg, Eterm* sp, int yreg)
return yreg;
}
+static void print_current_process_info(fmtfn_t, void *to_arg, ErtsSchedulerData*);
+
/*
* Print scheduler information
*/
void
-erts_print_scheduler_info(fmtfn_t to, void *to_arg, ErtsSchedulerData *esdp) {
+erts_print_scheduler_info(fmtfn_t to, void *to_arg, ErtsSchedulerData *esdp)
+{
int i;
erts_aint32_t flg;
- Process *p;
- erts_print(to, to_arg, "=scheduler:%u\n", esdp->no);
+ switch (esdp->type) {
+ case ERTS_SCHED_NORMAL:
+ erts_print(to, to_arg, "=scheduler:%u\n", esdp->no);
+ break;
+ case ERTS_SCHED_DIRTY_CPU:
+ erts_print(to, to_arg, "=dirty_cpu_scheduler:%u\n",
+ (esdp->dirty_no + erts_no_schedulers));
+ break;
+ case ERTS_SCHED_DIRTY_IO:
+ erts_print(to, to_arg, "=dirty_io_scheduler:%u\n",
+ (esdp->dirty_no + erts_no_schedulers + erts_no_dirty_cpu_schedulers));
+ break;
+ default:
+ erts_print(to, to_arg, "=unknown_scheduler_type:%u\n", esdp->type);
+ break;
+ }
flg = erts_atomic32_read_dirty(&esdp->ssi->flags);
erts_print(to, to_arg, "Scheduler Sleep Info Flags: ");
@@ -13443,10 +13237,24 @@ erts_print_scheduler_info(fmtfn_t to, void *to_arg, ErtsSchedulerData *esdp) {
}
erts_print(to, to_arg, "\n");
- erts_print(to, to_arg, "Current Port: ");
- if (esdp->current_port)
- erts_print(to, to_arg, "%T", esdp->current_port->common.id);
- erts_print(to, to_arg, "\n");
+ if (esdp->type == ERTS_SCHED_NORMAL) {
+ erts_print(to, to_arg, "Current Port: ");
+ if (esdp->current_port)
+ erts_print(to, to_arg, "%T", esdp->current_port->common.id);
+ erts_print(to, to_arg, "\n");
+
+ erts_print_run_queue_info(to, to_arg, esdp->run_queue);
+ }
+
+ /* This *MUST* to be the last information in scheduler block */
+ print_current_process_info(to, to_arg, esdp);
+}
+
+void erts_print_run_queue_info(fmtfn_t to, void *to_arg,
+ ErtsRunQueue *run_queue)
+{
+ erts_aint32_t flg;
+ int i;
for (i = 0; i < ERTS_NO_PROC_PRIO_LEVELS; i++) {
erts_print(to, to_arg, "Run Queue ");
@@ -13468,12 +13276,12 @@ erts_print_scheduler_info(fmtfn_t to, void *to_arg, ErtsSchedulerData *esdp) {
break;
}
erts_print(to, to_arg, "Length: %d\n",
- erts_atomic32_read_dirty(&esdp->run_queue->procs.prio_info[i].len));
+ erts_atomic32_read_dirty(&run_queue->procs.prio_info[i].len));
}
erts_print(to, to_arg, "Run Queue Port Length: %d\n",
- erts_atomic32_read_dirty(&esdp->run_queue->ports.info.len));
+ erts_atomic32_read_dirty(&run_queue->ports.info.len));
- flg = erts_atomic32_read_dirty(&esdp->run_queue->flags);
+ flg = erts_atomic32_read_dirty(&run_queue->flags);
erts_print(to, to_arg, "Run Queue Flags: ");
for (i = 0; i < ERTS_RUNQ_FLG_MAX && flg; i++) {
erts_aint32_t chk = (1 << i);
@@ -13540,9 +13348,15 @@ erts_print_scheduler_info(fmtfn_t to, void *to_arg, ErtsSchedulerData *esdp) {
}
}
erts_print(to, to_arg, "\n");
+}
+
+
+static void print_current_process_info(fmtfn_t to, void *to_arg,
+ ErtsSchedulerData* esdp)
+{
+ Process *p = esdp->current_process;
+ erts_aint32_t flg;
- /* This *MUST* to be the last information in scheduler block */
- p = esdp->current_process;
erts_print(to, to_arg, "Current Process: ");
if (esdp->current_process && !(ERTS_TRACE_FLAGS(p) & F_SENSITIVE)) {
flg = erts_atomic32_read_dirty(&p->state);
diff --git a/erts/emulator/beam/erl_process.h b/erts/emulator/beam/erl_process.h
index 66d7848f89..e2aa1d9f84 100644
--- a/erts/emulator/beam/erl_process.h
+++ b/erts/emulator/beam/erl_process.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1996-2017. All Rights Reserved.
+ * Copyright Ericsson AB 1996-2018. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -51,7 +51,7 @@ typedef struct process Process;
#include "erl_process_dict.h"
#include "erl_node_container_utils.h"
#include "erl_node_tables.h"
-#include "erl_monitors.h"
+#include "erl_monitor_link.h"
#include "erl_hl_timer.h"
#include "erl_time.h"
#include "erl_atom_table.h"
@@ -75,8 +75,6 @@ typedef struct process Process;
#include "erl_thr_progress.h"
#undef ERL_THR_PROGRESS_TSD_TYPE_ONLY
-struct ErtsNodesMonitor_;
-
#define ERTS_HAVE_SCHED_UTIL_BALANCING_SUPPORT_OPT 0
#define ERTS_HAVE_SCHED_UTIL_BALANCING_SUPPORT 0
@@ -105,13 +103,13 @@ struct saved_calls {
};
extern Export exp_send, exp_receive, exp_timeout;
-extern int erts_sched_compact_load;
-extern int erts_sched_balance_util;
-extern Uint erts_no_schedulers;
-extern Uint erts_no_total_schedulers;
-extern Uint erts_no_dirty_cpu_schedulers;
-extern Uint erts_no_dirty_io_schedulers;
-extern Uint erts_no_run_queues;
+extern int ERTS_WRITE_UNLIKELY(erts_sched_compact_load);
+extern int ERTS_WRITE_UNLIKELY(erts_sched_balance_util);
+extern Uint ERTS_WRITE_UNLIKELY(erts_no_schedulers);
+extern Uint ERTS_WRITE_UNLIKELY(erts_no_total_schedulers);
+extern Uint ERTS_WRITE_UNLIKELY(erts_no_dirty_cpu_schedulers);
+extern Uint ERTS_WRITE_UNLIKELY(erts_no_dirty_io_schedulers);
+extern Uint ERTS_WRITE_UNLIKELY(erts_no_run_queues);
extern int erts_sched_thread_suggested_stack_size;
extern int erts_dcpu_sched_thread_suggested_stack_size;
extern int erts_dio_sched_thread_suggested_stack_size;
@@ -244,6 +242,9 @@ extern int erts_dio_sched_thread_suggested_stack_size;
(erts_aint32_t) (MSK), \
(erts_aint32_t) (FLGS)))
+#define ERTS_RUNQ_POINTER_MASK (~((erts_aint_t) 3))
+#define ERTS_RUNQ_BOUND_FLAG ((erts_aint_t) 1)
+
typedef enum {
ERTS_SCHDLR_SSPND_DONE_MSCHED_BLOCKED,
ERTS_SCHDLR_SSPND_DONE_NMSCHED_BLOCKED,
@@ -308,7 +309,6 @@ typedef enum {
ERTS_SSI_AUX_WORK_ASYNC_READY_CLEAN_IX,
ERTS_SSI_AUX_WORK_MISC_THR_PRGR_IX,
ERTS_SSI_AUX_WORK_MISC_IX,
- ERTS_SSI_AUX_WORK_PENDING_EXITERS_IX,
ERTS_SSI_AUX_WORK_SET_TMO_IX,
ERTS_SSI_AUX_WORK_MSEG_CACHE_CHECK_IX,
ERTS_SSI_AUX_WORK_YIELD_IX,
@@ -342,8 +342,6 @@ typedef enum {
(((erts_aint32_t) 1) << ERTS_SSI_AUX_WORK_MISC_THR_PRGR_IX)
#define ERTS_SSI_AUX_WORK_MISC \
(((erts_aint32_t) 1) << ERTS_SSI_AUX_WORK_MISC_IX)
-#define ERTS_SSI_AUX_WORK_PENDING_EXITERS \
- (((erts_aint32_t) 1) << ERTS_SSI_AUX_WORK_PENDING_EXITERS_IX)
#define ERTS_SSI_AUX_WORK_SET_TMO \
(((erts_aint32_t) 1) << ERTS_SSI_AUX_WORK_SET_TMO_IX)
#define ERTS_SSI_AUX_WORK_MSEG_CACHE_CHECK \
@@ -402,9 +400,12 @@ typedef struct {
} ErtsRunPrioQueue;
typedef enum {
- ERTS_SCHED_NORMAL,
- ERTS_SCHED_DIRTY_CPU,
- ERTS_SCHED_DIRTY_IO
+ ERTS_SCHED_NORMAL = 0,
+ ERTS_SCHED_DIRTY_CPU = 1,
+ ERTS_SCHED_DIRTY_IO = 2,
+
+ ERTS_SCHED_TYPE_FIRST = ERTS_SCHED_NORMAL,
+ ERTS_SCHED_TYPE_LAST = ERTS_SCHED_DIRTY_IO
} ErtsSchedType;
typedef struct ErtsSchedulerData_ ErtsSchedulerData;
@@ -488,7 +489,6 @@ struct ErtsRunQueue_ {
int wakeup_other_reds;
struct {
- ErtsProcList *pending_exiters;
Uint context_switches;
Uint reductions;
@@ -522,7 +522,7 @@ typedef union {
char align[ERTS_ALC_CACHE_LINE_ALIGN_SIZE(sizeof(ErtsRunQueue))];
} ErtsAlignedRunQueue;
-extern ErtsAlignedRunQueue *erts_aligned_run_queues;
+extern ErtsAlignedRunQueue * ERTS_WRITE_UNLIKELY(erts_aligned_run_queues);
#define ERTS_PROC_REDUCTIONS_EXECUTED(SD, RQ, PRIO, REDS, AREDS)\
do { \
@@ -593,6 +593,7 @@ typedef struct {
ErtsDelayedAuxWorkWakeupJob *job;
} delayed_wakeup;
struct {
+ ErtsAlcuBlockscanYieldData alcu_blockscan;
ErtsEtsAllYieldData ets_all;
/* Other yielding operations... */
} yield;
@@ -637,6 +638,7 @@ struct ErtsSchedulerData_ {
ErtsSchedType type;
Uint no; /* Scheduler number for normal schedulers */
Uint dirty_no; /* Scheduler number for dirty schedulers */
+ struct enif_environment_t *current_nif;
Process *dirty_shadow_process;
Port *current_port;
ErtsRunQueue *run_queue;
@@ -675,9 +677,9 @@ typedef union {
char align[ERTS_ALC_CACHE_LINE_ALIGN_SIZE(sizeof(ErtsSchedulerData))];
} ErtsAlignedSchedulerData;
-extern ErtsAlignedSchedulerData *erts_aligned_scheduler_data;
-extern ErtsAlignedSchedulerData *erts_aligned_dirty_cpu_scheduler_data;
-extern ErtsAlignedSchedulerData *erts_aligned_dirty_io_scheduler_data;
+extern ErtsAlignedSchedulerData * ERTS_WRITE_UNLIKELY(erts_aligned_scheduler_data);
+extern ErtsAlignedSchedulerData * ERTS_WRITE_UNLIKELY(erts_aligned_dirty_cpu_scheduler_data);
+extern ErtsAlignedSchedulerData * ERTS_WRITE_UNLIKELY(erts_aligned_dirty_io_scheduler_data);
#if defined(ERTS_ENABLE_LOCK_CHECK)
@@ -983,14 +985,10 @@ struct process {
Process *next; /* Pointer to next process in run queue */
- struct ErtsNodesMonitor_ *nodes_monitors;
-
- ErtsSuspendMonitor *suspend_monitors; /* Processes suspended by
- this process via
- erlang:suspend_process/1 */
-
- ErlMessageQueue msg; /* Message queue */
+ ErtsMonitor *suspend_monitors; /* Processes suspended by this process via
+ erlang:suspend_process/1 */
+ ErtsSignalPrivQueues sig_qs; /* Signal queues */
ErtsBifTimers *bif_timers; /* Bif timers aiming at this process */
ProcDict *dictionary; /* Process dictionary, may be NULL */
@@ -1019,7 +1017,6 @@ struct process {
* Information mainly for post-mortem use (erl crash dump).
*/
Eterm parent; /* Pid of process that created this process. */
- erts_approx_time_t approx_started; /* Time when started. */
Uint32 static_flags; /* Flags that do *not* change */
@@ -1050,9 +1047,8 @@ struct process {
erts_atomic32_t state; /* Process state flags (see ERTS_PSFLG_*) */
erts_atomic32_t dirty_state; /* Process dirty state flags (see ERTS_PDSFLG_*) */
- ErlMessageInQueue msg_inq;
+ ErtsSignalInQueue sig_inq;
ErlTraceMessageQueue *trace_msg_q;
- ErtsPendExit pending_exit;
erts_proc_lock_t lock;
ErtsSchedulerData *scheduler_data;
Eterm suspendee;
@@ -1086,6 +1082,7 @@ struct process {
#endif
};
+extern Eterm erts_init_process_id; /* pid of init process */
extern const Process erts_invalid_process;
#ifdef CHECK_FOR_HOLES
@@ -1162,20 +1159,20 @@ void erts_check_for_holes(Process* p);
#define ERTS_PSFLG_IN_PRQ_LOW ERTS_PSFLG_BIT(3)
#define ERTS_PSFLG_FREE ERTS_PSFLG_BIT(4)
#define ERTS_PSFLG_EXITING ERTS_PSFLG_BIT(5)
-#define ERTS_PSFLG_PENDING_EXIT ERTS_PSFLG_BIT(6)
+#define ERTS_PSFLG_UNUSED ERTS_PSFLG_BIT(6)
#define ERTS_PSFLG_ACTIVE ERTS_PSFLG_BIT(7)
#define ERTS_PSFLG_IN_RUNQ ERTS_PSFLG_BIT(8)
#define ERTS_PSFLG_RUNNING ERTS_PSFLG_BIT(9)
#define ERTS_PSFLG_SUSPENDED ERTS_PSFLG_BIT(10)
#define ERTS_PSFLG_GC ERTS_PSFLG_BIT(11)
-#define ERTS_PSFLG_BOUND ERTS_PSFLG_BIT(12)
-#define ERTS_PSFLG_TRAP_EXIT ERTS_PSFLG_BIT(13)
+#define ERTS_PSFLG_SYS_TASKS ERTS_PSFLG_BIT(12)
+#define ERTS_PSFLG_SIG_IN_Q ERTS_PSFLG_BIT(13)
#define ERTS_PSFLG_ACTIVE_SYS ERTS_PSFLG_BIT(14)
#define ERTS_PSFLG_RUNNING_SYS ERTS_PSFLG_BIT(15)
#define ERTS_PSFLG_PROXY ERTS_PSFLG_BIT(16)
#define ERTS_PSFLG_DELAYED_SYS ERTS_PSFLG_BIT(17)
#define ERTS_PSFLG_OFF_HEAP_MSGQ ERTS_PSFLG_BIT(18)
-#define ERTS_PSFLG_ON_HEAP_MSGQ ERTS_PSFLG_BIT(19)
+#define ERTS_PSFLG_SIG_Q ERTS_PSFLG_BIT(19)
#define ERTS_PSFLG_DIRTY_CPU_PROC ERTS_PSFLG_BIT(20)
#define ERTS_PSFLG_DIRTY_IO_PROC ERTS_PSFLG_BIT(21)
#define ERTS_PSFLG_DIRTY_ACTIVE_SYS ERTS_PSFLG_BIT(22)
@@ -1194,7 +1191,6 @@ void erts_check_for_holes(Process* p);
| ERTS_PSFLG_IN_PRQ_LOW)
#define ERTS_PSFLGS_VOLATILE_HEAP (ERTS_PSFLG_EXITING \
- | ERTS_PSFLG_PENDING_EXIT \
| ERTS_PSFLG_DIRTY_RUNNING \
| ERTS_PSFLG_DIRTY_RUNNING_SYS)
@@ -1259,7 +1255,24 @@ void erts_check_for_holes(Process* p);
#define SEQ_TRACE_T_SENDER(token) (*(tuple_val(token) + 4))
#define SEQ_TRACE_T_LASTCNT(token) (*(tuple_val(token) + 5))
+#ifdef USE_VM_PROBES
+/* The dtrace probe for seq_trace only supports 'int' labels, so we represent
+ * all values that won't fit into a 32-bit signed integer as ERTS_SINT32_MIN
+ * (bigints, tuples, etc). */
+
+#define SEQ_TRACE_T_DTRACE_LABEL(token) \
+ DTRACE_SEQ_TRACE_LABEL__(SEQ_TRACE_T_LABEL(token))
+
+#define DTRACE_SEQ_TRACE_LABEL__(label_term) \
+ (is_small((label_term)) ? \
+ ((signed_val((label_term)) <= ERTS_SINT32_MAX && \
+ signed_val((label_term)) >= ERTS_SINT32_MIN) ? \
+ signed_val((label_term)) : ERTS_SINT32_MIN) \
+ : ERTS_SINT32_MIN)
+#endif
+
/*
+
* Possible flags for the flags field in ErlSpawnOpts below.
*/
@@ -1270,7 +1283,7 @@ void erts_check_for_holes(Process* p);
#define SPO_OFF_HEAP_MSGQ 16
#define SPO_ON_HEAP_MSGQ 32
-extern int erts_default_spo_flags;
+extern int ERTS_WRITE_UNLIKELY(erts_default_spo_flags);
/*
* The following struct contains options for a process to be spawned.
@@ -1326,10 +1339,10 @@ extern erts_rwmtx_t erts_cpu_bind_rwmtx;
** erts_system_monitor must be != NIL, to allow testing on just
** the erts_system_monitor_* variables.
*/
-extern Eterm erts_system_monitor;
-extern Uint erts_system_monitor_long_gc;
-extern Uint erts_system_monitor_long_schedule;
-extern Uint erts_system_monitor_large_heap;
+extern Eterm ERTS_WRITE_UNLIKELY(erts_system_monitor);
+extern Uint ERTS_WRITE_UNLIKELY(erts_system_monitor_long_gc);
+extern Uint ERTS_WRITE_UNLIKELY(erts_system_monitor_long_schedule);
+extern Uint ERTS_WRITE_UNLIKELY(erts_system_monitor_large_heap);
struct erts_system_monitor_flags_t {
unsigned int busy_port : 1;
unsigned int busy_dist_port : 1;
@@ -1377,6 +1390,10 @@ extern int erts_system_profile_ts_type;
#define F_DIRTY_MAJOR_GC (1 << 23) /* Dirty major GC scheduled */
#define F_DIRTY_MINOR_GC (1 << 24) /* Dirty minor GC scheduled */
#define F_HIBERNATED (1 << 25) /* Hibernated */
+#define F_LOCAL_SIGS_ONLY (1 << 26)
+#define F_TRAP_EXIT (1 << 27) /* Trapping exit */
+#define F_DEFERRED_SAVED_LAST (1 << 28)
+#define F_DELAYED_PSIGQS_LEN (1 << 29)
/*
* F_DISABLE_GC and F_DELAY_GC are similar. Both will prevent
@@ -1442,7 +1459,7 @@ extern int erts_system_profile_ts_type;
| F_TRACE_ARITY_ONLY | F_TRACE_RETURN_TO \
| F_TRACE_SILENT | F_TRACE_SCHED_PROCS | F_TRACE_PORTS \
| F_TRACE_SCHED_PORTS | F_TRACE_SCHED_NO \
- | F_TRACE_SCHED_EXIT)
+ | F_TRACE_SCHED_EXIT )
#define ERTS_TRACEE_MODIFIER_FLAGS \
@@ -1455,6 +1472,14 @@ extern int erts_system_profile_ts_type;
#define SEQ_TRACE_FLAG(N) (1 << (ERTS_TRACE_TS_TYPE_BITS + (N)))
+#define ERTS_SIG_ENABLE_TRACE_FLAGS \
+ ( F_TRACE_RECEIVE | F_TRACE_PROCS)
+
+/*
+ * F_TRACE_RECEIVE is always enabled/disable via signaling.
+ * F_TRACE_PROCS enable/disable F_TRACE_PROCS_SIG via signaling.
+ */
+
/* Sequential trace flags */
/* SEQ_TRACE_TIMESTAMP_MASK is a bit-field */
@@ -1481,10 +1506,6 @@ extern int erts_system_profile_ts_type;
#define DT_UTAG_FLAGS(P) ((P)->dt_utag_flags)
#endif
-/* Option flags to erts_send_exit_signal() */
-#define ERTS_XSIG_FLG_IGN_KILL (((Uint32) 1) << 0)
-#define ERTS_XSIG_FLG_NO_IGN_NORMAL (((Uint32) 1) << 1)
-
#define CANCEL_TIMER(P) \
do { \
if ((P)->flags & (F_INSLPQUEUE|F_TIMO)) { \
@@ -1546,6 +1567,7 @@ Uint64 erts_step_proc_interval(void);
ErtsProcList *erts_proclist_create(Process *);
ErtsProcList *erts_proclist_copy(ErtsProcList *);
void erts_proclist_destroy(ErtsProcList *);
+void erts_proclist_dump(fmtfn_t to, void *to_arg, ErtsProcList*);
ERTS_GLB_INLINE int erts_proclist_same(ErtsProcList *, Process *);
ERTS_GLB_INLINE void erts_proclist_store_first(ErtsProcList **, ErtsProcList *);
@@ -1713,9 +1735,9 @@ ERTS_GLB_INLINE int erts_proclist_is_last(ErtsProcList *list,
#endif
-int erts_sched_set_wakeup_other_thresold(char *str);
-int erts_sched_set_wakeup_other_type(char *str);
-int erts_sched_set_busy_wait_threshold(char *str);
+int erts_sched_set_wakeup_other_threshold(ErtsSchedType sched_type, char *str);
+int erts_sched_set_wakeup_other_type(ErtsSchedType sched_type, char *str);
+int erts_sched_set_busy_wait_threshold(ErtsSchedType sched_type, char *str);
int erts_sched_set_wake_cleanup_threshold(char *);
void erts_schedule_thr_prgr_later_op(void (*)(void *),
@@ -1730,6 +1752,7 @@ struct db_fixation;
void erts_schedule_ets_free_fixation(Eterm pid, struct db_fixation*);
void erts_schedule_flush_trace_messages(Process *proc, int force_on_proc);
int erts_flush_trace_messages(Process *c_p, ErtsProcLocks locks);
+int erts_sig_prio(Eterm pid, int prio);
#if defined(ERTS_ENABLE_LOCK_CHECK)
int erts_dbg_check_halloc_lock(Process *p);
@@ -1778,8 +1801,10 @@ ErtsRunQueue *erts_schedid2runq(Uint);
Process *erts_schedule(ErtsSchedulerData *, Process*, int);
void erts_schedule_misc_op(void (*)(void *), void *);
Eterm erl_create_process(Process*, Eterm, Eterm, Eterm, ErlSpawnOpts*);
+void erts_set_self_exiting(Process *, Eterm);
void erts_do_exit_process(Process*, Eterm);
void erts_continue_exit_process(Process *);
+void erts_proc_exit_link(Process *, ErtsLink *, Uint16, Eterm, Eterm);
/* Begin System profile */
Uint erts_runnable_process_count(void);
/* End System profile */
@@ -1792,10 +1817,22 @@ void erts_stack_dump(fmtfn_t to, void *to_arg, Process *);
void erts_limited_stack_trace(fmtfn_t to, void *to_arg, Process *);
void erts_program_counter_info(fmtfn_t to, void *to_arg, Process *);
void erts_print_scheduler_info(fmtfn_t to, void *to_arg, ErtsSchedulerData *esdp);
+void erts_print_run_queue_info(fmtfn_t, void *to_arg, ErtsRunQueue*);
void erts_dump_extended_process_state(fmtfn_t to, void *to_arg, erts_aint32_t psflg);
void erts_dump_process_state(fmtfn_t to, void *to_arg, erts_aint32_t psflg);
+Eterm erts_process_info(Process *c_p, ErtsHeapFactory *hfact,
+ Process *rp, ErtsProcLocks rp_locks,
+ int *item_ix, int item_ix_len,
+ int flags, Uint reserve_size, Uint *reds);
+
+typedef struct {
+ Process *c_p;
+ Eterm reason;
+} ErtsProcExitContext;
+void erts_proc_exit_handle_monitor(ErtsMonitor *mon, void *vctxt);
+void erts_proc_exit_handle_link(ErtsLink *lnk, void *vctxt);
-Eterm erts_get_process_priority(Process *p);
+Eterm erts_get_process_priority(erts_aint32_t state);
Eterm erts_set_process_priority(Process *p, Eterm prio);
Uint erts_get_total_context_switches(void);
@@ -1813,18 +1850,6 @@ void erts_suspend(Process*, ErtsProcLocks, Port*);
void erts_resume(Process*, ErtsProcLocks);
int erts_resume_processes(ErtsProcList *);
-int erts_send_exit_signal(Process *,
- Eterm,
- Process *,
- ErtsProcLocks *,
- Eterm,
- Eterm,
- Process *,
- Uint32);
-void erts_handle_pending_exit(Process *, ErtsProcLocks);
-#define ERTS_PROC_PENDING_EXIT(P) \
- (ERTS_PSFLG_PENDING_EXIT & erts_atomic32_read_acqb(&(P)->state))
-
void erts_deep_process_dump(fmtfn_t, void *);
Eterm erts_get_reader_groups_map(Process *c_p);
@@ -1837,7 +1862,7 @@ Uint erts_debug_nbalance(void);
int erts_debug_wait_completed(Process *c_p, int flags);
-Uint erts_process_memory(Process *c_p, int incl_msg_inq);
+Uint erts_process_memory(Process *c_p, int include_sigs_in_transit);
#ifdef ERTS_DO_VERIFY_UNUSED_TEMP_ALLOC
# define ERTS_VERIFY_UNUSED_TEMP_ALLOC(P) \
@@ -1856,6 +1881,8 @@ do { \
ErtsSchedulerData *erts_get_scheduler_data(void);
void erts_schedule_process(Process *, erts_aint32_t, ErtsProcLocks);
+erts_aint32_t erts_proc_sys_schedule(Process *p, erts_aint32_t state,
+ erts_aint32_t enable_flag);
ERTS_GLB_INLINE void erts_proc_notify_new_message(Process *p, ErtsProcLocks locks);
ERTS_GLB_INLINE void erts_schedule_dirty_sys_execution(Process *c_p);
@@ -1887,8 +1914,7 @@ erts_schedule_dirty_sys_execution(Process *c_p)
/* Don't set dirty-active-sys if we are about to exit... */
while (!(a & (ERTS_PSFLG_DIRTY_ACTIVE_SYS
- | ERTS_PSFLG_EXITING
- | ERTS_PSFLG_PENDING_EXIT))) {
+ | ERTS_PSFLG_EXITING))) {
e = a;
n = a | ERTS_PSFLG_DIRTY_ACTIVE_SYS;
a = erts_atomic32_cmpxchg_mb(&c_p->state, n, e);
@@ -2167,7 +2193,12 @@ ERTS_GLB_INLINE int erts_is_scheduler_bound(ErtsSchedulerData *esdp);
ERTS_GLB_INLINE Process *erts_get_current_process(void);
ERTS_GLB_INLINE Eterm erts_get_current_pid(void);
ERTS_GLB_INLINE Uint erts_get_scheduler_id(void);
-ERTS_GLB_INLINE ErtsRunQueue *erts_get_runq_proc(Process *p);
+ERTS_GLB_INLINE void erts_init_runq_proc(Process *p, ErtsRunQueue *rq, int bnd);
+ERTS_GLB_INLINE ErtsRunQueue *erts_set_runq_proc(Process *p, ErtsRunQueue *rq, int *boundp);
+ERTS_GLB_INLINE int erts_try_change_runq_proc(Process *p, ErtsRunQueue *rq);
+ERTS_GLB_INLINE ErtsRunQueue *erts_bind_runq_proc(Process *p, int bind);
+ERTS_GLB_INLINE int erts_proc_runq_is_bound(Process *p);
+ERTS_GLB_INLINE ErtsRunQueue *erts_get_runq_proc(Process *p, int *boundp);
ERTS_GLB_INLINE ErtsRunQueue *erts_get_runq_current(ErtsSchedulerData *esdp);
ERTS_GLB_INLINE void erts_runq_lock(ErtsRunQueue *rq);
ERTS_GLB_INLINE int erts_runq_trylock(ErtsRunQueue *rq);
@@ -2247,11 +2278,146 @@ Uint erts_get_scheduler_id(void)
return esdp ? esdp->no : (Uint) 0;
}
+/**
+ * Init run-queue of process.
+ *
+ * @param p[in,out] Process
+ * @param rq[in] Run-queue that process will be assigned to
+ * @param bnd[in,out] If non-zero binds process to run-queue.
+ */
+
+ERTS_GLB_INLINE void
+erts_init_runq_proc(Process *p, ErtsRunQueue *rq, int bnd)
+{
+ erts_aint_t rqint = (erts_aint_t) rq;
+ if (bnd)
+ rqint |= ERTS_RUNQ_BOUND_FLAG;
+ erts_atomic_init_nob(&p->run_queue, rqint);
+}
+
+/**
+ * Forcibly set run-queue of process.
+ *
+ * @param p[in,out] Process
+ * @param rq[in] Run-queue that process will be assigned to
+ * @param bndp[in,out] Pointer to integer. On input non-zero
+ * value causes the process to be bound to
+ * the run-queue. On output, indicating
+ * wether process previously was bound or
+ * not.
+ * @return Previous run-queue.
+ */
+
+ERTS_GLB_INLINE ErtsRunQueue *
+erts_set_runq_proc(Process *p, ErtsRunQueue *rq, int *bndp)
+{
+ erts_aint_t rqint = (erts_aint_t) rq;
+ ASSERT(bndp);
+ ASSERT(rq);
+ if (*bndp)
+ rqint |= ERTS_RUNQ_BOUND_FLAG;
+ rqint = erts_atomic_xchg_nob(&p->run_queue, rqint);
+ *bndp = (int) (rqint & ERTS_RUNQ_BOUND_FLAG);
+ return (ErtsRunQueue *) (rqint & ERTS_RUNQ_POINTER_MASK);
+}
+
+/**
+ * Try to change run-queue assignment of a process.
+ *
+ * @param p[in,out] Process
+ * @param rq[int] Run-queue that process will be assigned to
+ * @return Non-zero if the run-queue assignment was
+ * successfully changed.
+ */
+
+ERTS_GLB_INLINE int
+erts_try_change_runq_proc(Process *p, ErtsRunQueue *rq)
+{
+ erts_aint_t old_rqint, new_rqint;
+
+ ASSERT(rq);
+
+ new_rqint = (erts_aint_t) rq;
+ old_rqint = (erts_aint_t) erts_atomic_read_nob(&p->run_queue);
+ while (1) {
+ erts_aint_t act_rqint;
+
+ if (old_rqint & ERTS_RUNQ_BOUND_FLAG)
+ return 0;
+
+ act_rqint = erts_atomic_cmpxchg_nob(&p->run_queue,
+ new_rqint,
+ old_rqint);
+ if (act_rqint == old_rqint)
+ return !0;
+ }
+}
+
+/**
+ *
+ * Bind or unbind process to/from currently used run-queue.
+ *
+ * @param p Process
+ * @param bind Bind if non-zero; otherwise unbind
+ * @return Pointer to previously bound run-queue,
+ * or NULL if previously unbound
+ */
+
+ERTS_GLB_INLINE ErtsRunQueue *
+erts_bind_runq_proc(Process *p, int bind)
+{
+ erts_aint_t rqint;
+ if (bind)
+ rqint = erts_atomic_read_bor_nob(&p->run_queue,
+ ERTS_RUNQ_BOUND_FLAG);
+ else
+ rqint = erts_atomic_read_band_nob(&p->run_queue,
+ ~ERTS_RUNQ_BOUND_FLAG);
+ if (rqint & ERTS_RUNQ_BOUND_FLAG)
+ return (ErtsRunQueue *) (rqint & ERTS_RUNQ_POINTER_MASK);
+ else
+ return NULL;
+}
+
+/**
+ * Determine wether a process is bound to a run-queue or not.
+ *
+ * @return Returns a non-zero value if bound,
+ * and zero of not bound.
+ */
+
+ERTS_GLB_INLINE int
+erts_proc_runq_is_bound(Process *p)
+{
+ erts_aint_t rqint = erts_atomic_read_nob(&p->run_queue);
+ return (int) (rqint & ERTS_RUNQ_BOUND_FLAG);
+}
+
+/**
+ * Set run-queue of process.
+ *
+ * @param p[in,out] Process
+ * @param bndp[out] Pointer to integer. If non-NULL pointer,
+ * the integer will be set to a non-zero
+ * value if the process is bound to the
+ * run-queue.
+ * @return Pointer to the normal run-queue that
+ * the process currently is assigend to.
+ * A process is always assigned to a
+ * normal run-queue.
+ */
+
ERTS_GLB_INLINE ErtsRunQueue *
-erts_get_runq_proc(Process *p)
+erts_get_runq_proc(Process *p, int *bndp)
{
- ASSERT(ERTS_AINT_NULL != erts_atomic_read_nob(&p->run_queue));
- return (ErtsRunQueue *) erts_atomic_read_nob(&p->run_queue);
+ erts_aint_t rqint = erts_atomic_read_nob(&p->run_queue);
+ ErtsRunQueue *rq;
+ if (bndp)
+ *bndp = (int) (rqint & ERTS_RUNQ_BOUND_FLAG);
+ rqint &= ERTS_RUNQ_POINTER_MASK;
+ rq = (ErtsRunQueue *) rqint;
+ ASSERT(rq);
+ return rq;
}
ERTS_GLB_INLINE ErtsRunQueue *
@@ -2435,6 +2601,9 @@ ERTS_TIME2REDS_IMPL__(ErtsMonotonicTime start, ErtsMonotonicTime end)
}
#endif
+Process *erts_try_lock_sig_free_proc(Eterm pid,
+ ErtsProcLocks locks,
+ erts_aint32_t *statep);
Process *erts_pid2proc_not_running(Process *,
ErtsProcLocks,
diff --git a/erts/emulator/beam/erl_process_dict.c b/erts/emulator/beam/erl_process_dict.c
index 3c80f0e0f6..38be3938cd 100644
--- a/erts/emulator/beam/erl_process_dict.c
+++ b/erts/emulator/beam/erl_process_dict.c
@@ -79,6 +79,8 @@
/* Array access macro */
#define ARRAY_GET(PDict, Index) (ASSERT((Index) < (PDict)->arraySize), \
(PDict)->data[Index])
+#define ARRAY_GET_PTR(PDict, Index) (ASSERT((Index) < (PDict)->arraySize), \
+ &(PDict)->data[Index])
#define ARRAY_PUT(PDict, Index, Val) (ASSERT((Index) < (PDict)->arraySize), \
(PDict)->data[Index] = (Val))
@@ -92,7 +94,7 @@ static void pd_hash_erase_all(Process *p);
static Eterm pd_hash_get_with_hval(Process *p, Eterm bucket, Eterm id);
static Eterm pd_hash_get_keys(Process *p, Eterm value);
static Eterm pd_hash_get_all_keys(Process *p, ProcDict *pd);
-static Eterm pd_hash_get_all(Process *p, ProcDict *pd);
+static Eterm pd_hash_get_all(Process *p, ProcDict *pd, int keep_dict);
static Eterm pd_hash_put(Process *p, Eterm id, Eterm value);
static void shrink(Process *p, Eterm* ret);
@@ -237,39 +239,70 @@ erts_erase_dicts(Process *p)
/*
* Called from process_info/1,2.
*/
-Eterm erts_dictionary_copy(Process *p, ProcDict *pd)
+Eterm erts_dictionary_copy(ErtsHeapFactory *hfact, ProcDict *pd, Uint reserve_size)
{
- Eterm* hp;
- Eterm* heap_start;
- Eterm res = NIL;
- Eterm tmp, tmp2;
+ Eterm res;
unsigned int i, num;
+ Uint *sz;
+ Uint szi, rsz = reserve_size;
- if (pd == NULL) {
- return res;
- }
+ if (pd == NULL)
+ return NIL;
PD_CHECK(pd);
num = HASH_RANGE(pd);
- heap_start = hp = (Eterm *) erts_alloc(ERTS_ALC_T_TMP,
- sizeof(Eterm) * pd->numElements * 2);
- for (i = 0; i < num; ++i) {
- tmp = ARRAY_GET(pd, i);
+ sz = (Uint *) erts_alloc(ERTS_ALC_T_TMP, sizeof(Uint) * pd->numElements);
+
+ for (i = 0, szi = 0; i < num; ++i) {
+ Eterm tmp = ARRAY_GET(pd, i);
if (is_boxed(tmp)) {
+ Uint size;
ASSERT(is_tuple(tmp));
- res = CONS(hp, tmp, res);
- hp += 2;
- } else if (is_list(tmp)) {
+ size = size_object(tmp) + 2;
+ sz[szi++] = size;
+ rsz += size;
+ }
+ else if (is_list(tmp)) {
while (tmp != NIL) {
- tmp2 = TCAR(tmp);
- res = CONS(hp, tmp2, res);
- hp += 2;
+ Uint size = size_object(TCAR(tmp)) + 2;
+ sz[szi++] = size;
+ rsz += size;
+ tmp = TCDR(tmp);
+ }
+ }
+ }
+
+ res = NIL;
+
+ for (i = 0, szi = 0; i < num; ++i) {
+ Eterm tmp = ARRAY_GET(pd, i);
+ if (is_boxed(tmp)) {
+ Uint size;
+ Eterm el, *hp;
+ ASSERT(is_tuple(tmp));
+ size = sz[szi++];
+ rsz -= size;
+ hp = erts_produce_heap(hfact, size, rsz);
+ el = copy_struct(tmp, size-2, &hp, hfact->off_heap);
+ res = CONS(hp, el, res);
+ }
+ else if (is_list(tmp)) {
+ while (tmp != NIL) {
+ Uint size = sz[szi++];
+ Eterm el, *hp;
+ rsz -= size;
+ hp = erts_produce_heap(hfact, size, rsz);
+ el = copy_struct(TCAR(tmp), size-2, &hp, hfact->off_heap);
+ res = CONS(hp, el, res);
tmp = TCDR(tmp);
}
}
}
- res = copy_object(res, p);
- erts_free(ERTS_ALC_T_TMP, (void *) heap_start);
+
+ ASSERT(rsz == reserve_size);
+
+ erts_free(ERTS_ALC_T_TMP, sz);
+
return res;
}
@@ -281,7 +314,7 @@ BIF_RETTYPE get_0(BIF_ALIST_0)
{
Eterm ret;
PD_CHECK(BIF_P->dictionary);
- ret = pd_hash_get_all(BIF_P, BIF_P->dictionary);
+ ret = pd_hash_get_all(BIF_P, BIF_P->dictionary, 1);
PD_CHECK(BIF_P->dictionary);
BIF_RET(ret);
}
@@ -329,7 +362,7 @@ BIF_RETTYPE erase_0(BIF_ALIST_0)
{
Eterm ret;
PD_CHECK(BIF_P->dictionary);
- ret = pd_hash_get_all(BIF_P, BIF_P->dictionary);
+ ret = pd_hash_get_all(BIF_P, BIF_P->dictionary, 0);
pd_hash_erase_all(BIF_P);
PD_CHECK(BIF_P->dictionary);
BIF_RET(ret);
@@ -541,29 +574,46 @@ static Eterm pd_hash_get_keys(Process *p, Eterm value)
static Eterm
-pd_hash_get_all(Process *p, ProcDict *pd)
+pd_hash_get_all(Process *p, ProcDict *pd, int keep_dict)
{
Eterm* hp;
+ Eterm* tp;
Eterm res = NIL;
Eterm tmp, tmp2;
unsigned int i;
unsigned int num;
+ Uint need;
if (pd == NULL) {
return res;
}
num = HASH_RANGE(pd);
- hp = HAlloc(p, pd->numElements * 2);
-
+
+ /*
+ * If this is not erase/0, then must copy all key-value tuples
+ * as they may be mutated by put/2.
+ */
+ need = pd->numElements * (keep_dict ? 2+3 : 2);
+ hp = HAlloc(p, need);
+
for (i = 0; i < num; ++i) {
tmp = ARRAY_GET(pd, i);
if (is_boxed(tmp)) {
- ASSERT(is_tuple(tmp));
+ if (keep_dict) {
+ tp = tuple_val(tmp);
+ tmp = TUPLE2(hp, tp[1], tp[2]);
+ hp += 3;
+ }
res = CONS(hp, tmp, res);
hp += 2;
} else if (is_list(tmp)) {
while (tmp != NIL) {
tmp2 = TCAR(tmp);
+ if (keep_dict) {
+ tp = tuple_val(tmp2);
+ tmp2 = TUPLE2(hp, tp[1], tp[2]);
+ hp += 3;
+ }
res = CONS(hp, tmp2, res);
hp += 2;
tmp = TCDR(tmp);
@@ -577,11 +627,14 @@ static Eterm pd_hash_put(Process *p, Eterm id, Eterm value)
{
unsigned int hval;
Eterm *hp;
+ Eterm *tp;
+ Eterm *bucket;
Eterm tpl;
Eterm old;
+ Eterm old_val = am_undefined;
Eterm tmp;
int needed;
- int i = 0;
+ int new_key = 1;
#ifdef DEBUG
Eterm *hp_limit;
#endif
@@ -595,7 +648,8 @@ static Eterm pd_hash_put(Process *p, Eterm id, Eterm value)
p->dictionary->numElements = 0;
}
hval = pd_hash_value(p->dictionary, id);
- old = ARRAY_GET(p->dictionary, hval);
+ bucket = ARRAY_GET_PTR(p->dictionary, hval);
+ old = *bucket;
/*
* Calculate the number of heap words needed and garbage
@@ -603,32 +657,49 @@ static Eterm pd_hash_put(Process *p, Eterm id, Eterm value)
*/
needed = 3; /* {Key,Value} tuple */
if (is_boxed(old)) {
- /*
- * We don't want to compare keys twice, so we'll always
- * reserve the space for two CONS cells.
- */
- needed += 2+2;
+ ASSERT(is_tuple(old));
+ tp = tuple_val(old);
+ if (EQ(tp[1], id)) {
+ old_val = tp[2];
+ if (is_immed(value)) {
+ tp[2] = value; /* DESTRUCTIVE HEAP ASSIGNMENT */
+ return old_val;
+ }
+ new_key = 0;
+ }
+ else {
+ needed += 2+2;
+ }
} else if (is_list(old)) {
- i = 0;
- for (tmp = old; tmp != NIL && !EQ(tuple_val(TCAR(tmp))[1], id); tmp = TCDR(tmp)) {
- ++i;
- }
- if (is_nil(tmp)) {
- i = -1;
- needed += 2;
- } else {
- needed += 2*(i+1);
+ Eterm* prev_cdr = bucket;
+
+ needed += 2;
+ for (tmp = old; tmp != NIL; prev_cdr = &TCDR(tmp), tmp = *prev_cdr) {
+ tp = tuple_val(TCAR(tmp));
+ if (EQ(tp[1], id)) {
+ old_val = tp[2];
+ if (is_immed(value)) {
+ tp[2] = value; /* DESTRUCTIVE HEAP ASSIGNMENT */
+ return old_val;
+ }
+ new_key = 0;
+ /* Unlink old {Key,Value} from list */
+ *prev_cdr = TCDR(tmp); /* maybe DESTRUCTIVE HEAP ASSIGNMENT */
+ break;
+ }
}
}
if (HeapWordsLeft(p) < needed) {
Eterm root[3];
root[0] = id;
root[1] = value;
- root[2] = old;
+ root[2] = old_val;
erts_garbage_collect(p, needed, root, 3);
id = root[0];
value = root[1];
- old = root[2];
+ old_val = root[2];
+ ASSERT(bucket == ARRAY_GET_PTR(p->dictionary, hval));
+ old = *bucket;
}
#ifdef DEBUG
hp_limit = p->htop + needed;
@@ -644,66 +715,29 @@ static Eterm pd_hash_put(Process *p, Eterm id, Eterm value)
* Update the dictionary.
*/
if (is_nil(old)) {
- ARRAY_PUT(p->dictionary, hval, tpl);
- ++(p->dictionary->numElements);
+ *bucket = tpl;
} else if (is_boxed(old)) {
ASSERT(is_tuple(old));
- if (EQ(tuple_val(old)[1],id)) {
- ARRAY_PUT(p->dictionary, hval, tpl);
- return tuple_val(old)[2];
+ if (!new_key) {
+ ASSERT(EQ(tuple_val(old)[1],id));
+ *bucket = tpl;
+ return old_val;
} else {
hp = HeapOnlyAlloc(p, 4);
tmp = CONS(hp, old, NIL);
hp += 2;
- ++(p->dictionary->numElements);
- ARRAY_PUT(p->dictionary, hval, CONS(hp, tpl, tmp));
+ *bucket = CONS(hp, tpl, tmp);
hp += 2;
ASSERT(hp <= hp_limit);
}
} else if (is_list(old)) {
- if (i == -1) {
- /*
- * New key. Simply prepend the tuple to the beginning of the list.
- */
- hp = HeapOnlyAlloc(p, 2);
- ARRAY_PUT(p->dictionary, hval, CONS(hp, tpl, old));
- hp += 2;
- ASSERT(hp <= hp_limit);
- ++(p->dictionary->numElements);
- } else {
- /*
- * i = Number of CDRs to skip to reach the changed element in the list.
- *
- * Replace old value in list. To avoid pointers from the old generation
- * to the new, we must rebuild the list from the beginning up to and
- * including the changed element.
- */
- Eterm nlist;
- int j;
-
- hp = HeapOnlyAlloc(p, (i+1)*2);
-
- /* Find the list element to change. */
- for (j = 0, nlist = old; j < i; j++, nlist = TCDR(nlist)) {
- ;
- }
- ASSERT(EQ(tuple_val(TCAR(nlist))[1], id));
- nlist = TCDR(nlist); /* Unchanged part of list. */
-
- /* Rebuild list before the updated element. */
- for (tmp = old; i-- > 0; tmp = TCDR(tmp)) {
- nlist = CONS(hp, TCAR(tmp), nlist);
- hp += 2;
- }
- ASSERT(EQ(tuple_val(TCAR(tmp))[1], id));
-
- /* Put the updated element first in the new list. */
- nlist = CONS(hp, tpl, nlist);
- hp += 2;
- ASSERT(hp <= hp_limit);
- ARRAY_PUT(p->dictionary, hval, nlist);
- return tuple_val(TCAR(tmp))[2];
- }
+ /*
+ * Simply prepend the tuple to the beginning of the list.
+ */
+ hp = HeapOnlyAlloc(p, 2);
+ *bucket = CONS(hp, tpl, *bucket);
+ hp += 2;
+ ASSERT(hp <= hp_limit);
} else {
#ifdef DEBUG
erts_fprintf(stderr,
@@ -714,10 +748,13 @@ static Eterm pd_hash_put(Process *p, Eterm id, Eterm value)
erts_exit(ERTS_ERROR_EXIT, "Damaged process dictionary found during put/2.");
}
+
+ p->dictionary->numElements += new_key;
+
if (HASH_RANGE(p->dictionary) <= p->dictionary->numElements) {
grow(p);
}
- return am_undefined;
+ return old_val;
}
/*
diff --git a/erts/emulator/beam/erl_process_dict.h b/erts/emulator/beam/erl_process_dict.h
index ab58f3c239..b89b387f5a 100644
--- a/erts/emulator/beam/erl_process_dict.h
+++ b/erts/emulator/beam/erl_process_dict.h
@@ -40,7 +40,7 @@ void erts_erase_dicts(struct process *p);
void erts_dictionary_dump(fmtfn_t to, void *to_arg, ProcDict *pd);
void erts_deep_dictionary_dump(fmtfn_t to, void *to_arg,
ProcDict* pd, void (*cb)(fmtfn_t, void *, Eterm obj));
-Eterm erts_dictionary_copy(struct process *p, ProcDict *pd);
+Eterm erts_dictionary_copy(ErtsHeapFactory *hfact, ProcDict *pd, Uint reserve_size);
Eterm erts_pd_hash_get(struct process *p, Eterm id);
Uint32 erts_pd_make_hx(Eterm key);
diff --git a/erts/emulator/beam/erl_process_dump.c b/erts/emulator/beam/erl_process_dump.c
index 5a2c262ff1..243db4c734 100644
--- a/erts/emulator/beam/erl_process_dump.c
+++ b/erts/emulator/beam/erl_process_dump.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2003-2017. All Rights Reserved.
+ * Copyright Ericsson AB 2003-2018. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -32,8 +32,10 @@
#include "dist.h"
#include "beam_catches.h"
#include "erl_binary.h"
+#include "erl_map.h"
#define ERTS_WANT_EXTERNAL_TAGS
#include "external.h"
+#include "erl_proc_sig_queue.h"
#define PTR_FMT "%bpX"
#define ETERM_FMT "%beX"
@@ -50,7 +52,14 @@ static void stack_trace_dump(fmtfn_t to, void *to_arg, Eterm* sp);
static void print_function_from_pc(fmtfn_t to, void *to_arg, BeamInstr* x);
static void heap_dump(fmtfn_t to, void *to_arg, Eterm x);
static void dump_binaries(fmtfn_t to, void *to_arg, Binary* root);
+void erts_print_base64(fmtfn_t to, void *to_arg,
+ byte* src, Uint size);
static void dump_externally(fmtfn_t to, void *to_arg, Eterm term);
+static void mark_literal(Eterm* ptr);
+static void init_literal_areas(void);
+static void dump_literals(fmtfn_t to, void *to_arg);
+static void dump_module_literals(fmtfn_t to, void *to_arg,
+ ErtsLiteralArea* lit_area);
static Binary* all_binaries;
@@ -58,123 +67,190 @@ extern BeamInstr beam_apply[];
extern BeamInstr beam_exit[];
extern BeamInstr beam_continue_exit[];
-
void
erts_deep_process_dump(fmtfn_t to, void *to_arg)
{
int i, max = erts_ptab_max(&erts_proc);
all_binaries = NULL;
-
+ init_literal_areas();
+
for (i = 0; i < max; i++) {
Process *p = erts_pix2proc(i);
if (p && p->i != ENULL) {
erts_aint32_t state = erts_atomic32_read_acqb(&p->state);
- if (!(state & (ERTS_PSFLG_EXITING|ERTS_PSFLG_GC)))
- dump_process_info(to, to_arg, p);
+ if (state & ERTS_PSFLG_EXITING)
+ continue;
+ if (state & ERTS_PSFLG_GC) {
+ ErtsSchedulerData *sdp = erts_get_scheduler_data();
+ if (!sdp || p != sdp->current_process)
+ continue;
+
+ /* We want to dump the garbing process that caused the dump */
+ }
+
+ dump_process_info(to, to_arg, p);
}
}
+ dump_literals(to, to_arg);
dump_binaries(to, to_arg, all_binaries);
}
-Uint erts_process_memory(Process *p, int incl_msg_inq) {
- ErtsMessage *mp;
- Uint size = 0;
- struct saved_calls *scb;
- size += sizeof(Process);
-
- if (incl_msg_inq)
- ERTS_MSGQ_MV_INQ2PRIVQ(p);
+static void
+monitor_size(ErtsMonitor *mon, void *vsize)
+{
+ *((Uint *) vsize) += erts_monitor_size(mon);
+}
- erts_doforall_links(ERTS_P_LINKS(p), &erts_one_link_size, &size);
- erts_doforall_monitors(ERTS_P_MONITORS(p), &erts_one_mon_size, &size);
- size += (p->heap_sz + p->mbuf_sz) * sizeof(Eterm);
- if (p->abandoned_heap)
- size += (p->hend - p->heap) * sizeof(Eterm);
- if (p->old_hend && p->old_heap)
- size += (p->old_hend - p->old_heap) * sizeof(Eterm);
+static void
+link_size(ErtsMonitor *lnk, void *vsize)
+{
+ *((Uint *) vsize) += erts_link_size(lnk);
+}
+Uint erts_process_memory(Process *p, int include_sigs_in_transit)
+{
+ Uint size = 0;
+ struct saved_calls *scb;
+
+ size += sizeof(Process);
+
+ erts_link_tree_foreach(ERTS_P_LINKS(p),
+ link_size, (void *) &size);
+ erts_monitor_tree_foreach(ERTS_P_MONITORS(p),
+ monitor_size, (void *) &size);
+ erts_monitor_list_foreach(ERTS_P_LT_MONITORS(p),
+ monitor_size, (void *) &size);
+ size += (p->heap_sz + p->mbuf_sz) * sizeof(Eterm);
+ if (p->abandoned_heap)
+ size += (p->hend - p->heap) * sizeof(Eterm);
+ if (p->old_hend && p->old_heap)
+ size += (p->old_hend - p->old_heap) * sizeof(Eterm);
+
+ if (!include_sigs_in_transit) {
+ /*
+ * Size of message queue!
+ *
+ * Note that this assumes that any part of message
+ * queue located in middle queue have been moved
+ * into the inner queue prior to this call.
+ * process_info() management ensures this is done-
+ */
+ ErtsMessage *mp;
+ for (mp = p->sig_qs.first; mp; mp = mp->next) {
+ ASSERT(ERTS_SIG_IS_MSG((ErtsSignal *) mp));
+ size += sizeof(ErtsMessage);
+ if (mp->data.attached)
+ size += erts_msg_attached_data_size(mp) * sizeof(Eterm);
+ }
+ }
+ else {
+ /*
+ * Size of message queue plus size of all signals
+ * in transit to the process!
+ */
+ erts_proc_lock(p, ERTS_PROC_LOCK_MSGQ);
+ erts_proc_sig_fetch(p);
+ erts_proc_unlock(p, ERTS_PROC_LOCK_MSGQ);
+
+ ERTS_FOREACH_SIG_PRIVQS(
+ p, mp,
+ {
+ size += sizeof(ErtsMessage);
+ if (ERTS_SIG_IS_NON_MSG((ErtsSignal *) mp))
+ size += erts_proc_sig_signal_size((ErtsSignal *) mp);
+ else if (mp->data.attached)
+ size += erts_msg_attached_data_size(mp) * sizeof(Eterm);
+ });
+ }
- size += p->msg.len * sizeof(ErtsMessage);
+ if (p->arg_reg != p->def_arg_reg) {
+ size += p->arity * sizeof(p->arg_reg[0]);
+ }
- for (mp = p->msg.first; mp; mp = mp->next)
- if (mp->data.attached)
- size += erts_msg_attached_data_size(mp)*sizeof(Eterm);
+ if (erts_atomic_read_nob(&p->psd) != (erts_aint_t) NULL)
+ size += sizeof(ErtsPSD);
- if (p->arg_reg != p->def_arg_reg) {
- size += p->arity * sizeof(p->arg_reg[0]);
- }
+ scb = ERTS_PROC_GET_SAVED_CALLS_BUF(p);
+ if (scb) {
+ size += (sizeof(struct saved_calls)
+ + (scb->len-1) * sizeof(scb->ct[0]));
+ }
- if (erts_atomic_read_nob(&p->psd) != (erts_aint_t) NULL)
- size += sizeof(ErtsPSD);
+ size += erts_dicts_mem_size(p);
+ return size;
+}
- scb = ERTS_PROC_GET_SAVED_CALLS_BUF(p);
- if (scb) {
- size += (sizeof(struct saved_calls)
- + (scb->len-1) * sizeof(scb->ct[0]));
- }
+static ERTS_INLINE void
+dump_msg(fmtfn_t to, void *to_arg, ErtsMessage *mp)
+{
+ if (ERTS_SIG_IS_MSG((ErtsSignal *) mp)) {
+ Eterm mesg = ERL_MESSAGE_TERM(mp);
+ if (is_value(mesg))
+ dump_element(to, to_arg, mesg);
+ else
+ dump_dist_ext(to, to_arg, mp->data.dist_ext);
+ mesg = ERL_MESSAGE_TOKEN(mp);
+ erts_print(to, to_arg, ":");
+ dump_element(to, to_arg, mesg);
+ erts_print(to, to_arg, "\n");
+ }
+}
- size += erts_dicts_mem_size(p);
- return size;
+static ERTS_INLINE void
+heap_dump_msg(fmtfn_t to, void *to_arg, ErtsMessage *mp)
+{
+ if (ERTS_SIG_IS_MSG((ErtsSignal *) mp)) {
+ Eterm mesg = ERL_MESSAGE_TERM(mp);
+ if (is_value(mesg))
+ heap_dump(to, to_arg, mesg);
+ mesg = ERL_MESSAGE_TOKEN(mp);
+ heap_dump(to, to_arg, mesg);
+ }
}
static void
dump_process_info(fmtfn_t to, void *to_arg, Process *p)
{
Eterm* sp;
- ErtsMessage* mp;
int yreg = -1;
- ERTS_MSGQ_MV_INQ2PRIVQ(p);
+ if (ERTS_TRACE_FLAGS(p) & F_SENSITIVE)
+ return;
+
+ erts_proc_sig_fetch(p);
- if ((ERTS_TRACE_FLAGS(p) & F_SENSITIVE) == 0 && p->msg.first) {
+ if (p->sig_qs.first || p->sig_qs.cont) {
erts_print(to, to_arg, "=proc_messages:%T\n", p->common.id);
- for (mp = p->msg.first; mp != NULL; mp = mp->next) {
- Eterm mesg = ERL_MESSAGE_TERM(mp);
- if (is_value(mesg))
- dump_element(to, to_arg, mesg);
- else
- dump_dist_ext(to, to_arg, mp->data.dist_ext);
- mesg = ERL_MESSAGE_TOKEN(mp);
- erts_print(to, to_arg, ":");
- dump_element(to, to_arg, mesg);
- erts_print(to, to_arg, "\n");
- }
+ ERTS_FOREACH_SIG_PRIVQS(p, mp, dump_msg(to, to_arg, mp));
}
- if ((ERTS_TRACE_FLAGS(p) & F_SENSITIVE) == 0) {
- if (p->dictionary) {
- erts_print(to, to_arg, "=proc_dictionary:%T\n", p->common.id);
- erts_deep_dictionary_dump(to, to_arg,
- p->dictionary, dump_element_nl);
- }
+ if (p->dictionary) {
+ erts_print(to, to_arg, "=proc_dictionary:%T\n", p->common.id);
+ erts_deep_dictionary_dump(to, to_arg,
+ p->dictionary, dump_element_nl);
}
- if ((ERTS_TRACE_FLAGS(p) & F_SENSITIVE) == 0) {
- erts_print(to, to_arg, "=proc_stack:%T\n", p->common.id);
- for (sp = p->stop; sp < STACK_START(p); sp++) {
- yreg = stack_element_dump(to, to_arg, sp, yreg);
- }
+ erts_print(to, to_arg, "=proc_stack:%T\n", p->common.id);
+ for (sp = p->stop; sp < STACK_START(p); sp++) {
+ yreg = stack_element_dump(to, to_arg, sp, yreg);
+ }
- erts_print(to, to_arg, "=proc_heap:%T\n", p->common.id);
- for (sp = p->stop; sp < STACK_START(p); sp++) {
- Eterm term = *sp;
-
- if (!is_catch(term) && !is_CP(term)) {
- heap_dump(to, to_arg, term);
- }
- }
- for (mp = p->msg.first; mp != NULL; mp = mp->next) {
- Eterm mesg = ERL_MESSAGE_TERM(mp);
- if (is_value(mesg))
- heap_dump(to, to_arg, mesg);
- mesg = ERL_MESSAGE_TOKEN(mp);
- heap_dump(to, to_arg, mesg);
- }
- if (p->dictionary) {
- erts_deep_dictionary_dump(to, to_arg, p->dictionary, heap_dump);
- }
+ erts_print(to, to_arg, "=proc_heap:%T\n", p->common.id);
+ for (sp = p->stop; sp < STACK_START(p); sp++) {
+ Eterm term = *sp;
+
+ if (!is_catch(term) && !is_CP(term)) {
+ heap_dump(to, to_arg, term);
+ }
+ }
+
+ if (p->sig_qs.first || p->sig_qs.cont)
+ ERTS_FOREACH_SIG_PRIVQS(p, mp, heap_dump_msg(to, to_arg, mp));
+
+ if (p->dictionary) {
+ erts_deep_dictionary_dump(to, to_arg, p->dictionary, heap_dump);
}
}
@@ -186,6 +262,7 @@ dump_dist_ext(fmtfn_t to, void *to_arg, ErtsDistExternal *edep)
else {
byte *e;
size_t sz;
+
if (!(edep->flags & ERTS_DIST_EXT_ATOM_TRANS_TAB))
erts_print(to, to_arg, "D0:");
else {
@@ -203,12 +280,18 @@ dump_dist_ext(fmtfn_t to, void *to_arg, ErtsDistExternal *edep)
else {
ASSERT(*e == VERSION_MAGIC);
}
-
erts_print(to, to_arg, "E%X:", sz);
- if (edep->flags & ERTS_DIST_EXT_DFLAG_HDR)
- erts_print(to, to_arg, "%02X", VERSION_MAGIC);
- while (e < edep->ext_endp)
- erts_print(to, to_arg, "%02X", *e++);
+ if (edep->flags & ERTS_DIST_EXT_DFLAG_HDR) {
+ byte sbuf[3];
+ int i = 0;
+
+ sbuf[i++] = VERSION_MAGIC;
+ while (i < sizeof(sbuf) && e < edep->ext_endp) {
+ sbuf[i++] = *e++;
+ }
+ erts_print_base64(to, to_arg, sbuf, i);
+ }
+ erts_print_base64(to, to_arg, e, edep->ext_endp - e);
}
}
@@ -373,7 +456,9 @@ heap_dump(fmtfn_t to, void *to_arg, Eterm x)
next = (Eterm *) x;
} else if (is_list(x)) {
ptr = list_val(x);
- if (ptr[0] != OUR_NIL) {
+ if (erts_is_literal(x, ptr)) {
+ mark_literal(ptr);
+ } else if (ptr[0] != OUR_NIL) {
erts_print(to, to_arg, PTR_FMT ":l", ptr);
dump_element(to, to_arg, ptr[0]);
erts_putc(to, to_arg, '|');
@@ -392,7 +477,9 @@ heap_dump(fmtfn_t to, void *to_arg, Eterm x)
ptr = boxed_val(x);
hdr = *ptr;
- if (hdr != OUR_NIL) { /* If not visited */
+ if (erts_is_literal(x, ptr)) {
+ mark_literal(ptr);
+ } else if (hdr != OUR_NIL) {
erts_print(to, to_arg, PTR_FMT ":", ptr);
if (is_arity_value(hdr)) {
Uint i;
@@ -433,16 +520,13 @@ heap_dump(fmtfn_t to, void *to_arg, Eterm x)
} else if (is_binary_header(hdr)) {
Uint tag = thing_subtag(hdr);
Uint size = binary_size(x);
- Uint i;
if (tag == HEAP_BINARY_SUBTAG) {
byte* p;
erts_print(to, to_arg, "Yh%X:", size);
p = binary_bytes(x);
- for (i = 0; i < size; i++) {
- erts_print(to, to_arg, "%02X", p[i]);
- }
+ erts_print_base64(to, to_arg, p, size);
} else if (tag == REFC_BINARY_SUBTAG) {
ProcBin* pb = (ProcBin *) binary_val(x);
Binary* val = pb->val;
@@ -498,11 +582,77 @@ heap_dump(fmtfn_t to, void *to_arg, Eterm x)
erts_print(to, to_arg, "p<%beu.%beu>\n",
port_channel_no(x), port_number(x));
*ptr = OUR_NIL;
+ } else if (is_map_header(hdr)) {
+ if (is_flatmap_header(hdr)) {
+ flatmap_t* fmp = (flatmap_t *) flatmap_val(x);
+ Eterm* values = ptr + sizeof(flatmap_t) / sizeof(Eterm);
+ Uint map_size = fmp->size;
+ int i;
+
+ erts_print(to, to_arg, "Mf" ETERM_FMT ":", map_size);
+ dump_element(to, to_arg, fmp->keys);
+ erts_putc(to, to_arg, ':');
+ for (i = 0; i < map_size; i++) {
+ dump_element(to, to_arg, values[i]);
+ if (is_immed(values[i])) {
+ values[i] = make_small(0);
+ }
+ if (i < map_size-1) {
+ erts_putc(to, to_arg, ',');
+ }
+ }
+ erts_putc(to, to_arg, '\n');
+ *ptr = OUR_NIL;
+ x = fmp->keys;
+ if (map_size) {
+ fmp->keys = (Eterm) next;
+ next = &values[map_size-1];
+ }
+ continue;
+ } else {
+ Uint i;
+ Uint sz = 0;
+ Eterm* nodes = ptr + 1;
+
+ switch (MAP_HEADER_TYPE(hdr)) {
+ case MAP_HEADER_TAG_HAMT_HEAD_ARRAY:
+ nodes++;
+ sz = 16;
+ erts_print(to, to_arg, "Mh" ETERM_FMT ":" ETERM_FMT ":",
+ hashmap_size(x), sz);
+ break;
+ case MAP_HEADER_TAG_HAMT_HEAD_BITMAP:
+ nodes++;
+ sz = hashmap_bitcount(MAP_HEADER_VAL(hdr));
+ erts_print(to, to_arg, "Mh" ETERM_FMT ":" ETERM_FMT ":",
+ hashmap_size(x), sz);
+ break;
+ case MAP_HEADER_TAG_HAMT_NODE_BITMAP:
+ sz = hashmap_bitcount(MAP_HEADER_VAL(hdr));
+ erts_print(to, to_arg, "Mn" ETERM_FMT ":", sz);
+ break;
+ }
+ *ptr = OUR_NIL;
+ for (i = 0; i < sz; i++) {
+ dump_element(to, to_arg, nodes[i]);
+ if (is_immed(nodes[i])) {
+ nodes[i] = make_small(0);
+ }
+ if (i < sz-1) {
+ erts_putc(to, to_arg, ',');
+ }
+ }
+ erts_putc(to, to_arg, '\n');
+ x = nodes[0];
+ nodes[0] = (Eterm) next;
+ next = &nodes[sz-1];
+ continue;
+ }
} else {
/*
* All other we dump in the external term format.
*/
- dump_externally(to, to_arg, x);
+ dump_externally(to, to_arg, x);
erts_putc(to, to_arg, '\n');
*ptr = OUR_NIL;
}
@@ -519,16 +669,13 @@ static void
dump_binaries(fmtfn_t to, void *to_arg, Binary* current)
{
while (current) {
- long i;
- long size = current->orig_size;
+ SWord size = current->orig_size;
byte* bytes = (byte*) current->orig_bytes;
erts_print(to, to_arg, "=binary:" PTR_FMT "\n", current);
erts_print(to, to_arg, "%X:", size);
- for (i = 0; i < size; i++) {
- erts_print(to, to_arg, "%02X", bytes[i]);
- }
- erts_putc(to, to_arg, '\n');
+ erts_print_base64(to, to_arg, bytes, size);
+ erts_putc(to, to_arg, '\n');
current = (Binary *) current->intern.flags;
}
}
@@ -564,16 +711,287 @@ dump_externally(fmtfn_t to, void *to_arg, Eterm term)
}
}
- /* Do not handle maps */
- if (is_map(term)) {
- term = am_undefined;
- }
-
s = p = sbuf;
erts_encode_ext(term, &p);
erts_print(to, to_arg, "E%X:", p-s);
- while (s < p) {
- erts_print(to, to_arg, "%02X", *s++);
+ erts_print_base64(to, to_arg, sbuf, p-s);
+}
+
+/*
+ * Handle dumping of literal areas.
+ */
+
+static ErtsLiteralArea** lit_areas;
+static Uint num_lit_areas;
+
+static int compare_areas(const void * a, const void * b)
+{
+ ErtsLiteralArea** a_p = (ErtsLiteralArea **) a;
+ ErtsLiteralArea** b_p = (ErtsLiteralArea **) b;
+
+ if (*a_p < *b_p) {
+ return -1;
+ } else if (*b_p < *a_p) {
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+
+static void
+init_literal_areas(void)
+{
+ int i;
+ Module* modp;
+ ErtsCodeIndex code_ix;
+ ErtsLiteralArea** area_p;
+
+ code_ix = erts_active_code_ix();
+ erts_rlock_old_code(code_ix);
+
+ lit_areas = area_p = erts_dump_lit_areas;
+ num_lit_areas = 0;
+ for (i = 0; i < module_code_size(code_ix); i++) {
+ modp = module_code(i, code_ix);
+ if (modp == NULL) {
+ continue;
+ }
+ if (modp->curr.code_length > 0 &&
+ modp->curr.code_hdr->literal_area) {
+ *area_p++ = modp->curr.code_hdr->literal_area;
+ }
+ if (modp->old.code_length > 0 && modp->old.code_hdr->literal_area) {
+ *area_p++ = modp->old.code_hdr->literal_area;
+ }
+ }
+
+ num_lit_areas = area_p - lit_areas;
+ ASSERT(num_lit_areas <= erts_dump_num_lit_areas);
+ for (i = 0; i < num_lit_areas; i++) {
+ lit_areas[i]->off_heap = 0;
+ }
+
+ qsort(lit_areas, num_lit_areas, sizeof(ErtsLiteralArea *),
+ compare_areas);
+
+ erts_runlock_old_code(code_ix);
+}
+
+static int search_areas(const void * a, const void * b) {
+ Eterm* key = (Eterm *) a;
+ ErtsLiteralArea** b_p = (ErtsLiteralArea **) b;
+ if (key < b_p[0]->start) {
+ return -1;
+ } else if (b_p[0]->end <= key) {
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+static void mark_literal(Eterm* ptr)
+{
+ ErtsLiteralArea** ap;
+
+ ap = bsearch(ptr, lit_areas, num_lit_areas, sizeof(ErtsLiteralArea*),
+ search_areas);
+
+ /*
+ * If the literal was created by native code, this search will not
+ * find it and ap will be NULL.
+ */
+
+ if (ap) {
+ ap[0]->off_heap = (struct erl_off_heap_header *) 1;
+ }
+}
+
+
+static void
+dump_literals(fmtfn_t to, void *to_arg)
+{
+ ErtsCodeIndex code_ix;
+ int i;
+
+ code_ix = erts_active_code_ix();
+ erts_rlock_old_code(code_ix);
+
+ erts_print(to, to_arg, "=literals\n");
+ for (i = 0; i < num_lit_areas; i++) {
+ if (lit_areas[i]->off_heap) {
+ dump_module_literals(to, to_arg, lit_areas[i]);
+ }
+ }
+
+ erts_runlock_old_code(code_ix);
+}
+
+static void
+dump_module_literals(fmtfn_t to, void *to_arg, ErtsLiteralArea* lit_area)
+{
+ Eterm* htop;
+ Eterm* hend;
+
+ htop = lit_area->start;
+ hend = lit_area->end;
+ while (htop < hend) {
+ Eterm w = *htop;
+ Eterm term;
+ Uint size;
+
+ switch (primary_tag(w)) {
+ case TAG_PRIMARY_HEADER:
+ term = make_boxed(htop);
+ erts_print(to, to_arg, PTR_FMT ":", htop);
+ if (is_arity_value(w)) {
+ Uint i;
+ Uint arity = arityval(w);
+
+ erts_print(to, to_arg, "t" ETERM_FMT ":", arity);
+ for (i = 1; i <= arity; i++) {
+ dump_element(to, to_arg, htop[i]);
+ if (i < arity) {
+ erts_putc(to, to_arg, ',');
+ }
+ }
+ erts_putc(to, to_arg, '\n');
+ } else if (w == HEADER_FLONUM) {
+ FloatDef f;
+ char sbuf[31];
+ int i;
+
+ GET_DOUBLE_DATA((htop+1), f);
+ i = sys_double_to_chars(f.fd, sbuf, sizeof(sbuf));
+ sys_memset(sbuf+i, 0, 31-i);
+ erts_print(to, to_arg, "F%X:%s\n", i, sbuf);
+ } else if (_is_bignum_header(w)) {
+ erts_print(to, to_arg, "B%T\n", term);
+ } else if (is_binary_header(w)) {
+ Uint tag = thing_subtag(w);
+ Uint size = binary_size(term);
+
+ if (tag == HEAP_BINARY_SUBTAG) {
+ byte* p;
+
+ erts_print(to, to_arg, "Yh%X:", size);
+ p = binary_bytes(term);
+ erts_print_base64(to, to_arg, p, size);
+ } else if (tag == REFC_BINARY_SUBTAG) {
+ ProcBin* pb = (ProcBin *) binary_val(term);
+ Binary* val = pb->val;
+
+ if (erts_atomic_xchg_nob(&val->intern.refc, 0) != 0) {
+ val->intern.flags = (UWord) all_binaries;
+ all_binaries = val;
+ }
+ erts_print(to, to_arg,
+ "Yc" PTR_FMT ":" PTR_FMT ":" PTR_FMT,
+ val,
+ pb->bytes - (byte *)val->orig_bytes,
+ size);
+ } else if (tag == SUB_BINARY_SUBTAG) {
+ ErlSubBin* Sb = (ErlSubBin *) binary_val(term);
+ Eterm* real_bin;
+ void* val;
+
+ real_bin = boxed_val(Sb->orig);
+ if (thing_subtag(*real_bin) == REFC_BINARY_SUBTAG) {
+ /*
+ * Unvisited REFC_BINARY: Point directly to
+ * the binary.
+ */
+ ProcBin* pb = (ProcBin *) real_bin;
+ val = pb->val;
+ } else {
+ /*
+ * Heap binary or visited REFC binary: Point
+ * to heap binary or ProcBin on the heap.
+ */
+ val = real_bin;
+ }
+ erts_print(to, to_arg,
+ "Ys" PTR_FMT ":" PTR_FMT ":" PTR_FMT,
+ val, Sb->offs, size);
+ }
+ erts_putc(to, to_arg, '\n');
+ } else if (is_map_header(w)) {
+ if (is_flatmap_header(w)) {
+ flatmap_t* fmp = (flatmap_t *) flatmap_val(term);
+ Eterm* values = htop + sizeof(flatmap_t) / sizeof(Eterm);
+ Uint map_size = fmp->size;
+ int i;
+
+ erts_print(to, to_arg, "Mf" ETERM_FMT ":", map_size);
+ dump_element(to, to_arg, fmp->keys);
+ erts_putc(to, to_arg, ':');
+ for (i = 0; i < map_size; i++) {
+ dump_element(to, to_arg, values[i]);
+ if (i < map_size-1) {
+ erts_putc(to, to_arg, ',');
+ }
+ }
+ erts_putc(to, to_arg, '\n');
+ } else {
+ Uint i;
+ Uint sz = 0;
+ Eterm* nodes = htop + 1;
+
+ switch (MAP_HEADER_TYPE(w)) {
+ case MAP_HEADER_TAG_HAMT_HEAD_ARRAY:
+ nodes++;
+ sz = 16;
+ erts_print(to, to_arg, "Mh" ETERM_FMT ":" ETERM_FMT ":",
+ hashmap_size(term), sz);
+ break;
+ case MAP_HEADER_TAG_HAMT_HEAD_BITMAP:
+ nodes++;
+ sz = hashmap_bitcount(MAP_HEADER_VAL(w));
+ erts_print(to, to_arg, "Mh" ETERM_FMT ":" ETERM_FMT ":",
+ hashmap_size(term), sz);
+ break;
+ case MAP_HEADER_TAG_HAMT_NODE_BITMAP:
+ sz = hashmap_bitcount(MAP_HEADER_VAL(w));
+ erts_print(to, to_arg, "Mn" ETERM_FMT ":", sz);
+ break;
+ }
+ for (i = 0; i < sz; i++) {
+ dump_element(to, to_arg, nodes[i]);
+ if (i < sz-1) {
+ erts_putc(to, to_arg, ',');
+ }
+ }
+ erts_putc(to, to_arg, '\n');
+ }
+ } else if (is_export_header(w)) {
+ dump_externally(to, to_arg, term);
+ erts_putc(to, to_arg, '\n');
+ }
+ size = 1 + header_arity(w);
+ switch (w & _HEADER_SUBTAG_MASK) {
+ case MAP_SUBTAG:
+ if (is_flatmap_header(w)) {
+ size += 1 + flatmap_get_size(htop);
+ } else {
+ size += hashmap_bitcount(MAP_HEADER_VAL(w));
+ }
+ break;
+ case SUB_BINARY_SUBTAG:
+ size += 1;
+ break;
+ }
+ break;
+ default:
+ ASSERT(!is_header(htop[1]));
+ erts_print(to, to_arg, PTR_FMT ":l", htop);
+ dump_element(to, to_arg, htop[0]);
+ erts_putc(to, to_arg, '|');
+ dump_element(to, to_arg, htop[1]);
+ erts_putc(to, to_arg, '\n');
+ size = 2;
+ break;
+ }
+ htop += size;
}
}
@@ -641,8 +1059,8 @@ erts_dump_extended_process_state(fmtfn_t to, void *to_arg, erts_aint32_t psflg)
erts_print(to, to_arg, "FREE"); break;
case ERTS_PSFLG_EXITING:
erts_print(to, to_arg, "EXITING"); break;
- case ERTS_PSFLG_PENDING_EXIT:
- erts_print(to, to_arg, "PENDING_EXIT"); break;
+ case ERTS_PSFLG_UNUSED:
+ erts_print(to, to_arg, "UNUSED"); break;
case ERTS_PSFLG_ACTIVE:
erts_print(to, to_arg, "ACTIVE"); break;
case ERTS_PSFLG_IN_RUNQ:
@@ -653,10 +1071,10 @@ erts_dump_extended_process_state(fmtfn_t to, void *to_arg, erts_aint32_t psflg)
erts_print(to, to_arg, "SUSPENDED"); break;
case ERTS_PSFLG_GC:
erts_print(to, to_arg, "GC"); break;
- case ERTS_PSFLG_BOUND:
- erts_print(to, to_arg, "BOUND"); break;
- case ERTS_PSFLG_TRAP_EXIT:
- erts_print(to, to_arg, "TRAP_EXIT"); break;
+ case ERTS_PSFLG_SYS_TASKS:
+ erts_print(to, to_arg, "SYS_TASKS"); break;
+ case ERTS_PSFLG_SIG_IN_Q:
+ erts_print(to, to_arg, "SIG_IN_Q"); break;
case ERTS_PSFLG_ACTIVE_SYS:
erts_print(to, to_arg, "ACTIVE_SYS"); break;
case ERTS_PSFLG_RUNNING_SYS:
@@ -667,8 +1085,8 @@ erts_dump_extended_process_state(fmtfn_t to, void *to_arg, erts_aint32_t psflg)
erts_print(to, to_arg, "DELAYED_SYS"); break;
case ERTS_PSFLG_OFF_HEAP_MSGQ:
erts_print(to, to_arg, "OFF_HEAP_MSGQ"); break;
- case ERTS_PSFLG_ON_HEAP_MSGQ:
- erts_print(to, to_arg, "ON_HEAP_MSGQ"); break;
+ case ERTS_PSFLG_SIG_Q:
+ erts_print(to, to_arg, "SIG_Q"); break;
case ERTS_PSFLG_DIRTY_CPU_PROC:
erts_print(to, to_arg, "DIRTY_CPU_PROC"); break;
case ERTS_PSFLG_DIRTY_IO_PROC:
diff --git a/erts/emulator/beam/erl_process_lock.c b/erts/emulator/beam/erl_process_lock.c
index 431867f27e..44c7892040 100644
--- a/erts/emulator/beam/erl_process_lock.c
+++ b/erts/emulator/beam/erl_process_lock.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2007-2017. All Rights Reserved.
+ * Copyright Ericsson AB 2007-2018. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -101,7 +101,6 @@ static void cleanup_tse(void);
#ifdef ERTS_ENABLE_LOCK_CHECK
static struct {
Sint16 proc_lock_main;
- Sint16 proc_lock_link;
Sint16 proc_lock_msgq;
Sint16 proc_lock_btm;
Sint16 proc_lock_status;
@@ -140,7 +139,6 @@ erts_init_proc_lock(int cpus)
#endif
#ifdef ERTS_ENABLE_LOCK_CHECK
lc_id.proc_lock_main = erts_lc_get_lock_order_id("proc_main");
- lc_id.proc_lock_link = erts_lc_get_lock_order_id("proc_link");
lc_id.proc_lock_msgq = erts_lc_get_lock_order_id("proc_msgq");
lc_id.proc_lock_btm = erts_lc_get_lock_order_id("proc_btm");
lc_id.proc_lock_status = erts_lc_get_lock_order_id("proc_status");
@@ -1055,12 +1053,6 @@ erts_proc_lock_init(Process *p)
#ifdef ERTS_ENABLE_LOCK_CHECK
erts_lc_trylock(1, &p->lock.main.lc);
#endif
- erts_mtx_init(&p->lock.link, "proc_link", p->common.id,
- ERTS_LOCK_FLAGS_CATEGORY_PROCESS);
- ethr_mutex_lock(&p->lock.link.mtx);
-#ifdef ERTS_ENABLE_LOCK_CHECK
- erts_lc_trylock(1, &p->lock.link.lc);
-#endif
erts_mtx_init(&p->lock.msgq, "proc_msgq", p->common.id,
ERTS_LOCK_FLAGS_CATEGORY_PROCESS);
ethr_mutex_lock(&p->lock.msgq.mtx);
@@ -1102,7 +1094,6 @@ erts_proc_lock_fin(Process *p)
{
#if ERTS_PROC_LOCK_RAW_MUTEX_IMPL
erts_mtx_destroy(&p->lock.main);
- erts_mtx_destroy(&p->lock.link);
erts_mtx_destroy(&p->lock.msgq);
erts_mtx_destroy(&p->lock.btm);
erts_mtx_destroy(&p->lock.status);
@@ -1144,8 +1135,6 @@ void erts_lcnt_enable_proc_lock_count(Process *proc, int enable) {
erts_lcnt_init_lock_info_idx(carrier, ERTS_LCNT_PROCLOCK_IDX_MAIN,
"proc_main", proc->common.id, ERTS_LOCK_TYPE_PROCLOCK);
- erts_lcnt_init_lock_info_idx(carrier, ERTS_LCNT_PROCLOCK_IDX_LINK,
- "proc_link", proc->common.id, ERTS_LOCK_TYPE_PROCLOCK);
erts_lcnt_init_lock_info_idx(carrier, ERTS_LCNT_PROCLOCK_IDX_MSGQ,
"proc_msgq", proc->common.id, ERTS_LOCK_TYPE_PROCLOCK);
erts_lcnt_init_lock_info_idx(carrier, ERTS_LCNT_PROCLOCK_IDX_BTM,
@@ -1200,10 +1189,6 @@ erts_proc_lc_lock(Process *p, ErtsProcLocks locks, char *file, unsigned int line
lck.id = lc_id.proc_lock_main;
erts_lc_lock_x(&lck,file,line);
}
- if (locks & ERTS_PROC_LOCK_LINK) {
- lck.id = lc_id.proc_lock_link;
- erts_lc_lock_x(&lck,file,line);
- }
if (locks & ERTS_PROC_LOCK_MSGQ) {
lck.id = lc_id.proc_lock_msgq;
erts_lc_lock_x(&lck,file,line);
@@ -1233,10 +1218,6 @@ erts_proc_lc_trylock(Process *p, ErtsProcLocks locks, int locked,
lck.id = lc_id.proc_lock_main;
erts_lc_trylock_x(locked, &lck, file, line);
}
- if (locks & ERTS_PROC_LOCK_LINK) {
- lck.id = lc_id.proc_lock_link;
- erts_lc_trylock_x(locked, &lck, file, line);
- }
if (locks & ERTS_PROC_LOCK_MSGQ) {
lck.id = lc_id.proc_lock_msgq;
erts_lc_trylock_x(locked, &lck, file, line);
@@ -1277,10 +1258,6 @@ erts_proc_lc_unlock(Process *p, ErtsProcLocks locks)
lck.id = lc_id.proc_lock_msgq;
erts_lc_unlock(&lck);
}
- if (locks & ERTS_PROC_LOCK_LINK) {
- lck.id = lc_id.proc_lock_link;
- erts_lc_unlock(&lck);
- }
if (locks & ERTS_PROC_LOCK_MAIN) {
lck.id = lc_id.proc_lock_main;
erts_lc_unlock(&lck);
@@ -1312,10 +1289,6 @@ erts_proc_lc_might_unlock(Process *p, ErtsProcLocks locks)
lck.id = lc_id.proc_lock_msgq;
erts_lc_might_unlock(&lck);
}
- if (locks & ERTS_PROC_LOCK_LINK) {
- lck.id = lc_id.proc_lock_link;
- erts_lc_might_unlock(&lck);
- }
if (locks & ERTS_PROC_LOCK_MAIN) {
lck.id = lc_id.proc_lock_main;
erts_lc_might_unlock(&lck);
@@ -1323,8 +1296,6 @@ erts_proc_lc_might_unlock(Process *p, ErtsProcLocks locks)
#elif ERTS_PROC_LOCK_RAW_MUTEX_IMPL
if (locks & ERTS_PROC_LOCK_MAIN)
erts_lc_might_unlock(&p->lock.main.lc);
- if (locks & ERTS_PROC_LOCK_LINK)
- erts_lc_might_unlock(&p->lock.link.lc);
if (locks & ERTS_PROC_LOCK_MSGQ)
erts_lc_might_unlock(&p->lock.msgq.lc);
if (locks & ERTS_PROC_LOCK_BTM)
@@ -1348,10 +1319,6 @@ erts_proc_lc_require_lock(Process *p, ErtsProcLocks locks, char *file,
lck.id = lc_id.proc_lock_main;
erts_lc_require_lock(&lck, file, line);
}
- if (locks & ERTS_PROC_LOCK_LINK) {
- lck.id = lc_id.proc_lock_link;
- erts_lc_require_lock(&lck, file, line);
- }
if (locks & ERTS_PROC_LOCK_MSGQ) {
lck.id = lc_id.proc_lock_msgq;
erts_lc_require_lock(&lck, file, line);
@@ -1371,8 +1338,6 @@ erts_proc_lc_require_lock(Process *p, ErtsProcLocks locks, char *file,
#elif ERTS_PROC_LOCK_RAW_MUTEX_IMPL
if (locks & ERTS_PROC_LOCK_MAIN)
erts_lc_require_lock(&p->lock.main.lc, file, line);
- if (locks & ERTS_PROC_LOCK_LINK)
- erts_lc_require_lock(&p->lock.link.lc, file, line);
if (locks & ERTS_PROC_LOCK_MSGQ)
erts_lc_require_lock(&p->lock.msgq.lc, file, line);
if (locks & ERTS_PROC_LOCK_BTM)
@@ -1407,10 +1372,6 @@ erts_proc_lc_unrequire_lock(Process *p, ErtsProcLocks locks)
lck.id = lc_id.proc_lock_msgq;
erts_lc_unrequire_lock(&lck);
}
- if (locks & ERTS_PROC_LOCK_LINK) {
- lck.id = lc_id.proc_lock_link;
- erts_lc_unrequire_lock(&lck);
- }
if (locks & ERTS_PROC_LOCK_MAIN) {
lck.id = lc_id.proc_lock_main;
erts_lc_unrequire_lock(&lck);
@@ -1418,8 +1379,6 @@ erts_proc_lc_unrequire_lock(Process *p, ErtsProcLocks locks)
#elif ERTS_PROC_LOCK_RAW_MUTEX_IMPL
if (locks & ERTS_PROC_LOCK_MAIN)
erts_lc_unrequire_lock(&p->lock.main.lc);
- if (locks & ERTS_PROC_LOCK_LINK)
- erts_lc_unrequire_lock(&p->lock.link.lc);
if (locks & ERTS_PROC_LOCK_MSGQ)
erts_lc_unrequire_lock(&p->lock.msgq.lc);
if (locks & ERTS_PROC_LOCK_BTM)
@@ -1443,8 +1402,6 @@ erts_proc_lc_trylock_force_busy(Process *p, ErtsProcLocks locks)
if (locks & ERTS_PROC_LOCK_MAIN)
lck.id = lc_id.proc_lock_main;
- else if (locks & ERTS_PROC_LOCK_LINK)
- lck.id = lc_id.proc_lock_link;
else if (locks & ERTS_PROC_LOCK_MSGQ)
lck.id = lc_id.proc_lock_msgq;
else if (locks & ERTS_PROC_LOCK_BTM)
@@ -1487,10 +1444,6 @@ void erts_proc_lc_chk_only_proc(Process *p, ErtsProcLocks locks)
have_locks[have_locks_len].id = lc_id.proc_lock_main;
have_locks[have_locks_len++].extra = p->common.id;
}
- if (locks & ERTS_PROC_LOCK_LINK) {
- have_locks[have_locks_len].id = lc_id.proc_lock_link;
- have_locks[have_locks_len++].extra = p->common.id;
- }
if (locks & ERTS_PROC_LOCK_MSGQ) {
have_locks[have_locks_len].id = lc_id.proc_lock_msgq;
have_locks[have_locks_len++].extra = p->common.id;
@@ -1511,8 +1464,6 @@ void erts_proc_lc_chk_only_proc(Process *p, ErtsProcLocks locks)
erts_lc_lock_t have_locks[6];
if (locks & ERTS_PROC_LOCK_MAIN)
have_locks[have_locks_len++] = p->lock.main.lc;
- if (locks & ERTS_PROC_LOCK_LINK)
- have_locks[have_locks_len++] = p->lock.link.lc;
if (locks & ERTS_PROC_LOCK_MSGQ)
have_locks[have_locks_len++] = p->lock.msgq.lc;
if (locks & ERTS_PROC_LOCK_BTM)
@@ -1540,10 +1491,6 @@ erts_proc_lc_chk_have_proc_locks(Process *p, ErtsProcLocks locks)
have_locks[have_locks_len].id = lc_id.proc_lock_main;
have_locks[have_locks_len++].extra = p->common.id;
}
- if (locks & ERTS_PROC_LOCK_LINK) {
- have_locks[have_locks_len].id = lc_id.proc_lock_link;
- have_locks[have_locks_len++].extra = p->common.id;
- }
if (locks & ERTS_PROC_LOCK_MSGQ) {
have_locks[have_locks_len].id = lc_id.proc_lock_msgq;
have_locks[have_locks_len++].extra = p->common.id;
@@ -1564,8 +1511,6 @@ erts_proc_lc_chk_have_proc_locks(Process *p, ErtsProcLocks locks)
erts_lc_lock_t have_locks[6];
if (locks & ERTS_PROC_LOCK_MAIN)
have_locks[have_locks_len++] = p->lock.main.lc;
- if (locks & ERTS_PROC_LOCK_LINK)
- have_locks[have_locks_len++] = p->lock.link.lc;
if (locks & ERTS_PROC_LOCK_MSGQ)
have_locks[have_locks_len++] = p->lock.msgq.lc;
if (locks & ERTS_PROC_LOCK_BTM)
@@ -1603,14 +1548,6 @@ erts_proc_lc_chk_proc_locks(Process *p, ErtsProcLocks locks)
have_not_locks[have_not_locks_len].id = lc_id.proc_lock_main;
have_not_locks[have_not_locks_len++].extra = p->common.id;
}
- if (locks & ERTS_PROC_LOCK_LINK) {
- have_locks[have_locks_len].id = lc_id.proc_lock_link;
- have_locks[have_locks_len++].extra = p->common.id;
- }
- else {
- have_not_locks[have_not_locks_len].id = lc_id.proc_lock_link;
- have_not_locks[have_not_locks_len++].extra = p->common.id;
- }
if (locks & ERTS_PROC_LOCK_MSGQ) {
have_locks[have_locks_len].id = lc_id.proc_lock_msgq;
have_locks[have_locks_len++].extra = p->common.id;
@@ -1651,10 +1588,6 @@ erts_proc_lc_chk_proc_locks(Process *p, ErtsProcLocks locks)
have_locks[have_locks_len++] = p->lock.main.lc;
else
have_not_locks[have_not_locks_len++] = p->lock.main.lc;
- if (locks & ERTS_PROC_LOCK_LINK)
- have_locks[have_locks_len++] = p->lock.link.lc;
- else
- have_not_locks[have_not_locks_len++] = p->lock.link.lc;
if (locks & ERTS_PROC_LOCK_MSGQ)
have_locks[have_locks_len++] = p->lock.msgq.lc;
else
@@ -1680,13 +1613,10 @@ erts_proc_lc_chk_proc_locks(Process *p, ErtsProcLocks locks)
ErtsProcLocks
erts_proc_lc_my_proc_locks(Process *p)
{
- int resv[6];
+ int resv[5];
ErtsProcLocks res = 0;
#if ERTS_PROC_LOCK_OWN_IMPL
- erts_lc_lock_t locks[6] = {ERTS_LC_LOCK_INIT(lc_id.proc_lock_main,
- p->common.id,
- ERTS_LOCK_TYPE_PROCLOCK),
- ERTS_LC_LOCK_INIT(lc_id.proc_lock_link,
+ erts_lc_lock_t locks[5] = {ERTS_LC_LOCK_INIT(lc_id.proc_lock_main,
p->common.id,
ERTS_LOCK_TYPE_PROCLOCK),
ERTS_LC_LOCK_INIT(lc_id.proc_lock_msgq,
@@ -1702,26 +1632,23 @@ erts_proc_lc_my_proc_locks(Process *p)
p->common.id,
ERTS_LOCK_TYPE_PROCLOCK)};
#elif ERTS_PROC_LOCK_RAW_MUTEX_IMPL
- erts_lc_lock_t locks[6] = {p->lock.main.lc,
- p->lock.link.lc,
+ erts_lc_lock_t locks[5] = {p->lock.main.lc,
p->lock.msgq.lc,
p->lock.btm.lc,
p->lock.status.lc,
p->lock.trace.lc};
#endif
- erts_lc_have_locks(resv, locks, 6);
+ erts_lc_have_locks(resv, locks, 5);
if (resv[0])
res |= ERTS_PROC_LOCK_MAIN;
if (resv[1])
- res |= ERTS_PROC_LOCK_LINK;
- if (resv[2])
res |= ERTS_PROC_LOCK_MSGQ;
- if (resv[3])
+ if (resv[2])
res |= ERTS_PROC_LOCK_BTM;
- if (resv[4])
+ if (resv[3])
res |= ERTS_PROC_LOCK_STATUS;
- if (resv[5])
+ if (resv[4])
res |= ERTS_PROC_LOCK_TRACE;
return res;
@@ -1730,15 +1657,14 @@ erts_proc_lc_my_proc_locks(Process *p)
void
erts_proc_lc_chk_no_proc_locks(char *file, int line)
{
- int resv[6];
- int ids[6] = {lc_id.proc_lock_main,
- lc_id.proc_lock_link,
+ int resv[5];
+ int ids[5] = {lc_id.proc_lock_main,
lc_id.proc_lock_msgq,
lc_id.proc_lock_btm,
lc_id.proc_lock_status,
lc_id.proc_lock_trace};
- erts_lc_have_lock_ids(resv, ids, 6);
- if (!ERTS_IS_CRASH_DUMPING && (resv[0] || resv[1] || resv[2] || resv[3] || resv[4] || resv[5])) {
+ erts_lc_have_lock_ids(resv, ids, 5);
+ if (!ERTS_IS_CRASH_DUMPING && (resv[0] || resv[1] || resv[2] || resv[3] || resv[4])) {
erts_lc_fail("%s:%d: Thread has process locks locked when expected "
"not to have any process locks locked",
file, line);
diff --git a/erts/emulator/beam/erl_process_lock.h b/erts/emulator/beam/erl_process_lock.h
index 9d5691d3c4..43f396c547 100644
--- a/erts/emulator/beam/erl_process_lock.h
+++ b/erts/emulator/beam/erl_process_lock.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2007-2017. All Rights Reserved.
+ * Copyright Ericsson AB 2007-2018. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -66,7 +66,7 @@
#endif
-#define ERTS_PROC_LOCK_MAX_BIT 5
+#define ERTS_PROC_LOCK_MAX_BIT 4
typedef erts_aint32_t ErtsProcLocks;
@@ -82,19 +82,17 @@ typedef struct erts_proc_lock_t_ {
/* Each erts_mtx_t has its own lock counter ^ */
#define ERTS_LCNT_PROCLOCK_IDX_MAIN 0
- #define ERTS_LCNT_PROCLOCK_IDX_LINK 1
- #define ERTS_LCNT_PROCLOCK_IDX_MSGQ 2
- #define ERTS_LCNT_PROCLOCK_IDX_BTM 3
- #define ERTS_LCNT_PROCLOCK_IDX_STATUS 4
- #define ERTS_LCNT_PROCLOCK_IDX_TRACE 5
+ #define ERTS_LCNT_PROCLOCK_IDX_MSGQ 1
+ #define ERTS_LCNT_PROCLOCK_IDX_BTM 2
+ #define ERTS_LCNT_PROCLOCK_IDX_STATUS 3
+ #define ERTS_LCNT_PROCLOCK_IDX_TRACE 4
- #define ERTS_LCNT_PROCLOCK_COUNT 6
+ #define ERTS_LCNT_PROCLOCK_COUNT 5
erts_lcnt_ref_t lcnt_carrier;
#endif
#elif ERTS_PROC_LOCK_RAW_MUTEX_IMPL
erts_mtx_t main;
- erts_mtx_t link;
erts_mtx_t msgq;
erts_mtx_t btm;
erts_mtx_t status;
@@ -118,27 +116,18 @@ typedef struct erts_proc_lock_t_ {
#define ERTS_PROC_LOCK_MAIN (((ErtsProcLocks) 1) << 0)
/*
- * Link lock:
- * Protects the following fields in the process structure:
- * * nlinks
- * * monitors
- * * suspend_monitors
- */
-#define ERTS_PROC_LOCK_LINK (((ErtsProcLocks) 1) << 1)
-
-/*
* Message queue lock:
* Protects the following fields in the process structure:
* * msg_inq
*/
-#define ERTS_PROC_LOCK_MSGQ (((ErtsProcLocks) 1) << 2)
+#define ERTS_PROC_LOCK_MSGQ (((ErtsProcLocks) 1) << 1)
/*
* Bif timer lock:
* Protects the following fields in the process structure:
* * bif_timers
*/
-#define ERTS_PROC_LOCK_BTM (((ErtsProcLocks) 1) << 3)
+#define ERTS_PROC_LOCK_BTM (((ErtsProcLocks) 1) << 2)
/*
* Status lock:
@@ -148,7 +137,7 @@ typedef struct erts_proc_lock_t_ {
* * sys_tasks
* * ...
*/
-#define ERTS_PROC_LOCK_STATUS (((ErtsProcLocks) 1) << 4)
+#define ERTS_PROC_LOCK_STATUS (((ErtsProcLocks) 1) << 3)
/*
* Trace message lock:
@@ -277,9 +266,6 @@ void erts_lcnt_proc_lock(erts_proc_lock_t *lock, ErtsProcLocks locks) {
if (locks & ERTS_PROC_LOCK_MAIN) {
erts_lcnt_lock_idx(carrier, ERTS_LCNT_PROCLOCK_IDX_MAIN);
}
- if (locks & ERTS_PROC_LOCK_LINK) {
- erts_lcnt_lock_idx(carrier, ERTS_LCNT_PROCLOCK_IDX_LINK);
- }
if (locks & ERTS_PROC_LOCK_MSGQ) {
erts_lcnt_lock_idx(carrier, ERTS_LCNT_PROCLOCK_IDX_MSGQ);
}
@@ -307,9 +293,6 @@ void erts_lcnt_proc_lock_post_x(erts_proc_lock_t *lock, ErtsProcLocks locks,
if (locks & ERTS_PROC_LOCK_MAIN) {
erts_lcnt_lock_post_x_idx(carrier, ERTS_LCNT_PROCLOCK_IDX_MAIN, file, line);
}
- if (locks & ERTS_PROC_LOCK_LINK) {
- erts_lcnt_lock_post_x_idx(carrier, ERTS_LCNT_PROCLOCK_IDX_LINK, file, line);
- }
if (locks & ERTS_PROC_LOCK_MSGQ) {
erts_lcnt_lock_post_x_idx(carrier, ERTS_LCNT_PROCLOCK_IDX_MSGQ, file, line);
}
@@ -336,9 +319,6 @@ void erts_lcnt_proc_lock_unacquire(erts_proc_lock_t *lock, ErtsProcLocks locks)
if (locks & ERTS_PROC_LOCK_MAIN) {
erts_lcnt_lock_unacquire_idx(carrier, ERTS_LCNT_PROCLOCK_IDX_MAIN);
}
- if (locks & ERTS_PROC_LOCK_LINK) {
- erts_lcnt_lock_unacquire_idx(carrier, ERTS_LCNT_PROCLOCK_IDX_LINK);
- }
if (locks & ERTS_PROC_LOCK_MSGQ) {
erts_lcnt_lock_unacquire_idx(carrier, ERTS_LCNT_PROCLOCK_IDX_MSGQ);
}
@@ -365,9 +345,6 @@ void erts_lcnt_proc_unlock(erts_proc_lock_t *lock, ErtsProcLocks locks) {
if (locks & ERTS_PROC_LOCK_MAIN) {
erts_lcnt_unlock_idx(carrier, ERTS_LCNT_PROCLOCK_IDX_MAIN);
}
- if (locks & ERTS_PROC_LOCK_LINK) {
- erts_lcnt_unlock_idx(carrier, ERTS_LCNT_PROCLOCK_IDX_LINK);
- }
if (locks & ERTS_PROC_LOCK_MSGQ) {
erts_lcnt_unlock_idx(carrier, ERTS_LCNT_PROCLOCK_IDX_MSGQ);
}
@@ -394,9 +371,6 @@ void erts_lcnt_proc_trylock(erts_proc_lock_t *lock, ErtsProcLocks locks, int res
if (locks & ERTS_PROC_LOCK_MAIN) {
erts_lcnt_trylock_idx(carrier, ERTS_LCNT_PROCLOCK_IDX_MAIN, res);
}
- if (locks & ERTS_PROC_LOCK_LINK) {
- erts_lcnt_trylock_idx(carrier, ERTS_LCNT_PROCLOCK_IDX_LINK, res);
- }
if (locks & ERTS_PROC_LOCK_MSGQ) {
erts_lcnt_trylock_idx(carrier, ERTS_LCNT_PROCLOCK_IDX_MSGQ, res);
}
@@ -640,9 +614,6 @@ erts_proc_raw_trylock__(Process *p, ErtsProcLocks locks)
if (locks & ERTS_PROC_LOCK_MAIN)
if (erts_mtx_trylock(&p->lock.main) == EBUSY)
goto busy_main;
- if (locks & ERTS_PROC_LOCK_LINK)
- if (erts_mtx_trylock(&p->lock.link) == EBUSY)
- goto busy_link;
if (locks & ERTS_PROC_LOCK_MSGQ)
if (erts_mtx_trylock(&p->lock.msgq) == EBUSY)
goto busy_msgq;
@@ -668,9 +639,6 @@ busy_btm:
if (locks & ERTS_PROC_LOCK_MSGQ)
erts_mtx_unlock(&p->lock.msgq);
busy_msgq:
- if (locks & ERTS_PROC_LOCK_LINK)
- erts_mtx_unlock(&p->lock.link);
-busy_link:
if (locks & ERTS_PROC_LOCK_MAIN)
erts_mtx_unlock(&p->lock.main);
busy_main:
@@ -741,8 +709,6 @@ erts_proc_lock__(Process *p,
#elif ERTS_PROC_LOCK_RAW_MUTEX_IMPL
if (locks & ERTS_PROC_LOCK_MAIN)
erts_mtx_lock(&p->lock.main);
- if (locks & ERTS_PROC_LOCK_LINK)
- erts_mtx_lock(&p->lock.link);
if (locks & ERTS_PROC_LOCK_MSGQ)
erts_mtx_lock(&p->lock.msgq);
if (locks & ERTS_PROC_LOCK_BTM)
@@ -844,8 +810,6 @@ erts_proc_unlock__(Process *p,
erts_mtx_unlock(&p->lock.btm);
if (locks & ERTS_PROC_LOCK_MSGQ)
erts_mtx_unlock(&p->lock.msgq);
- if (locks & ERTS_PROC_LOCK_LINK)
- erts_mtx_unlock(&p->lock.link);
if (locks & ERTS_PROC_LOCK_MAIN)
erts_mtx_unlock(&p->lock.main);
#endif
diff --git a/erts/emulator/beam/erl_ptab.h b/erts/emulator/beam/erl_ptab.h
index 4858cc8ab8..94f0247492 100644
--- a/erts/emulator/beam/erl_ptab.h
+++ b/erts/emulator/beam/erl_ptab.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2012-2016. All Rights Reserved.
+ * Copyright Ericsson AB 2012-2018. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -35,7 +35,7 @@
#include "erl_thr_progress.h"
#undef ERL_THR_PROGRESS_TSD_TYPE_ONLY
#include "erl_alloc.h"
-#include "erl_monitors.h"
+#include "erl_monitor_link.h"
#define ERTS_TRACER(P) ((P)->common.tracer)
#define ERTS_TRACER_MODULE(T) (CAR(list_val(T)))
@@ -44,6 +44,7 @@
#define ERTS_P_LINKS(P) ((P)->common.u.alive.links)
#define ERTS_P_MONITORS(P) ((P)->common.u.alive.monitors)
+#define ERTS_P_LT_MONITORS(P) ((P)->common.u.alive.lt_monitors)
#define IS_TRACED(p) \
(ERTS_TRACER(p) != NIL)
@@ -68,6 +69,7 @@ typedef struct {
struct reg_proc *reg;
ErtsLink *links;
ErtsMonitor *monitors;
+ ErtsMonitor *lt_monitors;
} alive;
/* --- While being released --- */
diff --git a/erts/emulator/beam/erl_rbtree.h b/erts/emulator/beam/erl_rbtree.h
index e59d6900b0..e50abf5cec 100644
--- a/erts/emulator/beam/erl_rbtree.h
+++ b/erts/emulator/beam/erl_rbtree.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2015-2017. All Rights Reserved.
+ * Copyright Ericsson AB 2015-2018. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -50,8 +50,14 @@
* - ERTS_RBT_GET_LEFT(T) - Get left child node.
* - ERTS_RBT_SET_LEFT(T, L) - Set left child node.
* - ERTS_RBT_GET_KEY(T) - Get key of node.
- * - ERTS_RBT_IS_LT(KX, KY) - Is key KX less than key KY?
- * - ERTS_RBT_IS_EQ(KX, KY) - Is key KX equal to key KY?
+ * Either:
+ * - ERTS_RBT_CMP_KEYS(KX, KY) - Compare keys...
+ * or:
+ * - ERTS_RBT_IS_LT(KX, KY) - Is key KX less than key KY?
+ * - ERTS_RBT_IS_EQ(KX, KY) - Is key KX equal to key KY?
+ *
+ * If ERTS_RBT_CMP_KEYS is defined ERTS_RBT_IS_LT and
+ * ERTS_RBT_IS_EQ will be redefined using ERTS_RBT_CMP_KEYS
*
* Optional defines:
*
@@ -337,6 +343,15 @@
* Should only be used for debuging.
*/
+#ifdef ERTS_RBT_CMP_KEYS
+
+# undef ERTS_RBT_IS_LT
+# define ERTS_RBT_IS_LT(KX, KY) (ERTS_RBT_CMP_KEYS((KX), (KY)) < 0)
+
+# undef ERTS_RBT_IS_EQ
+# define ERTS_RBT_IS_EQ(KX, KY) (ERTS_RBT_CMP_KEYS((KX), (KY)) == 0)
+
+#endif
/*
* Check that we have all mandatory defines
@@ -396,6 +411,16 @@
# error Missing definition of ERTS_RBT_IS_EQ
#endif
+#undef ERTS_RBT_IS_GT__
+#ifdef ERTS_RBT_CMP_KEYS
+# define ERTS_RBT_IS_GT__(KX, KY) \
+ (ERTS_RBT_CMP_KEYS((KX), (KY)) > 0)
+#else
+# define ERTS_RBT_IS_GT__(KX, KY) \
+ (!ERTS_RBT_IS_LT((KX), (KY)) && !ERTS_RBT_IS_EQ((KX), (KY)))
+
+#endif
+
#if defined(ERTS_RBT_HARD_DEBUG) || defined(DEBUG)
# ifndef ERTS_RBT_DEBUG
# define ERTS_RBT_DEBUG 1
@@ -1007,19 +1032,30 @@ ERTS_RBT_FUNC__(insert_aux__)(ERTS_RBT_T **root, ERTS_RBT_T *n, int lookup)
ERTS_RBT_T *p, *x = *root;
while (1) {
- ERTS_RBT_KEY_T kx;
+ ERTS_RBT_KEY_T kx = ERTS_RBT_GET_KEY(x);
ERTS_RBT_T *c;
+ int kres;
+#ifdef ERTS_RBT_CMP_KEYS
+ int kcmp = ERTS_RBT_CMP_KEYS(kn, kx);
+ kres = kcmp == 0;
+#else
+ kres = ERTS_RBT_IS_EQ(kn, kx);
+#endif
- kx = ERTS_RBT_GET_KEY(x);
-
- if (lookup && ERTS_RBT_IS_EQ(kn, kx)) {
+ if (lookup && kres) {
ERTS_RBT_HDBG_CHECK_TREE__(*root, NULL);
return x;
}
- if (ERTS_RBT_IS_LT(kn, kx)) {
+#ifdef ERTS_RBT_CMP_KEYS
+ kres = kcmp < 0;
+#else
+ kres = ERTS_RBT_IS_LT(kn, kx);
+#endif
+
+ if (kres) {
c = ERTS_RBT_GET_LEFT(x);
if (!c) {
ERTS_RBT_SET_PARENT(n, x);
@@ -1075,6 +1111,101 @@ ERTS_RBT_FUNC__(insert)(ERTS_RBT_T **root, ERTS_RBT_T *n)
#endif /* ERTS_RBT_WANT_INSERT */
+#ifdef ERTS_RBT_WANT_LOOKUP_CREATE
+static ERTS_INLINE ERTS_RBT_T *
+ERTS_RBT_FUNC__(lookup_create)(ERTS_RBT_T **root,
+ ERTS_RBT_KEY_T kn,
+ ERTS_RBT_T *(*create)(ERTS_RBT_KEY_T, void *),
+ void *arg,
+ int *created)
+{
+ ERTS_RBT_T *n;
+ ERTS_RBT_HDBG_CHECK_TREE__(*root, NULL);
+
+ if (!*root) {
+ n = (*create)(kn, arg);
+ ERTS_RBT_INIT_EMPTY_TNODE(n);
+ ERTS_RBT_ASSERT(ERTS_RBT_IS_EQ(ERTS_RBT_GET_KEY(n), kn));
+ ERTS_RBT_SET_BLACK(n);
+ *root = n;
+ *created = !0;
+#ifdef ERTS_RBT_UPDATE_ATTACHED_DATA_CHGROOT
+ ERTS_RBT_UPDATE_ATTACHED_DATA_CHGROOT(NULL, n);
+#endif
+ }
+ else {
+ ERTS_RBT_T *p, *x = *root;
+
+ while (1) {
+ ERTS_RBT_KEY_T kx = ERTS_RBT_GET_KEY(x);
+ ERTS_RBT_T *c;
+ int kres;
+#ifdef ERTS_RBT_CMP_KEYS
+ int kcmp = ERTS_RBT_CMP_KEYS(kn, kx);
+ kres = kcmp == 0;
+#else
+ kres = ERTS_RBT_IS_EQ(kn, kx);
+#endif
+
+ if (kres) {
+
+ ERTS_RBT_HDBG_CHECK_TREE__(*root, NULL);
+
+ *created = 0;
+ return x;
+ }
+
+#ifdef ERTS_RBT_CMP_KEYS
+ kres = kcmp < 0;
+#else
+ kres = ERTS_RBT_IS_LT(kn, kx);
+#endif
+
+ if (kres) {
+ c = ERTS_RBT_GET_LEFT(x);
+ if (!c) {
+ n = (*create)(kn, arg);
+ ERTS_RBT_INIT_EMPTY_TNODE(n);
+ ERTS_RBT_ASSERT(ERTS_RBT_IS_EQ(ERTS_RBT_GET_KEY(n), kn));
+ *created = !0;
+ ERTS_RBT_SET_PARENT(n, x);
+ ERTS_RBT_SET_LEFT(x, n);
+ p = x;
+ break;
+ }
+ }
+ else {
+ c = ERTS_RBT_GET_RIGHT(x);
+ if (!c) {
+ n = (*create)(kn, arg);
+ ERTS_RBT_INIT_EMPTY_TNODE(n);
+ ERTS_RBT_ASSERT(ERTS_RBT_IS_EQ(ERTS_RBT_GET_KEY(n), kn));
+ *created = !0;
+ ERTS_RBT_SET_PARENT(n, x);
+ ERTS_RBT_SET_RIGHT(x, n);
+ p = x;
+ break;
+ }
+ }
+
+ x = c;
+ }
+
+ ERTS_RBT_ASSERT(ERTS_RBT_IS_EQ(ERTS_RBT_GET_KEY(n), kn));
+ ERTS_RBT_ASSERT(p);
+
+ ERTS_RBT_SET_RED(n);
+ if (ERTS_RBT_IS_RED(p))
+ ERTS_RBT_FUNC__(insert_fixup__)(root, n);
+ }
+
+ ERTS_RBT_HDBG_CHECK_TREE__(*root, n);
+
+ return n;
+}
+
+#endif /* ERTS_RBT_WANT_LOOKUP_CREATE */
+
#ifdef ERTS_RBT_WANT_LOOKUP
static ERTS_RBT_API_INLINE__ ERTS_RBT_T *
@@ -1088,11 +1219,24 @@ ERTS_RBT_FUNC__(lookup)(ERTS_RBT_T *root, ERTS_RBT_KEY_T key)
while (1) {
ERTS_RBT_KEY_T kx = ERTS_RBT_GET_KEY(x);
ERTS_RBT_T *c;
+ int kres;
+#ifdef ERTS_RBT_CMP_KEYS
+ int kcmp = ERTS_RBT_CMP_KEYS(key, kx);
+ kres = kcmp == 0;
+#else
+ kres = ERTS_RBT_IS_EQ(key, kx);
+#endif
- if (ERTS_RBT_IS_EQ(key, kx))
+ if (kres)
return x;
- if (ERTS_RBT_IS_LT(key, kx)) {
+#ifdef ERTS_RBT_CMP_KEYS
+ kres = kcmp < 0;
+#else
+ kres = ERTS_RBT_IS_LT(key, kx);
+#endif
+
+ if (kres) {
c = ERTS_RBT_GET_LEFT(x);
if (!c)
return NULL;
@@ -1426,14 +1570,14 @@ ERTS_RBT_FUNC__(foreach_large)(ERTS_RBT_T *root,
#ifdef ERTS_RBT_WANT_FOREACH_YIELDING
-static ERTS_RBT_API_INLINE__ void
+static ERTS_RBT_API_INLINE__ int
ERTS_RBT_FUNC__(foreach_yielding)(ERTS_RBT_T *root,
void (*op)(ERTS_RBT_T *, void *),
void *arg,
ERTS_RBT_YIELD_STATE_T__ *ystate,
Sint ylimit)
{
- (void) ERTS_RBT_FUNC__(foreach_unordered__)(*root, 0, op, arg,
+ return ERTS_RBT_FUNC__(foreach_unordered__)(&root, 0, op, arg,
1, ystate, ylimit);
}
@@ -1630,8 +1774,7 @@ ERTS_RBT_FUNC__(hdbg_check_tree)(ERTS_RBT_T *root, ERTS_RBT_T *n)
kx = ERTS_RBT_GET_KEY(x);
kc = ERTS_RBT_GET_KEY(c);
- ERTS_RBT_ASSERT(ERTS_RBT_IS_LT(kc, kx)
- || ERTS_RBT_IS_EQ(kc, kx));
+ ERTS_RBT_ASSERT(!ERTS_RBT_IS_GT__(kc, kx));
x = c;
}
@@ -1649,8 +1792,8 @@ ERTS_RBT_FUNC__(hdbg_check_tree)(ERTS_RBT_T *root, ERTS_RBT_T *n)
kx = ERTS_RBT_GET_KEY(x);
kc = ERTS_RBT_GET_KEY(c);
- ERTS_RBT_ASSERT(ERTS_RBT_IS_LT(kx, kc)
- || ERTS_RBT_IS_EQ(kx, kc));
+ ERTS_RBT_ASSERT(!ERTS_RBT_IS_GT__(kx, kc));
+
x = c;
}
@@ -1672,8 +1815,8 @@ ERTS_RBT_FUNC__(hdbg_check_tree)(ERTS_RBT_T *root, ERTS_RBT_T *n)
kx = ERTS_RBT_GET_KEY(x);
kc = ERTS_RBT_GET_KEY(c);
- ERTS_RBT_ASSERT(ERTS_RBT_IS_LT(kx, kc)
- || ERTS_RBT_IS_EQ(kx, kc));
+ ERTS_RBT_ASSERT(!ERTS_RBT_IS_GT__(kx, kc));
+
/* Go down tree of x's sibling... */
x = c;
break;
@@ -1707,6 +1850,7 @@ ERTS_RBT_FUNC__(hdbg_check_tree)(ERTS_RBT_T *root, ERTS_RBT_T *n)
#undef ERTS_RBT_NEED_FOREACH_ORDERED__
#undef ERTS_RBT_NEED_HDBG_CHECK_TREE__
#undef ERTS_RBT_HDBG_CHECK_TREE__
+#undef ERTS_RBT_IS_GT__
#ifdef ERTS_RBT_UNDEF
# undef ERTS_RBT_PREFIX
@@ -1727,6 +1871,7 @@ ERTS_RBT_FUNC__(hdbg_check_tree)(ERTS_RBT_T *root, ERTS_RBT_T *n)
# undef ERTS_RBT_GET_LEFT
# undef ERTS_RBT_SET_LEFT
# undef ERTS_RBT_GET_KEY
+# undef ERTS_RBT_CMP_KEYS
# undef ERTS_RBT_IS_LT
# undef ERTS_RBT_IS_EQ
# undef ERTS_RBT_UNDEF
diff --git a/erts/emulator/beam/erl_sched_spec_pre_alloc.c b/erts/emulator/beam/erl_sched_spec_pre_alloc.c
index ab204303d7..4a6e02281a 100644
--- a/erts/emulator/beam/erl_sched_spec_pre_alloc.c
+++ b/erts/emulator/beam/erl_sched_spec_pre_alloc.c
@@ -47,6 +47,15 @@ erts_sspa_create(size_t blk_sz, int pa_size, int nthreads, const char* name)
int cix;
int no_blocks = pa_size;
int no_blocks_per_chunk;
+ size_t aligned_blk_sz;
+
+#if !defined(ERTS_STRUCTURE_ALIGNED_ALLOC)
+ /* Force 64-bit alignment... */
+ aligned_blk_sz = ((blk_sz - 1) / 8) * 8 + 8;
+#else
+ /* Alignment of structure is enough... */
+ aligned_blk_sz = blk_sz;
+#endif
if (!name) { /* schedulers only variant */
ASSERT(!nthreads);
@@ -68,7 +77,7 @@ erts_sspa_create(size_t blk_sz, int pa_size, int nthreads, const char* name)
}
no_blocks = no_blocks_per_chunk * nthreads;
chunk_mem_size = ERTS_ALC_CACHE_LINE_ALIGN_SIZE(sizeof(erts_sspa_chunk_header_t));
- chunk_mem_size += blk_sz * no_blocks_per_chunk;
+ chunk_mem_size += aligned_blk_sz * no_blocks_per_chunk;
chunk_mem_size = ERTS_ALC_CACHE_LINE_ALIGN_SIZE(chunk_mem_size);
tot_size = ERTS_ALC_CACHE_LINE_ALIGN_SIZE(sizeof(erts_sspa_data_t));
tot_size += chunk_mem_size * nthreads;
@@ -115,7 +124,7 @@ erts_sspa_create(size_t blk_sz, int pa_size, int nthreads, const char* name)
blk = (erts_sspa_blk_t *) p;
for (i = 0; i < no_blocks_per_chunk; i++) {
blk = (erts_sspa_blk_t *) p;
- p += blk_sz;
+ p += aligned_blk_sz;
blk->next_ptr = (erts_sspa_blk_t *) p;
}
diff --git a/erts/emulator/beam/erl_term.h b/erts/emulator/beam/erl_term.h
index 842802f8d9..18483fca35 100644
--- a/erts/emulator/beam/erl_term.h
+++ b/erts/emulator/beam/erl_term.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2000-2017. All Rights Reserved.
+ * Copyright Ericsson AB 2000-2018. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -55,9 +55,6 @@ struct erl_node_; /* Declared in erl_node_tables.h */
#if defined(ARCH_64)
# define TAG_PTR_MASK__ 0x7
# if !defined(ERTS_HAVE_OS_PHYSICAL_MEMORY_RESERVATION)
-# ifdef HIPE
-# error Hipe on 64-bit needs a real mmap as it does not support the literal tag
-# endif
# define TAG_LITERAL_PTR 0x4
# else
# undef TAG_LITERAL_PTR
@@ -270,7 +267,6 @@ _ET_DECLARE_CHECKED(Eterm*,list_val,Wterm)
#define is_byte(x) (((x) & ((~(Uint)0 << (_TAG_IMMED1_SIZE+8)) + _TAG_IMMED1_MASK)) == _TAG_IMMED1_SMALL)
#define is_valid_bit_size(x) (((Sint)(x)) >= 0 && ((x) & 0x7F) == _TAG_IMMED1_SMALL)
#define is_not_valid_bit_size(x) (!is_valid_bit_size((x)))
-#define MY_IS_SSMALL(x) (((Uint) ((((x)) >> (SMALL_BITS-1)) + 1)) < 2)
#define _unchecked_unsigned_val(x) ((x) >> _TAG_IMMED1_SIZE)
_ET_DECLARE_CHECKED(Uint,unsigned_val,Eterm)
#define unsigned_val(x) _ET_APPLY(unsigned_val,(x))
@@ -317,7 +313,8 @@ _ET_DECLARE_CHECKED(Uint,header_arity,Eterm)
#define MAX_ARITYVAL ((((Uint)1) << 24) - 1)
#define ERTS_MAX_TUPLE_SIZE MAX_ARITYVAL
-#define make_arityval(sz) _make_header((sz),_TAG_HEADER_ARITYVAL)
+#define make_arityval(sz) (ASSERT((sz) <= MAX_ARITYVAL), \
+ _make_header((sz),_TAG_HEADER_ARITYVAL))
#define is_arity_value(x) (((x) & _TAG_HEADER_MASK) == _TAG_HEADER_ARITYVAL)
#define is_sane_arity_value(x) ((((x) & _TAG_HEADER_MASK) == _TAG_HEADER_ARITYVAL) && \
(((x) >> _HEADER_ARITY_OFFS) <= MAX_ARITYVAL))
@@ -348,6 +345,9 @@ _ET_DECLARE_CHECKED(Uint,thing_subtag,Eterm)
*
* To help find code which makes unwarranted assumptions about zero,
* we now use a non-zero bit-pattern in debug mode.
+ *
+ * In order to be able to differentiata against values, the non-value
+ * needs to be tagged as a header of some sort.
*/
#if ET_DEBUG
# ifdef HIPE
@@ -358,7 +358,7 @@ _ET_DECLARE_CHECKED(Uint,thing_subtag,Eterm)
# define THE_NON_VALUE _make_header(0,_TAG_HEADER_FLOAT)
# endif
#else
-#define THE_NON_VALUE (0)
+#define THE_NON_VALUE (TAG_PRIMARY_HEADER)
#endif
#define is_non_value(x) ((x) == THE_NON_VALUE)
#define is_value(x) ((x) != THE_NON_VALUE)
@@ -863,7 +863,7 @@ do { \
((ErtsMRefThing *) (Hp))->mb = (Binp); \
((ErtsMRefThing *) (Hp))->next = (Ohp)->first; \
(Ohp)->first = (struct erl_off_heap_header*) (Hp); \
- ASSERT(erts_is_ref_numbers_magic(&(Binp)->refn)); \
+ ASSERT(erts_is_ref_numbers_magic((Binp)->refn)); \
} while (0)
#endif /* ARCH_32 */
diff --git a/erts/emulator/beam/erl_thr_progress.h b/erts/emulator/beam/erl_thr_progress.h
index fa936b5707..8c029bcf99 100644
--- a/erts/emulator/beam/erl_thr_progress.h
+++ b/erts/emulator/beam/erl_thr_progress.h
@@ -28,7 +28,7 @@
* Author: Rickard Green
*/
-#if !defined(ERL_THR_PROGRESS_H__TSD_TYPE__)
+#ifndef ERL_THR_PROGRESS_H__TSD_TYPE__
#define ERL_THR_PROGRESS_H__TSD_TYPE__
#include "sys.h"
diff --git a/erts/emulator/beam/erl_time.h b/erts/emulator/beam/erl_time.h
index 27164d50a0..968f21fd51 100644
--- a/erts/emulator/beam/erl_time.h
+++ b/erts/emulator/beam/erl_time.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2006-2017. All Rights Reserved.
+ * Copyright Ericsson AB 2006-2018. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -21,6 +21,8 @@
#ifndef ERL_TIME_H__
#define ERL_TIME_H__
+#include "erl_monitor_link.h"
+
#if 0
# define ERTS_TW_DEBUG
#endif
@@ -79,8 +81,8 @@ typedef ErtsMonotonicTime * ErtsNextTimeoutRef;
extern SysTimeval erts_first_emu_time;
-void erts_monitor_time_offset(Eterm id, Eterm ref);
-int erts_demonitor_time_offset(Eterm ref);
+void erts_monitor_time_offset(ErtsMonitor *mon);
+void erts_demonitor_time_offset(ErtsMonitor *mon);
int erts_init_time_sup(int, ErtsTimeWarpMode);
void erts_late_init_time_sup(void);
@@ -107,9 +109,6 @@ void erts_p_slpq(void);
void erts_get_now_cpu(Uint* megasec, Uint* sec, Uint* microsec);
#endif
-typedef UWord erts_approx_time_t;
-erts_approx_time_t erts_get_approx_time(void);
-
int erts_has_time_correction(void);
int erts_check_time_adj_support(int time_correction,
ErtsTimeWarpMode time_warp_mode);
diff --git a/erts/emulator/beam/erl_time_sup.c b/erts/emulator/beam/erl_time_sup.c
index f2e0900fec..4f91d9ad07 100644
--- a/erts/emulator/beam/erl_time_sup.c
+++ b/erts/emulator/beam/erl_time_sup.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1999-2017. All Rights Reserved.
+ * Copyright Ericsson AB 1999-2018. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -35,6 +35,7 @@
#include "erl_time.h"
#include "erl_driver.h"
#include "erl_nif.h"
+#include "erl_proc_sig_queue.h"
static erts_mtx_t erts_get_time_mtx;
@@ -191,17 +192,6 @@ static struct {
ErtsTimeSupData erts_time_sup__ erts_align_attribute(ERTS_CACHE_LINE_SIZE);
-/*
- * erts_get_approx_time() returns an *approximate* time
- * in seconds. NOTE that this time may jump backwards!!!
- */
-erts_approx_time_t
-erts_get_approx_time(void)
-{
- ErtsSystemTime stime = erts_os_system_time();
- return (erts_approx_time_t) ERTS_MONOTONIC_TO_SEC(stime);
-}
-
static ERTS_INLINE void
init_time_offset(ErtsMonotonicTime offset)
{
@@ -1881,36 +1871,33 @@ void erts_get_now_cpu(Uint* megasec, Uint* sec, Uint* microsec) {
#include "big.h"
void
-erts_monitor_time_offset(Eterm id, Eterm ref)
+erts_monitor_time_offset(ErtsMonitor *mon)
{
erts_mtx_lock(&erts_get_time_mtx);
- erts_add_monitor(&time_offset_monitors, MON_TIME_OFFSET, ref, id, NIL);
+ erts_monitor_list_insert(&time_offset_monitors, mon);
no_time_offset_monitors++;
erts_mtx_unlock(&erts_get_time_mtx);
}
-int
-erts_demonitor_time_offset(Eterm ref)
+void
+erts_demonitor_time_offset(ErtsMonitor *mon)
{
- int res;
- ErtsMonitor *mon;
- ASSERT(is_internal_ref(ref));
+ ErtsMonitorData *mdp = erts_monitor_to_data(mon);
+ ASSERT(erts_monitor_is_origin(mon));
+ ASSERT(mon->type == ERTS_MON_TYPE_TIME_OFFSET);
+
erts_mtx_lock(&erts_get_time_mtx);
- if (is_internal_ordinary_ref(ref))
- mon = erts_remove_monitor(&time_offset_monitors, ref);
- else
- mon = NULL;
- if (!mon)
- res = 0;
- else {
- ASSERT(no_time_offset_monitors > 0);
- no_time_offset_monitors--;
- res = 1;
- }
+
+ ASSERT(erts_monitor_is_in_table(&mdp->target));
+
+ erts_monitor_list_delete(&time_offset_monitors, &mdp->target);
+
+ ASSERT(no_time_offset_monitors > 0);
+ no_time_offset_monitors--;
+
erts_mtx_unlock(&erts_get_time_mtx);
- if (res)
- erts_destroy_monitor(mon);
- return res;
+
+ erts_monitor_release_both(mdp);
}
typedef struct {
@@ -1928,17 +1915,19 @@ static void
save_time_offset_monitor(ErtsMonitor *mon, void *vcntxt)
{
ErtsTimeOffsetMonitorContext *cntxt;
+ ErtsMonitorData *mdp = erts_monitor_to_data(mon);
Eterm *from_hp, *to_hp;
Uint mix;
int hix;
cntxt = (ErtsTimeOffsetMonitorContext *) vcntxt;
mix = (cntxt->ix)++;
- cntxt->to_mon_info[mix].pid = mon->u.pid;
+ ASSERT(is_internal_pid(mon->other.item));
+ cntxt->to_mon_info[mix].pid = mon->other.item;
to_hp = &cntxt->to_mon_info[mix].heap[0];
- ASSERT(is_internal_ordinary_ref(mon->ref));
- from_hp = internal_ref_val(mon->ref);
+ ASSERT(is_internal_ordinary_ref(mdp->ref));
+ from_hp = internal_ref_val(mdp->ref);
ASSERT(thing_arityval(*from_hp) + 1 == ERTS_REF_THING_SIZE);
for (hix = 0; hix < ERTS_REF_THING_SIZE; hix++)
@@ -1983,9 +1972,9 @@ send_time_offset_changed_notifications(void *new_offsetp)
cntxt.ix = 0;
cntxt.to_mon_info = to_mon_info;
- erts_doforall_monitors(time_offset_monitors,
- save_time_offset_monitor,
- &cntxt);
+ erts_monitor_list_foreach(time_offset_monitors,
+ save_time_offset_monitor,
+ &cntxt);
ASSERT(cntxt.ix == no_monitors);
}
@@ -2019,26 +2008,14 @@ send_time_offset_changed_notifications(void *new_offsetp)
ASSERT(*patch_refp == THE_NON_VALUE);
for (mix = 0; mix < no_monitors; mix++) {
- Process *rp = erts_proc_lookup(to_mon_info[mix].pid);
- if (rp) {
- Eterm ref = to_mon_info[mix].ref;
- ErtsProcLocks rp_locks = ERTS_PROC_LOCK_LINK;
- erts_proc_lock(rp, ERTS_PROC_LOCK_LINK);
- if (erts_lookup_monitor(ERTS_P_MONITORS(rp), ref)) {
- ErtsMessage *mp;
- ErlOffHeap *ohp;
- Eterm message;
-
- mp = erts_alloc_message_heap(rp, &rp_locks,
- hsz, &hp, &ohp);
- *patch_refp = ref;
- ASSERT(hsz == size_object(message_template));
- message = copy_struct(message_template, hsz, &hp, ohp);
- erts_queue_message(rp, rp_locks, mp, message, am_clock_service);
- }
- erts_proc_unlock(rp, rp_locks);
- }
- }
+ *patch_refp = to_mon_info[mix].ref;
+ erts_proc_sig_send_persistent_monitor_msg(ERTS_MON_TYPE_TIME_OFFSET,
+ *patch_refp,
+ am_clock_service,
+ to_mon_info[mix].pid,
+ message_template,
+ hsz);
+ }
erts_free(ERTS_ALC_T_TMP, tmp);
}
@@ -2227,6 +2204,8 @@ time_unit_conversion(Process *c_p, Eterm term, ErtsMonotonicTime val, ErtsMonoto
ERTS_BIF_PREP_RET(ret, make_time_val(c_p, result));
break;
#endif
+ case am_perf_counter:
+ goto trap_to_erlang_code;
default: {
Eterm value, native_res;
#ifndef ARCH_64
diff --git a/erts/emulator/beam/erl_trace.c b/erts/emulator/beam/erl_trace.c
index 4b996d8fc2..1e833539b3 100644
--- a/erts/emulator/beam/erl_trace.c
+++ b/erts/emulator/beam/erl_trace.c
@@ -2533,7 +2533,7 @@ load_tracer_nif(const ErtsTracer tracer)
for(i = 0; i < num_of_funcs; i++) {
for (j = 0; j < NIF_TRACER_TYPES; j++) {
- if (strcmp(tracers[j].name, funcs[i].name) == 0 && tracers[j].arity == funcs[i].arity) {
+ if (sys_strcmp(tracers[j].name, funcs[i].name) == 0 && tracers[j].arity == funcs[i].arity) {
tracers[j].cb = &(funcs[i]);
break;
}
@@ -2593,7 +2593,7 @@ erts_term_to_tracer(Eterm prefix, Eterm t)
state = tp[3];
}
} else {
- if (arityval(tp[0]) == 2 && is_atom(tp[2])) {
+ if (arityval(tp[0]) == 2 && is_atom(tp[1])) {
module = tp[1];
state = tp[2];
}
@@ -2783,24 +2783,28 @@ is_tracer_enabled(Process* c_p, ErtsProcLocks c_p_locks,
ASSERT(0);
}
- /* Only remove tracer on self() and ports */
+ /* Only remove tracer on (self() or ports) AND we are on a normal scheduler */
if (is_internal_port(t_p->id) || (c_p && c_p->common.id == t_p->id)) {
+ ErtsSchedulerData *esdp = erts_get_scheduler_data();
ErtsProcLocks c_p_xlocks = 0;
- if (is_internal_pid(t_p->id)) {
- ERTS_LC_ASSERT(erts_proc_lc_my_proc_locks(c_p) & ERTS_PROC_LOCK_MAIN);
- if (c_p_locks != ERTS_PROC_LOCKS_ALL) {
- c_p_xlocks = ~c_p_locks & ERTS_PROC_LOCKS_ALL;
- if (erts_proc_trylock(c_p, c_p_xlocks) == EBUSY) {
- erts_proc_unlock(c_p, c_p_locks & ~ERTS_PROC_LOCK_MAIN);
- erts_proc_lock(c_p, ERTS_PROC_LOCKS_ALL_MINOR);
+ if (esdp && !ERTS_SCHEDULER_IS_DIRTY(esdp)) {
+ if (is_internal_pid(t_p->id)) {
+ ERTS_LC_ASSERT(erts_proc_lc_my_proc_locks(c_p) & ERTS_PROC_LOCK_MAIN);
+ if (c_p_locks != ERTS_PROC_LOCKS_ALL) {
+ c_p_xlocks = ~c_p_locks & ERTS_PROC_LOCKS_ALL;
+ if (erts_proc_trylock(c_p, c_p_xlocks) == EBUSY) {
+ erts_proc_unlock(c_p, c_p_locks & ~ERTS_PROC_LOCK_MAIN);
+ erts_proc_lock(c_p, ERTS_PROC_LOCKS_ALL_MINOR);
+ }
}
}
- }
- erts_tracer_replace(t_p, erts_tracer_nil);
- t_p->trace_flags &= ~TRACEE_FLAGS;
- if (c_p_xlocks)
- erts_proc_unlock(c_p, c_p_xlocks);
+ erts_tracer_replace(t_p, erts_tracer_nil);
+ t_p->trace_flags &= ~TRACEE_FLAGS;
+
+ if (c_p_xlocks)
+ erts_proc_unlock(c_p, c_p_xlocks);
+ }
}
return 0;
@@ -3015,7 +3019,7 @@ static void *tracer_alloc_fun(void* tmpl)
ErtsTracerNif *obj = erts_alloc(ERTS_ALC_T_TRACER_NIF,
sizeof(ErtsTracerNif) +
sizeof(ErtsThrPrgrLaterOp));
- memcpy(obj, tmpl, sizeof(*obj));
+ sys_memcpy(obj, tmpl, sizeof(*obj));
return obj;
}
diff --git a/erts/emulator/beam/erl_unicode.c b/erts/emulator/beam/erl_unicode.c
index 2d1d1443a7..8673e029e6 100644
--- a/erts/emulator/beam/erl_unicode.c
+++ b/erts/emulator/beam/erl_unicode.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2008-2017. All Rights Reserved.
+ * Copyright Ericsson AB 2008-2018. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -144,7 +144,7 @@ static Eterm make_magic_bin_for_restart(Process *p, RestartContext *rc)
cleanup_restart_context_bin);
RestartContext *restartp = ERTS_MAGIC_BIN_DATA(mbp);
Eterm *hp;
- memcpy(restartp,rc,sizeof(RestartContext));
+ sys_memcpy(restartp,rc,sizeof(RestartContext));
hp = HAlloc(p, ERTS_MAGIC_REF_THING_SIZE);
return erts_mk_magic_ref(&hp, &MSO(p), mbp);
}
@@ -254,12 +254,12 @@ static Uint copy_utf8_bin(byte *target, byte *source, Uint size,
ASSERT(need > 0);
ASSERT(from_source > 0);
if (size < from_source) {
- memcpy(leftover + (*num_leftovers), source, size);
+ sys_memcpy(leftover + (*num_leftovers), source, size);
*num_leftovers += size;
return 0;
}
/* leftover has room for four bytes (see bif) */
- memcpy(leftover + (*num_leftovers),source,from_source);
+ sys_memcpy(leftover + (*num_leftovers),source,from_source);
c = copy_utf8_bin(target, leftover, need, NULL, NULL, &tmp_err_pos, characters);
if (tmp_err_pos != 0) {
*err_pos = source;
@@ -291,7 +291,7 @@ static Uint copy_utf8_bin(byte *target, byte *source, Uint size,
size -= 2; copied += 2;
} else if (((*source) & ((byte) 0xF0)) == 0xE0) {
if (leftover && size < 3) {
- memcpy(leftover, source, (int) size);
+ sys_memcpy(leftover, source, (int) size);
*num_leftovers = (int) size;
break;
}
@@ -313,7 +313,7 @@ static Uint copy_utf8_bin(byte *target, byte *source, Uint size,
size -= 3; copied += 3;
} else if (((*source) & ((byte) 0xF8)) == 0xF0) {
if (leftover && size < 4) {
- memcpy(leftover, source, (int) size);
+ sys_memcpy(leftover, source, (int) size);
*num_leftovers = (int) size;
break;
}
@@ -1158,14 +1158,15 @@ static ERTS_INLINE int
analyze_utf8(byte *source, Uint size, byte **err_pos, Uint *num_chars, int *left,
Sint *num_latin1_chars, Uint max_chars)
{
+ int res = ERTS_UTF8_OK;
Uint latin1_count;
int is_latin1;
+ Uint nchars = 0;
*err_pos = source;
if (num_latin1_chars) {
is_latin1 = 1;
latin1_count = 0;
}
- *num_chars = 0;
while (size) {
if (((*source) & ((byte) 0x80)) == 0) {
source++;
@@ -1174,11 +1175,13 @@ analyze_utf8(byte *source, Uint size, byte **err_pos, Uint *num_chars, int *left
latin1_count++;
} else if (((*source) & ((byte) 0xE0)) == 0xC0) {
if (size < 2) {
- return ERTS_UTF8_INCOMPLETE;
+ res = ERTS_UTF8_INCOMPLETE;
+ break;
}
if (((source[1] & ((byte) 0xC0)) != 0x80) ||
((*source) < 0xC2) /* overlong */) {
- return ERTS_UTF8_ERROR;
+ res = ERTS_UTF8_ERROR;
+ break;
}
if (num_latin1_chars) {
latin1_count++;
@@ -1189,16 +1192,19 @@ analyze_utf8(byte *source, Uint size, byte **err_pos, Uint *num_chars, int *left
size -= 2;
} else if (((*source) & ((byte) 0xF0)) == 0xE0) {
if (size < 3) {
- return ERTS_UTF8_INCOMPLETE;
+ res = ERTS_UTF8_INCOMPLETE;
+ break;
}
if (((source[1] & ((byte) 0xC0)) != 0x80) ||
((source[2] & ((byte) 0xC0)) != 0x80) ||
(((*source) == 0xE0) && (source[1] < 0xA0)) /* overlong */ ) {
- return ERTS_UTF8_ERROR;
+ res = ERTS_UTF8_ERROR;
+ break;
}
if ((((*source) & ((byte) 0xF)) == 0xD) &&
((source[1] & 0x20) != 0)) {
- return ERTS_UTF8_ERROR;
+ res = ERTS_UTF8_ERROR;
+ break;
}
source += 3;
size -= 3;
@@ -1206,37 +1212,47 @@ analyze_utf8(byte *source, Uint size, byte **err_pos, Uint *num_chars, int *left
is_latin1 = 0;
} else if (((*source) & ((byte) 0xF8)) == 0xF0) {
if (size < 4) {
- return ERTS_UTF8_INCOMPLETE;
+ res = ERTS_UTF8_INCOMPLETE;
+ break;
}
if (((source[1] & ((byte) 0xC0)) != 0x80) ||
((source[2] & ((byte) 0xC0)) != 0x80) ||
((source[3] & ((byte) 0xC0)) != 0x80) ||
(((*source) == 0xF0) && (source[1] < 0x90)) /* overlong */) {
- return ERTS_UTF8_ERROR;
+ res = ERTS_UTF8_ERROR;
+ break;
}
if ((((*source) & ((byte)0x7)) > 0x4U) ||
((((*source) & ((byte)0x7)) == 0x4U) &&
((source[1] & ((byte)0x3F)) > 0xFU))) {
- return ERTS_UTF8_ERROR;
+ res = ERTS_UTF8_ERROR;
+ break;
}
source += 4;
size -= 4;
if (num_latin1_chars)
is_latin1 = 0;
} else {
- return ERTS_UTF8_ERROR;
+ res = ERTS_UTF8_ERROR;
+ break;
}
- ++(*num_chars);
+ ++nchars;
*err_pos = source;
- if (max_chars && size > 0 && *num_chars == max_chars)
- return ERTS_UTF8_OK_MAX_CHARS;
+ if (max_chars && size > 0 && nchars == max_chars) {
+ res = ERTS_UTF8_OK_MAX_CHARS;
+ break;
+ }
if (left && --(*left) <= 0 && size) {
- return ERTS_UTF8_ANALYZE_MORE;
+ res = ERTS_UTF8_ANALYZE_MORE;
+ break;
}
}
+
+ *num_chars = nchars;
if (num_latin1_chars)
*num_latin1_chars = is_latin1 ? latin1_count : -1;
- return ERTS_UTF8_OK;
+
+ return res;
}
int erts_analyze_utf8(byte *source, Uint size,
@@ -1252,29 +1268,18 @@ int erts_analyze_utf8_x(byte *source, Uint size,
return analyze_utf8(source, size, err_pos, num_chars, left, num_latin1_chars, max_chars);
}
-/*
- * No errors should be able to occur - no overlongs, no malformed, no nothing
- */
-static Eterm do_utf8_to_list(Process *p, Uint num, byte *bytes, Uint sz,
- Uint left,
- Uint *num_built, Uint *num_eaten, Eterm tail)
+static ERTS_INLINE Eterm
+make_list_from_utf8_buf(Eterm **hpp, Uint num,
+ byte *bytes, Uint sz,
+ Uint *num_built, Uint *num_eaten,
+ Eterm tail)
{
Eterm *hp;
Eterm ret;
+ Uint left = num;
byte *source, *ssource;
Uint unipoint;
-
- ASSERT(num > 0);
- if (left < num) {
- if (left > 0)
- num = left;
- else
- num = 1;
- }
-
- *num_built = num; /* Always */
-
- hp = HAlloc(p,num * 2);
+ hp = *hpp;
ret = tail;
source = bytes + sz;
ssource = source;
@@ -1302,20 +1307,97 @@ static Eterm do_utf8_to_list(Process *p, Uint num, byte *bytes, Uint sz,
}
ret = CONS(hp,make_small(unipoint),ret);
hp += 2;
- if (--num <= 0) {
+ if (--left <= 0) {
break;
}
}
+ *hpp = hp;
+ *num_built = num; /* Always */
*num_eaten = (ssource - source);
return ret;
}
+/*
+ * No errors should be able to occur - no overlongs, no malformed, no nothing
+ */
+static Eterm do_utf8_to_list(Process *p, Uint num, byte *bytes, Uint sz,
+ Uint left,
+ Uint *num_built, Uint *num_eaten, Eterm tail)
+{
+ Eterm *hp;
+
+ ASSERT(num > 0);
+ if (left < num) {
+ if (left > 0)
+ num = left;
+ else
+ num = 1;
+ }
+
+ hp = HAlloc(p,num * 2);
+
+ return make_list_from_utf8_buf(&hp, num, bytes, sz,
+ num_built, num_eaten,
+ tail);
+}
Eterm erts_utf8_to_list(Process *p, Uint num, byte *bytes, Uint sz, Uint left,
Uint *num_built, Uint *num_eaten, Eterm tail)
{
return do_utf8_to_list(p, num, bytes, sz, left, num_built, num_eaten, tail);
}
+Uint erts_atom_to_string_length(Eterm atom)
+{
+ Atom *ap;
+
+ ASSERT(is_atom(atom));
+ ap = atom_tab(atom_val(atom));
+
+ if (ap->latin1_chars >= 0)
+ return (Uint) ap->len;
+ else {
+ byte* err_pos;
+ Uint num_chars;
+#ifdef DEBUG
+ int ares =
+#endif
+ erts_analyze_utf8(ap->name, ap->len, &err_pos, &num_chars, NULL);
+ ASSERT(ares == ERTS_UTF8_OK);
+
+ return num_chars;
+ }
+}
+
+Eterm erts_atom_to_string(Eterm **hpp, Eterm atom)
+{
+ Atom *ap;
+
+ ASSERT(is_atom(atom));
+ ap = atom_tab(atom_val(atom));
+ if (ap->latin1_chars >= 0)
+ return buf_to_intlist(hpp, (char*)ap->name, ap->len, NIL);
+ else {
+ Eterm res;
+ byte* err_pos;
+ Uint num_chars, num_built, num_eaten;
+#ifdef DEBUG
+ Eterm *hp_start = *hpp;
+ int ares =
+#endif
+ erts_analyze_utf8(ap->name, ap->len, &err_pos, &num_chars, NULL);
+ ASSERT(ares == ERTS_UTF8_OK);
+
+ res = make_list_from_utf8_buf(hpp, num_chars, ap->name, ap->len,
+ &num_built, &num_eaten, NIL);
+
+ ASSERT(num_built == num_chars);
+ ASSERT(num_eaten == ap->len);
+ ASSERT(*hpp - hp_start == 2*num_chars);
+
+ return res;
+ }
+}
+
static int is_candidate(Uint cp)
{
int index,pos;
@@ -1988,7 +2070,7 @@ char *erts_convert_filename_to_encoding(Eterm name, char *statbuf, size_t statbu
is_list(name) ||
(allow_empty && is_nil(name))) {
Sint need;
- if ((need = erts_native_filename_need(name,encoding)) < 0) {
+ if ((need = erts_native_filename_need(name, encoding)) < 0) {
return NULL;
}
if (encoding == ERL_FILENAME_WIN_WCHAR) {
@@ -2026,7 +2108,7 @@ char *erts_convert_filename_to_encoding(Eterm name, char *statbuf, size_t statbu
} else {
name_buf = statbuf;
}
- memcpy(name_buf,bytes,size);
+ sys_memcpy(name_buf,bytes,size);
name_buf[size]=0;
} else {
name_buf = erts_convert_filename_to_wchar(bytes, size,
@@ -2083,18 +2165,9 @@ char* erts_convert_filename_to_wchar(byte* bytes, Uint size,
return name_buf;
}
-
-static int filename_len_16bit(byte *str)
-{
- byte *p = str;
- while(*p != '\0' || p[1] != '\0') {
- p += 2;
- }
- return (p - str);
-}
-Eterm erts_convert_native_to_filename(Process *p, byte *bytes)
+Eterm erts_convert_native_to_filename(Process *p, size_t size, byte *bytes)
{
- Uint size,num_chars;
+ Uint num_chars;
Eterm *hp;
byte *err_pos;
Uint num_built; /* characters */
@@ -2108,7 +2181,6 @@ Eterm erts_convert_native_to_filename(Process *p, byte *bytes)
case ERL_FILENAME_UTF8_MAC:
mac = 1;
case ERL_FILENAME_UTF8:
- size = strlen((char *) bytes);
if (size == 0)
return NIL;
if (erts_analyze_utf8(bytes,size,&err_pos,&num_chars,NULL) != ERTS_UTF8_OK) {
@@ -2123,7 +2195,6 @@ Eterm erts_convert_native_to_filename(Process *p, byte *bytes)
}
return ret;
case ERL_FILENAME_WIN_WCHAR:
- size=filename_len_16bit(bytes);
if ((size % 2) != 0) { /* Panic fixup to avoid crashing the emulator */
size--;
hp = HAlloc(p, size+2);
@@ -2146,13 +2217,12 @@ Eterm erts_convert_native_to_filename(Process *p, byte *bytes)
goto noconvert;
}
noconvert:
- size = strlen((char *) bytes);
hp = HAlloc(p, 2 * size);
return erts_bin_bytes_to_list(NIL, hp, bytes, size, 0);
}
-Sint erts_native_filename_need(Eterm ioterm, int encoding)
+Sint erts_native_filename_need(Eterm ioterm, int encoding)
{
Eterm *objp;
Eterm obj;
@@ -2194,6 +2264,20 @@ Sint erts_native_filename_need(Eterm ioterm, int encoding)
default:
need = -1;
}
+ /*
+ * Do not allow null in
+ * the middle of filenames
+ */
+ if (need > 0) {
+ byte *name = ap->name;
+ int len = ap->len;
+ for (i = 0; i < len; i++) {
+ if (name[i] == 0) {
+ need = -1;
+ break;
+ }
+ }
+ }
DESTROY_ESTACK(stack);
return need;
}
@@ -2224,6 +2308,14 @@ L_Again: /* Restart with sublist, old listend was pushed on stack */
if (is_small(obj)) { /* Always small */
for(;;) {
Uint x = unsigned_val(obj);
+ /*
+ * Do not allow null in
+ * the middle of filenames
+ */
+ if (x == 0) {
+ DESTROY_ESTACK(stack);
+ return ((Sint) -1);
+ }
switch (encoding) {
case ERL_FILENAME_LATIN1:
if (x > 255) {
@@ -2497,6 +2589,38 @@ void erts_copy_utf8_to_utf16_little(byte *target, byte *bytes, int num_chars)
}
/*
+ * *** Requirements on Raw Filename Format ***
+ *
+ * These requirements are due to the 'filename' module
+ * in stdlib. This since it is documented that it
+ * should be able to operate on raw filenames as well
+ * as ordinary filenames.
+ *
+ * A raw filename *must* be a byte sequence where:
+ * 1. Codepoints 0-127 (7-bit ascii) *must* be encoded
+ * as a byte with the corresponding value. That is,
+ * the most significant bit in the byte encoding the
+ * codepoint is never set.
+ * 2. Codepoints greater than 127 *must* be encoded
+ * with the most significant bit set in *every* byte
+ * encoding it.
+ *
+ * Latin1 and UTF-8 meet these requirements while
+ * UTF-16 and UTF-32 don't.
+ *
+ * On Windows filenames are natively stored as malformed
+ * UTF-16LE (lonely surrogates may appear). A more correct
+ * description than UTF-16 would be an array of 16-bit
+ * words... In order to meet the requirements of the
+ * raw file format we convert the malformed UTF-16LE to
+ * malformed UTF-8 which meet the requirements.
+ *
+ * Note that these requirements are today only OTP
+ * internal (erts-stdlib internal) requirements that
+ * could be changed.
+ */
+
+/*
* This internal bif converts a filename to whatever format is suitable for the file driver
* It also adds zero termination so that prim_file needn't bother with the character encoding
* of the file driver
@@ -2507,6 +2631,12 @@ BIF_RETTYPE prim_file_internal_name2native_1(BIF_ALIST_1)
Sint need;
Eterm bin_term;
byte* bin_p;
+
+ /*
+ * See comment on "Requirements on Raw Filename Format"
+ * above.
+ */
+
/* Prim file explicitly does not allow atoms, although we could
very well cope with it. Instead of letting 'file' handle them,
it would probably be more efficient to handle them here. Subject to
@@ -2524,10 +2654,16 @@ BIF_RETTYPE prim_file_internal_name2native_1(BIF_ALIST_1)
size = binary_size(BIF_ARG_1);
bytes = erts_get_aligned_binary_bytes(BIF_ARG_1, &temp_alloc);
if (encoding != ERL_FILENAME_WIN_WCHAR) {
+ Uint i;
/*Add 0 termination only*/
bin_term = new_binary(BIF_P, NULL, size+1);
bin_p = binary_bytes(bin_term);
- memcpy(bin_p,bytes,size);
+ for (i = 0; i < size; i++) {
+ /* Don't allow null in the middle of filenames... */
+ if (bytes[i] == 0)
+ goto bin_name_error;
+ bin_p[i] = bytes[i];
+ }
bin_p[size]=0;
erts_free_aligned_binary_bytes(temp_alloc);
BIF_RET(bin_term);
@@ -2541,6 +2677,9 @@ BIF_RETTYPE prim_file_internal_name2native_1(BIF_ALIST_1)
bin_term = new_binary(BIF_P, 0, (size+1)*2);
bin_p = binary_bytes(bin_term);
while (size--) {
+ /* Don't allow null in the middle of filenames... */
+ if (*bytes == 0)
+ goto bin_name_error;
*bin_p++ = *bytes++;
*bin_p++ = 0;
}
@@ -2558,11 +2697,14 @@ BIF_RETTYPE prim_file_internal_name2native_1(BIF_ALIST_1)
bin_p[num_chars*2+1] = 0;
erts_free_aligned_binary_bytes(temp_alloc);
BIF_RET(bin_term);
+ bin_name_error:
+ erts_free_aligned_binary_bytes(temp_alloc);
+ BIF_ERROR(BIF_P,BADARG);
} /* binary */
- if ((need = erts_native_filename_need(BIF_ARG_1,encoding)) < 0) {
- BIF_ERROR(BIF_P,BADARG);
+ if ((need = erts_native_filename_need(BIF_ARG_1, encoding)) < 0) {
+ BIF_ERROR(BIF_P,BADARG);
}
if (encoding == ERL_FILENAME_WIN_WCHAR) {
need += 2;
@@ -2596,6 +2738,11 @@ BIF_RETTYPE prim_file_internal_native2name_1(BIF_ALIST_1)
Eterm ret;
int mac = 0;
+ /*
+ * See comment on "Requirements on Raw Filename Format"
+ * above.
+ */
+
if (is_not_binary(BIF_ARG_1)) {
BIF_ERROR(BIF_P,BADARG);
}
diff --git a/erts/emulator/beam/erl_unicode.h b/erts/emulator/beam/erl_unicode.h
index e01eaa787e..31369fc8f9 100644
--- a/erts/emulator/beam/erl_unicode.h
+++ b/erts/emulator/beam/erl_unicode.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2008-2016. All Rights Reserved.
+ * Copyright Ericsson AB 2008-2018. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -21,4 +21,7 @@
#ifndef _ERL_UNICODE_H
#define _ERL_UNICODE_H
+Uint erts_atom_to_string_length(Eterm atom);
+Eterm erts_atom_to_string(Eterm **hpp, Eterm atom);
+
#endif /* _ERL_UNICODE_H */
diff --git a/erts/emulator/beam/erl_utils.h b/erts/emulator/beam/erl_utils.h
index 44d8c85867..e4087e0ac8 100644
--- a/erts/emulator/beam/erl_utils.h
+++ b/erts/emulator/beam/erl_utils.h
@@ -91,7 +91,7 @@ Eterm erts_bld_tuple(Uint **hpp, Uint *szp, Uint arity, ...);
#define erts_bld_tuple5(H,S,E1,E2,E3,E4,E5) erts_bld_tuple(H,S,5,E1,E2,E3,E4,E5)
Eterm erts_bld_tuplev(Uint **hpp, Uint *szp, Uint arity, Eterm terms[]);
Eterm erts_bld_string_n(Uint **hpp, Uint *szp, const char *str, Sint len);
-#define erts_bld_string(hpp,szp,str) erts_bld_string_n(hpp,szp,str,strlen(str))
+#define erts_bld_string(hpp,szp,str) erts_bld_string_n(hpp,szp,str,sys_strlen(str))
Eterm erts_bld_list(Uint **hpp, Uint *szp, Sint length, Eterm terms[]);
Eterm erts_bld_2tup_list(Uint **hpp, Uint *szp,
Sint length, Eterm terms1[], Uint terms2[]);
diff --git a/erts/emulator/beam/erl_vm.h b/erts/emulator/beam/erl_vm.h
index 295130f60c..f8391fb665 100644
--- a/erts/emulator/beam/erl_vm.h
+++ b/erts/emulator/beam/erl_vm.h
@@ -69,7 +69,7 @@
# ifdef CHECK_FOR_HOLES
# define INIT_HEAP_MEM(p,sz) erts_set_hole_marker(HEAP_TOP(p), (sz))
# else
-# define INIT_HEAP_MEM(p,sz) memset(HEAP_TOP(p),0x01,(sz)*sizeof(Eterm*))
+# define INIT_HEAP_MEM(p,sz) sys_memset(HEAP_TOP(p),0x01,(sz)*sizeof(Eterm*))
# endif
#else
# define INIT_HEAP_MEM(p,sz) ((void)0)
@@ -200,11 +200,24 @@ extern int erts_pd_initial_size;/* Initial Process dictionary table size */
#include "erl_term.h"
-#ifdef NO_JUMP_TABLE
-#define BeamOp(Op) (Op)
+#if defined(NO_JUMP_TABLE)
+# define BeamOpsAreInitialized() (1)
+# define BeamOpCodeAddr(OpCode) ((BeamInstr)(OpCode))
#else
extern void** beam_ops;
-#define BeamOp(Op) beam_ops[(Op)]
+# define BeamOpsAreInitialized() (beam_ops != 0)
+# define BeamOpCodeAddr(OpCode) ((BeamInstr)beam_ops[(OpCode)])
#endif
+#if defined(ARCH_64) && defined(CODE_MODEL_SMALL)
+# define BeamCodeAddr(InstrWord) ((BeamInstr)(Uint32)(InstrWord))
+# define BeamSetCodeAddr(InstrWord, Addr) (((InstrWord) & ~((1ull << 32)-1)) | (Addr))
+# define BeamExtraData(InstrWord) ((InstrWord) >> 32)
+#else
+# define BeamCodeAddr(InstrWord) ((BeamInstr)(InstrWord))
+# define BeamSetCodeAddr(InstrWord, Addr) (Addr)
+#endif
+
+#define BeamIsOpCode(InstrWord, OpCode) (BeamCodeAddr(InstrWord) == BeamOpCodeAddr(OpCode))
+
#endif /* __ERL_VM_H__ */
diff --git a/erts/emulator/beam/erlang_dtrace.d b/erts/emulator/beam/erlang_dtrace.d
index 237889e0f5..c47a37eb62 100644
--- a/erts/emulator/beam/erlang_dtrace.d
+++ b/erts/emulator/beam/erlang_dtrace.d
@@ -55,7 +55,8 @@ provider erlang {
* @param sender the PID (string form) of the sender
* @param receiver the PID (string form) of the receiver
* @param size the size of the message being delivered (words)
- * @param token_label for the sender's sequential trace token
+ * @param token_label for the sender's sequential trace token. This will be
+ * INT_MIN if the label does not fit into a 32-bit integer.
* @param token_previous count for the sender's sequential trace token
* @param token_current count for the sender's sequential trace token
*/
@@ -73,7 +74,8 @@ provider erlang {
* @param node_name the Erlang node name (string form) of the receiver
* @param receiver the PID/name (string form) of the receiver
* @param size the size of the message being delivered (words)
- * @param token_label for the sender's sequential trace token
+ * @param token_label for the sender's sequential trace token. This will be
+ * INT_MIN if the label does not fit into a 32-bit integer.
* @param token_previous count for the sender's sequential trace token
* @param token_current count for the sender's sequential trace token
*/
@@ -98,7 +100,8 @@ provider erlang {
* @param receiver the PID (string form) of the receiver
* @param size the size of the message being delivered (words)
* @param queue_len length of the queue of the receiving process
- * @param token_label for the sender's sequential trace token
+ * @param token_label for the sender's sequential trace token. This will be
+ * INT_MIN if the label does not fit into a 32-bit integer.
* @param token_previous count for the sender's sequential trace token
* @param token_current count for the sender's sequential trace token
*/
@@ -122,7 +125,8 @@ provider erlang {
* @param receiver the PID (string form) of the receiver
* @param size the size of the message being delivered (words)
* @param queue_len length of the queue of the receiving process
- * @param token_label for the sender's sequential trace token
+ * @param token_label for the sender's sequential trace token. This will be
+ * INT_MIN if the label does not fit into a 32-bit integer.
* @param token_previous count for the sender's sequential trace token
* @param token_current count for the sender's sequential trace token
*/
@@ -273,7 +277,8 @@ provider erlang {
* @param node_name the Erlang node name (string form) of the receiver
* @param receiver the PID (string form) of the process receiving EXIT signal
* @param reason the reason for the exit (may be truncated)
- * @param token_label for the sender's sequential trace token
+ * @param token_label for the sender's sequential trace token. This will be
+ * INT_MIN if the label does not fit into a 32-bit integer.
* @param token_previous count for the sender's sequential trace token
* @param token_current count for the sender's sequential trace token
*/
@@ -634,72 +639,6 @@ provider erlang {
*/
probe aio_pool__get(char *, int);
- /* Probes for efile_drv.c */
-
- /**
- * Entry into the efile_drv.c file I/O driver
- *
- * For a list of command numbers used by this driver, see the section
- * "Guide to efile_drv.c probe arguments" in ../../../HOWTO/DTRACE.md.
- * That section also contains explanation of the various integer and
- * string arguments that may be present when any particular probe fires.
- *
- * NOTE: Not all Linux platforms (using SystemTap) can support
- * arguments beyond arg9.
- *
- *
- * TODO: Adding the port string, args[10], is a pain. Making that
- * port string available to all the other efile_drv.c probes
- * will be more pain. Is the pain worth it? If yes, then
- * add them everywhere else and grit our teeth. If no, then
- * rip it out.
- *
- * @param thread-id number of the scheduler Pthread arg0
- * @param tag number: {thread-id, tag} uniquely names a driver operation
- * @param user-tag string arg2
- * @param command number arg3
- * @param string argument 1 arg4
- * @param string argument 2 arg5
- * @param integer argument 1 arg6
- * @param integer argument 2 arg7
- * @param integer argument 3 arg8
- * @param integer argument 4 arg9
- * @param port the port ID of the busy port args[10]
- */
- probe efile_drv__entry(int, int, char *, int, char *, char *,
- int64_t, int64_t, int64_t, int64_t, char *);
-
- /**
- * Entry into the driver's internal work function. Computation here
- * is performed by a async worker pool Pthread.
- *
- * @param thread-id number
- * @param tag number
- * @param command number
- */
- probe efile_drv__int_entry(int, int, int);
-
- /**
- * Return from the driver's internal work function.
- *
- * @param thread-id number
- * @param tag number
- * @param command number
- */
- probe efile_drv__int_return(int, int, int);
-
- /**
- * Return from the efile_drv.c file I/O driver
- *
- * @param thread-id number arg0
- * @param tag number arg1
- * @param user-tag string arg2
- * @param command number arg3
- * @param Success? 1 is success, 0 is failure arg4
- * @param If failure, the errno of the error. arg5
- */
- probe efile_drv__return(int, int, char *, int, int, int);
-
/*
* The set of probes called by the erlang tracer nif backend. In order
diff --git a/erts/emulator/beam/export.c b/erts/emulator/beam/export.c
index c81503f722..946ffeffb8 100644
--- a/erts/emulator/beam/export.c
+++ b/erts/emulator/beam/export.c
@@ -48,7 +48,6 @@ static erts_atomic_t total_entries_bytes;
*/
erts_mtx_t export_staging_lock;
-extern BeamInstr* em_call_error_handler;
extern BeamInstr* em_call_traced_function;
struct export_entry
@@ -130,7 +129,10 @@ 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] = (BeamInstr) em_call_error_handler;
+ obj->beam[0] = 0;
+ if (BeamOpsAreInitialized()) {
+ obj->beam[0] = BeamOpCodeAddr(op_call_error_handler);
+ }
obj->beam[1] = 0;
for (ix=0; ix<ERTS_NUM_CODE_IX; ix++) {
@@ -267,7 +269,7 @@ 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 &&
- ee->ep->beam[0] != (BeamInstr) BeamOp(op_i_generic_breakpoint))) {
+ ! BeamIsOpCode(ee->ep->beam[0], op_i_generic_breakpoint))) {
return NULL;
}
return ee->ep;
diff --git a/erts/emulator/beam/export.h b/erts/emulator/beam/export.h
index be6cce07bf..194e514b12 100644
--- a/erts/emulator/beam/export.h
+++ b/erts/emulator/beam/export.h
@@ -73,7 +73,7 @@ extern erts_mtx_t export_staging_lock;
#include "beam_load.h" /* For em_* extern declarations */
#define ExportIsBuiltIn(EntryPtr) \
(((EntryPtr)->addressv[erts_active_code_ix()] == (EntryPtr)->beam) && \
- ((EntryPtr)->beam[0] == (BeamInstr) em_apply_bif))
+ (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 60cf09dc07..fb42969a8f 100644
--- a/erts/emulator/beam/external.c
+++ b/erts/emulator/beam/external.c
@@ -122,8 +122,9 @@ static int encode_size_struct_int(struct TTBSizeContext_*, ErtsAtomCacheMap *acm
static Export binary_to_term_trap_export;
static BIF_RETTYPE binary_to_term_trap_1(BIF_ALIST_1);
-static BIF_RETTYPE binary_to_term_int(Process* p, Uint32 flags, Eterm bin, Binary* context_b,
- Export *bif, Eterm arg0, Eterm arg1);
+static Sint transcode_dist_obuf(ErtsDistOutputBuf*, DistEntry*, Uint32 dflags, Sint reds);
+
+
void erts_init_external(void) {
erts_init_trap_export(&term_to_binary_trap_export,
@@ -222,23 +223,10 @@ erts_destroy_atom_cache_map(ErtsAtomCacheMap *acmp)
static ERTS_INLINE void
insert_acache_map(ErtsAtomCacheMap *acmp, Eterm atom, Uint32 dflags)
{
- /*
- * If the receiver do not understand utf8 atoms
- * and this atom cannot be represented in latin1,
- * we are not allowed to cache it.
- *
- * In this case all atoms are assumed to have
- * latin1 encoding in the cache. By refusing it
- * in the cache we will instead encode it using
- * ATOM_UTF8_EXT/SMALL_ATOM_UTF8_EXT which the
- * receiver do not recognize and tear down the
- * connection.
- */
- if (acmp && acmp->sz < ERTS_MAX_INTERNAL_ATOM_CACHE_ENTRIES
- && ((dflags & DFLAG_UTF8_ATOMS)
- || atom_tab(atom_val(atom))->latin1_chars >= 0)) {
+ if (acmp && acmp->sz < ERTS_MAX_INTERNAL_ATOM_CACHE_ENTRIES) {
int ix;
ASSERT(acmp->hdr_sz < 0);
+ ASSERT(dflags & DFLAG_UTF8_ATOMS);
ix = atom2cix(atom);
if (acmp->cache[ix].iix < 0) {
acmp->cache[ix].iix = acmp->sz;
@@ -258,9 +246,7 @@ get_iix_acache_map(ErtsAtomCacheMap *acmp, Eterm atom, Uint32 dflags)
ASSERT(is_atom(atom));
ix = atom2cix(atom);
if (acmp->cache[ix].iix < 0) {
- ASSERT(acmp->sz == ERTS_MAX_INTERNAL_ATOM_CACHE_ENTRIES
- || (!(dflags & DFLAG_UTF8_ATOMS)
- && atom_tab(atom_val(atom))->latin1_chars < 0));
+ ASSERT(acmp->sz == ERTS_MAX_INTERNAL_ATOM_CACHE_ENTRIES);
return -1;
}
else {
@@ -274,7 +260,6 @@ void
erts_finalize_atom_cache_map(ErtsAtomCacheMap *acmp, Uint32 dflags)
{
if (acmp) {
- int utf8_atoms = (int) (dflags & DFLAG_UTF8_ATOMS);
int long_atoms = 0; /* !0 if one or more atoms are longer than 255. */
int i;
int sz;
@@ -285,6 +270,7 @@ erts_finalize_atom_cache_map(ErtsAtomCacheMap *acmp, Uint32 dflags)
+ 1 /* number of internal cache entries */
;
int min_sz;
+ ASSERT(dflags & DFLAG_UTF8_ATOMS);
ASSERT(acmp->hdr_sz < 0);
/* Make sure cache update instructions fit */
min_sz = fix_sz+(2+4)*acmp->sz;
@@ -296,7 +282,7 @@ erts_finalize_atom_cache_map(ErtsAtomCacheMap *acmp, Uint32 dflags)
atom = acmp->cache[acmp->cix[i]].atom;
ASSERT(is_atom(atom));
a = atom_tab(atom_val(atom));
- len = (int) (utf8_atoms ? a->len : a->latin1_chars);
+ len = (int) a->len;
ASSERT(len >= 0);
if (!long_atoms && len > 255)
long_atoms = 1;
@@ -366,18 +352,77 @@ byte *erts_encode_ext_dist_header_setup(byte *ctl_ext, ErtsAtomCacheMap *acmp)
}
}
-byte *erts_encode_ext_dist_header_finalize(byte *ext, ErtsAtomCache *cache, Uint32 dflags)
+
+#define PASS_THROUGH 'p'
+
+Sint erts_encode_ext_dist_header_finalize(ErtsDistOutputBuf* ob,
+ DistEntry* dep,
+ Uint32 dflags,
+ Sint reds)
{
byte *ip;
byte instr_buf[(2+4)*ERTS_ATOM_CACHE_SIZE];
int ci, sz;
byte dist_hdr_flags;
int long_atoms;
- int utf8_atoms = (int) (dflags & DFLAG_UTF8_ATOMS);
- register byte *ep = ext;
- ASSERT(ep[0] == VERSION_MAGIC);
- if (ep[1] != DIST_HEADER)
- return ext;
+ register byte *ep = ob->extp;
+ 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]
+ * 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) {
+ 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;
+ }
dist_hdr_flags = ep[2];
long_atoms = ERTS_DIST_HDR_LONG_ATOMS_FLG & ((int) dist_hdr_flags);
@@ -405,6 +450,7 @@ byte *erts_encode_ext_dist_header_finalize(byte *ext, ErtsAtomCache *cache, Uint
/ sizeof(Uint32))+1];
register Uint32 flgs;
int iix, flgs_bytes, flgs_buf_ix, used_half_bytes;
+ ErtsAtomCache* cache = dep->cache;
#ifdef DEBUG
int tot_used_half_bytes;
#endif
@@ -447,17 +493,9 @@ byte *erts_encode_ext_dist_header_finalize(byte *ext, ErtsAtomCache *cache, Uint
Atom *a;
cache->out_arr[cix] = atom;
a = atom_tab(atom_val(atom));
- if (utf8_atoms) {
- sz = a->len;
- ep -= sz;
- sys_memcpy((void *) ep, (void *) a->name, sz);
- }
- else {
- ASSERT(0 <= a->latin1_chars && a->latin1_chars <= MAX_ATOM_CHARACTERS);
- ep -= a->latin1_chars;
- sz = erts_utf8_to_latin1(ep, a->name, a->len);
- ASSERT(a->latin1_chars == sz);
- }
+ sz = a->len;
+ ep -= sz;
+ sys_memcpy((void *) ep, (void *) a->name, sz);
if (long_atoms) {
ep -= 2;
put_int16(sz, ep);
@@ -504,12 +542,16 @@ byte *erts_encode_ext_dist_header_finalize(byte *ext, ErtsAtomCache *cache, Uint
break;
}
}
+ reds -= 3; /*was ERTS_PORT_REDS_DIST_CMD_FINALIZE*/
}
--ep;
put_int8(ci, ep);
*--ep = DIST_HEADER;
*--ep = VERSION_MAGIC;
- return ep;
+done:
+ ob->extp = ep;
+ ASSERT(&ob->data[0] <= ob->extp && ob->extp < ob->ext_endp);
+ return reds < 0 ? 0 : reds;
}
int erts_encode_dist_ext_size(Eterm term, Uint32 flags, ErtsAtomCacheMap *acmp,
@@ -520,7 +562,7 @@ int erts_encode_dist_ext_size(Eterm term, Uint32 flags, ErtsAtomCacheMap *acmp,
return -1;
} else {
#ifndef ERTS_DEBUG_USE_DIST_SEP
- if (!(flags & DFLAG_DIST_HDR_ATOM_CACHE))
+ if (!(flags & (DFLAG_DIST_HDR_ATOM_CACHE | DFLAG_NO_MAGIC)))
#endif
sz++ /* VERSION_MAGIC */;
@@ -536,7 +578,7 @@ int erts_encode_dist_ext_size_int(Eterm term, struct erts_dsig_send_context* ctx
return -1;
} else {
#ifndef ERTS_DEBUG_USE_DIST_SEP
- if (!(ctx->flags & DFLAG_DIST_HDR_ATOM_CACHE))
+ if (!(ctx->flags & (DFLAG_DIST_HDR_ATOM_CACHE | DFLAG_NO_MAGIC)))
#endif
sz++ /* VERSION_MAGIC */;
@@ -568,7 +610,7 @@ int erts_encode_dist_ext(Eterm term, byte **ext, Uint32 flags, ErtsAtomCacheMap
{
if (!ctx || !ctx->wstack.wstart) {
#ifndef ERTS_DEBUG_USE_DIST_SEP
- if (!(flags & DFLAG_DIST_HDR_ATOM_CACHE))
+ if (!(flags & (DFLAG_DIST_HDR_ATOM_CACHE | DFLAG_NO_MAGIC)))
#endif
*(*ext)++ = VERSION_MAGIC;
}
@@ -643,7 +685,7 @@ erts_prepare_dist_ext(ErtsDistExternal *edep,
#endif
register byte *ep = ext;
- int utf8_atoms = (int) (dep->flags & DFLAG_UTF8_ATOMS);
+ ASSERT(dep->flags & DFLAG_UTF8_ATOMS);
edep->heap_size = -1;
edep->ext_endp = ext+size;
@@ -669,17 +711,16 @@ erts_prepare_dist_ext(ErtsDistExternal *edep,
erts_de_rlock(dep);
- if ((dep->status & (ERTS_DE_SFLG_EXITING|ERTS_DE_SFLG_CONNECTED))
- != ERTS_DE_SFLG_CONNECTED) {
+ if (dep->state != ERTS_DE_STATE_CONNECTED &&
+ dep->state != ERTS_DE_STATE_PENDING) {
erts_de_runlock(dep);
return ERTS_PREP_DIST_EXT_CLOSED;
}
-
if (dep->flags & DFLAG_DIST_HDR_ATOM_CACHE)
edep->flags |= ERTS_DIST_EXT_DFLAG_HDR;
*connection_id = dep->connection_id;
- edep->flags |= (dep->connection_id & ERTS_DIST_EXT_CON_ID_MASK);
+ edep->connection_id = dep->connection_id;
if (ep[1] != DIST_HEADER) {
if (edep->flags & ERTS_DIST_EXT_DFLAG_HDR)
@@ -806,9 +847,7 @@ erts_prepare_dist_ext(ErtsDistExternal *edep,
CHKSIZE(len);
atom = erts_atom_put((byte *) ep,
len,
- (utf8_atoms
- ? ERTS_ATOM_ENC_UTF8
- : ERTS_ATOM_ENC_LATIN1),
+ ERTS_ATOM_ENC_UTF8,
0);
if (is_non_value(atom))
ERTS_EXT_HDR_FAIL;
@@ -895,7 +934,7 @@ bad_dist_ext(ErtsDistExternal *edep)
erts_dsprintf(dsbufp, ", %d=%T", i, edep->attab.atom[i]);
}
erts_send_warning_to_logger_nogl(dsbufp);
- erts_kill_dist_connection(dep, ERTS_DIST_EXT_CON_ID(edep));
+ erts_kill_dist_connection(dep, edep->connection_id);
}
}
@@ -1220,7 +1259,8 @@ typedef struct B2TContext_t {
ErtsBinary2TermState b2ts;
Uint32 flags;
SWord reds;
- Eterm trap_bin;
+ Uint used_bytes; /* In: boolean, Out: bytes */
+ Eterm trap_bin; /* THE_NON_VALUE if not exported */
Export *bif;
Eterm arg[2];
enum B2TState state;
@@ -1314,6 +1354,11 @@ binary2term_prepare(ErtsBinary2TermState *state, byte *data, Sint data_size,
ctx->u.uc.dbytes = state->extp;
ctx->u.uc.dleft = dest_len;
+ if (ctx->used_bytes) {
+ ASSERT(ctx->used_bytes == 1);
+ /* to be subtracted by stream.avail_in when done */
+ ctx->used_bytes = data_size;
+ }
ctx->state = B2TUncompressChunk;
*ctxp = ctx;
}
@@ -1416,13 +1461,15 @@ static int b2t_context_destructor(Binary *context_bin)
return 1;
}
+static BIF_RETTYPE binary_to_term_int(Process*, Eterm bin, B2TContext*);
+
+
static BIF_RETTYPE binary_to_term_trap_1(BIF_ALIST_1)
{
Binary *context_bin = erts_magic_ref2bin(BIF_ARG_1);
ASSERT(ERTS_MAGIC_BIN_DESTRUCTOR(context_bin) == b2t_context_destructor);
- return binary_to_term_int(BIF_P, 0, THE_NON_VALUE, context_bin, NULL,
- THE_NON_VALUE, THE_NON_VALUE);
+ return binary_to_term_int(BIF_P, THE_NON_VALUE, ERTS_MAGIC_BIN_DATA(context_bin));
}
@@ -1448,6 +1495,8 @@ static B2TContext* b2t_export_context(Process* p, B2TContext* src)
b2t_context_destructor);
B2TContext* ctx = ERTS_MAGIC_BIN_DATA(context_b);
Eterm* hp;
+
+ ASSERT(is_non_value(src->trap_bin));
sys_memcpy(ctx, src, sizeof(B2TContext));
if (ctx->state >= B2TDecode && ctx->u.dc.next == &src->u.dc.res) {
ctx->u.dc.next = &ctx->u.dc.res;
@@ -1457,8 +1506,7 @@ static B2TContext* b2t_export_context(Process* p, B2TContext* src)
return ctx;
}
-static BIF_RETTYPE binary_to_term_int(Process* p, Uint32 flags, Eterm bin, Binary* context_b,
- Export *bif_init, Eterm arg0, Eterm arg1)
+static BIF_RETTYPE binary_to_term_int(Process* p, Eterm bin, B2TContext *ctx)
{
BIF_RETTYPE ret_val;
#ifdef EXTREME_B2T_TRAPPING
@@ -1466,25 +1514,17 @@ static BIF_RETTYPE binary_to_term_int(Process* p, Uint32 flags, Eterm bin, Binar
#else
SWord initial_reds = (Uint)(ERTS_BIF_REDS_LEFT(p) * B2T_BYTES_PER_REDUCTION);
#endif
- B2TContext c_buff;
- B2TContext *ctx;
int is_first_call;
- if (context_b == NULL) {
+ if (is_value(bin)) {
/* Setup enough to get started */
is_first_call = 1;
- ctx = &c_buff;
ctx->state = B2TPrepare;
ctx->aligned_alloc = NULL;
- ctx->flags = flags;
- ctx->bif = bif_init;
- ctx->arg[0] = arg0;
- ctx->arg[1] = arg1;
- IF_DEBUG(ctx->trap_bin = THE_NON_VALUE;)
} else {
- is_first_call = 0;
- ctx = ERTS_MAGIC_BIN_DATA(context_b);
+ ASSERT(is_value(ctx->trap_bin));
ASSERT(ctx->state != B2TPrepare);
+ is_first_call = 0;
}
ctx->reds = initial_reds;
@@ -1528,6 +1568,10 @@ static BIF_RETTYPE binary_to_term_int(Process* p, Uint32 flags, Eterm bin, Binar
&& zret == Z_STREAM_END
&& ctx->u.uc.dleft == 0) {
ctx->reds -= chunk;
+ if (ctx->used_bytes) {
+ ASSERT(ctx->used_bytes > 5 + ctx->u.uc.stream.avail_in);
+ ctx->used_bytes -= ctx->u.uc.stream.avail_in;
+ }
ctx->state = B2TSizeInit;
}
else {
@@ -1546,11 +1590,11 @@ static BIF_RETTYPE binary_to_term_int(Process* p, Uint32 flags, Eterm bin, Binar
break;
case B2TDecodeInit:
- if (ctx == &c_buff && ctx->b2ts.extsize > ctx->reds) {
+ if (is_non_value(ctx->trap_bin) && ctx->b2ts.extsize > ctx->reds) {
/* dec_term will maybe trap, allocate space for magic bin
before result term to make it easy to trim with HRelease.
*/
- ctx = b2t_export_context(p, &c_buff);
+ ctx = b2t_export_context(p, ctx);
}
ctx->u.dc.ep = ctx->b2ts.extp;
ctx->u.dc.res = (Eterm) (UWord) NULL;
@@ -1593,6 +1637,25 @@ static BIF_RETTYPE binary_to_term_int(Process* p, Uint32 flags, Eterm bin, Binar
return ret_val;
case B2TDone:
+ if (ctx->used_bytes) {
+ Eterm *hp;
+ Eterm used;
+ if (!ctx->b2ts.exttmp) {
+ ASSERT(ctx->used_bytes == 1);
+ ctx->used_bytes = (ctx->u.dc.ep - ctx->b2ts.extp
+ +1); /* VERSION_MAGIC */
+ }
+ if (IS_USMALL(0, ctx->used_bytes)) {
+ hp = erts_produce_heap(&ctx->u.dc.factory, 3, 0);
+ used = make_small(ctx->used_bytes);
+ }
+ else {
+ hp = erts_produce_heap(&ctx->u.dc.factory, 3+BIG_UINT_HEAP_SIZE, 0);
+ used = uint_to_big(ctx->used_bytes, hp);
+ hp += BIG_UINT_HEAP_SIZE;
+ }
+ ctx->u.dc.res = TUPLE2(hp, ctx->u.dc.res, used);
+ }
b2t_destroy_context(ctx);
if (ctx->u.dc.factory.hp > ctx->u.dc.factory.hp_end) {
@@ -1613,11 +1676,10 @@ static BIF_RETTYPE binary_to_term_int(Process* p, Uint32 flags, Eterm bin, Binar
}
}while (ctx->reds > 0 || ctx->state >= B2TDone);
- if (ctx == &c_buff) {
- ASSERT(ctx->trap_bin == THE_NON_VALUE);
- ctx = b2t_export_context(p, &c_buff);
+ if (is_non_value(ctx->trap_bin)) {
+ ctx = b2t_export_context(p, ctx);
+ ASSERT(is_value(ctx->trap_bin));
}
- ASSERT(ctx->trap_bin != THE_NON_VALUE);
if (is_first_call) {
erts_set_gc_state(p, 0);
@@ -1634,23 +1696,35 @@ HIPE_WRAPPER_BIF_DISABLE_GC(binary_to_term, 1)
BIF_RETTYPE binary_to_term_1(BIF_ALIST_1)
{
- return binary_to_term_int(BIF_P, 0, BIF_ARG_1, NULL, bif_export[BIF_binary_to_term_1],
- BIF_ARG_1, THE_NON_VALUE);
+ B2TContext ctx;
+
+ ctx.flags = 0;
+ ctx.used_bytes = 0;
+ ctx.trap_bin = THE_NON_VALUE;
+ ctx.bif = bif_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);
}
HIPE_WRAPPER_BIF_DISABLE_GC(binary_to_term, 2)
BIF_RETTYPE binary_to_term_2(BIF_ALIST_2)
{
+ B2TContext ctx;
Eterm opts;
Eterm opt;
- Uint32 flags = 0;
+ ctx.flags = 0;
+ ctx.used_bytes = 0;
opts = BIF_ARG_2;
while (is_list(opts)) {
opt = CAR(list_val(opts));
if (opt == am_safe) {
- flags |= ERTS_DIST_EXT_BTT_SAFE;
+ ctx.flags |= ERTS_DIST_EXT_BTT_SAFE;
+ }
+ else if (opt == am_used) {
+ ctx.used_bytes = 1;
}
else {
goto error;
@@ -1661,8 +1735,11 @@ BIF_RETTYPE binary_to_term_2(BIF_ALIST_2)
if (is_not_nil(opts))
goto error;
- return binary_to_term_int(BIF_P, flags, BIF_ARG_1, NULL, bif_export[BIF_binary_to_term_2],
- BIF_ARG_1, BIF_ARG_2);
+ ctx.trap_bin = THE_NON_VALUE;
+ ctx.bif = bif_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);
error:
BIF_ERROR(BIF_P, BADARG);
@@ -1876,7 +1953,7 @@ static Eterm erts_term_to_binary_int(Process* p, Eterm Term, int level, Uint fla
context_b = erts_create_magic_binary(sizeof(TTBContext), \
ttb_context_destructor); \
context = ERTS_MAGIC_BIN_DATA(context_b); \
- memcpy(context,&c_buff,sizeof(TTBContext)); \
+ sys_memcpy(context,&c_buff,sizeof(TTBContext)); \
} \
} while (0)
@@ -1955,23 +2032,14 @@ static Eterm erts_term_to_binary_int(Process* p, Eterm Term, int level, Uint fla
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 */
- ProcBin* pb;
return_normal:
context->s.ec.result_bin = NULL;
context->alive = 0;
- pb = (ProcBin *) HAlloc(p, PROC_BIN_SIZE);
- pb->thing_word = HEADER_PROC_BIN;
- pb->size = real_size;
- pb->next = MSO(p).first;
- MSO(p).first = (struct erl_off_heap_header*)pb;
- pb->val = result_bin;
- pb->bytes = (byte*) result_bin->orig_bytes;
- pb->flags = 0;
- OH_OVERHEAD(&(MSO(p)), pb->size / sizeof(Eterm));
if (context_b && erts_refc_read(&context_b->intern.refc,0) == 0) {
erts_bin_free(context_b);
}
- return make_binary(pb);
+ return erts_build_proc_bin(&MSO(p), HAlloc(p, PROC_BIN_SIZE),
+ result_bin);
}
/* Continue with compression... */
/* To make absolutely sure that zlib does not barf on a reallocated context,
@@ -2028,16 +2096,7 @@ static Eterm erts_term_to_binary_int(Process* p, Eterm Term, int level, Uint fla
result_bin = erts_bin_realloc(context->s.cc.destination_bin,
context->s.cc.dest_len+6);
context->s.cc.destination_bin = NULL;
- pb = (ProcBin *) HAlloc(p, PROC_BIN_SIZE);
- pb->thing_word = HEADER_PROC_BIN;
- pb->size = context->s.cc.dest_len+6;
- pb->next = MSO(p).first;
- MSO(p).first = (struct erl_off_heap_header*)pb;
- pb->val = result_bin;
ASSERT(erts_refc_read(&result_bin->intern.refc, 1));
- pb->bytes = (byte*) result_bin->orig_bytes;
- pb->flags = 0;
- OH_OVERHEAD(&(MSO(p)), pb->size / sizeof(Eterm));
erts_bin_free(context->s.cc.result_bin);
context->s.cc.result_bin = NULL;
context->alive = 0;
@@ -2045,7 +2104,9 @@ static Eterm erts_term_to_binary_int(Process* p, Eterm Term, int level, Uint fla
if (context_b && erts_refc_read(&context_b->intern.refc,0) == 0) {
erts_bin_free(context_b);
}
- return make_binary(pb);
+ return erts_build_proc_bin(&MSO(p),
+ HAlloc(p, PROC_BIN_SIZE),
+ result_bin);
}
default: /* Compression error, revert to uncompressed binary (still in
context) */
@@ -2099,7 +2160,7 @@ enc_atom(ErtsAtomCacheMap *acmp, Eterm atom, byte *ep, Uint32 dflags)
{
int iix;
int len;
- int utf8_atoms = (int) (dflags & DFLAG_UTF8_ATOMS);
+ const int utf8_atoms = (int) (dflags & DFLAG_UTF8_ATOMS);
ASSERT(is_atom(atom));
@@ -2494,8 +2555,6 @@ enc_term_int(TTBEncodeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, byte* ep,
break;
}
- L_jump_start:
-
if (ctx && --r <= 0) {
*reds = 0;
ctx->obj = obj;
@@ -2503,6 +2562,8 @@ enc_term_int(TTBEncodeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, byte* ep,
WSTACK_SAVE(s, &ctx->wstack);
return -1;
}
+
+ L_jump_start:
switch(tag_val_def(obj)) {
case NIL_DEF:
*ep++ = NIL_EXT;
@@ -2812,6 +2873,8 @@ enc_term_int(TTBEncodeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, byte* ep,
ep[j] = 0; /* Zero unused bits at end of binary */
data_dst = ep;
ep += j + 1;
+ if (ctx)
+ ctx->hopefull_flags |= DFLAG_BIT_BINARIES;
} else {
/*
* Bit-level binary, but the receiver doesn't support it.
@@ -2847,6 +2910,8 @@ enc_term_int(TTBEncodeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, byte* ep,
ep = enc_atom(acmp, exp->info.mfa.function, ep, dflags);
ep = enc_term(acmp, make_small(exp->info.mfa.arity),
ep, dflags, off_heap);
+ if (ctx)
+ ctx->hopefull_flags |= DFLAG_EXPORT_PTR_TAG;
} else {
/* Tag, arity */
*ep++ = SMALL_TUPLE_EXT;
@@ -2867,62 +2932,27 @@ enc_term_int(TTBEncodeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, byte* ep,
ErlFunThing* funp = (ErlFunThing *) fun_val(obj);
int ei;
- if ((dflags & DFLAG_NEW_FUN_TAGS) != 0) {
- *ep++ = NEW_FUN_EXT;
- WSTACK_PUSH2(s, ENC_PATCH_FUN_SIZE,
- (UWord) ep); /* Position for patching in size */
- ep += 4;
- *ep = funp->arity;
- ep += 1;
- sys_memcpy(ep, funp->fe->uniq, 16);
- ep += 16;
- put_int32(funp->fe->index, ep);
- ep += 4;
- put_int32(funp->num_free, ep);
- ep += 4;
- ep = enc_atom(acmp, funp->fe->module, ep, dflags);
- ep = enc_term(acmp, make_small(funp->fe->old_index), ep, dflags, off_heap);
- ep = enc_term(acmp, make_small(funp->fe->old_uniq), ep, dflags, off_heap);
- ep = enc_pid(acmp, funp->creator, ep, dflags);
- } else {
- /*
- * Communicating with an obsolete erl_interface or
- * jinterface node. Convert the fun to a tuple to
- * avoid crasching.
- */
-
- /* Tag, arity */
- *ep++ = SMALL_TUPLE_EXT;
- put_int8(5, ep);
- ep += 1;
-
- /* 'fun' */
- ep = enc_atom(acmp, am_fun, ep, dflags);
-
- /* Module name */
- ep = enc_atom(acmp, funp->fe->module, ep, dflags);
-
- /* Index, Uniq */
- *ep++ = INTEGER_EXT;
- put_int32(funp->fe->old_index, ep);
- ep += 4;
- *ep++ = INTEGER_EXT;
- put_int32(funp->fe->old_uniq, ep);
- ep += 4;
-
- /* Environment sub-tuple arity */
- ASSERT(funp->num_free < MAX_ARG);
- *ep++ = SMALL_TUPLE_EXT;
- put_int8(funp->num_free, ep);
- ep += 1;
- }
- for (ei = funp->num_free-1; ei > 0; ei--) {
+ ASSERT(dflags & DFLAG_NEW_FUN_TAGS);
+ *ep++ = NEW_FUN_EXT;
+ WSTACK_PUSH2(s, ENC_PATCH_FUN_SIZE,
+ (UWord) ep); /* Position for patching in size */
+ ep += 4;
+ *ep = funp->arity;
+ ep += 1;
+ sys_memcpy(ep, funp->fe->uniq, 16);
+ ep += 16;
+ put_int32(funp->fe->index, ep);
+ ep += 4;
+ put_int32(funp->num_free, ep);
+ ep += 4;
+ ep = enc_atom(acmp, funp->fe->module, ep, dflags);
+ ep = enc_term(acmp, make_small(funp->fe->old_index), ep, dflags, off_heap);
+ ep = enc_term(acmp, make_small(funp->fe->old_uniq), ep, dflags, off_heap);
+ ep = enc_pid(acmp, funp->creator, ep, dflags);
+
+ for (ei = funp->num_free-1; ei >= 0; ei--) {
WSTACK_PUSH2(s, ENC_TERM, (UWord) funp->env[ei]);
}
- if (funp->num_free != 0) {
- obj = funp->env[0];
- goto L_jump_start;
- }
}
break;
}
@@ -3115,7 +3145,7 @@ dec_term(ErtsDistExternal *edep,
#if defined(ARCH_64)
*objp = make_small(sn);
#else
- if (MY_IS_SSMALL(sn)) {
+ if (IS_SSMALL(sn)) {
*objp = make_small(sn);
} else {
*objp = small_to_big(sn, hp);
@@ -3260,6 +3290,7 @@ dec_term_atom_common:
n--;
if (ctx) {
if (reds < n) {
+ ASSERT(reds > 0);
ctx->state = B2TDecodeList;
ctx->u.dc.remaining_n = n - reds;
n = reds;
@@ -3541,18 +3572,9 @@ dec_term_atom_common:
*objp = make_binary(hb);
} else {
Binary* dbin = erts_bin_nrml_alloc(n);
- ProcBin* pb;
- pb = (ProcBin *) hp;
+
+ *objp = erts_build_proc_bin(factory->off_heap, hp, dbin);
hp += PROC_BIN_SIZE;
- pb->thing_word = HEADER_PROC_BIN;
- pb->size = n;
- pb->next = factory->off_heap->first;
- factory->off_heap->first = (struct erl_off_heap_header*)pb;
- OH_OVERHEAD(factory->off_heap, pb->size / sizeof(Eterm));
- pb->val = dbin;
- pb->bytes = (byte*) dbin->orig_bytes;
- pb->flags = 0;
- *objp = make_binary(pb);
if (ctx) {
int n_limit = reds * B2T_MEMCPY_FACTOR;
if (n > n_limit) {
@@ -3592,18 +3614,9 @@ dec_term_atom_common:
ep += n;
} else {
Binary* dbin = erts_bin_nrml_alloc(n);
- ProcBin* pb;
+ Uint n_copy = n;
- pb = (ProcBin *) hp;
- pb->thing_word = HEADER_PROC_BIN;
- pb->size = n;
- pb->next = factory->off_heap->first;
- factory->off_heap->first = (struct erl_off_heap_header*)pb;
- OH_OVERHEAD(factory->off_heap, pb->size / sizeof(Eterm));
- pb->val = dbin;
- pb->bytes = (byte*) dbin->orig_bytes;
- pb->flags = 0;
- bin = make_binary(pb);
+ bin = erts_build_proc_bin(factory->off_heap, hp, dbin);
hp += PROC_BIN_SIZE;
if (ctx) {
int n_limit = reds * B2T_MEMCPY_FACTOR;
@@ -3611,15 +3624,15 @@ dec_term_atom_common:
ctx->state = B2TDecodeBinary;
ctx->u.dc.remaining_n = n - n_limit;
ctx->u.dc.remaining_bytes = dbin->orig_bytes + n_limit;
- n = n_limit;
+ n_copy = n_limit;
reds = 0;
}
- else
+ else {
reds -= n / B2T_MEMCPY_FACTOR;
+ }
}
- sys_memcpy(dbin->orig_bytes, ep, n);
- ep += n;
- n = pb->size;
+ sys_memcpy(dbin->orig_bytes, ep, n_copy);
+ ep += n_copy;
}
if (bitsize == 8 || n == 0) {
@@ -4000,6 +4013,7 @@ dec_term_atom_common:
if (ctx) {
ctx->state = B2TDone;
ctx->reds = reds;
+ ctx->u.dc.ep = ep;
}
return ep;
@@ -4269,24 +4283,12 @@ encode_size_struct_int(TTBSizeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj,
{
ErlFunThing* funp = (ErlFunThing *) fun_val(obj);
- if ((dflags & DFLAG_NEW_FUN_TAGS) != 0) {
- result += 20+1+1+4; /* New ID + Tag */
- result += 4; /* Length field (number of free variables */
- result += encode_size_struct2(acmp, funp->creator, dflags);
- result += encode_size_struct2(acmp, funp->fe->module, dflags);
- result += 2 * (1+4); /* Index, Uniq */
- } else {
- /*
- * Size when fun is mapped to a tuple.
- */
- result += 1 + 1; /* Tuple tag, arity */
- result += 1 + 1 + 2 +
- atom_tab(atom_val(am_fun))->len; /* 'fun' */
- result += 1 + 1 + 2 +
- atom_tab(atom_val(funp->fe->module))->len; /* Module name */
- result += 2 * (1 + 4); /* Index + Uniq */
- result += 1 + (funp->num_free < 0x100 ? 1 : 4);
- }
+ ASSERT(dflags & DFLAG_NEW_FUN_TAGS);
+ result += 20+1+1+4; /* New ID + Tag */
+ result += 4; /* Length field (number of free variables */
+ result += encode_size_struct2(acmp, funp->creator, dflags);
+ result += encode_size_struct2(acmp, funp->fe->module, dflags);
+ result += 2 * (1+4); /* Index, Uniq */
if (funp->num_free > 1) {
WSTACK_PUSH2(s, (UWord) (funp->env + 1),
(UWord) TERM_ARRAY_OP(funp->num_free-1));
@@ -4374,7 +4376,7 @@ decoded_size(byte *ep, byte* endp, int internal_tags, B2TContext* ctx)
}
}
else
- reds = 0; /* not used but compiler warns anyway */
+ ERTS_UNDEF(reds, 0);
heap_size = 0;
terms = 1;
@@ -4684,3 +4686,182 @@ error:
#undef SKIP2
#undef CHKSIZE
}
+
+
+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)
+{
+ struct transcode_context* ctx = dep->transcode_ctx;
+
+ erts_factory_close(&ctx->ctl_factory);
+ erts_free(ERTS_ALC_T_DIST_TRANSCODE, ctx->ctl_heap);
+
+ if (ctx->msg_heap) {
+ erts_factory_close(&ctx->b2t.u.dc.factory);
+ erts_free(ERTS_ALC_T_DIST_TRANSCODE, ctx->msg_heap);
+ }
+ 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;
+
+ 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);
+ 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;
+ }
+ }
+ 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);
+ }
+ *--(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;
+ }
+ 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);
+ 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);
+
+ if (!have_msg) {
+ break;
+ }
+ 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;
+ }
+ reds /= TERM_TO_BINARY_LOOP_FACTOR;
+
+ ASSERT(ob->ext_endp <= ob->alloc_endp);
+ ASSERT(!ctx->ttb.hopefull_flags);
+ }
+ transcode_free_ctx(dep);
+
+done:
+ if (!(dflags & DFLAG_DIST_HDR_ATOM_CACHE))
+ *--(ob->extp) = PASS_THROUGH;
+
+ if (reds < 0)
+ reds = 0;
+
+ return reds;
+}
diff --git a/erts/emulator/beam/external.h b/erts/emulator/beam/external.h
index 3c61d013da..bbd9b4bad2 100644
--- a/erts/emulator/beam/external.h
+++ b/erts/emulator/beam/external.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1996-2016. All Rights Reserved.
+ * Copyright Ericsson AB 1996-2018. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -83,7 +83,10 @@
#ifndef ERL_EXTERNAL_H__
#define ERL_EXTERNAL_H__
+#define ERL_NODE_TABLES_BASIC_ONLY
#include "erl_node_tables.h"
+#undef ERL_NODE_TABLES_BASIC_ONLY
+#include "erl_alloc.h"
#define ERTS_ATOM_CACHE_SIZE 2048
@@ -109,35 +112,26 @@ typedef struct {
} ErtsAtomTranslationTable;
/*
- * These flags are tagged onto the high bits of a connection ID and stored in
- * the ErtsDistExternal structure's flags field. They are used to indicate
- * various bits of state necessary to decode binaries in a variety of
- * scenarios. The mask ERTS_DIST_EXT_CON_ID_MASK is used later to separate the
- * connection ID from the flags. Be careful to ensure that the mask does not
- * overlap any of the bits used for flags, or ERTS will leak flags bits into
- * connection IDs and leak connection ID bits into the flags.
+ * These flags are stored in the ErtsDistExternal structure's flags field.
+ * They are used to indicate various bits of state necessary to decode binaries
+ * in a variety of scenarios.
*/
-#define ERTS_DIST_EXT_DFLAG_HDR ((Uint32) 0x80000000)
-#define ERTS_DIST_EXT_ATOM_TRANS_TAB ((Uint32) 0x40000000)
-#define ERTS_DIST_EXT_BTT_SAFE ((Uint32) 0x20000000)
-#define ERTS_DIST_EXT_CON_ID_MASK ((Uint32) 0x1fffffff)
+#define ERTS_DIST_EXT_DFLAG_HDR ((Uint32) 0x1)
+#define ERTS_DIST_EXT_ATOM_TRANS_TAB ((Uint32) 0x2)
+#define ERTS_DIST_EXT_BTT_SAFE ((Uint32) 0x4)
+
+#define ERTS_DIST_CON_ID_MASK ((Uint32) 0x00ffffff) /* also in net_kernel.erl */
-#define ERTS_DIST_EXT_CON_ID(DIST_EXTP) \
- ((DIST_EXTP)->flags & ERTS_DIST_EXT_CON_ID_MASK)
typedef struct {
DistEntry *dep;
byte *extp;
byte *ext_endp;
Sint heap_size;
+ Uint32 connection_id;
Uint32 flags;
ErtsAtomTranslationTable attab;
} ErtsDistExternal;
-typedef struct {
- int have_header;
- int cache_entries;
-} ErtsDistHeaderPeek;
-
#define ERTS_DIST_EXT_SIZE(EDEP) \
(sizeof(ErtsDistExternal) \
- (((EDEP)->flags & ERTS_DIST_EXT_ATOM_TRANS_TAB) \
@@ -163,7 +157,7 @@ void erts_finalize_atom_cache_map(ErtsAtomCacheMap *, Uint32);
Uint erts_encode_ext_dist_header_size(ErtsAtomCacheMap *);
byte *erts_encode_ext_dist_header_setup(byte *, ErtsAtomCacheMap *);
-byte *erts_encode_ext_dist_header_finalize(byte *, ErtsAtomCache *, Uint32);
+Sint erts_encode_ext_dist_header_finalize(ErtsDistOutputBuf*, DistEntry *, Uint32 dflags, Sint reds);
struct erts_dsig_send_context;
int erts_encode_dist_ext_size(Eterm, Uint32, ErtsAtomCacheMap*, Uint* szp);
int erts_encode_dist_ext_size_int(Eterm term, struct erts_dsig_send_context* ctx, Uint* szp);
@@ -177,9 +171,6 @@ Uint erts_encode_ext_size_ets(Eterm);
void erts_encode_ext(Eterm, byte **);
byte* erts_encode_ext_ets(Eterm, byte *, struct erl_off_heap_header** ext_off_heap);
-#ifdef ERTS_WANT_EXTERNAL_TAGS
-ERTS_GLB_INLINE void erts_peek_dist_header(ErtsDistHeaderPeek *, byte *, Uint);
-#endif
ERTS_GLB_INLINE void erts_free_dist_ext_copy(ErtsDistExternal *);
ERTS_GLB_INLINE void *erts_dist_ext_trailer(ErtsDistExternal *);
ErtsDistExternal *erts_make_dist_ext_copy(ErtsDistExternal *, Uint);
@@ -207,23 +198,9 @@ void erts_binary2term_abort(ErtsBinary2TermState *);
Eterm erts_binary2term_create(ErtsBinary2TermState *, ErtsHeapFactory*);
int erts_debug_max_atom_out_cache_index(void);
int erts_debug_atom_to_out_cache_index(Eterm);
-
+void transcode_free_ctx(DistEntry* dep);
#if ERTS_GLB_INLINE_INCL_FUNC_DEF
-#ifdef ERTS_WANT_EXTERNAL_TAGS
-ERTS_GLB_INLINE void
-erts_peek_dist_header(ErtsDistHeaderPeek *dhpp, byte *ext, Uint sz)
-{
- if (ext[0] == VERSION_MAGIC
- || ext[1] != DIST_HEADER
- || sz < (1+1+1))
- dhpp->have_header = 0;
- else {
- dhpp->have_header = 1;
- dhpp->cache_entries = (int) get_int8(&ext[2]);
- }
-}
-#endif
ERTS_GLB_INLINE void
erts_free_dist_ext_copy(ErtsDistExternal *edep)
diff --git a/erts/emulator/beam/global.h b/erts/emulator/beam/global.h
index 09aeba00fa..2cf268162d 100644
--- a/erts/emulator/beam/global.h
+++ b/erts/emulator/beam/global.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1996-2017. All Rights Reserved.
+ * Copyright Ericsson AB 1996-2018. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -87,9 +87,7 @@ typedef struct
{
erts_mtx_t lock;
ErtsMonitor* root;
- int pending_failed_fire;
- int is_dying;
-
+ Uint refc;
size_t user_data_sz;
} ErtsResourceMonitors;
@@ -116,13 +114,16 @@ extern void erts_pre_nif(struct enif_environment_t*, Process*,
struct erl_module_nif*, Process* tracee);
extern void erts_post_nif(struct enif_environment_t* env);
extern void erts_resource_stop(ErtsResource*, ErlNifEvent, int is_direct_call);
-void erts_fire_nif_monitor(ErtsResource*, Eterm pid, Eterm ref);
+void erts_fire_nif_monitor(ErtsMonitor *tmon);
+void erts_nif_demonitored(ErtsResource* resource);
+extern 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);
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*,
struct enif_func_t **funcs);
+extern Module *erts_nif_get_module(struct erl_module_nif*);
extern Eterm erts_nif_call_function(Process *p, Process *tracee,
struct erl_module_nif*,
struct enif_func_t *,
@@ -200,6 +201,7 @@ typedef struct {
struct erts_driver_t_ {
erts_driver_t *next;
erts_driver_t *prev;
+ Eterm name_atom;
char *name;
struct {
int major;
@@ -368,7 +370,7 @@ do {\
UWord _wsz = ESTACK_COUNT(s);\
(dst)->start = erts_alloc((s).alloc_type,\
DEF_ESTACK_SIZE * sizeof(Eterm));\
- memcpy((dst)->start, (s).start,_wsz*sizeof(Eterm));\
+ sys_memcpy((dst)->start, (s).start,_wsz*sizeof(Eterm));\
(dst)->sp = (dst)->start + _wsz;\
(dst)->end = (dst)->start + DEF_ESTACK_SIZE;\
(dst)->edefault = NULL;\
@@ -536,7 +538,7 @@ do {\
UWord _wsz = WSTACK_COUNT(s);\
(dst)->wstart = erts_alloc(s.alloc_type,\
DEF_WSTACK_SIZE * sizeof(UWord));\
- memcpy((dst)->wstart, s.wstart,_wsz*sizeof(UWord));\
+ sys_memcpy((dst)->wstart, s.wstart,_wsz*sizeof(UWord));\
(dst)->wsp = (dst)->wstart + _wsz;\
(dst)->wend = (dst)->wstart + DEF_WSTACK_SIZE;\
(dst)->wdefault = NULL;\
@@ -860,7 +862,10 @@ void erts_emasculate_writable_binary(ProcBin* pb);
Eterm erts_new_heap_binary(Process *p, byte *buf, int len, byte** datap);
Eterm erts_new_mso_binary(Process*, byte*, Uint);
Eterm new_binary(Process*, byte*, Uint);
+Eterm erts_heap_factory_new_binary(ErtsHeapFactory *hfact, byte *buf,
+ Uint len, Uint reserve_size);
Eterm erts_realloc_binary(Eterm bin, size_t size);
+Eterm erts_build_proc_bin(ErlOffHeap*, Eterm*, Binary*);
/* erl_bif_info.c */
@@ -885,6 +890,7 @@ void erts_init_trap_export(Export* ep, Eterm m, Eterm f, Uint a,
Eterm (*bif)(Process*, Eterm*, BeamInstr*));
void erts_init_bif(void);
Eterm erl_send(Process *p, Eterm to, Eterm msg);
+int erts_set_group_leader(Process *proc, Eterm new_gl);
/* erl_bif_op.c */
@@ -907,7 +913,6 @@ extern erts_atomic_t erts_copy_literal_area__;
#define ERTS_COPY_LITERAL_AREA() \
((ErtsLiteralArea *) erts_atomic_read_nob(&erts_copy_literal_area__))
extern Process *erts_literal_area_collector;
-extern Process *erts_dirty_process_code_checker;
extern Process *erts_code_purger;
@@ -947,6 +952,8 @@ void erts_update_ranges(BeamInstr* code, Uint size);
void erts_remove_from_ranges(BeamInstr* code);
UWord erts_ranges_sz(void);
void erts_lookup_function_info(FunctionInfo* fi, BeamInstr* pc, int full_info);
+extern ErtsLiteralArea** erts_dump_lit_areas;
+extern Uint erts_dump_num_lit_areas;
/* break.c */
void init_break_handler(void);
@@ -956,6 +963,7 @@ void process_info(fmtfn_t, void *);
void print_process_info(fmtfn_t, void *, Process*);
void info(fmtfn_t, void *);
void loaded(fmtfn_t, void *);
+void erts_print_base64(fmtfn_t to, void *to_arg, byte* src, Uint size);
/* sighandler sys.c */
int erts_set_signal(Eterm signal, Eterm type);
@@ -1062,13 +1070,13 @@ Eterm copy_struct_x(Eterm, Uint, Eterm**, ErlOffHeap*, Uint*, erts_literal_area_
#define copy_struct_litopt(Obj,Sz,HPP,OH,LitArea) \
copy_struct_x(Obj,Sz,HPP,OH,NULL,LitArea)
-Eterm copy_shallow(Eterm*, Uint, Eterm**, ErlOffHeap*);
+Eterm copy_shallow(Eterm* ERTS_RESTRICT, Uint, Eterm**, ErlOffHeap*);
void erts_move_multi_frags(Eterm** hpp, ErlOffHeap*, ErlHeapFragment* first,
Eterm* refs, unsigned nrefs, int literals);
/* Utilities */
-extern void erts_delete_nodes_monitors(Process *, ErtsProcLocks);
+void erts_monitor_nodes_delete(ErtsMonitor *);
extern Eterm erts_monitor_nodes(Process *, Eterm, Eterm);
extern Eterm erts_processes_monitoring_nodes(Process *);
extern int erts_do_net_exits(DistEntry*, Eterm);
@@ -1119,7 +1127,6 @@ extern int erts_no_line_info;
extern Eterm erts_error_logger_warnings;
extern int erts_initialized;
extern int erts_compat_rel;
-extern int erts_use_sender_punish;
void erl_start(int, char**);
void erts_usage(void);
Eterm erts_preloaded(Process* p);
@@ -1148,7 +1155,7 @@ typedef struct {
#define ERTS_SPAWN_DRIVER 1
#define ERTS_SPAWN_EXECUTABLE 2
#define ERTS_SPAWN_ANY (ERTS_SPAWN_DRIVER | ERTS_SPAWN_EXECUTABLE)
-int erts_add_driver_entry(ErlDrvEntry *drv, DE_Handle *handle, int driver_list_locked);
+int erts_add_driver_entry(ErlDrvEntry *drv, DE_Handle *handle, int driver_list_locked, int taint);
void erts_destroy_driver(erts_driver_t *drv);
int erts_save_suspend_process_on_port(Port*, Process*);
Port *erts_open_driver(erts_driver_t*, Eterm, char*, SysDriverOpts*, int *, int *);
@@ -1173,8 +1180,17 @@ void erts_lcnt_update_port_locks(int enable);
#endif
/* driver_tab.c */
+typedef struct {
+ ErlDrvEntry* de;
+ int taint;
+} ErtsStaticDriver;
typedef void *(*ErtsStaticNifInitFPtr)(void);
-ErtsStaticNifInitFPtr erts_static_nif_get_nif_init(const char *name, int len);
+typedef struct ErtsStaticNifEntry_ {
+ const char *nif_name;
+ ErtsStaticNifInitFPtr nif_init;
+ int taint;
+} ErtsStaticNifEntry;
+ErtsStaticNifEntry* erts_static_nif_get_nif_init(const char *name, int len);
int erts_is_static_nif(void *handle);
void erts_init_static_drivers(void);
@@ -1261,7 +1277,7 @@ char* erts_convert_filename_to_wchar(byte* bytes, Uint size,
char *statbuf, size_t statbuf_size,
ErtsAlcType_t alloc_type, Sint* used,
Uint extra_wchars);
-Eterm erts_convert_native_to_filename(Process *p, byte *bytes);
+Eterm erts_convert_native_to_filename(Process *p, size_t size, byte *bytes);
Eterm erts_utf8_to_list(Process *p, Uint num, byte *bytes, Uint sz, Uint left,
Uint *num_built, Uint *num_eaten, Eterm tail);
int erts_utf8_to_latin1(byte* dest, const byte* source, int slen);
diff --git a/erts/emulator/beam/hash.c b/erts/emulator/beam/hash.c
index 8548e30e8b..6a31489473 100644
--- a/erts/emulator/beam/hash.c
+++ b/erts/emulator/beam/hash.c
@@ -152,7 +152,7 @@ Hash* hash_init(int type, Hash* h, char* name, int size, HashFunctions fun)
h->bucket = (HashBucket**) fun.meta_alloc(h->meta_alloc_type, sz);
- sys_memzero(h->bucket, sz);
+ memzero(h->bucket, sz);
h->is_allocated = 0;
h->name = name;
h->fun = fun;
@@ -224,7 +224,7 @@ static void rehash(Hash* h, int grow)
sz = h->size*sizeof(HashBucket*);
new_bucket = (HashBucket **) h->fun.meta_alloc(h->meta_alloc_type, sz);
- sys_memzero(new_bucket, sz);
+ memzero(new_bucket, sz);
for (i = 0; i < old_size; i++) {
HashBucket* b = h->bucket[i];
diff --git a/erts/emulator/beam/index.c b/erts/emulator/beam/index.c
index 93d1111904..be1771b037 100644
--- a/erts/emulator/beam/index.c
+++ b/erts/emulator/beam/index.c
@@ -58,7 +58,7 @@ IndexTable*
erts_index_init(ErtsAlcType_t type, IndexTable* t, char* name,
int size, int limit, HashFunctions fun)
{
- Uint base_size = ((limit+INDEX_PAGE_SIZE-1)/INDEX_PAGE_SIZE)*sizeof(IndexSlot*);
+ Uint base_size = (((Uint)limit+INDEX_PAGE_SIZE-1)/INDEX_PAGE_SIZE)*sizeof(IndexSlot*);
hash_init(type, &t->htable, name, 3*size/4, fun);
t->size = 0;
diff --git a/erts/emulator/beam/instrs.tab b/erts/emulator/beam/instrs.tab
index 7ea9dee299..6a531fcc09 100644
--- a/erts/emulator/beam/instrs.tab
+++ b/erts/emulator/beam/instrs.tab
@@ -322,6 +322,16 @@ get_list(Src, Hd, Tl) {
$Tl = tl;
}
+get_hd(Src, Hd) {
+ Eterm* tmp_ptr = list_val($Src);
+ $Hd = CAR(tmp_ptr);
+}
+
+get_tl(Src, Tl) {
+ Eterm* tmp_ptr = list_val($Src);
+ $Tl = CDR(tmp_ptr);
+}
+
i_get(Src, Dst) {
$Dst = erts_pd_hash_get(c_p, $Src);
}
@@ -375,7 +385,6 @@ i_element := element_group.fetch.execute;
element_group.head() {
- Eterm element_index;
Eterm element_tuple;
}
@@ -384,7 +393,7 @@ element_group.fetch(Src) {
}
element_group.execute(Fail, Index, Dst) {
- element_index = $Index;
+ Eterm element_index = $Index;
if (ERTS_LIKELY(is_small(element_index) && is_tuple(element_tuple))) {
Eterm* tp = tuple_val(element_tuple);
@@ -543,6 +552,13 @@ put_list(Hd, Tl, Dst) {
HTOP += 2;
}
+update_list(Hd, Dst) {
+ HTOP[0] = $Hd;
+ HTOP[1] = $Dst;
+ $Dst = make_list(HTOP);
+ HTOP += 2;
+}
+
i_put_tuple := i_put_tuple.make.fill;
i_put_tuple.make(Dst) {
@@ -789,7 +805,8 @@ is_eq_exact(Fail, X, Y) {
}
i_is_eq_exact_literal(Fail, Src, Literal) {
- if (!eq($Src, $Literal)) {
+ Eterm src = $Src;
+ if (is_immed(src) || !eq(src, $Literal)) {
$FAIL($Fail);
}
}
@@ -801,7 +818,8 @@ is_ne_exact(Fail, X, Y) {
}
i_is_ne_exact_literal(Fail, Src, Literal) {
- if (eq($Src, $Literal)) {
+ Eterm src = $Src;
+ if (!is_immed(src) && eq(src, $Literal)) {
$FAIL($Fail);
}
}
@@ -831,12 +849,14 @@ badmatch(Src) {
c_p->fvalue = $Src;
c_p->freason = BADMATCH;
goto find_func_info;
+ //| -no_next;
}
case_end(Src) {
c_p->fvalue = $Src;
c_p->freason = EXC_CASE_CLAUSE;
goto find_func_info;
+ //| -no_next;
}
if_end() {
@@ -856,8 +876,7 @@ catch(Y, Fail) {
}
catch_end(Y) {
- c_p->catches--;
- make_blank($Y);
+ $try_end($Y);
if (is_non_value(r(0))) {
c_p->fvalue = NIL;
if (x(1) == am_throw) {
@@ -887,12 +906,15 @@ catch_end(Y) {
try_end(Y) {
c_p->catches--;
make_blank($Y);
- if (is_non_value(r(0))) {
- c_p->fvalue = NIL;
- r(0) = x(1);
- x(1) = x(2);
- x(2) = x(3);
- }
+}
+
+try_case(Y) {
+ $try_end($Y);
+ ASSERT(is_non_value(r(0)));
+ c_p->fvalue = NIL;
+ r(0) = x(1);
+ x(1) = x(2);
+ x(2) = x(3);
}
try_case_end(Src) {
@@ -919,3 +941,33 @@ i_raise() {
//| -no_next
}
+build_stacktrace() {
+ SWAPOUT;
+ x(0) = build_stacktrace(c_p, x(0));
+ SWAPIN;
+}
+
+raw_raise() {
+ Eterm class = x(0);
+ Eterm value = x(1);
+ Eterm stacktrace = x(2);
+
+ if (class == am_error) {
+ c_p->freason = EXC_ERROR & ~EXF_SAVETRACE;
+ c_p->fvalue = value;
+ c_p->ftrace = stacktrace;
+ goto find_func_info;
+ } else if (class == am_exit) {
+ c_p->freason = EXC_EXIT & ~EXF_SAVETRACE;
+ c_p->fvalue = value;
+ c_p->ftrace = stacktrace;
+ goto find_func_info;
+ } else if (class == am_throw) {
+ c_p->freason = EXC_THROWN & ~EXF_SAVETRACE;
+ c_p->fvalue = value;
+ c_p->ftrace = stacktrace;
+ goto find_func_info;
+ } else {
+ x(0) = am_badarg;
+ }
+}
diff --git a/erts/emulator/beam/io.c b/erts/emulator/beam/io.c
index 85013af3ad..2446b3c074 100644
--- a/erts/emulator/beam/io.c
+++ b/erts/emulator/beam/io.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1996-2017. All Rights Reserved.
+ * Copyright Ericsson AB 1996-2018. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -53,6 +53,7 @@
#include "erl_hl_timer.h"
#include "erl_time.h"
#include "erl_io_queue.h"
+#include "erl_proc_sig_queue.h"
extern ErlDrvEntry fd_driver_entry;
extern ErlDrvEntry vanilla_driver_entry;
@@ -60,7 +61,7 @@ extern ErlDrvEntry spawn_driver_entry;
#ifndef __WIN32__
extern ErlDrvEntry forker_driver_entry;
#endif
-extern ErlDrvEntry *driver_tab[]; /* table of static drivers, only used during initialization */
+extern ErtsStaticDriver driver_tab[]; /* table of static drivers, only used during initialization */
erts_driver_t *driver_list; /* List of all drivers, static and dynamic. */
erts_rwmtx_t erts_driver_list_lock; /* Mutex for driver list */
@@ -298,7 +299,6 @@ static Port *create_port(char *name,
erts_aint32_t state = ERTS_PORT_SFLG_CONNECTED;
erts_aint32_t x_pts_flgs = 0;
- ErtsRunQueue *runq;
if (!driver_lock) {
/* Align size for mutex following port struct */
port_size = size = ERTS_ALC_DATA_ALIGN_SIZE(sizeof(Port));
@@ -347,11 +347,16 @@ static Port *create_port(char *name,
p += sizeof(erts_mtx_t);
state |= ERTS_PORT_SFLG_PORT_SPECIFIC_LOCK;
}
- if (erts_get_scheduler_data())
- runq = erts_get_runq_current(NULL);
- else
- runq = ERTS_RUNQ_IX(0);
- erts_atomic_set_nob(&prt->run_queue, (erts_aint_t) runq);
+
+ {
+ ErtsRunQueue *runq;
+ ErtsSchedulerData *esdp = erts_get_scheduler_data();
+ if (esdp)
+ runq = erts_get_runq_current(esdp);
+ else
+ runq = ERTS_RUNQ_IX(0);
+ erts_init_runq_port(prt, runq);
+ }
prt->xports = NULL;
@@ -362,6 +367,7 @@ static Port *create_port(char *name,
prt->drv_ptr = driver;
ERTS_P_LINKS(prt) = NULL;
ERTS_P_MONITORS(prt) = NULL;
+ ERTS_P_LT_MONITORS(prt) = NULL;
prt->linebuf = NULL;
prt->suspended = NULL;
erts_init_port_data(prt);
@@ -584,7 +590,7 @@ erts_open_driver(erts_driver_t* driver, /* Pointer to driver. */
*/
for (d = driver_list; d; d = d->next) {
- if (strcmp(d->name, name) == 0 &&
+ if (sys_strcmp(d->name, name) == 0 &&
erts_ddll_driver_ok(d->handle)) {
driver = d;
break;
@@ -634,7 +640,7 @@ erts_open_driver(erts_driver_t* driver, /* Pointer to driver. */
trace_port_open(port,
pid,
erts_atom_put((byte *) port->name,
- strlen(port->name),
+ sys_strlen(port->name),
ERTS_ATOM_ENC_LATIN1,
1));
}
@@ -744,8 +750,8 @@ driver_create_port(ErlDrvPort creator_port_ix, /* Creating port */
Port *creator_port;
Port* port;
erts_driver_t *driver;
- Process *rp;
erts_mtx_t *driver_lock = NULL;
+ ErtsLinkData *ldp;
ERTS_CHK_NO_PROC_LOCKS;
@@ -757,17 +763,13 @@ driver_create_port(ErlDrvPort creator_port_ix, /* Creating port */
if (creator_port == ERTS_INVALID_ERL_DRV_PORT)
return ERTS_INVALID_ERL_DRV_PORT;
- rp = erts_proc_lookup(pid);
- if (!rp)
- return ERTS_INVALID_ERL_DRV_PORT;
-
ERTS_LC_ASSERT(erts_lc_is_port_locked(creator_port));
driver = creator_port->drv_ptr;
erts_rwmtx_rlock(&erts_driver_list_lock);
if (!erts_ddll_driver_ok(driver->handle)) {
erts_rwmtx_runlock(&erts_driver_list_lock);
- return ERTS_INVALID_ERL_DRV_PORT;
+ return ERTS_INVALID_ERL_DRV_PORT;
}
if (driver->handle != NULL) {
@@ -795,9 +797,15 @@ driver_create_port(ErlDrvPort creator_port_ix, /* Creating port */
}
ERTS_LC_ASSERT(erts_lc_is_port_locked(port));
- erts_proc_lock(rp, ERTS_PROC_LOCK_LINK);
- if (ERTS_PROC_IS_EXITING(rp)) {
- erts_proc_unlock(rp, ERTS_PROC_LOCK_LINK);
+ ldp = erts_link_create(ERTS_LNK_TYPE_PORT,
+ port->common.id, pid);
+ ASSERT(ldp->a.other.item == pid);
+ ASSERT(ldp->b.other.item == port->common.id);
+ erts_link_tree_insert(&ERTS_P_LINKS(port), &ldp->a);
+
+ if (!erts_proc_sig_send_link(NULL, pid, &ldp->b)) {
+ erts_link_tree_delete(&ERTS_P_LINKS(port), &ldp->a);
+ erts_link_release_both(ldp);
if (driver->handle) {
erts_rwmtx_rlock(&erts_driver_list_lock);
erts_ddll_decrement_port_count(driver->handle);
@@ -808,10 +816,6 @@ driver_create_port(ErlDrvPort creator_port_ix, /* Creating port */
return ERTS_INVALID_ERL_DRV_PORT;
}
- erts_add_link(&ERTS_P_LINKS(port), LINK_PID, pid);
- erts_add_link(&ERTS_P_LINKS(rp), LINK_PID, port->common.id);
- erts_proc_unlock(rp, ERTS_PROC_LOCK_LINK);
-
if (!driver_lock) {
ErtsXPortsList *xplp = xports_list_alloc();
xplp->port = port;
@@ -952,6 +956,9 @@ try_imm_drv_call(ErtsTryImmDrvCallState *sp)
reds_left_in = ERTS_BIF_REDS_LEFT(c_p);
erts_proc_unlock(c_p, ERTS_PROC_LOCK_MAIN);
+
+ ASSERT((c_p->scheduler_data)->current_port == NULL);
+ (c_p->scheduler_data)->current_port = prt;
}
ASSERT(0 <= reds_left_in && reds_left_in <= CONTEXT_REDS);
@@ -1013,6 +1020,9 @@ finalize_imm_drv_call(ErtsTryImmDrvCallState *sp)
erts_port_release(prt);
if (c_p) {
+ ASSERT((c_p->scheduler_data)->current_port == prt);
+ (c_p->scheduler_data)->current_port = NULL;
+
erts_proc_lock(c_p, ERTS_PROC_LOCK_MAIN);
if (reds != (CONTEXT_REDS - sp->reds_left_in)) {
@@ -1163,21 +1173,10 @@ erts_schedule_proc2port_signal(Process *c_p,
* otherwise, next receive will *not* work
* as expected!
*/
- erts_proc_lock(c_p, ERTS_PROC_LOCKS_MSG_RECEIVE);
- if (ERTS_PROC_PENDING_EXIT(c_p)) {
- /* need to exit caller instead */
- erts_proc_unlock(c_p, ERTS_PROC_LOCKS_MSG_RECEIVE);
- KILL_CATCHES(c_p);
- c_p->freason = EXC_EXIT;
- return ERTS_PORT_OP_CALLER_EXIT;
- }
-
- ERTS_MSGQ_MV_INQ2PRIVQ(c_p);
- c_p->msg.save = c_p->msg.last;
-
- erts_proc_unlock(c_p, (ERTS_PROC_LOCKS_MSG_RECEIVE
- | ERTS_PROC_LOCK_MAIN));
+ ERTS_RECV_MARK_SAVE(c_p);
+ ERTS_RECV_MARK_SET(c_p);
+ erts_proc_unlock(c_p, ERTS_PROC_LOCK_MAIN);
}
@@ -1235,29 +1234,12 @@ erts_schedule_port2port_signal(Eterm port_num, ErtsProc2PortSigData *sigdp,
static ERTS_INLINE void
send_badsig(Port *prt) {
- ErtsProcLocks rp_locks = ERTS_PROC_LOCKS_XSIG_SEND;
- Process* rp;
Eterm connected = ERTS_PORT_GET_CONNECTED(prt);
ERTS_CHK_NO_PROC_LOCKS;
ERTS_LC_ASSERT(erts_get_scheduler_id());
-
ASSERT(is_internal_pid(connected));
-
- rp = erts_proc_lookup_raw(connected);
- if (rp) {
- erts_proc_lock(rp, rp_locks);
- if (!ERTS_PROC_IS_EXITING(rp))
- (void) erts_send_exit_signal(NULL,
- prt->common.id,
- rp,
- &rp_locks,
- am_badsig,
- NIL,
- NULL,
- 0);
- if (rp_locks)
- erts_proc_unlock(rp, rp_locks);
- } /* exit sent */
+ erts_proc_sig_send_exit(NULL, prt->common.id, connected,
+ am_badsig, NIL, 0);
} /* send_badsig */
static void
@@ -2190,11 +2172,11 @@ call_deliver_port_exit(int bang_op,
}
if (broken_link) {
- ErtsLink *lnk = erts_remove_link(&ERTS_P_LINKS(prt), from);
- if (lnk)
- erts_destroy_link(lnk);
- else
+ ErtsLink *lnk = erts_link_tree_lookup(ERTS_P_LINKS(prt), from);
+ if (!lnk)
return ERTS_PORT_OP_DROPPED;
+ erts_link_tree_delete(&ERTS_P_LINKS(prt), lnk);
+ erts_link_release(lnk);
}
if (IS_TRACED_FL(prt, F_TRACE_RECEIVE))
@@ -2363,27 +2345,45 @@ set_port_connected(int bang_op,
#endif
}
else { /* Port BIF operation */
- Process *rp = erts_proc_lookup_raw(connect);
- if (!rp)
- return ERTS_PORT_OP_DROPPED;
- erts_proc_lock(rp, ERTS_PROC_LOCK_LINK);
- if (ERTS_PROC_IS_EXITING(rp)) {
- erts_proc_unlock(rp, ERTS_PROC_LOCK_LINK);
- return ERTS_PORT_OP_DROPPED;
- }
+ int created;
+ ErtsLink *lnk;
+
+ if (is_not_internal_pid(connect))
+ return ERTS_PORT_OP_DROPPED;
+
+ lnk = erts_link_tree_lookup_create(&ERTS_P_LINKS(prt), &created,
+ ERTS_LNK_TYPE_PORT, prt->common.id,
+ connect);
+ if (created) {
+ ErtsLinkData *ldp;
+ ErtsLink *olnk = erts_link_to_other(lnk, &ldp);
+ ASSERT(olnk->other.item == prt->common.id);
+ if (!erts_proc_sig_send_link(NULL, connect, olnk)) {
+ erts_link_tree_delete(&ERTS_P_LINKS(prt), lnk);
+ erts_link_release_both(ldp);
+ return ERTS_PORT_OP_DROPPED;
+ }
+ if (IS_TRACED_FL(prt, F_TRACE_PORTS))
+ trace_port(prt, am_getting_linked, connect);
+ }
- erts_add_link(&ERTS_P_LINKS(rp), LINK_PID, prt->common.id);
- erts_add_link(&ERTS_P_LINKS(prt), LINK_PID, connect);
+#ifdef USE_VM_PROBES
+ if (DTRACE_ENABLED(port_connect)) {
+ Eterm old_connected = ERTS_PORT_GET_CONNECTED(prt);
+ DTRACE_CHARBUF(process_str, DTRACE_TERM_BUF_SIZE);
+ DTRACE_CHARBUF(port_str, DTRACE_TERM_BUF_SIZE);
+ DTRACE_CHARBUF(newprocess_str, DTRACE_TERM_BUF_SIZE);
- if (IS_TRACED_FL(rp, F_TRACE_PROCS))
- trace_proc(NULL, 0, rp, am_getting_linked, prt->common.id);
+ dtrace_pid_str(old_connected, process_str);
+ erts_snprintf(port_str, sizeof(DTRACE_CHARBUF_NAME(port_str)),
+ "%T", prt->common.id);
+ dtrace_pid_str(connect, newprocess_str);
+ DTRACE4(port_connect, process_str, port_str, prt->name, newprocess_str);
+ }
+#endif
ERTS_PORT_SET_CONNECTED(prt, connect);
- erts_proc_unlock(rp, ERTS_PROC_LOCK_LINK);
-
- if (IS_TRACED_FL(prt, F_TRACE_PORTS))
- trace_port(prt, am_getting_linked, connect);
if (IS_TRACED_FL(prt, F_TRACE_RECEIVE))
trace_port_receive(prt, from, am_connect, connect);
if (IS_TRACED_FL(prt, F_TRACE_SEND)) {
@@ -2391,18 +2391,6 @@ set_port_connected(int bang_op,
trace_port_send(prt, from, TUPLE2(hp, prt->common.id, am_connected), 1);
}
-#ifdef USE_VM_PROBES
- if (DTRACE_ENABLED(port_connect)) {
- DTRACE_CHARBUF(process_str, DTRACE_TERM_BUF_SIZE);
- DTRACE_CHARBUF(port_str, DTRACE_TERM_BUF_SIZE);
- DTRACE_CHARBUF(newprocess_str, DTRACE_TERM_BUF_SIZE);
-
- dtrace_pid_str(connect, process_str);
- erts_snprintf(port_str, sizeof(DTRACE_CHARBUF_NAME(port_str)), "%T", prt->common.id);
- dtrace_proc_str(rp, newprocess_str);
- DTRACE4(port_connect, process_str, port_str, prt->name, newprocess_str);
- }
-#endif
}
return ERTS_PORT_OP_DONE;
@@ -2490,13 +2478,24 @@ erts_port_connect(Process *c_p,
}
static void
-port_unlink(Port *prt, Eterm from)
+port_unlink(Port *prt, ErtsLink *lnk)
{
- ErtsLink *lnk = erts_remove_link(&ERTS_P_LINKS(prt), from);
- if (lnk) {
+ ErtsLinkData *ldp;
+ ErtsLink *dlnk, *llnk;
+
+ llnk = erts_link_to_other(lnk, &ldp);
+ dlnk = erts_link_tree_key_delete(&ERTS_P_LINKS(prt), llnk);
+ if (!dlnk)
+ erts_link_release(lnk);
+ else {
if (IS_TRACED_FL(prt, F_TRACE_PORTS))
- trace_port(prt, am_getting_unlinked, from);
- erts_destroy_link(lnk);
+ trace_port(prt, am_getting_unlinked, llnk->other.item);
+ if (dlnk == llnk)
+ erts_link_release_both(ldp);
+ else {
+ erts_link_release(lnk);
+ erts_link_release(dlnk);
+ }
}
}
@@ -2504,14 +2503,14 @@ static int
port_sig_unlink(Port *prt, erts_aint32_t state, int op, ErtsProc2PortSigData *sigdp)
{
if (op == ERTS_PROC2PORT_SIG_EXEC)
- port_unlink(prt, sigdp->u.unlink.from);
+ port_unlink(prt, sigdp->u.unlink.lnk);
if (sigdp->flags & ERTS_P2P_SIG_DATA_FLG_REPLY)
port_sched_op_reply(sigdp->caller, sigdp->ref, am_true, prt);
return ERTS_PORT_REDS_UNLINK;
}
ErtsPortOpResult
-erts_port_unlink(Process *c_p, Port *prt, Eterm from, Eterm *refp)
+erts_port_unlink(Process *c_p, Port *prt, ErtsLink *lnk, Eterm *refp)
{
ErtsProc2PortSigData *sigdp;
ErtsTryImmDrvCallState try_call_state
@@ -2524,7 +2523,7 @@ erts_port_unlink(Process *c_p, Port *prt, Eterm from, Eterm *refp)
switch (try_imm_drv_call(&try_call_state)) {
case ERTS_TRY_IMM_DRV_CALL_OK:
- port_unlink(prt, from);
+ port_unlink(prt, lnk);
finalize_imm_drv_call(&try_call_state);
BUMP_REDS(c_p, ERTS_PORT_REDS_UNLINK);
return ERTS_PORT_OP_DONE;
@@ -2537,11 +2536,12 @@ erts_port_unlink(Process *c_p, Port *prt, Eterm from, Eterm *refp)
sigdp = erts_port_task_alloc_p2p_sig_data();
sigdp->flags = ERTS_P2P_SIG_TYPE_UNLINK;
- sigdp->u.unlink.from = from;
-
+ sigdp->u.unlink.port_id = prt->common.id;
+ sigdp->u.unlink.lnk = lnk;
+
return erts_schedule_proc2port_signal(c_p,
prt,
- c_p ? c_p->common.id : from,
+ c_p->common.id,
refp,
sigdp,
0,
@@ -2550,45 +2550,24 @@ erts_port_unlink(Process *c_p, Port *prt, Eterm from, Eterm *refp)
}
static void
-port_link_failure(Eterm port_id, Eterm linker)
+port_link_failure(Eterm port_id, ErtsLink *lnk)
{
- Process *rp;
- ErtsProcLocks rp_locks = ERTS_PROC_LOCK_LINK|ERTS_PROC_LOCKS_XSIG_SEND;
- ASSERT(is_internal_pid(linker));
- rp = erts_pid2proc(NULL, 0, linker, rp_locks);
- if (rp) {
- ErtsLink *rlnk = erts_remove_link(&ERTS_P_LINKS(rp), port_id);
- if (rlnk) {
- int xres = erts_send_exit_signal(NULL,
- port_id,
- rp,
- &rp_locks,
- am_noproc,
- NIL,
- NULL,
- 0);
- if (xres >= 0) {
- /* We didn't exit the process and it is traced */
- if (IS_TRACED_FL(rp, F_TRACE_PROCS))
- trace_proc(NULL, 0, rp, am_getting_unlinked, port_id);
- }
- if (rp_locks)
- erts_proc_unlock(rp, rp_locks);
- }
- }
+ erts_proc_sig_send_link_exit(NULL, port_id, lnk, am_noproc, NIL);
}
static void
-port_link(Port *prt, erts_aint32_t state, Eterm to)
+port_link(Port *prt, erts_aint32_t state, ErtsLink *lnk)
{
- if (IS_TRACED_FL(prt, F_TRACE_PORTS))
- trace_port(prt, am_getting_linked, to);
- if (!(state & ERTS_PORT_SFLGS_INVALID_LOOKUP)) {
- erts_add_link(&ERTS_P_LINKS(prt), LINK_PID, to);
- } else {
- port_link_failure(prt->common.id, to);
- if (IS_TRACED_FL(prt, F_TRACE_PORTS))
- trace_port(prt, am_unlink, to);
+ if (state & ERTS_PORT_SFLGS_INVALID_LOOKUP)
+ port_link_failure(prt->common.id, lnk);
+ else {
+ ErtsLink *rlnk;
+ rlnk = erts_link_tree_insert_addr_replace(&ERTS_P_LINKS(prt),
+ lnk);
+ if (rlnk)
+ erts_link_release(rlnk);
+ else if (IS_TRACED_FL(prt, F_TRACE_PORTS))
+ trace_port(prt, am_getting_linked, lnk->other.item);
}
}
@@ -2596,17 +2575,16 @@ static int
port_sig_link(Port *prt, erts_aint32_t state, int op, ErtsProc2PortSigData *sigdp)
{
if (op == ERTS_PROC2PORT_SIG_EXEC)
- port_link(prt, state, sigdp->u.link.to);
- else {
- port_link_failure(sigdp->u.link.port, sigdp->u.link.to);
- }
+ port_link(prt, state, sigdp->u.link.lnk);
+ else
+ port_link_failure(sigdp->u.link.port_id, sigdp->u.link.lnk);
if (sigdp->flags & ERTS_P2P_SIG_DATA_FLG_REPLY)
port_sched_op_reply(sigdp->caller, sigdp->ref, am_true, prt);
return ERTS_PORT_REDS_LINK;
}
ErtsPortOpResult
-erts_port_link(Process *c_p, Port *prt, Eterm to, Eterm *refp)
+erts_port_link(Process *c_p, Port *prt, ErtsLink *lnk, Eterm *refp)
{
ErtsProc2PortSigData *sigdp;
ErtsTryImmDrvCallState try_call_state
@@ -2619,7 +2597,7 @@ erts_port_link(Process *c_p, Port *prt, Eterm to, Eterm *refp)
switch (try_imm_drv_call(&try_call_state)) {
case ERTS_TRY_IMM_DRV_CALL_OK:
- port_link(prt, try_call_state.state, to);
+ port_link(prt, try_call_state.state, lnk);
finalize_imm_drv_call(&try_call_state);
BUMP_REDS(c_p, ERTS_PORT_REDS_LINK);
return ERTS_PORT_OP_DONE;
@@ -2632,12 +2610,12 @@ erts_port_link(Process *c_p, Port *prt, Eterm to, Eterm *refp)
sigdp = erts_port_task_alloc_p2p_sig_data();
sigdp->flags = ERTS_P2P_SIG_TYPE_LINK;
- sigdp->u.link.port = prt->common.id;
- sigdp->u.link.to = to;
+ sigdp->u.link.port_id = prt->common.id;
+ sigdp->u.link.lnk = lnk;
return erts_schedule_proc2port_signal(c_p,
prt,
- c_p ? c_p->common.id : to,
+ c_p->common.id,
refp,
sigdp,
0,
@@ -2646,51 +2624,23 @@ erts_port_link(Process *c_p, Port *prt, Eterm to, Eterm *refp)
}
static void
-port_monitor_failure(Eterm port_id, Eterm origin, Eterm ref_DOWN)
+port_monitor_failure(Eterm port_id, ErtsMonitor *mon)
{
- Process *origin_p;
- ErtsProcLocks p_locks = ERTS_PROC_LOCK_LINK;
- ASSERT(is_internal_pid(origin));
-
- origin_p = erts_pid2proc(NULL, 0, origin, p_locks);
- if (! origin_p) { return; }
-
- /* Send the DOWN message immediately. Ref is made on the fly because
- * caller has never seen it yet. */
- erts_queue_monitor_message(origin_p, &p_locks, ref_DOWN,
- am_port, port_id, am_noproc);
- erts_proc_unlock(origin_p, p_locks);
+ erts_proc_sig_send_monitor_down(mon, am_noproc);
}
/* Origin wants to monitor port Prt. State contains possible error, which has
* happened just before. Name is either NIL or an atom, if user monitors
* a port by name. Ref is premade reference that will be returned to user */
static void
-port_monitor(Port *prt, erts_aint32_t state, Eterm origin,
- Eterm name, Eterm ref)
+port_monitor(Port *prt, erts_aint32_t state, ErtsMonitor *mon)
{
- Eterm name_or_nil = is_atom(name) ? name : NIL;
-
- ASSERT(is_pid(origin));
- ASSERT(is_atom(name) || is_port(name) || name == NIL);
- ASSERT(is_internal_ordinary_ref(ref));
-
- if (!(state & ERTS_PORT_SFLGS_INVALID_LOOKUP)) {
- ErtsProcLocks p_locks = ERTS_PROC_LOCK_LINK;
-
- Process *origin_p = erts_pid2proc(NULL, 0, origin, p_locks);
- if (! origin_p) {
- goto failure;
- }
- erts_add_monitor(&ERTS_P_MONITORS(origin_p), MON_ORIGIN, ref,
- prt->common.id, name_or_nil);
- erts_add_monitor(&ERTS_P_MONITORS(prt), MON_TARGET, ref,
- origin, name_or_nil);
-
- erts_proc_unlock(origin_p, p_locks);
- } else {
-failure:
- port_monitor_failure(prt->common.id, origin, ref);
+ ASSERT(prt);
+ if (state & ERTS_PORT_SFLGS_INVALID_LOOKUP)
+ port_monitor_failure(prt->common.id, mon);
+ else {
+ ASSERT(erts_monitor_is_target(mon));
+ erts_monitor_list_insert(&ERTS_P_LT_MONITORS(prt), mon);
}
}
@@ -2698,24 +2648,11 @@ static int
port_sig_monitor(Port *prt, erts_aint32_t state, int op,
ErtsProc2PortSigData *sigdp)
{
- Eterm hp[ERTS_REF_THING_SIZE];
- Eterm ref = make_internal_ref(&hp);
- write_ref_thing(hp, sigdp->ref[0], sigdp->ref[1], sigdp->ref[2]);
-
- if (op == ERTS_PROC2PORT_SIG_EXEC) {
- /* erts_add_monitor call inside port_monitor will copy ref from hp */
- port_monitor(prt, state,
- sigdp->u.monitor.origin,
- sigdp->u.monitor.name,
- ref);
- } else {
- port_monitor_failure(sigdp->u.monitor.name,
- sigdp->u.monitor.origin,
- ref);
- }
- if (sigdp->flags & ERTS_P2P_SIG_DATA_FLG_REPLY) {
- port_sched_op_reply(sigdp->caller, sigdp->ref, am_true, prt);
- }
+ if (op == ERTS_PROC2PORT_SIG_EXEC)
+ port_monitor(prt, state, sigdp->u.monitor.mon);
+ else
+ port_monitor_failure(sigdp->u.monitor.port_id,
+ sigdp->u.monitor.mon);
return ERTS_PORT_REDS_MONITOR;
}
@@ -2723,95 +2660,63 @@ port_sig_monitor(Port *prt, erts_aint32_t state, int op,
* a reference (ref may be rewritten to be used to serve additionally as a
* signal id). Name is atom if user monitors port by name or NIL */
ErtsPortOpResult
-erts_port_monitor(Process *origin, Port *port, Eterm name, Eterm *refp)
+erts_port_monitor(Process *c_p, Port *port, ErtsMonitor *mon)
{
ErtsProc2PortSigData *sigdp;
ErtsTryImmDrvCallState try_call_state
= ERTS_INIT_TRY_IMM_DRV_CALL_STATE(
- origin, port, ERTS_PORT_SFLGS_INVALID_LOOKUP,
+ c_p,
+ port,
+ ERTS_PORT_SFLGS_INVALID_LOOKUP,
0,
- 0, /* trap_ref is always set so !trap_ref always is false */
+ !0,
am_monitor);
- ASSERT(origin);
+ ASSERT(c_p);
ASSERT(port);
- ASSERT(is_atom(name) || is_port(name));
- ASSERT(refp);
+ ASSERT(mon);
switch (try_imm_drv_call(&try_call_state)) {
case ERTS_TRY_IMM_DRV_CALL_OK:
- port_monitor(port, try_call_state.state, origin->common.id, name, *refp);
+ port_monitor(port, try_call_state.state, mon);
finalize_imm_drv_call(&try_call_state);
- BUMP_REDS(origin, ERTS_PORT_REDS_MONITOR);
+ BUMP_REDS(c_p, ERTS_PORT_REDS_MONITOR);
return ERTS_PORT_OP_DONE;
case ERTS_TRY_IMM_DRV_CALL_INVALID_PORT:
- return ERTS_PORT_OP_BADARG;
+ return ERTS_PORT_OP_DROPPED;
default:
break; /* Schedule call instead... */
}
sigdp = erts_port_task_alloc_p2p_sig_data();
sigdp->flags = ERTS_P2P_SIG_TYPE_MONITOR;
- sigdp->u.monitor.origin = origin->common.id;
- sigdp->u.monitor.name = name; /* either named monitor, or port id */
+ sigdp->u.monitor.port_id = port->common.id;
+ sigdp->u.monitor.mon = mon;
/* Ref contents will be initialized here */
- return erts_schedule_proc2port_signal(origin, port, origin->common.id,
- refp, sigdp, 0, NULL,
+ return erts_schedule_proc2port_signal(c_p,
+ port,
+ c_p->common.id,
+ NULL,
+ sigdp,
+ 0,
+ NULL,
port_sig_monitor);
}
-static void
-port_demonitor_failure(Eterm port_id, Eterm origin, Eterm ref)
-{
- Process *origin_p;
- ErtsProcLocks rp_locks = ERTS_PROC_LOCK_LINK;
- ErtsMonitor *mon1;
- ASSERT(is_internal_pid(origin));
-
- origin_p = erts_pid2proc(NULL, 0, origin, rp_locks);
- if (! origin_p) { return; }
-
- /* do not send any DOWN messages, drop monitors on process */
- mon1 = erts_remove_monitor(&ERTS_P_MONITORS(origin_p), ref);
- if (mon1 != NULL) {
- erts_destroy_monitor(mon1);
- }
-
- erts_proc_unlock(origin_p, rp_locks);
-}
-
/* Origin wants to demonitor port Prt. State contains possible error, which has
* happened just before. Ref is reference to monitor */
static void
-port_demonitor(Port *port, erts_aint32_t state, Eterm origin, Eterm ref)
+port_demonitor(Port *port, erts_aint32_t state, ErtsMonitor *mon)
{
- ASSERT(port);
- ASSERT(is_pid(origin));
- ASSERT(is_internal_ref(ref));
-
- if (!(state & ERTS_PORT_SFLGS_INVALID_LOOKUP)) {
- ErtsProcLocks p_locks = ERTS_PROC_LOCK_LINK;
- Process *origin_p = erts_pid2proc(NULL, 0, origin, p_locks);
- if (origin_p) {
- ErtsMonitor *mon1 = erts_remove_monitor(&ERTS_P_MONITORS(origin_p),
- ref);
- if (mon1 != NULL) {
- erts_destroy_monitor(mon1);
- }
- }
- if (1) {
- ErtsMonitor *mon2 = erts_remove_monitor(&ERTS_P_MONITORS(port),
- ref);
- if (mon2 != NULL) {
- erts_destroy_monitor(mon2);
- }
- }
- if (origin_p) { /* when origin is dying, it won't be found */
- erts_proc_unlock(origin_p, p_locks);
- }
- } else {
- port_demonitor_failure(port->common.id, origin, ref);
+ ErtsMonitorData *mdp = erts_monitor_to_data(mon);
+ ASSERT(port && mon);
+ ASSERT(erts_monitor_is_origin(mon));
+ if (!erts_monitor_is_in_table(&mdp->target))
+ erts_monitor_release(mon);
+ else {
+ erts_monitor_list_delete(&ERTS_P_LT_MONITORS(port), &mdp->target);
+ erts_monitor_release_both(mdp);
}
}
@@ -2819,73 +2724,47 @@ static int
port_sig_demonitor(Port *prt, erts_aint32_t state, int op,
ErtsProc2PortSigData *sigdp)
{
- Eterm hp[ERTS_REF_THING_SIZE];
- Eterm ref = make_internal_ref(&hp);
- write_ref_thing(hp, sigdp->u.demonitor.ref[0],
- sigdp->u.demonitor.ref[1],
- sigdp->u.demonitor.ref[2]);
- if (op == ERTS_PROC2PORT_SIG_EXEC) {
- port_demonitor(prt, state, sigdp->u.demonitor.origin, ref);
- } else {
- port_demonitor_failure(sigdp->u.demonitor.name,
- sigdp->u.demonitor.origin,
- ref);
- }
- if (sigdp->flags & ERTS_P2P_SIG_DATA_FLG_REPLY) {
- port_sched_op_reply(sigdp->caller, sigdp->ref, am_true, prt);
- }
+ if (op == ERTS_PROC2PORT_SIG_EXEC)
+ port_demonitor(prt, state, sigdp->u.demonitor.mon);
+ else
+ erts_monitor_release(sigdp->u.demonitor.mon);
return ERTS_PORT_REDS_DEMONITOR;
}
-/* Removes monitor between origin and target, identified by ref.
- * Mode defines normal or relaxed demonitor rules (process is at death) */
-ErtsPortOpResult erts_port_demonitor(Process *origin, ErtsDemonitorMode mode,
- Port *target, Eterm ref,
- Eterm *trap_ref)
+ErtsPortOpResult
+erts_port_demonitor(Process *c_p, Port *prt, ErtsMonitor *mon)
{
- Process *c_p = mode == ERTS_PORT_DEMONITOR_NORMAL ? origin : NULL;
ErtsProc2PortSigData *sigdp;
ErtsTryImmDrvCallState try_call_state
= ERTS_INIT_TRY_IMM_DRV_CALL_STATE(
c_p,
- target, ERTS_PORT_SFLGS_INVALID_LOOKUP,
+ prt, ERTS_PORT_SFLGS_INVALID_LOOKUP,
0,
- !trap_ref,
+ !0,
am_demonitor);
- ASSERT(origin);
- ASSERT(target);
- ASSERT(is_internal_ref(ref));
+ ASSERT(c_p && prt && mon);
switch (try_imm_drv_call(&try_call_state)) {
case ERTS_TRY_IMM_DRV_CALL_OK:
- port_demonitor(target, try_call_state.state, origin->common.id, ref);
+ port_demonitor(prt, try_call_state.state, mon);
finalize_imm_drv_call(&try_call_state);
- if (mode == ERTS_PORT_DEMONITOR_NORMAL) {
- BUMP_REDS(origin, ERTS_PORT_REDS_DEMONITOR);
- }
+ BUMP_REDS(c_p, ERTS_PORT_REDS_DEMONITOR);
return ERTS_PORT_OP_DONE;
case ERTS_TRY_IMM_DRV_CALL_INVALID_PORT:
- return ERTS_PORT_OP_BADARG;
+ return ERTS_PORT_OP_DROPPED;
default:
break; /* Schedule call instead... */
}
sigdp = erts_port_task_alloc_p2p_sig_data();
sigdp->flags = ERTS_P2P_SIG_TYPE_DEMONITOR;
- sigdp->u.demonitor.origin = origin->common.id;
- sigdp->u.demonitor.name = target->common.id;
- {
- Uint32 *nums = internal_ref_numbers(ref);
- /* Start from 1 skip ref arity */
- sys_memcpy(sigdp->u.demonitor.ref,
- nums,
- sizeof(sigdp->u.demonitor.ref));
- }
+ sigdp->u.demonitor.port_id = prt->common.id;
+ sigdp->u.demonitor.mon = mon;
/* Ref contents will be initialized here */
- return erts_schedule_proc2port_signal(c_p, target, origin->common.id,
- trap_ref, sigdp, 0, NULL,
+ return erts_schedule_proc2port_signal(c_p, prt, c_p->common.id,
+ NULL, sigdp, 0, NULL,
port_sig_demonitor);
}
@@ -2894,11 +2773,13 @@ init_ack_send_reply(Port *port, Eterm resp)
{
if (!is_internal_port(resp)) {
- Process *rp = erts_proc_lookup_raw(port->async_open_port->to);
- erts_proc_lock(rp, ERTS_PROC_LOCK_LINK);
- erts_remove_link(&ERTS_P_LINKS(port), port->async_open_port->to);
- erts_remove_link(&ERTS_P_LINKS(rp), port->common.id);
- erts_proc_unlock(rp, ERTS_PROC_LOCK_LINK);
+ Eterm proc = port->async_open_port->to;
+ ErtsLink *lnk = erts_link_tree_lookup(ERTS_P_LINKS(port),
+ proc);
+ if (lnk) {
+ erts_link_tree_delete(&ERTS_P_LINKS(port), lnk);
+ erts_proc_sig_send_unlink(NULL, lnk);
+ }
}
port_sched_op_reply(port->async_open_port->to,
port->async_open_port->ref,
@@ -2925,7 +2806,7 @@ erl_drv_init_ack(ErlDrvPort ix, ErlDrvData res) {
break;
case -2: {
char *str = erl_errno_id(errno);
- resp = erts_atom_put((byte *) str, strlen(str),
+ resp = erts_atom_put((byte *) str, sys_strlen(str),
ERTS_ATOM_ENC_LATIN1, 1);
break;
}
@@ -2960,7 +2841,7 @@ void erts_init_io(int port_tab_size,
int port_tab_size_ignore_files,
int legacy_port_tab)
{
- ErlDrvEntry** dp;
+ ErtsStaticDriver* dp;
UWord common_element_size;
erts_rwmtx_opt_t drv_list_rwmtx_opts = ERTS_RWMTX_OPT_DEFAULT_INITER;
drv_list_rwmtx_opts.type = ERTS_RWMTX_TYPE_EXTREMELY_FREQUENT_READ;
@@ -3019,8 +2900,8 @@ void erts_init_io(int port_tab_size,
init_driver(&forker_driver, &forker_driver_entry, NULL);
#endif
erts_init_static_drivers();
- for (dp = driver_tab; *dp != NULL; dp++)
- erts_add_driver_entry(*dp, NULL, 1);
+ for (dp = driver_tab; dp->de != NULL; dp++)
+ erts_add_driver_entry(dp->de, NULL, 1, dp->taint);
erts_tsd_set(driver_list_lock_status_key, NULL);
erts_rwmtx_rwunlock(&erts_driver_list_lock);
@@ -3031,11 +2912,8 @@ static void lcnt_enable_driver_lock_count(erts_driver_t *dp, int enable)
{
if (dp->lock) {
if (enable) {
- Eterm name_as_atom = erts_atom_put((byte*)dp->name, sys_strlen(dp->name),
- ERTS_ATOM_ENC_LATIN1, 1);
-
- erts_lcnt_install_new_lock_info(&dp->lock->lcnt, "driver_lock", name_as_atom,
- ERTS_LOCK_TYPE_MUTEX | ERTS_LOCK_FLAGS_CATEGORY_IO);
+ erts_lcnt_install_new_lock_info(&dp->lock->lcnt, "driver_lock",
+ dp->name_atom, ERTS_LOCK_TYPE_MUTEX | ERTS_LOCK_FLAGS_CATEGORY_IO);
} else {
erts_lcnt_uninstall(&dp->lock->lcnt);
}
@@ -3384,24 +3262,11 @@ static void deliver_read_message(Port* prt, erts_aint32_t state, Eterm to,
if ((state & ERTS_PORT_SFLG_BINARY_IO) == 0) {
listp = buf_to_intlist(&hp, buf, len, listp);
} else if (buf != NULL) {
- ProcBin* pb;
- Binary* bptr;
-
- bptr = erts_bin_nrml_alloc(len);
+ Binary* bptr = erts_bin_nrml_alloc(len);
sys_memcpy(bptr->orig_bytes, buf, len);
- pb = (ProcBin *) hp;
- pb->thing_word = HEADER_PROC_BIN;
- pb->size = len;
- pb->next = ohp->first;
- ohp->first = (struct erl_off_heap_header*)pb;
- pb->val = bptr;
- pb->bytes = (byte*) bptr->orig_bytes;
- pb->flags = 0;
+ listp = erts_build_proc_bin(ohp, hp, bptr);
hp += PROC_BIN_SIZE;
-
- OH_OVERHEAD(ohp, pb->size / sizeof(Eterm));
- listp = make_binary(pb);
}
/* Prepend the header */
@@ -3689,6 +3554,7 @@ terminate_port(Port *prt)
ASSERT(!ERTS_P_LINKS(prt));
ASSERT(!ERTS_P_MONITORS(prt));
+ ASSERT(!ERTS_P_LT_MONITORS(prt));
/* state may be altered by kill_port() below */
state = erts_atomic32_read_band_nob(&prt->state,
@@ -3776,145 +3642,25 @@ erts_terminate_port(Port *pp)
terminate_port(pp);
}
-static void port_fire_one_monitor(ErtsMonitor *mon, void *ctx0);
-static void sweep_one_monitor(ErtsMonitor *mon, void *vpsc)
-{
- switch (mon->type) {
- case MON_ORIGIN: {
- ErtsMonitor *rmon;
- Process *rp;
-
- ASSERT(is_internal_pid(mon->u.pid));
- rp = erts_pid2proc(NULL, 0, mon->u.pid, ERTS_PROC_LOCK_LINK);
- if (!rp) {
- goto done;
- }
- rmon = erts_remove_monitor(&ERTS_P_MONITORS(rp), mon->ref);
- erts_proc_unlock(rp, ERTS_PROC_LOCK_LINK);
- if (rmon == NULL) {
- goto done;
- }
- erts_destroy_monitor(rmon);
- } break;
- case MON_TARGET: {
- port_fire_one_monitor(mon, vpsc); /* forward call */
- } break;
- }
- done:
- erts_destroy_monitor(mon);
-}
-
-
-
typedef struct {
- Port *port;
+ Eterm port_id;
Eterm reason;
-} SweepContext;
+} ErtsPortExitContext;
-static void sweep_one_link(ErtsLink *lnk, void *vpsc)
+static void link_port_exit(ErtsLink *lnk, void *vpectxt)
{
- SweepContext *psc = vpsc;
- DistEntry *dep;
- Process *rp;
- Eterm port_id = psc->port->common.id;
-
- ASSERT(lnk->type == LINK_PID);
-
- if (IS_TRACED_FL(psc->port, F_TRACE_PORTS))
- trace_port(psc->port, am_unlink, lnk->pid);
-
- if (is_external_pid(lnk->pid)) {
- dep = external_pid_dist_entry(lnk->pid);
- if(dep != erts_this_dist_entry) {
- ErtsDistLinkData dld;
- ErtsDSigData dsd;
- int code;
- code = erts_dsig_prepare(&dsd, dep, NULL, ERTS_DSP_NO_LOCK, 0);
- switch (code) {
- case ERTS_DSIG_PREP_NOT_ALIVE:
- case ERTS_DSIG_PREP_NOT_CONNECTED:
- break;
- case ERTS_DSIG_PREP_CONNECTED:
- erts_remove_dist_link(&dld, port_id, lnk->pid, dep);
- erts_destroy_dist_link(&dld);
- code = erts_dsig_send_exit(&dsd, port_id, lnk->pid,
- psc->reason);
- ASSERT(code == ERTS_DSIG_SEND_OK);
- break;
- default:
- ASSERT(! "Invalid dsig prepare result");
- break;
- }
- }
- } else {
- ErtsProcLocks rp_locks = ERTS_PROC_LOCK_LINK|ERTS_PROC_LOCKS_XSIG_SEND;
- ASSERT(is_internal_pid(lnk->pid));
- rp = erts_pid2proc(NULL, 0, lnk->pid, rp_locks);
- if (rp) {
- ErtsLink *rlnk = erts_remove_link(&ERTS_P_LINKS(rp), port_id);
-
- if (rlnk) {
- int xres = erts_send_exit_signal(NULL,
- port_id,
- rp,
- &rp_locks,
- psc->reason,
- NIL,
- NULL,
- 0);
- if (xres >= 0) {
- if (rp_locks & ERTS_PROC_LOCKS_XSIG_SEND) {
- erts_proc_unlock(rp, ERTS_PROC_LOCKS_XSIG_SEND);
- rp_locks &= ~ERTS_PROC_LOCKS_XSIG_SEND;
- }
- /* We didn't exit the process and it is traced */
- if (IS_TRACED_FL(rp, F_TRACE_PROCS))
- trace_proc(NULL, 0, rp, am_getting_unlinked, port_id);
- }
- erts_destroy_link(rlnk);
- }
-
- erts_proc_unlock(rp, rp_locks);
- }
- }
- erts_destroy_link(lnk);
+ ErtsPortExitContext *pectxt = vpectxt;
+ erts_proc_sig_send_link_exit(NULL, pectxt->port_id,
+ lnk, pectxt->reason, NIL);
}
-static void
-port_fire_one_monitor(ErtsMonitor *mon, void *ctx0)
+static void monitor_port_exit(ErtsMonitor *mon, void *vpectxt)
{
- Process *origin;
- ErtsProcLocks origin_locks;
-
- if (mon->type != MON_TARGET || ! is_pid(mon->u.pid)) {
- return;
- }
- /*
- * Proceed here if someone monitors us, we (port) are the target and
- * origin is some process
- */
- origin_locks = ERTS_PROC_LOCKS_MSG_SEND | ERTS_PROC_LOCK_LINK;
-
- origin = erts_pid2proc(NULL, 0, mon->u.pid, origin_locks);
- if (origin) {
- DeclareTmpHeapNoproc(lhp,3);
- SweepContext *ctx = (SweepContext *)ctx0;
- ErtsMonitor *rmon;
- Eterm watched = (is_atom(mon->name)
- ? TUPLE2(lhp, mon->name, erts_this_dist_entry->sysname)
- : ctx->port->common.id);
-
- erts_queue_monitor_message(origin, &origin_locks, mon->ref, am_port,
- watched, ctx->reason);
- UnUseTmpHeapNoproc(3);
-
- rmon = erts_remove_monitor(&ERTS_P_MONITORS(origin), mon->ref);
- erts_proc_unlock(origin, origin_locks);
-
- if (rmon) {
- erts_destroy_monitor(rmon);
- }
- }
+ ErtsPortExitContext *pectxt = vpectxt;
+ if (erts_monitor_is_target(mon))
+ erts_proc_sig_send_monitor_down(mon, pectxt->reason);
+ else
+ erts_proc_sig_send_demonitor(mon);
}
/* 'from' is sending 'this_port' an exit signal, (this_port must be internal).
@@ -3931,9 +3677,12 @@ int
erts_deliver_port_exit(Port *prt, Eterm from, Eterm reason, int send_closed,
int drop_normal)
{
- ErtsLink *lnk;
+ ErtsLink *links;
+ ErtsMonitor *monitors;
+ ErtsMonitor *lt_monitors;
Eterm modified_reason;
erts_aint32_t state, set_state_flags;
+ ErtsPortExitContext pectxt;
ERTS_CHK_NO_PROC_LOCKS;
ERTS_LC_ASSERT(erts_lc_is_port_locked(prt));
@@ -3981,23 +3730,36 @@ erts_deliver_port_exit(Port *prt, Eterm from, Eterm reason, int send_closed,
set_busy_port(ERTS_Port2ErlDrvPort(prt), 0);
+ links = ERTS_P_LINKS(prt);
+ ERTS_P_LINKS(prt) = NULL;
+ monitors = ERTS_P_MONITORS(prt);
+ ERTS_P_MONITORS(prt) = NULL;
+ lt_monitors = ERTS_P_LT_MONITORS(prt);
+ ERTS_P_LT_MONITORS(prt) = NULL;
+
if (prt->common.u.alive.reg != NULL)
(void) erts_unregister_name(NULL, 0, prt, prt->common.u.alive.reg->name);
- {
- SweepContext sc = {prt, modified_reason};
- lnk = ERTS_P_LINKS(prt);
- ERTS_P_LINKS(prt) = NULL;
- erts_sweep_links(lnk, &sweep_one_link, &sc);
+ pectxt.port_id = prt->common.id;
+ pectxt.reason = modified_reason;
+
+ if (links)
+ erts_monitor_tree_foreach_delete(&links,
+ link_port_exit,
+ (void *) &pectxt);
+
+ if (monitors || lt_monitors) {
+ DRV_MONITOR_LOCK_PDL(prt);
+ if (monitors)
+ erts_monitor_tree_foreach_delete(&monitors,
+ monitor_port_exit,
+ (void *) &pectxt);
+ if (lt_monitors)
+ erts_monitor_list_foreach_delete(&lt_monitors,
+ monitor_port_exit,
+ (void *) &pectxt);
+ DRV_MONITOR_UNLOCK_PDL(prt);
}
- DRV_MONITOR_LOCK_PDL(prt);
- {
- SweepContext ctx = {prt, modified_reason};
- ErtsMonitor *moni = ERTS_P_MONITORS(prt);
- ERTS_P_MONITORS(prt) = NULL;
- erts_sweep_monitors(moni, &sweep_one_monitor, &ctx);
- }
- DRV_MONITOR_UNLOCK_PDL(prt);
if ((state & ERTS_PORT_SFLG_DISTRIBUTION) && prt->dist_entry) {
erts_do_net_exits(prt->dist_entry, modified_reason);
@@ -4203,17 +3965,9 @@ write_port_control_result(int control_flags,
else {
dbin = (ErlDrvBinary *) resp_bufp;
if (dbin->orig_size > ERL_ONHEAP_BIN_LIMIT) {
- ProcBin* pb = (ProcBin *) *hpp;
+ res = erts_build_proc_bin(ohp, *hpp, ErlDrvBinary2Binary(dbin));
*hpp += PROC_BIN_SIZE;
- pb->thing_word = HEADER_PROC_BIN;
- pb->size = dbin->orig_size;
- pb->next = ohp->first;
- ohp->first = (struct erl_off_heap_header *) pb;
- pb->val = ErlDrvBinary2Binary(dbin);
- pb->bytes = (byte*) dbin->orig_bytes;
- pb->flags = 0;
- OH_OVERHEAD(ohp, dbin->orig_size / sizeof(Eterm));
- return make_binary(pb);
+ return res;
}
resp_bufp = dbin->orig_bytes;
resp_size = dbin->orig_size;
@@ -5085,14 +4839,105 @@ typedef struct {
static void prt_one_monitor(ErtsMonitor *mon, void *vprtd)
{
+ ErtsMonitorData *mdp = erts_monitor_to_data(mon);
prt_one_lnk_data *prtd = (prt_one_lnk_data *) vprtd;
- erts_print(prtd->to, prtd->arg, "(%T,%T)", mon->u.pid, mon->ref);
+ if (mon->type == ERTS_MON_TYPE_RESOURCE && erts_monitor_is_target(mon))
+ erts_print(prtd->to, prtd->arg, "(%p,%T)", mon->other.ptr, mdp->ref);
+ else
+ erts_print(prtd->to, prtd->arg, "(%T,%T)", mon->other.item, mdp->ref);
}
static void prt_one_lnk(ErtsLink *lnk, void *vprtd)
{
prt_one_lnk_data *prtd = (prt_one_lnk_data *) vprtd;
- erts_print(prtd->to, prtd->arg, "%T", lnk->pid);
+ erts_print(prtd->to, prtd->arg, "%T", lnk->other.item);
+}
+
+static void dump_port_state(fmtfn_t to, void *arg, erts_aint32_t state)
+{
+ erts_aint32_t rest;
+ int unknown = 0;
+ char delim = ' ';
+
+ erts_print(to, arg, "State:");
+
+ rest = state;
+ while (rest) {
+ erts_aint32_t chk = (rest ^ (rest-1)) & rest; /* lowest set bit */
+ char* s;
+
+ rest &= ~chk;
+ switch (chk) {
+ case ERTS_PORT_SFLG_CONNECTED: s = "CONNECTED"; break;
+ case ERTS_PORT_SFLG_EXITING: s = "EXITING"; break;
+ case ERTS_PORT_SFLG_DISTRIBUTION: s = "DISTR"; break;
+ case ERTS_PORT_SFLG_BINARY_IO: s = "BINARY_IO"; break;
+ case ERTS_PORT_SFLG_SOFT_EOF: s = "SOFT_EOF"; break;
+ case ERTS_PORT_SFLG_CLOSING: s = "CLOSING"; break;
+ case ERTS_PORT_SFLG_SEND_CLOSED: s = "SEND_CLOSED"; break;
+ case ERTS_PORT_SFLG_LINEBUF_IO: s = "LINEBUF_IO"; break;
+ case ERTS_PORT_SFLG_FREE: s = "FREE"; break;
+ case ERTS_PORT_SFLG_INITIALIZING: s = "INITIALIZING"; break;
+ case ERTS_PORT_SFLG_PORT_SPECIFIC_LOCK: s = "PORT_LOCK"; break;
+ case ERTS_PORT_SFLG_INVALID: s = "INVALID"; break;
+ case ERTS_PORT_SFLG_HALT: s = "HALT"; break;
+#ifdef DEBUG
+ case ERTS_PORT_SFLG_PORT_DEBUG: s = "DEBUG"; break;
+#endif
+ default:
+ unknown = 1;
+ continue;
+ }
+ erts_print(to, arg, "%c%s", delim, s);
+ delim = '|';
+ }
+ if (unknown || !state)
+ erts_print(to, arg, "%c0x%x\n", delim, state);
+ else
+ erts_print(to, arg, "\n");
+}
+
+static void dump_port_task_flags(fmtfn_t to, void *arg, Port* p)
+{
+ erts_aint32_t flags = erts_atomic32_read_nob(&p->sched.flags);
+ erts_aint32_t unknown = 0;
+ char delim = ' ';
+
+ if (!flags)
+ return;
+
+ erts_print(to, arg, "Task Flags:");
+
+ while (flags) {
+ erts_aint32_t chk = (flags ^ (flags-1)) & flags; /* lowest set bit */
+ char* s;
+
+ flags &= ~chk;
+ switch (chk) {
+ case ERTS_PTS_FLG_IN_RUNQ: s = "IN_RUNQ"; break;
+ case ERTS_PTS_FLG_EXEC: s = "EXEC"; break;
+ case ERTS_PTS_FLG_HAVE_TASKS: s = "HAVE_TASKS"; break;
+ case ERTS_PTS_FLG_EXIT: s = "EXIT"; break;
+ case ERTS_PTS_FLG_BUSY_PORT: s = "BUSY_PORT"; break;
+ case ERTS_PTS_FLG_BUSY_PORT_Q: s = "BUSY_Q"; break;
+ case ERTS_PTS_FLG_CHK_UNSET_BUSY_PORT_Q: s = "CHK_UNSET_BUSY_Q"; break;
+ case ERTS_PTS_FLG_HAVE_BUSY_TASKS: s = "BUSY_TASKS"; break;
+ case ERTS_PTS_FLG_HAVE_NS_TASKS: s = "NS_TASKS"; break;
+ case ERTS_PTS_FLG_PARALLELISM: s = "PARALLELISM"; break;
+ case ERTS_PTS_FLG_FORCE_SCHED: s = "FORCE_SCHED"; break;
+ case ERTS_PTS_FLG_EXITING: s = "EXITING"; break;
+ case ERTS_PTS_FLG_EXEC_IMM: s = "EXEC_IMM"; break;
+ default:
+ unknown |= chk;
+ continue;
+ }
+ erts_print(to, arg, "%c%s", delim, s);
+ delim = '|';
+ }
+ if (unknown)
+ erts_print(to, arg, "%cUNKNOWN(0x%x)\n", delim, unknown);
+ else
+ erts_print(to, arg, "\n");
}
void
@@ -5104,6 +4949,8 @@ print_port_info(Port *p, fmtfn_t to, void *arg)
return;
erts_print(to, arg, "=port:%T\n", p->common.id);
+ dump_port_state(to, arg, state);
+ dump_port_task_flags(to, arg, p);
erts_print(to, arg, "Slot: %d\n", internal_port_index(p->common.id));
if (state & ERTS_PORT_SFLG_CONNECTED) {
erts_print(to, arg, "Connected: %T", ERTS_PORT_GET_CONNECTED(p));
@@ -5115,17 +4962,24 @@ print_port_info(Port *p, fmtfn_t to, void *arg)
prtd.to = to;
prtd.arg = arg;
erts_print(to, arg, "Links: ");
- erts_doforall_links(ERTS_P_LINKS(p), &prt_one_lnk, &prtd);
+ erts_link_tree_foreach(ERTS_P_LINKS(p), prt_one_lnk, (void *) &prtd);
erts_print(to, arg, "\n");
}
- if (ERTS_P_MONITORS(p)) {
+ if (ERTS_P_MONITORS(p) || ERTS_P_LT_MONITORS(p)) {
prt_one_lnk_data prtd;
prtd.to = to;
prtd.arg = arg;
erts_print(to, arg, "Monitors: ");
- erts_doforall_monitors(ERTS_P_MONITORS(p), &prt_one_monitor, &prtd);
+ erts_monitor_tree_foreach(ERTS_P_MONITORS(p), prt_one_monitor,
+ (void *) &prtd);
+ erts_monitor_list_foreach(ERTS_P_LT_MONITORS(p), prt_one_monitor,
+ (void *) &prtd);
erts_print(to, arg, "\n");
}
+ if (p->suspended) {
+ erts_print(to, arg, "Suspended: ");
+ erts_proclist_dump(to, arg, p->suspended);
+ }
if (p->common.u.alive.reg != NULL)
erts_print(to, arg, "Registered as: %T\n", p->common.u.alive.reg->name);
@@ -5143,6 +4997,14 @@ print_port_info(Port *p, fmtfn_t to, void *arg)
} else {
erts_print(to, arg, "Port controls linked-in driver: %s\n",p->name);
}
+ erts_print(to, arg, "Input: %beu\n", p->bytes_in);
+ erts_print(to, arg, "Output: %beu\n", p->bytes_out);
+ erts_print(to, arg, "Queue: %beu\n", erts_ioq_size(&p->ioq));
+ {
+ Eterm port_data = erts_port_data_read(p);
+ if (port_data != am_undefined)
+ erts_print(to, arg, "Port Data: %T\n", port_data);
+ }
}
void
@@ -5977,21 +5839,12 @@ driver_deliver_term(Port *prt, Eterm to, ErlDrvTermData* data, int len)
mess = make_binary(hbp);
}
else {
- ProcBin* pbp;
+ Eterm* hp;
Binary* bp = erts_bin_nrml_alloc(size);
ASSERT(bufp);
sys_memcpy((void *) bp->orig_bytes, (void *) bufp, size);
- pbp = (ProcBin *) erts_produce_heap(&factory,
- PROC_BIN_SIZE, HEAP_EXTRA);
- pbp->thing_word = HEADER_PROC_BIN;
- pbp->size = size;
- pbp->next = factory.off_heap->first;
- factory.off_heap->first = (struct erl_off_heap_header*)pbp;
- pbp->val = bp;
- pbp->bytes = (byte*) bp->orig_bytes;
- pbp->flags = 0;
- OH_OVERHEAD(factory.off_heap, pbp->size / sizeof(Eterm));
- mess = make_binary(pbp);
+ hp = erts_produce_heap(&factory, PROC_BIN_SIZE, HEAP_EXTRA);
+ mess = erts_build_proc_bin(factory.off_heap, hp, bp);
}
ptr += 2;
break;
@@ -6957,24 +6810,23 @@ static int do_driver_monitor_process(Port *prt,
ErlDrvMonitor *monitor)
{
Eterm buf[ERTS_REF_THING_SIZE];
- Process *rp;
Eterm ref;
+ ErtsMonitorData *mdp;
- if (prt->drv_ptr->process_exit == NULL) {
+ if (!prt->drv_ptr->process_exit)
return -1;
- }
- rp = erts_pid2proc_opt(NULL, 0,
- (Eterm) process, ERTS_PROC_LOCK_LINK,
- ERTS_P2P_FLG_ALLOW_OTHER_X);
- if (!rp) {
- return 1;
- }
ref = erts_make_ref_in_buffer(buf);
- erts_add_monitor(&ERTS_P_MONITORS(prt), MON_ORIGIN, ref, rp->common.id, NIL);
- erts_add_monitor(&ERTS_P_MONITORS(rp), MON_TARGET, ref, prt->common.id, NIL);
+ mdp = erts_monitor_create(ERTS_MON_TYPE_PORT, ref,
+ prt->common.id, process, NIL);
+
+ if (!erts_proc_sig_send_monitor(&mdp->target, process)) {
+ erts_monitor_release_both(mdp);
+ return 1;
+ }
+
+ erts_monitor_tree_insert(&ERTS_P_MONITORS(prt), &mdp->origin);
- erts_proc_unlock(rp, ERTS_PROC_LOCK_LINK);
erts_ref_to_driver_monitor(ref,monitor);
return 0;
}
@@ -7007,37 +6859,17 @@ int driver_monitor_process(ErlDrvPort drvport,
static int do_driver_demonitor_process(Port *prt, const ErlDrvMonitor *monitor)
{
Eterm heap[ERTS_REF_THING_SIZE];
- Process *rp;
Eterm ref;
ErtsMonitor *mon;
- Eterm to;
ref = erts_driver_monitor_to_ref(heap, monitor);
- mon = erts_lookup_monitor(ERTS_P_MONITORS(prt), ref);
- if (mon == NULL) {
- return 1;
- }
- ASSERT(mon->type == MON_ORIGIN);
- to = mon->u.pid;
- ASSERT(is_internal_pid(to));
- rp = erts_pid2proc_opt(NULL,
- 0,
- to,
- ERTS_PROC_LOCK_LINK,
- ERTS_P2P_FLG_ALLOW_OTHER_X);
- mon = erts_remove_monitor(&ERTS_P_MONITORS(prt), ref);
- if (mon) {
- erts_destroy_monitor(mon);
- }
- if (rp) {
- ErtsMonitor *rmon;
- rmon = erts_remove_monitor(&ERTS_P_MONITORS(rp), ref);
- erts_proc_unlock(rp, ERTS_PROC_LOCK_LINK);
- if (rmon != NULL) {
- erts_destroy_monitor(rmon);
- }
- }
+ mon = erts_monitor_tree_lookup(ERTS_P_MONITORS(prt), ref);
+ if (!mon || !erts_monitor_is_origin(mon))
+ return 1;
+
+ erts_monitor_tree_delete(&ERTS_P_MONITORS(prt), mon);
+ erts_proc_sig_send_demonitor(mon);
return 0;
}
@@ -7066,19 +6898,16 @@ static ErlDrvTermData do_driver_get_monitored_process(Port *prt,const ErlDrvMoni
{
Eterm ref;
ErtsMonitor *mon;
- Eterm to;
Eterm heap[ERTS_REF_THING_SIZE];
ref = erts_driver_monitor_to_ref(heap, monitor);
- mon = erts_lookup_monitor(ERTS_P_MONITORS(prt), ref);
- if (mon == NULL) {
+ mon = erts_monitor_tree_lookup(ERTS_P_MONITORS(prt), ref);
+ if (!mon || !erts_monitor_is_origin(mon))
return driver_term_nil;
- }
- ASSERT(mon->type == MON_ORIGIN);
- to = mon->u.pid;
- ASSERT(is_internal_pid(to));
- return (ErlDrvTermData) to;
+
+ ASSERT(is_internal_pid(mon->other.item));
+ return (ErlDrvTermData) mon->other.item;
}
@@ -7110,24 +6939,27 @@ int driver_compare_monitors(const ErlDrvMonitor *monitor1,
ERTS_REF_THING_SIZE*sizeof(Eterm));
}
-void erts_fire_port_monitor(Port *prt, Eterm ref)
+void erts_fire_port_monitor(Port *prt, ErtsMonitor *tmon)
{
- ErtsMonitor *rmon;
+ ErtsMonitorData *mdp;
void (*callback)(ErlDrvData drv_data, ErlDrvMonitor *monitor);
ErlDrvMonitor drv_monitor;
int fpe_was_unmasked;
ERTS_MSACC_PUSH_STATE_M();
ERTS_LC_ASSERT(erts_lc_is_port_locked(prt));
- ASSERT(prt->drv_ptr != NULL);
+ ASSERT(prt->drv_ptr != NULL);
+ ASSERT(erts_monitor_is_target(tmon));
+ mdp = erts_monitor_to_data(tmon);
DRV_MONITOR_LOCK_PDL(prt);
- if (erts_lookup_monitor(ERTS_P_MONITORS(prt), ref) == NULL) {
+ if (!erts_monitor_is_in_table(&mdp->origin)) {
DRV_MONITOR_UNLOCK_PDL(prt);
+ erts_monitor_release(tmon);
return;
}
callback = prt->drv_ptr->process_exit;
ASSERT(callback != NULL);
- erts_ref_to_driver_monitor(ref,&drv_monitor);
+ erts_ref_to_driver_monitor(mdp->ref,&drv_monitor);
ERTS_MSACC_SET_STATE_CACHED_M(ERTS_MSACC_STATE_PORT);
DRV_MONITOR_UNLOCK_PDL(prt);
#ifdef USE_VM_PROBES
@@ -7151,11 +6983,9 @@ void erts_fire_port_monitor(Port *prt, Eterm ref)
DRV_MONITOR_LOCK_PDL(prt);
ERTS_MSACC_POP_STATE_M();
/* remove monitor *after* callback */
- rmon = erts_remove_monitor(&ERTS_P_MONITORS(prt), ref);
+ erts_monitor_tree_delete(&ERTS_P_MONITORS(prt), &mdp->origin);
DRV_MONITOR_UNLOCK_PDL(prt);
- if (rmon) {
- erts_destroy_monitor(rmon);
- }
+ erts_monitor_release_both(mdp);
}
@@ -7200,8 +7030,7 @@ driver_failure_term(ErlDrvPort ix, Eterm term, int eof)
int driver_exit(ErlDrvPort ix, int err)
{
Port* prt = erts_drvport2port(ix);
- Process* rp;
- ErtsLink *lnk, *rlnk = NULL;
+ ErtsLink *lnk;
Eterm connected;
ERTS_CHK_NO_PROC_LOCKS;
@@ -7210,22 +7039,10 @@ int driver_exit(ErlDrvPort ix, int err)
return -1;
connected = ERTS_PORT_GET_CONNECTED(prt);
- rp = erts_pid2proc(NULL, 0, connected, ERTS_PROC_LOCK_LINK);
- if (rp) {
- rlnk = erts_remove_link(&ERTS_P_LINKS(rp),prt->common.id);
- }
-
- lnk = erts_remove_link(&ERTS_P_LINKS(prt), connected);
-
- if (rp)
- erts_proc_unlock(rp, ERTS_PROC_LOCK_LINK);
-
- if (rlnk != NULL) {
- erts_destroy_link(rlnk);
- }
-
- if (lnk != NULL) {
- erts_destroy_link(lnk);
+ lnk = erts_link_tree_lookup(ERTS_P_LINKS(prt), connected);
+ if (lnk) {
+ erts_link_tree_delete(&ERTS_P_LINKS(prt), lnk);
+ erts_proc_sig_send_unlink(NULL, lnk);
}
if (err == 0)
@@ -7248,7 +7065,7 @@ int driver_failure_atom(ErlDrvPort ix, char* string)
{
return driver_failure_term(ix,
erts_atom_put((byte *) string,
- strlen(string),
+ sys_strlen(string),
ERTS_ATOM_ENC_LATIN1,
1),
0);
@@ -7537,12 +7354,17 @@ no_stop_select_callback(ErlDrvEvent event, void* private)
}
#define IS_DRIVER_VERSION_GE(DE,MAJOR,MINOR) \
- ((DE)->major_version >= (MAJOR) && (DE)->minor_version >= (MINOR))
+ ((DE)->major_version > (MAJOR) || \
+ ((DE)->major_version == (MAJOR) && (DE)->minor_version >= (MINOR)))
static int
init_driver(erts_driver_t *drv, ErlDrvEntry *de, DE_Handle *handle)
{
+ drv->name_atom = erts_atom_put((byte*)de->driver_name,
+ sys_strlen(de->driver_name),
+ ERTS_ATOM_ENC_LATIN1, 1);
drv->name = de->driver_name;
+
ASSERT(de->extended_marker == ERL_DRV_EXTENDED_MARKER);
ASSERT(de->major_version >= 2);
drv->version.major = de->major_version;
@@ -7552,13 +7374,10 @@ init_driver(erts_driver_t *drv, ErlDrvEntry *de, DE_Handle *handle)
if (drv->flags & ERL_DRV_FLAG_USE_PORT_LOCKING) {
drv->lock = NULL;
} else {
- Eterm driver_id = erts_atom_put((byte *) drv->name,
- sys_strlen(drv->name),
- ERTS_ATOM_ENC_LATIN1, 1);
-
drv->lock = erts_alloc(ERTS_ALC_T_DRIVER_LOCK, sizeof(erts_mtx_t));
- erts_mtx_init(drv->lock, "driver_lock", driver_id, ERTS_LOCK_FLAGS_CATEGORY_IO);
+ erts_mtx_init(drv->lock, "driver_lock", drv->name_atom,
+ ERTS_LOCK_FLAGS_CATEGORY_IO);
}
drv->entry = de;
@@ -7620,13 +7439,14 @@ void add_driver_entry(ErlDrvEntry *drv){
* Ignore result of erts_add_driver_entry, the init is not
* allowed to fail when drivers are added by drivers.
*/
- erts_add_driver_entry(drv, NULL, rec_lock != NULL);
+ erts_add_driver_entry(drv, NULL, rec_lock != NULL, 0);
}
-int erts_add_driver_entry(ErlDrvEntry *de, DE_Handle *handle, int driver_list_locked)
+int erts_add_driver_entry(ErlDrvEntry *de, DE_Handle *handle,
+ int driver_list_locked, int taint)
{
erts_driver_t *dp = erts_alloc(ERTS_ALC_T_DRIVER, sizeof(erts_driver_t));
- int res;
+ int err = 0;
if (!driver_list_locked) {
erts_rwmtx_rwlock(&erts_driver_list_lock);
@@ -7643,9 +7463,15 @@ int erts_add_driver_entry(ErlDrvEntry *de, DE_Handle *handle, int driver_list_lo
erts_tsd_set(driver_list_lock_status_key, (void *) 1);
}
- res = init_driver(dp, de, handle);
+ if (!err) {
+ err = init_driver(dp, de, handle);
+
+ if (taint) {
+ erts_add_taint(dp->name_atom);
+ }
+ }
- if (res != 0) {
+ if (err) {
/*
* Remove it all again...
*/
@@ -7660,7 +7486,7 @@ int erts_add_driver_entry(ErlDrvEntry *de, DE_Handle *handle, int driver_list_lo
erts_tsd_set(driver_list_lock_status_key, NULL);
erts_rwmtx_rwunlock(&erts_driver_list_lock);
}
- return res;
+ return err;
}
/* Not allowed for dynamic drivers */
@@ -7714,13 +7540,27 @@ int null_func(void)
int
erl_drv_putenv(const char *key, char *value)
{
- return erts_sys_putenv_raw((char*)key, value);
+ switch (erts_sys_explicit_8bit_putenv((char*)key, value)) {
+ case -1: /* Insufficient buffer space */
+ return 1;
+ case 1: /* Success */
+ return 0;
+ default: /* Not found */
+ return -1;
+ }
}
int
erl_drv_getenv(const char *key, char *value, size_t *value_size)
{
- return erts_sys_getenv_raw((char*)key, value, value_size);
+ switch (erts_sys_explicit_8bit_getenv((char*)key, value, value_size)) {
+ case -1: /* Insufficient buffer space */
+ return 1;
+ case 1: /* Success */
+ return 0;
+ default: /* Not found */
+ return -1;
+ }
}
/* get heart_port
diff --git a/erts/emulator/beam/lttng-wrapper.h b/erts/emulator/beam/lttng-wrapper.h
index 0bc75c1552..e7f2971bf7 100644
--- a/erts/emulator/beam/lttng-wrapper.h
+++ b/erts/emulator/beam/lttng-wrapper.h
@@ -61,13 +61,13 @@
#define lttng_proc_to_mfa_str(p, Name) \
do { \
if (ERTS_PROC_IS_EXITING((p))) { \
- strcpy(Name, "<exiting>"); \
+ sys_strcpy(Name, "<exiting>"); \
} else { \
BeamInstr *_fptr = find_function_from_pc((p)->i); \
if (_fptr) { \
lttng_mfa_to_str(_fptr[0],_fptr[1],_fptr[2], Name); \
} else { \
- strcpy(Name, "<unknown>"); \
+ sys_strcpy(Name, "<unknown>"); \
} \
} \
} while(0)
diff --git a/erts/emulator/beam/macros.tab b/erts/emulator/beam/macros.tab
index 6f9b78af6f..494fe8961e 100644
--- a/erts/emulator/beam/macros.tab
+++ b/erts/emulator/beam/macros.tab
@@ -20,17 +20,25 @@
//
//
-// Use if there is a garbage collection before storing to a
-// general destination (either X or Y register).
+// Define a regular expression that will match instructions that
+// perform GC. That will allow beam_makeops to check for instructions
+// that don't use $REFRESH_GEN_DEST() when they should.
//
-REFRESH_GEN_DEST() {
- dst_ptr = REG_TARGET_PTR(dst);
-}
+GC_REGEXP=erts_garbage_collect|erts_gc|GcBifFunction;
+
+// $Offset is relative to the start of the instruction (not to the
+// location of the failure label reference). Since combined
+// instructions may increment the instruction pointer (e.g. in
+// 'increment') for some of the instructions in the group, we actually
+// use a virtual start position common to all instructions in the
+// group. To calculate the correct virtual position, we will need to
+// add $IP_ADJUSTMENT to the offset. ($IP_ADJUSTMENT will usually be
+// zero, except in a few bit syntax instructions.)
SET_I_REL(Offset) {
- ASSERT(VALID_INSTR(*(I + ($Offset))));
- I += $Offset;
+ ASSERT(VALID_INSTR(*(I + ($Offset) + $IP_ADJUSTMENT)));
+ I += $Offset + $IP_ADJUSTMENT;
}
SET_CP_I_ABS(Target) {
diff --git a/erts/emulator/beam/map_instrs.tab b/erts/emulator/beam/map_instrs.tab
index bbb2f49b66..c594a87298 100644
--- a/erts/emulator/beam/map_instrs.tab
+++ b/erts/emulator/beam/map_instrs.tab
@@ -31,7 +31,7 @@ new_map(Dst, Live, N) {
Eterm res;
HEAVY_SWAPOUT;
- res = new_map(c_p, reg, $Live, $N, $NEXT_INSTRUCTION);
+ res = erts_gc_new_map(c_p, reg, $Live, $N, $NEXT_INSTRUCTION);
HEAVY_SWAPIN;
$REFRESH_GEN_DEST();
$Dst = res;
@@ -44,7 +44,7 @@ i_new_small_map_lit(Dst, Live, Keys) {
Eterm keys = $Keys;
HEAVY_SWAPOUT;
- res = new_small_map_lit(c_p, reg, keys, $Live, $NEXT_INSTRUCTION);
+ res = erts_gc_new_small_map_lit(c_p, reg, keys, $Live, $NEXT_INSTRUCTION);
HEAVY_SWAPIN;
$REFRESH_GEN_DEST();
$Dst = res;
@@ -133,7 +133,7 @@ update_map_assoc(Src, Dst, Live, N) {
reg[live] = $Src;
HEAVY_SWAPOUT;
- res = update_map_assoc(c_p, reg, live, $N, $NEXT_INSTRUCTION);
+ res = erts_gc_update_map_assoc(c_p, reg, live, $N, $NEXT_INSTRUCTION);
HEAVY_SWAPIN;
ASSERT(is_value(res));
$REFRESH_GEN_DEST();
@@ -147,7 +147,7 @@ update_map_exact(Fail, Src, Dst, Live, N) {
reg[live] = $Src;
HEAVY_SWAPOUT;
- res = update_map_exact(c_p, reg, live, $N, $NEXT_INSTRUCTION);
+ res = erts_gc_update_map_exact(c_p, reg, live, $N, $NEXT_INSTRUCTION);
HEAVY_SWAPIN;
if (is_value(res)) {
$REFRESH_GEN_DEST();
diff --git a/erts/emulator/beam/module.c b/erts/emulator/beam/module.c
index baeec115ea..1712dc803c 100644
--- a/erts/emulator/beam/module.c
+++ b/erts/emulator/beam/module.c
@@ -254,4 +254,3 @@ void module_end_staging(int commit)
IF_DEBUG(dbg_load_code_ix = -1);
}
-
diff --git a/erts/emulator/beam/module.h b/erts/emulator/beam/module.h
index 9a81e6035b..a3f1ce1705 100644
--- a/erts/emulator/beam/module.h
+++ b/erts/emulator/beam/module.h
@@ -45,7 +45,7 @@ typedef struct erl_module {
int seen; /* Used by finish_loading() */
struct erl_module_instance curr;
- struct erl_module_instance old; /* protected by "old_code" rwlock */
+ struct erl_module_instance old; /* active protected by "old_code" rwlock */
struct erl_module_instance* on_load;
} Module;
diff --git a/erts/emulator/beam/msg_instrs.tab b/erts/emulator/beam/msg_instrs.tab
index 8055a8616f..26bea0efc6 100644
--- a/erts/emulator/beam/msg_instrs.tab
+++ b/erts/emulator/beam/msg_instrs.tab
@@ -2,7 +2,7 @@
//
// %CopyrightBegin%
//
-// Copyright Ericsson AB 2017. All Rights Reserved.
+// Copyright Ericsson AB 2017-2018. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -43,29 +43,18 @@
// *
// */
-recv_mark(Dest) {
+i_recv_mark() {
/*
- * Save the current position in message buffer and the
- * the label for the loop_rec/2 instruction for the
- * the receive statement.
+ * Save the current end of message queue
*/
- $SET_REL_I(c_p->msg.mark, $Dest);
- c_p->msg.saved_last = c_p->msg.last;
+ ERTS_RECV_MARK_SAVE(c_p);
}
i_recv_set() {
/*
- * If the mark is valid (points to the loop_rec/2
- * instruction that follows), we know that the saved
- * position points to the first message that could
- * possibly be matched out.
- *
- * If the mark is invalid, we do nothing, meaning that
- * we will look through all messages in the message queue.
+ * If previously saved recv mark, set peek position to it
*/
- if (c_p->msg.mark == (BeamInstr *) ($NEXT_INSTRUCTION)) {
- c_p->msg.save = c_p->msg.saved_last;
- }
+ ERTS_RECV_MARK_SET(c_p);
SET_I($NEXT_INSTRUCTION);
goto loop_rec_top__;
//| -no_next
@@ -83,7 +72,6 @@ i_loop_rec(Dest) {
/* Entry point from recv_set */
loop_rec_top__:
- ;
/*
* We need to disable GC while matching messages
@@ -93,34 +81,59 @@ i_loop_rec(Dest) {
ASSERT(!(c_p->flags & F_DELAY_GC));
c_p->flags |= F_DELAY_GC;
- /* Entry point from loop_rec_end */
+ /* Entry point from loop_rec_end (and locally) */
loop_rec__:
+ if (FCALLS <= 0 && FCALLS <= neg_o_reds) {
+ $SET_CP_I_ABS(I);
+ c_p->flags &= ~F_DELAY_GC;
+ SWAPOUT;
+ c_p->arity = 0;
+ c_p->current = NULL;
+ goto do_schedule;
+ }
+
+ ASSERT(!ERTS_PROC_IS_EXITING(c_p));
+
PROCESS_MAIN_CHK_LOCKS(c_p);
msgp = PEEK_MESSAGE(c_p);
- if (!msgp) {
- erts_proc_lock(c_p, ERTS_PROC_LOCKS_MSG_RECEIVE);
- /* Make sure messages wont pass exit signals... */
- if (ERTS_PROC_PENDING_EXIT(c_p)) {
- erts_proc_unlock(c_p, ERTS_PROC_LOCKS_MSG_RECEIVE);
- SWAPOUT;
- c_p->flags &= ~F_DELAY_GC;
- c_p->arity = 0;
- goto do_schedule; /* Will be rescheduled for exit */
- }
- ERTS_MSGQ_MV_INQ2PRIVQ(c_p);
- msgp = PEEK_MESSAGE(c_p);
- if (msgp) {
- erts_proc_unlock(c_p, ERTS_PROC_LOCKS_MSG_RECEIVE);
- } else {
+ if (ERTS_UNLIKELY(msgp == NULL)) {
+ int get_out;
+ SWAPOUT;
+ FCALLS -= erts_proc_sig_receive_helper(c_p, FCALLS, neg_o_reds,
+ &msgp, &get_out);
+ SWAPIN;
+ if (ERTS_UNLIKELY(msgp == NULL)) {
+ if (get_out) {
+ if (get_out < 0) {
+ ASSERT(FCALLS <= 0 && FCALLS <= neg_o_reds);
+ goto loop_rec__; /* yield */
+ }
+ else {
+ ASSERT(ERTS_PROC_IS_EXITING(c_p));
+ goto do_schedule; /* exit */
+ }
+ }
+
+ /*
+ * If there are no more messages in queue
+ * (and we are not yielding or exiting)
+ * erts_proc_sig_receive_helper()
+ * returns with message queue lock locked...
+ */
c_p->flags &= ~F_DELAY_GC;
$SET_I_REL($Dest);
Goto(*I); /* Jump to a wait or wait_timeout instruction */
}
}
- if (is_non_value(ERL_MESSAGE_TERM(msgp))) {
+
+ ASSERT(msgp == PEEK_MESSAGE(c_p));
+ ASSERT(msgp && ERTS_SIG_IS_MSG(msgp));
+
+ if (ERTS_UNLIKELY(ERTS_SIG_IS_EXTERNAL_MSG(msgp))) {
+ FCALLS -= 10; /* FIXME: bump appropriate amount... */
SWAPOUT; /* erts_decode_dist_message() may write to heap... */
if (!erts_decode_dist_message(c_p, ERTS_PROC_LOCK_MAIN, msgp, 0)) {
/*
@@ -137,6 +150,10 @@ i_loop_rec(Dest) {
}
SWAPIN;
}
+
+ ASSERT(msgp == PEEK_MESSAGE(c_p));
+ ASSERT(ERTS_SIG_IS_INTERNAL_MSG(msgp));
+
r(0) = ERL_MESSAGE_TERM(msgp);
}
@@ -208,17 +225,19 @@ remove_message() {
Sint tok_label = 0;
Sint tok_lastcnt = 0;
Sint tok_serial = 0;
+ Sint len = erts_proc_sig_privqs_len(c_p);
dtrace_proc_str(c_p, receiver_name);
token2 = SEQ_TRACE_TOKEN(c_p);
if (have_seqtrace(token2)) {
- tok_label = signed_val(SEQ_TRACE_T_LABEL(token2));
+ tok_label = SEQ_TRACE_T_DTRACE_LABEL(token2);
tok_lastcnt = signed_val(SEQ_TRACE_T_LASTCNT(token2));
tok_serial = signed_val(SEQ_TRACE_T_SERIAL(token2));
}
DTRACE6(message_receive,
receiver_name, size_object(ERL_MESSAGE_TERM(msgp)),
- c_p->msg.len - 1, tok_label, tok_lastcnt, tok_serial);
+ len, /* This is NOT message queue len, but its something... */
+ tok_label, tok_lastcnt, tok_serial);
}
#endif
UNLINK_MESSAGE(c_p, msgp);
@@ -255,17 +274,8 @@ loop_rec_end(Dest) {
$SET_I_REL($Dest);
SAVE_MESSAGE(c_p);
- if (FCALLS > 0 || FCALLS > neg_o_reds) {
- FCALLS--;
- goto loop_rec__;
- }
-
- c_p->flags &= ~F_DELAY_GC;
- $SET_CP_I_ABS(I);
- SWAPOUT;
- c_p->arity = 0;
- c_p->current = NULL;
- goto do_schedule;
+ FCALLS--;
+ goto loop_rec__;
}
timeout_locked() {
diff --git a/erts/emulator/beam/ops.tab b/erts/emulator/beam/ops.tab
index 87ff92d354..8b2d9098a8 100644
--- a/erts/emulator/beam/ops.tab
+++ b/erts/emulator/beam/ops.tab
@@ -99,21 +99,21 @@ line Loc | func_info M F A => func_info M F A | line Loc
line I
-allocate t t
-allocate_heap t I t
+allocate t t?
+allocate_heap t I t?
%cold
deallocate Q
%hot
init y
-allocate_zero t t
-allocate_heap_zero t I t
+allocate_zero t t?
+allocate_heap_zero t I t?
trim N Remaining => i_trim N
i_trim t
-test_heap I t
+test_heap I t?
allocate_heap S u==0 R => allocate S R
allocate_heap_zero S u==0 R => allocate_zero S R
@@ -158,19 +158,19 @@ is_tuple Fail=f S | select_tuple_arity S=d Fail=f Size=u Rest=* => \
select_tuple_arity S=d Fail=f Size=u Rest=* => \
gen_select_tuple_arity(S, Fail, Size, Rest)
-i_select_val_bins xy f I
+i_select_val_bins xy f? I
-i_select_val_lins xy f I
+i_select_val_lins xy f? I
-i_select_val2 xy f c c
+i_select_val2 xy f? c c
-i_select_tuple_arity xy f I
+i_select_tuple_arity xy f? I
-i_select_tuple_arity2 xy f A A
+i_select_tuple_arity2 xy f? A A
-i_jump_on_val_zero xy f I
+i_jump_on_val_zero xy f? I
-i_jump_on_val xy f I W
+i_jump_on_val xy f? I W
get_list xy xy xy
@@ -182,13 +182,17 @@ get_list r x y
get_list r y r
get_list r x r
+get_hd xy xy
+get_tl xy xy
+
# Old-style catch.
catch y f
catch_end y
# Try/catch.
try Y F => catch Y F
-try_case Y => try_end Y
+
+try_case y
try_end y
%cold
@@ -213,9 +217,9 @@ i_get_tuple_element2y x P y y
i_get_tuple_element3 x P x
%cold
-is_number f x
-is_number f y
+is_number f? xy
%hot
+
is_number Fail=f i =>
is_number Fail=f na => jump Fail
is_number Fail Literal=q => move Literal x | is_number Fail x
@@ -446,37 +450,37 @@ is_ne_exact Lbl C=c R=xy => is_ne_exact Lbl R C
is_ne_exact Lbl R=xy C=ian => i_is_ne_exact_immed Lbl R C
is_ne_exact Lbl R=xy C=q => i_is_ne_exact_literal Lbl R C
-i_is_eq_exact_immed f rxy c
+i_is_eq_exact_immed f? rxy c
-i_is_eq_exact_literal f xy c
+i_is_eq_exact_literal f? xy c
-i_is_ne_exact_immed f xy c
+i_is_ne_exact_immed f? xy c
-i_is_ne_exact_literal f xy c
+i_is_ne_exact_literal f? xy c
is_eq_exact Lbl Y=y X=x => is_eq_exact Lbl X Y
-is_eq_exact f x xy
-is_eq_exact f y y
+is_eq_exact f? x xy
+is_eq_exact f? y y
-is_ne_exact f S S
+is_ne_exact f? S S
-is_lt f x x
-is_lt f x c
-is_lt f c x
+is_lt f? x x
+is_lt f? x c
+is_lt f? c x
%cold
-is_lt f s s
+is_lt f? s s
%hot
-is_ge f x x
-is_ge f x c
-is_ge f c x
+is_ge f? x x
+is_ge f? x c
+is_ge f? c x
%cold
-is_ge f s s
+is_ge f? s s
%hot
-is_eq f s s
+is_eq f? s s
-is_ne f s s
+is_ne f? s s
#
# Putting things.
@@ -502,6 +506,10 @@ i_put_tuple xy I
#
put_list Const=c n Dst => move Const x | put_list x n Dst
+put_list Src Dst=x Dst => update_list Src Dst
+
+update_list xyc x
+
put_list x n x
put_list y n x
put_list x x x
@@ -510,8 +518,6 @@ put_list y x x
put_list y y x
put_list x y x
-put_list y x x
-
# put_list SrcReg Constant Dst
put_list x c x
@@ -526,8 +532,6 @@ put_list c y x
# The following put_list instructions using x(0) are frequently used.
-put_list y r r
-put_list x r r
put_list r n r
put_list r n x
put_list r x x
@@ -538,6 +542,7 @@ put_list x x r
put_list s s d
%hot
+
#
# Some more only used by the emulator
#
@@ -583,7 +588,7 @@ is_tagged_tuple Fail Literal=q Arity Atom => \
move Literal x | is_tagged_tuple Fail x Arity Atom
is_tagged_tuple Fail=f c Arity Atom => jump Fail
-is_tagged_tuple f rxy A a
+is_tagged_tuple f? rxy A a
# Test tuple & arity (head)
@@ -591,14 +596,14 @@ is_tuple Fail Literal=q => move Literal x | is_tuple Fail x
is_tuple Fail=f c => jump Fail
is_tuple Fail=f S=xy | test_arity Fail=f S=xy Arity => is_tuple_of_arity Fail S Arity
-is_tuple_of_arity f rxy A
+is_tuple_of_arity f? rxy A
-is_tuple f rxy
+is_tuple f? rxy
test_arity Fail Literal=q Arity => move Literal x | test_arity Fail x Arity
test_arity Fail=f c Arity => jump Fail
-test_arity f xy A
+test_arity f? xy A
get_tuple_element Reg=x P1 D1=x | get_tuple_element Reg=x P2 D2=x | \
get_tuple_element Reg=x P3 D3=x | \
@@ -619,16 +624,16 @@ is_integer Fail Literal=q => move Literal x | is_integer Fail x
is_integer Fail=f S=x | allocate Need Regs => is_integer_allocate Fail S Need Regs
-is_integer_allocate f x t t
+is_integer_allocate f? x t t
-is_integer f xy
+is_integer f? xy
is_list Fail=f n =>
is_list Fail Literal=q => move Literal x | is_list Fail x
is_list Fail=f c => jump Fail
-is_list f x
+is_list f? x
%cold
-is_list f y
+is_list f? y
%hot
is_nonempty_list Fail=f S=x | allocate Need Rs => is_nonempty_list_allocate Fail S Need Rs
@@ -638,21 +643,21 @@ is_nonempty_list F=f x==0 | test_heap I1 I2 => is_nonempty_list_test_heap F I1 I
is_nonempty_list Fail=f S=x | get_list S D1=x D2=x => \
is_nonempty_list_get_list Fail S D1 D2
-is_nonempty_list_allocate f rx t t
-is_nonempty_list_test_heap f I t
-is_nonempty_list_get_list f rx x x
-is_nonempty_list f xy
+is_nonempty_list_allocate f? rx t t
+is_nonempty_list_test_heap f? I t
+is_nonempty_list_get_list f? rx x x
+is_nonempty_list f? xy
-is_atom f x
+is_atom f? x
%cold
-is_atom f y
+is_atom f? y
%hot
is_atom Fail=f a =>
is_atom Fail=f niq => jump Fail
-is_float f x
+is_float f? x
%cold
-is_float f y
+is_float f? y
%hot
is_float Fail=f nai => jump Fail
is_float Fail Literal=q => move Literal x | is_float Fail x
@@ -660,13 +665,13 @@ is_float Fail Literal=q => move Literal x | is_float Fail x
is_nil Fail=f n =>
is_nil Fail=f qia => jump Fail
-is_nil f xy
+is_nil f? xy
is_binary Fail Literal=q => move Literal x | is_binary Fail x
is_binary Fail=f c => jump Fail
-is_binary f x
+is_binary f? x
%cold
-is_binary f y
+is_binary f? y
%hot
# XXX Deprecated.
@@ -674,27 +679,27 @@ is_bitstr Fail Term => is_bitstring Fail Term
is_bitstring Fail Literal=q => move Literal x | is_bitstring Fail x
is_bitstring Fail=f c => jump Fail
-is_bitstring f x
+is_bitstring f? x
%cold
-is_bitstring f y
+is_bitstring f? y
%hot
is_reference Fail=f cq => jump Fail
-is_reference f x
+is_reference f? x
%cold
-is_reference f y
+is_reference f? y
%hot
is_pid Fail=f cq => jump Fail
-is_pid f x
+is_pid f? x
%cold
-is_pid f y
+is_pid f? y
%hot
is_port Fail=f cq => jump Fail
-is_port f x
+is_port f? x
%cold
-is_port f y
+is_port f? y
%hot
is_boolean Fail=f a==am_true =>
@@ -702,19 +707,20 @@ is_boolean Fail=f a==am_false =>
is_boolean Fail=f ac => jump Fail
%cold
-is_boolean f xy
+is_boolean f? xy
%hot
-is_function2 Fail=f acq Arity => jump Fail
+is_function2 Fail=f Literal=q Arity | literal_is_export(Literal) =>
+is_function2 Fail=f c Arity => jump Fail
is_function2 Fail=f Fun a => jump Fail
-is_function2 f S s
+is_function2 f? S s
# Allocating & initializing.
allocate Need Regs | init Y => allocate_init Need Regs Y
init Y1 | init Y2 => init2 Y1 Y2
-allocate_init t t y
+allocate_init t t? y
#################################################################
# External function and bif calls.
@@ -961,11 +967,11 @@ call_ext_last Ar Func D => i_call_ext_last Func D
call_ext_only Ar Func => i_call_ext_only Func
i_apply
-i_apply_last P
+i_apply_last Q
i_apply_only
i_apply_fun
-i_apply_fun_last P
+i_apply_fun_last Q
i_apply_fun_only
%cold
@@ -1004,13 +1010,13 @@ node y
# Note: 'I' is sufficient because this instruction will only be used
# if the arity fits in 24 bits.
-i_fast_element xy j I d
+i_fast_element xy j? I d
-i_element xy j s d
+i_element xy j? s d
-bif1 f b s d
+bif1 f? b s d
bif1_body b s d
-i_bif2 f b s s d
+i_bif2 f? b s s d
i_bif2_body b s s d
#
@@ -1062,7 +1068,7 @@ make_fun2 OldIndex=u => gen_make_fun2(OldIndex)
i_make_fun W t
%hot
-is_function f xy
+is_function f? xy
is_function Fail=f c => jump Fail
func_info M F A => i_func_info u M F A
@@ -1091,24 +1097,24 @@ i_bs_match_string x f W W
bs_get_integer2 Fail=f Ms=x Live=u Sz=sq Unit=u Flags=u Dst=d => \
gen_get_integer2(Fail, Ms, Live, Sz, Unit, Flags, Dst)
-i_bs_get_integer_small_imm x W f t x
-i_bs_get_integer_imm x W t f t x
-i_bs_get_integer f t t x s x
-i_bs_get_integer_8 x f x
-i_bs_get_integer_16 x f x
+i_bs_get_integer_small_imm x W f? t x
+i_bs_get_integer_imm x W t f? t x
+i_bs_get_integer f? t t x s x
+i_bs_get_integer_8 x f? x
+i_bs_get_integer_16 x f? x
%if ARCH_64
-i_bs_get_integer_32 x f x
+i_bs_get_integer_32 x f? x
%endif
# Fetching binaries from binaries.
bs_get_binary2 Fail=f Ms=x 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 f x t W t x
-i_bs_get_binary2 f x t s t x
-i_bs_get_binary_all2 f x t t x
-i_bs_get_binary_all_reuse x f t
+i_bs_get_binary_imm2 f? x t W t x
+i_bs_get_binary2 f x t? s t x
+i_bs_get_binary_all2 f? x t t x
+i_bs_get_binary_all_reuse x f? t
# Fetching float from binaries.
bs_get_float2 Fail=f Ms=x Live=u Sz=s Unit=u Flags=u Dst=d => \
@@ -1116,29 +1122,32 @@ bs_get_float2 Fail=f Ms=x Live=u Sz=s Unit=u Flags=u Dst=d => \
bs_get_float2 Fail=f Ms=x Live=u Sz=q Unit=u Flags=u Dst=d => jump Fail
-i_bs_get_float2 f x t s t x
+i_bs_get_float2 f? x t s t x
# Miscellanous
bs_skip_bits2 Fail=f Ms=x Sz=sq Unit=u Flags=u => \
gen_skip_bits2(Fail, Ms, Sz, Unit, Flags)
-i_bs_skip_bits_imm2 f x W
-i_bs_skip_bits2 f x xy t
-i_bs_skip_bits_all2 f x t
+i_bs_skip_bits_imm2 f? x W
+i_bs_skip_bits2 f? x xy t
+i_bs_skip_bits_all2 f? x t
bs_test_tail2 Fail=f Ms=x Bits=u==0 => bs_test_zero_tail2 Fail Ms
bs_test_tail2 Fail=f Ms=x Bits=u => bs_test_tail_imm2 Fail Ms Bits
-bs_test_zero_tail2 f x
-bs_test_tail_imm2 f x W
+bs_test_zero_tail2 f? x
+bs_test_tail_imm2 f? x W
bs_test_unit F Ms Unit=u==8 => bs_test_unit8 F Ms
-bs_test_unit f x t
-bs_test_unit8 f x
+bs_test_unit f? x t
+bs_test_unit8 f? x
# An y register operand for bs_context_to_binary is rare,
# but can happen because of inlining.
+bs_context_to_binary Y=y | line L | badmatch Y => \
+ move Y x | bs_context_to_binary x | line L | badmatch x
+
bs_context_to_binary Y=y => move Y x | bs_context_to_binary x
bs_context_to_binary x
@@ -1147,14 +1156,14 @@ bs_context_to_binary x
# Utf8/utf16/utf32 support. (R12B-5)
#
bs_get_utf8 Fail=f Ms=x u u Dst=d => i_bs_get_utf8 Ms Fail Dst
-i_bs_get_utf8 x f x
+i_bs_get_utf8 x f? x
bs_skip_utf8 Fail=f Ms=x u u => i_bs_get_utf8 Ms Fail x
bs_get_utf16 Fail=f Ms=x u Flags=u Dst=d => i_bs_get_utf16 Ms Fail Flags Dst
bs_skip_utf16 Fail=f Ms=x u Flags=u => i_bs_get_utf16 Ms Fail Flags x
-i_bs_get_utf16 x f t x
+i_bs_get_utf16 x f? t x
bs_get_utf32 Fail=f Ms=x Live=u Flags=u Dst=d => \
bs_get_integer2 Fail Ms Live i=32 u=1 Flags Dst | \
@@ -1183,13 +1192,13 @@ bs_init2 Fail Sz Words=u==0 Regs Flags Dst => \
bs_init2 Fail Sz Words Regs Flags Dst => \
i_bs_init_fail_heap Sz Words Fail Regs Dst
-i_bs_init_fail xy j t x
+i_bs_init_fail xy j? t? x
-i_bs_init_fail_heap s I j t x
+i_bs_init_fail_heap s I j? t? x
-i_bs_init W t x
+i_bs_init W t? x
-i_bs_init_heap W I t x
+i_bs_init_heap W I t? x
bs_init_bits Fail Sz=o Words Regs Flags Dst => system_limit Fail
@@ -1202,16 +1211,16 @@ bs_init_bits Fail Sz Words=u==0 Regs Flags Dst => \
bs_init_bits Fail Sz Words Regs Flags Dst => \
i_bs_init_bits_fail_heap Sz Words Fail Regs Dst
-i_bs_init_bits_fail xy j t x
+i_bs_init_bits_fail xy j? t? x
-i_bs_init_bits_fail_heap s I j t x
+i_bs_init_bits_fail_heap s I j? t? x
-i_bs_init_bits W t x
-i_bs_init_bits_heap W I t x
+i_bs_init_bits W t? x
+i_bs_init_bits_heap W I t? x
bs_add Fail S1=i==0 S2 Unit=u==1 D => move S2 D
-bs_add j s s t x
+bs_add j? s s t? x
bs_append Fail Size Extra Live Unit Bin Flags Dst => \
move Bin x | i_bs_append Fail Extra Live Unit Size Dst
@@ -1221,8 +1230,8 @@ bs_private_append Fail Size Unit Bin Flags Dst => \
bs_init_writable
-i_bs_append j I t t s x
-i_bs_private_append j t s S x
+i_bs_append j? I t? t s x
+i_bs_private_append j? t s S x
#
# Storing integers into binaries.
@@ -1231,8 +1240,8 @@ i_bs_private_append j t s S x
bs_put_integer Fail=j Sz=sq Unit=u Flags=u Src=s => \
gen_put_integer(Fail, Sz, Unit, Flags, Src)
-i_new_bs_put_integer j s t s
-i_new_bs_put_integer_imm j W t s
+i_new_bs_put_integer j? s t s
+i_new_bs_put_integer_imm j? W t s
#
# Utf8/utf16/utf32 support. (R12B-5)
@@ -1248,14 +1257,14 @@ i_bs_utf16_size s x
bs_put_utf8 Fail u Src=s => i_bs_put_utf8 Fail Src
-i_bs_put_utf8 j s
+i_bs_put_utf8 j? s
-bs_put_utf16 j t s
+bs_put_utf16 j? t s
bs_put_utf32 Fail=j Flags=u Src=s => \
i_bs_validate_unicode Fail Src | bs_put_integer Fail i=32 u=1 Flags Src
-i_bs_validate_unicode j s
+i_bs_validate_unicode j? s
#
# Storing floats into binaries.
@@ -1265,8 +1274,8 @@ bs_put_float Fail Sz=q Unit Flags Val => badarg Fail
bs_put_float Fail=j Sz=s Unit=u Flags=u Src=s => \
gen_put_float(Fail, Sz, Unit, Flags, Src)
-i_new_bs_put_float j s t s
-i_new_bs_put_float_imm j W t s
+i_new_bs_put_float j? s t s
+i_new_bs_put_float_imm j? W t s
#
# Storing binaries into binaries.
@@ -1275,9 +1284,9 @@ i_new_bs_put_float_imm j W t s
bs_put_binary Fail=j Sz=s Unit=u Flags=u Src=s => \
gen_put_binary(Fail, Sz, Unit, Flags, Src)
-i_new_bs_put_binary j s t s
-i_new_bs_put_binary_imm j W s
-i_new_bs_put_binary_all j s t
+i_new_bs_put_binary j? s t s
+i_new_bs_put_binary_imm j? W s
+i_new_bs_put_binary_all j? s t
#
# Warning: The i_bs_put_string and i_new_bs_put_string instructions
@@ -1391,12 +1400,12 @@ new_map Dst Live Size Rest=* | is_small_map_literal_keys(Size, Rest) => \
new_map d t I
i_new_small_map_lit d t q
update_map_assoc s d t I
-update_map_exact j s d t I
+update_map_exact j? s d t I
is_map Fail Lit=q | literal_is_map(Lit) =>
is_map Fail cq => jump Fail
-is_map f xy
+is_map f? xy
## Transform has_map_fields #{ K1 := _, K2 := _ } to has_map_elements
@@ -1405,19 +1414,23 @@ has_map_fields Fail Src Size Rest=* => \
## Transform get_map_elements(s) #{ K1 := V1, K2 := V2 }
-get_map_elements Fail Src=xy Size=u==2 Rest=* => \
+get_map_elements Fail Src Size=u==2 Rest=* => \
gen_get_map_element(Fail, Src, Size, Rest)
get_map_elements Fail Src Size Rest=* | map_key_sort(Size, Rest) => \
gen_get_map_elements(Fail, Src, Size, Rest)
-i_get_map_elements f s I
+i_get_map_elements f? s I
i_get_map_element Fail Src=xy Key=y Dst => \
move Key x | i_get_map_element Fail Src x Dst
-i_get_map_element_hash f xy c I xy
+i_get_map_element_hash Fail Src=c Key Hash Dst => \
+ move Src x | i_get_map_element_hash Fail x Key Hash Dst
+i_get_map_element_hash f? xy c I xy
-i_get_map_element f xy x xy
+i_get_map_element Fail Src=c Key Dst => \
+ move Src x | i_get_map_element Fail x Key Dst
+i_get_map_element f? xy x xy
#
# Convert the plus operations to a generic plus instruction.
@@ -1485,32 +1498,32 @@ gc_bif1 Fail I u$bif:erlang:bnot/1 Src Dst=d => i_int_bnot Fail Src I Dst
i_increment rxy W t d
-i_plus x xy j t d
-i_plus s s j t d
+i_plus x xy j? t d
+i_plus s s j? t d
-i_minus x x j t d
-i_minus s s j t d
+i_minus x x j? t d
+i_minus s s j? t d
-i_times j t s s d
+i_times j? t s s d
-i_m_div j t s s d
-i_int_div j t s s d
+i_m_div j? t s s d
+i_int_div j? t s s d
-i_rem x x j t d
-i_rem s s j t d
+i_rem x x j? t d
+i_rem s s j? t d
-i_bsl s s j t d
-i_bsr s s j t d
+i_bsl s s j? t d
+i_bsr s s j? t d
-i_band x c j t d
-i_band s s j t d
+i_band x c j? t d
+i_band s s j? t d
-i_bor j I s s d
-i_bxor j I s s d
+i_bor j? I s s d
+i_bxor j? I s s d
i_int_bnot Fail Src=c Live Dst => move Src x | i_int_bnot Fail x Live Dst
-i_int_bnot j S t d
+i_int_bnot j? S t d
#
# Old guard BIFs that creates heap fragments are no longer allowed.
@@ -1534,9 +1547,9 @@ gc_bif2 Fail I Bif S1 S2 Dst => \
gc_bif3 Fail I Bif S1 S2 S3 Dst => \
gen_guard_bif3(Fail, I, Bif, S1, S2, S3, Dst)
-i_gc_bif1 j W s t d
+i_gc_bif1 j? W s t? d
-i_gc_bif2 j W t s s d
+i_gc_bif2 j? W t? s s d
ii_gc_bif3/7
@@ -1545,7 +1558,7 @@ ii_gc_bif3/7
ii_gc_bif3 Fail Bif Live S1 S2 S3 Dst => \
move S1 x | i_gc_bif3 Fail Bif Live S2 S3 Dst
-i_gc_bif3 j W t s s d
+i_gc_bif3 j? W t? s s d
#
# The following instruction is specially handled in beam_load.c
@@ -1563,8 +1576,20 @@ on_load
#
# R14A.
#
-recv_mark f
+# Modified in OTP 21 because it turns out that we don't need the
+# label after all.
+#
+
+recv_mark f => i_recv_mark
+i_recv_mark
recv_set Fail | label Lbl | loop_rec Lf Reg => \
i_recv_set | label Lbl | loop_rec Lf Reg
i_recv_set
+
+#
+# OTP 21.
+#
+
+build_stacktrace
+raw_raise
diff --git a/erts/emulator/beam/packet_parser.c b/erts/emulator/beam/packet_parser.c
index f14910bc72..de1d481105 100644
--- a/erts/emulator/beam/packet_parser.c
+++ b/erts/emulator/beam/packet_parser.c
@@ -200,7 +200,7 @@ static int http_init(void)
for (i = 0; i < HTTP_HDR_HASH_SIZE; i++)
http_hdr_hash[i] = NULL;
for (i = 0; http_hdr_strings[i] != NULL; i++) {
- ASSERT(strlen(http_hdr_strings[i]) <= HTTP_MAX_NAME_LEN);
+ ASSERT(sys_strlen(http_hdr_strings[i]) <= HTTP_MAX_NAME_LEN);
http_hdr_table[i].index = i;
http_hash_insert(http_hdr_strings[i],
&http_hdr_table[i],
@@ -516,7 +516,7 @@ static http_atom_t* http_hash_lookup(const char* name, int len,
while (ap != NULL) {
if ((ap->h == h) && (ap->len == len) &&
- (strncmp(ap->name, name, len) == 0))
+ (sys_strncmp(ap->name, name, len) == 0))
return ap;
ap = ap->next;
}
@@ -656,7 +656,7 @@ int packet_parse_http(const char* buf, int len, int* statep,
if (*statep == 0) {
/* start-line = Request-Line | Status-Line */
- if (n >= 5 && (strncmp(buf, "HTTP/", 5) == 0)) {
+ if (n >= 5 && (sys_strncmp(buf, "HTTP/", 5) == 0)) {
int major = 0;
int minor = 0;
int status = 0;
@@ -750,7 +750,7 @@ int packet_parse_http(const char* buf, int len, int* statep,
}
if (n < 8)
return -1;
- if (strncmp(ptr, "HTTP/", 5) != 0)
+ if (sys_strncmp(ptr, "HTTP/", 5) != 0)
return -1;
ptr += 5;
n -= 5;
diff --git a/erts/emulator/beam/sys.h b/erts/emulator/beam/sys.h
index 9106091ca6..be6ab57eeb 100644
--- a/erts/emulator/beam/sys.h
+++ b/erts/emulator/beam/sys.h
@@ -366,29 +366,11 @@ typedef UWord BeamInstr;
# define HAVE_INT64 1
typedef unsigned long Uint64;
typedef long Sint64;
-# ifdef ULONG_MAX
-# define ERTS_UINT64_MAX ULONG_MAX
-# endif
-# ifdef LONG_MAX
-# define ERTS_SINT64_MAX LONG_MAX
-# endif
-# ifdef LONG_MIN
-# define ERTS_SINT64_MIN LONG_MIN
-# endif
# define ErtsStrToSint64 strtol
# elif SIZEOF_LONG_LONG == 8
# define HAVE_INT64 1
typedef unsigned long long Uint64;
typedef long long Sint64;
-# ifdef ULLONG_MAX
-# define ERTS_UINT64_MAX ULLONG_MAX
-# endif
-# ifdef LLONG_MAX
-# define ERTS_SINT64_MAX LLONG_MAX
-# endif
-# ifdef LLONG_MIN
-# define ERTS_SINT64_MIN LLONG_MIN
-# endif
# define ErtsStrToSint64 strtoll
# else
# error "No 64-bit integer type found"
@@ -402,7 +384,7 @@ typedef long long Sint64;
# define ERTS_SINT64_MAX ((Sint64) ((((Uint64) 1) << 63)-1))
#endif
#ifndef ERTS_SINT64_MIN
-# define ERTS_SINT64_MIN (-1*(((Sint64) 1) << 63))
+# define ERTS_SINT64_MIN ((Sint64) ((((Uint64) 1) << 63)))
#endif
#if SIZEOF_LONG == 4
@@ -415,6 +397,16 @@ typedef int Sint32;
#error Found no appropriate type to use for 'Uint32' and 'Sint32'
#endif
+#ifndef ERTS_UINT32_MAX
+# define ERTS_UINT32_MAX (~((Uint32) 0))
+#endif
+#ifndef ERTS_SINT32_MAX
+# define ERTS_SINT32_MAX ((Sint32) ((((Uint32) 1) << 31)-1))
+#endif
+#ifndef ERTS_SINT32_MIN
+# define ERTS_SINT32_MIN ((Sint32) ((((Uint32) 1) << 31)))
+#endif
+
#if SIZEOF_INT == 2
typedef unsigned int Uint16;
typedef int Sint16;
@@ -425,6 +417,16 @@ typedef short Sint16;
#error Found no appropriate type to use for 'Uint16' and 'Sint16'
#endif
+#ifndef ERTS_UINT16_MAX
+# define ERTS_UINT16_MAX (~((Uint16) 0))
+#endif
+#ifndef ERTS_SINT16_MAX
+# define ERTS_SINT16_MAX ((Sint16) ((((Uint16) 1) << 15)-1))
+#endif
+#ifndef ERTS_SINT16_MIN
+# define ERTS_SINT16_MIN ((Sint16) ((((Uint16) 1) << 15)))
+#endif
+
#if CHAR_BIT == 8
typedef unsigned char byte;
#else
@@ -636,6 +638,8 @@ typedef struct preload {
*/
typedef Eterm ErtsTracer;
+#include "erl_osenv.h"
+
/*
* This structure contains options to all built in drivers.
* None of the drivers use all of the fields.
@@ -651,8 +655,7 @@ typedef struct _SysDriverOpts {
int hide_window; /* Hide this windows (Windows). */
int exit_status; /* Report exit status of subprocess. */
int overlapped_io; /* Only has effect on windows NT et al */
- char *envir; /* Environment of the port process, */
- /* in Windows format. */
+ erts_osenv_t envir; /* Environment of the port process */
char **argv; /* Argument vector in Unix'ish format. */
char *wd; /* Working directory. */
unsigned spawn_type; /* Bitfield of ERTS_SPAWN_DRIVER |
@@ -782,9 +785,6 @@ void set_break_quit(void (*)(void), void (*)(void));
void os_flavor(char*, unsigned);
void os_version(int*, int*, int*);
-void init_getenv_state(GETENV_STATE *);
-char * getenv_string(GETENV_STATE *);
-void fini_getenv_state(GETENV_STATE *);
#define HAVE_ERTS_CHECK_IO_DEBUG
typedef struct {
@@ -805,30 +805,36 @@ int sys_double_to_chars_ext(double, char*, size_t, size_t);
int sys_double_to_chars_fast(double, char*, int, int, int);
void sys_get_pid(char *, size_t);
-/* erts_sys_putenv() returns, 0 on success and a value != 0 on failure. */
-int erts_sys_putenv(char *key, char *value);
-/* Simple variant used from drivers, raw eightbit interface */
-int erts_sys_putenv_raw(char *key, char *value);
-/* erts_sys_getenv() returns 0 on success (length of value string in
- *size), a value > 0 if value buffer is too small (*size is set to needed
- size), and a value < 0 on failure. */
-int erts_sys_getenv(char *key, char *value, size_t *size);
-/* Simple variant used from drivers, raw eightbit interface */
-int erts_sys_getenv_raw(char *key, char *value, size_t *size);
-/* erts_sys_getenv__() is only allowed to be used in early init phase */
-int erts_sys_getenv__(char *key, char *value, size_t *size);
-/* erst_sys_unsetenv() returns 0 on success and a value != 0 on failure. */
-int erts_sys_unsetenv(char *key);
+/* erl_drv_get/putenv have been implicitly 8-bit for so long that we can't
+ * change them without breaking things on Windows. Their return values are
+ * identical to erts_osenv_get/putenv */
+int erts_sys_explicit_8bit_getenv(char *key, char *value, size_t *size);
+int erts_sys_explicit_8bit_putenv(char *key, char *value);
+
+/* This is identical to erts_sys_explicit_8bit_getenv but falls down to the
+ * host OS implementation instead of erts_osenv. */
+int erts_sys_explicit_host_getenv(char *key, char *value, size_t *size);
+
+const erts_osenv_t *erts_sys_rlock_global_osenv(void);
+void erts_sys_runlock_global_osenv(void);
+
+erts_osenv_t *erts_sys_rwlock_global_osenv(void);
+void erts_sys_rwunlock_global_osenv(void);
/* Easier to use, but not as efficient, environment functions */
char *erts_read_env(char *key);
void erts_free_read_env(void *value);
-#if defined(ERTS_THR_HAVE_SIG_FUNCS) && !defined(ETHR_UNUSABLE_SIGUSRX)
+#if defined(ERTS_THR_HAVE_SIG_FUNCS) && \
+ (!defined(ETHR_UNUSABLE_SIGUSRX) || defined(SIGRTMIN))
extern void sys_thr_resume(erts_tid_t tid);
extern void sys_thr_suspend(erts_tid_t tid);
-#define ERTS_SYS_SUSPEND_SIGNAL SIGUSR2
-#endif
+#ifdef SIGRTMIN
+#define ERTS_SYS_SUSPEND_SIGNAL (SIGRTMIN+1)
+#else
+#define ERTS_SYS_SUSPEND_SIGNAL (SIGUSR2)
+#endif /* SIGRTMIN */
+#endif /* HAVE_SIG_FUNCS */
/* utils.c */
@@ -990,18 +996,82 @@ erts_refc_read(erts_refc_t *refcp, erts_aint_t min_val)
return val;
}
-#endif /* #if ERTS_GLB_INLINE_INCL_FUNC_DEF */
+#endif /* #if ERTS_GLB_INLINE_INCL_FUNC_DEF */
+
+
+/* Thin wrappers around memcpy and friends, which should always be used in
+ * place of plain memcpy, memset, etc.
+ *
+ * Passing NULL to any of these functions is undefined behavior even though it
+ * may seemingly work when the length (if any) is zero; a compiler can take
+ * this as a hint that the passed operand may *never* be NULL and then optimize
+ * based on that information.
+ */
+ERTS_GLB_INLINE void *sys_memcpy(void *dest, const void *src, size_t n);
+ERTS_GLB_INLINE void *sys_memmove(void *dest, const void *src, size_t n);
+ERTS_GLB_INLINE int sys_memcmp(const void *s1, const void *s2, size_t n);
+ERTS_GLB_INLINE void *sys_memset(void *s, int c, size_t n);
+ERTS_GLB_INLINE void *sys_memzero(void *s, size_t n);
+ERTS_GLB_INLINE int sys_strcmp(const char *s1, const char *s2);
+ERTS_GLB_INLINE int sys_strncmp(const char *s1, const char *s2, size_t n);
+ERTS_GLB_INLINE char *sys_strcpy(char *dest, const char *src);
+ERTS_GLB_INLINE char *sys_strncpy(char *dest, const char *src, size_t n);
+ERTS_GLB_INLINE size_t sys_strlen(const char *s);
+
+#if ERTS_GLB_INLINE_INCL_FUNC_DEF
+
+ERTS_GLB_INLINE void *sys_memcpy(void *dest, const void *src, size_t n)
+{
+ ASSERT(dest != NULL && src != NULL);
+ return memcpy(dest,src,n);
+}
+ERTS_GLB_INLINE void *sys_memmove(void *dest, const void *src, size_t n)
+{
+ ASSERT(dest != NULL && src != NULL);
+ return memmove(dest,src,n);
+}
+ERTS_GLB_INLINE int sys_memcmp(const void *s1, const void *s2, size_t n)
+{
+ ASSERT(s1 != NULL && s2 != NULL);
+ return memcmp(s1,s2,n);
+}
+ERTS_GLB_INLINE void *sys_memset(void *s, int c, size_t n)
+{
+ ASSERT(s != NULL);
+ return memset(s,c,n);
+}
+ERTS_GLB_INLINE void *sys_memzero(void *s, size_t n)
+{
+ ASSERT(s != NULL);
+ return memset(s,'\0',n);
+}
+ERTS_GLB_INLINE int sys_strcmp(const char *s1, const char *s2)
+{
+ ASSERT(s1 != NULL && s2 != NULL);
+ return strcmp(s1,s2);
+}
+ERTS_GLB_INLINE int sys_strncmp(const char *s1, const char *s2, size_t n)
+{
+ ASSERT(s1 != NULL && s2 != NULL);
+ return strncmp(s1,s2,n);
+}
+ERTS_GLB_INLINE char *sys_strcpy(char *dest, const char *src)
+{
+ ASSERT(dest != NULL && src != NULL);
+ return strcpy(dest,src);
-#define sys_memcpy(s1,s2,n) memcpy(s1,s2,n)
-#define sys_memmove(s1,s2,n) memmove(s1,s2,n)
-#define sys_memcmp(s1,s2,n) memcmp(s1,s2,n)
-#define sys_memset(s,c,n) memset(s,c,n)
-#define sys_memzero(s, n) memset(s,'\0',n)
-#define sys_strcmp(s1,s2) strcmp(s1,s2)
-#define sys_strncmp(s1,s2,n) strncmp(s1,s2,n)
-#define sys_strcpy(s1,s2) strcpy(s1,s2)
-#define sys_strncpy(s1,s2,n) strncpy(s1,s2,n)
-#define sys_strlen(s) strlen(s)
+}
+ERTS_GLB_INLINE char *sys_strncpy(char *dest, const char *src, size_t n)
+{
+ ASSERT(dest != NULL && src != NULL);
+ return strncpy(dest,src,n);
+}
+ERTS_GLB_INLINE size_t sys_strlen(const char *s)
+{
+ ASSERT(s != NULL);
+ return strlen(s);
+}
+#endif /* #if ERTS_GLB_INLINE_INCL_FUNC_DEF */
/* define function symbols (needed in sys_drv_api) */
#define sys_fp_alloc sys_alloc
@@ -1170,4 +1240,52 @@ int erts_get_printable_characters(void);
void erts_init_sys_common_misc(void);
+ERTS_GLB_INLINE Sint erts_raw_env_7bit_ascii_char_need(int encoding);
+ERTS_GLB_INLINE byte *erts_raw_env_7bit_ascii_char_put(byte c, byte *p,
+ int encoding);
+ERTS_GLB_INLINE int erts_raw_env_char_is_7bit_ascii_char(byte c, byte *p,
+ int encoding);
+ERTS_GLB_INLINE byte *erts_raw_env_next_char(byte *p, int encoding);
+
+#if ERTS_GLB_INLINE_INCL_FUNC_DEF
+
+ERTS_GLB_INLINE Sint
+erts_raw_env_7bit_ascii_char_need(int encoding)
+{
+ return (encoding == ERL_FILENAME_WIN_WCHAR) ? 2 : 1;
+}
+
+ERTS_GLB_INLINE byte *
+erts_raw_env_7bit_ascii_char_put(byte c,
+ byte *p,
+ int encoding)
+{
+ *(p++) = c;
+ if (encoding == ERL_FILENAME_WIN_WCHAR)
+ *(p++) = 0;
+ return p;
+}
+
+ERTS_GLB_INLINE int
+erts_raw_env_char_is_7bit_ascii_char(byte c,
+ byte *p,
+ int encoding)
+{
+ if (encoding == ERL_FILENAME_WIN_WCHAR)
+ return (p[0] == c) & (p[1] == 0);
+ else
+ return p[0] == c;
+}
+
+ERTS_GLB_INLINE byte *
+erts_raw_env_next_char(byte *p, int encoding)
+{
+ if (encoding == ERL_FILENAME_WIN_WCHAR)
+ return p + 2;
+ else
+ return p + 1;
+}
+
+#endif /* #if ERTS_GLB_INLINE_INCL_FUNC_DEF */
+
#endif
diff --git a/erts/emulator/beam/trace_instrs.tab b/erts/emulator/beam/trace_instrs.tab
index b10442c5e7..3eee81c053 100644
--- a/erts/emulator/beam/trace_instrs.tab
+++ b/erts/emulator/beam/trace_instrs.tab
@@ -61,12 +61,12 @@ i_return_to_trace() {
Uint *cpp = (Uint*) E;
for(;;) {
ASSERT(is_CP(*cpp));
- if (*cp_val(*cpp) == (BeamInstr) OpCode(return_trace)) {
+ if (IsOpCode(*cp_val(*cpp), return_trace)) {
do
++cpp;
while (is_not_CP(*cpp));
cpp += 2;
- } else if (*cp_val(*cpp) == (BeamInstr) OpCode(i_return_to_trace)) {
+ } else if (IsOpCode(*cp_val(*cpp), i_return_to_trace)) {
do
++cpp;
while (is_not_CP(*cpp));
diff --git a/erts/emulator/beam/utils.c b/erts/emulator/beam/utils.c
index 993585be10..2e22130524 100644
--- a/erts/emulator/beam/utils.c
+++ b/erts/emulator/beam/utils.c
@@ -59,6 +59,7 @@
#endif
#define ERTS_WANT_NFUNC_SCHED_INTERNALS__
#include "erl_nfunc_sched.h"
+#include "erl_proc_sig_queue.h"
#undef M_TRIM_THRESHOLD
#undef M_TOP_PAD
@@ -139,7 +140,7 @@ Eterm*
erts_set_hole_marker(Eterm* ptr, Uint sz)
{
Eterm* p = ptr;
- int i;
+ Uint i;
for (i = 0; i < sz; i++) {
*p++ = ERTS_HOLE_MARKER;
@@ -1960,7 +1961,7 @@ static void do_send_logger_message(Eterm *hp, ErlOffHeap *ohp, ErlHeapFragment *
{notify,{info_msg,gleader,{emulator,format,[args]}}} |
{notify,{error,gleader,{emulator,format,[args]}}} |
{notify,{warning_msg,gleader,{emulator,format,[args}]}} */
-static int do_send_to_logger(Eterm tag, Eterm gleader, char *buf, int len)
+static int do_send_to_logger(Eterm tag, Eterm gleader, char *buf, size_t len)
{
Uint sz;
Eterm gl;
@@ -1973,7 +1974,7 @@ static int do_send_to_logger(Eterm tag, Eterm gleader, char *buf, int len)
ASSERT(is_atom(tag));
- if (len <= 0) {
+ if (len == 0) {
return -1;
}
@@ -2006,7 +2007,7 @@ static int do_send_to_logger(Eterm tag, Eterm gleader, char *buf, int len)
}
static int do_send_term_to_logger(Eterm tag, Eterm gleader,
- char *buf, int len, Eterm args)
+ char *buf, size_t len, Eterm args)
{
Uint sz;
Eterm gl;
@@ -2047,13 +2048,13 @@ static int do_send_term_to_logger(Eterm tag, Eterm gleader,
}
static ERTS_INLINE int
-send_info_to_logger(Eterm gleader, char *buf, int len)
+send_info_to_logger(Eterm gleader, char *buf, size_t len)
{
return do_send_to_logger(am_info_msg, gleader, buf, len);
}
static ERTS_INLINE int
-send_warning_to_logger(Eterm gleader, char *buf, int len)
+send_warning_to_logger(Eterm gleader, char *buf, size_t len)
{
Eterm tag;
switch (erts_error_logger_warnings) {
@@ -2065,13 +2066,13 @@ send_warning_to_logger(Eterm gleader, char *buf, int len)
}
static ERTS_INLINE int
-send_error_to_logger(Eterm gleader, char *buf, int len)
+send_error_to_logger(Eterm gleader, char *buf, size_t len)
{
return do_send_to_logger(am_error, gleader, buf, len);
}
static ERTS_INLINE int
-send_error_term_to_logger(Eterm gleader, char *buf, int len, Eterm args)
+send_error_term_to_logger(Eterm gleader, char *buf, size_t len, Eterm args)
{
return do_send_term_to_logger(am_error, gleader, buf, len, args);
}
@@ -3152,6 +3153,9 @@ tailrecur_ne:
int cmp;
byte* a_ptr;
byte* b_ptr;
+ if (eq_only && a_size != b_size) {
+ RETURN_NEQ(a_size - b_size);
+ }
ERTS_GET_BINARY_BYTES(a, a_ptr, a_bitoffs, a_bitsize);
ERTS_GET_BINARY_BYTES(b, b_ptr, b_bitoffs, b_bitsize);
if ((a_bitsize | b_bitsize | a_bitoffs | b_bitoffs) == 0) {
@@ -4360,15 +4364,20 @@ erts_read_env(char *key)
char *value = erts_alloc(ERTS_ALC_T_TMP, value_len);
int res;
while (1) {
- res = erts_sys_getenv_raw(key, value, &value_len);
- if (res <= 0)
- break;
- value = erts_realloc(ERTS_ALC_T_TMP, value, value_len);
+ res = erts_sys_explicit_8bit_getenv(key, value, &value_len);
+
+ if (res >= 0) {
+ break;
+ }
+
+ value = erts_realloc(ERTS_ALC_T_TMP, value, value_len);
}
- if (res != 0) {
- erts_free(ERTS_ALC_T_TMP, value);
- return NULL;
+
+ if (res != 1) {
+ erts_free(ERTS_ALC_T_TMP, value);
+ return NULL;
}
+
return value;
}
diff --git a/erts/emulator/drivers/common/efile_drv.c b/erts/emulator/drivers/common/efile_drv.c
deleted file mode 100644
index a1b15a2199..0000000000
--- a/erts/emulator/drivers/common/efile_drv.c
+++ /dev/null
@@ -1,4240 +0,0 @@
-/*
- * %CopyrightBegin%
- *
- * Copyright Ericsson AB 1996-2017. All Rights Reserved.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- * %CopyrightEnd%
- */
-/*
- * Purpose: Provides file and directory operations.
- *
- * This file is generic, and does the work of decoding the commands
- * and encoding the responses. System-specific functions are found in
- * the unix_efile.c and win_efile.c files.
- */
-
-/* Operations */
-
-#define FILE_OPEN 1 /* Essential for startup */
-#define FILE_READ 2
-#define FILE_LSEEK 3
-#define FILE_WRITE 4
-#define FILE_FSTAT 5 /* Essential for startup */
-#define FILE_PWD 6 /* Essential for startup */
-#define FILE_READDIR 7 /* Essential for startup */
-#define FILE_CHDIR 8
-#define FILE_FSYNC 9
-#define FILE_MKDIR 10
-#define FILE_DELETE 11
-#define FILE_RENAME 12
-#define FILE_RMDIR 13
-#define FILE_TRUNCATE 14
-#define FILE_READ_FILE 15 /* Essential for startup */
-#define FILE_WRITE_INFO 16
-#define FILE_LSTAT 19
-#define FILE_READLINK 20
-#define FILE_LINK 21
-#define FILE_SYMLINK 22
-#define FILE_CLOSE 23
-#define FILE_PWRITEV 24
-#define FILE_PREADV 25
-#define FILE_SETOPT 26
-#define FILE_IPREAD 27
-#define FILE_ALTNAME 28
-#define FILE_READ_LINE 29
-#define FILE_FDATASYNC 30
-#define FILE_FADVISE 31
-#define FILE_SENDFILE 32
-#define FILE_FALLOCATE 33
-#define FILE_CLOSE_ON_PORT_EXIT 34
-/* Return codes */
-
-#define FILE_RESP_OK 0
-#define FILE_RESP_ERROR 1
-#define FILE_RESP_DATA 2
-#define FILE_RESP_NUMBER 3
-#define FILE_RESP_INFO 4
-#define FILE_RESP_NUMERR 5
-#define FILE_RESP_LDATA 6
-#define FILE_RESP_N2DATA 7
-#define FILE_RESP_EOF 8
-#define FILE_RESP_FNAME 9
-#define FILE_RESP_ALL_DATA 10
-#define FILE_RESP_LFNAME 11
-
-/* Options */
-
-#define FILE_OPT_DELAYED_WRITE 0
-#define FILE_OPT_READ_AHEAD 1
-
-/* IPREAD variants */
-
-#define IPREAD_S32BU_P32BU 0
-
-/* Limits */
-
-#define FILE_SEGMENT_READ (256*1024)
-#define FILE_SEGMENT_WRITE (256*1024)
-
-/* Internal */
-
-/* Set to 1 to test having read_ahead implicitly for read_line */
-#define ALWAYS_READ_LINE_AHEAD 0
-
-
-/* Must not be possible to get from malloc()! */
-#define FILE_FD_INVALID ((Sint)(-1))
-
-#ifdef HAVE_CONFIG_H
-# include "config.h"
-#endif
-
-#include <ctype.h>
-#include <sys/types.h>
-#include <stdlib.h>
-
-/* Need (NON)BLOCKING macros for sendfile */
-#ifndef WANT_NONBLOCKING
-#define WANT_NONBLOCKING
-#endif
-
-#include "sys.h"
-
-#include "erl_driver.h"
-#include "erl_efile.h"
-#include "erl_threads.h"
-#include "gzio.h"
-#include "dtrace-wrapper.h"
-
-
-static ErlDrvSysInfo sys_info;
-
-/* For explanation of this var, see comment for same var in erl_async.c */
-static unsigned gcc_optimizer_hack = 0;
-
-#ifdef USE_VM_PROBES
-
-#define DTRACE_EFILE_BUFSIZ 128
-
-#define DTRACE_INVOKE_SETUP(op) \
- do { DTRACE3(efile_drv_int_entry, d->sched_i1, d->sched_i2, op); } while (0)
-#define DTRACE_INVOKE_SETUP_BY_NAME(op) \
- struct t_data *d = (struct t_data *) data ; \
- DTRACE_INVOKE_SETUP(op)
-#define DTRACE_INVOKE_RETURN(op) \
- do { DTRACE3(efile_drv_int_return, d->sched_i1, d->sched_i2, \
- op); } while (0) ; gcc_optimizer_hack++ ;
-
-/* Assign human-friendlier id numbers to scheduler & I/O worker threads */
-int dt_driver_idnum = 0;
-int dt_driver_io_worker_base = 5000;
-erts_mtx_t dt_driver_mutex;
-pthread_key_t dt_driver_key;
-
-typedef struct {
- int thread_num;
- Uint64 tag;
-} dt_private;
-
-dt_private *get_dt_private(int);
-#else /* USE_VM_PROBES */
-#define DTRACE_INVOKE_SETUP(op) do {} while (0)
-#define DTRACE_INVOKE_SETUP_BY_NAME(op) do {} while (0)
-#define DTRACE_INVOKE_RETURN(op) do {} while (0)
-#endif /* USE_VM_PROBES */
-
-/* #define TRACE 1 */
-#ifdef TRACE
-# define TRACE_C(c) do { putchar(c); fflush(stdout); } while (0)
-# define TRACE_S(s) do { fputs((s), stdout); fflush(stdout); } while (0)
-# define TRACE_F(args) do { printf args ;fflush(stdout); } while (0)
-#else
-# define TRACE_C(c) ((void)(0))
-# define TRACE_S(s) ((void)(0))
-# define TRACE_F(args) ((void)(0))
-#endif
-
-
-#define THRDS_AVAILABLE (sys_info.async_threads > 0)
-#ifdef HARDDEBUG /* HARDDEBUG in io.c is expected too */
-#define TRACE_DRIVER fprintf(stderr, "Efile: ")
-#else
-#define TRACE_DRIVER
-#endif
-#define MUTEX_INIT(m, p) do { IF_THRDS { TRACE_DRIVER; (m = driver_pdl_create(p)); } } while (0)
-#define MUTEX_LOCK(m) do { IF_THRDS { TRACE_DRIVER; driver_pdl_lock(m); } } while (0)
-#define MUTEX_UNLOCK(m) do { IF_THRDS { TRACE_DRIVER; driver_pdl_unlock(m); } } while (0)
-#define IF_THRDS if (THRDS_AVAILABLE)
-
-
-#define SENDFILE_FLGS_USE_THREADS (1 << 0)
-/**
- * On DARWIN sendfile can deadlock with close if called in
- * different threads. So until Apple fixes so that sendfile
- * is not buggy we disable usage of the async pool for
- * DARWIN. The testcase t_sendfile_crashduring reproduces
- * this error when using +A 10 and enabling SENDFILE_FLGS_USE_THREADS.
- */
-#if defined(__APPLE__) && defined(__MACH__)
-#define USE_THRDS_FOR_SENDFILE(DATA) 0
-#else
-#define USE_THRDS_FOR_SENDFILE(DATA) (DATA->flags & SENDFILE_FLGS_USE_THREADS)
-#endif /* defined(__APPLE__) && defined(__MACH__) */
-
-
-
-#if 0
-/* Experimental, for forcing all file operations to use the same thread. */
- static unsigned file_fixed_key = 1;
-# define KEY(desc) (&file_fixed_key)
-#else
-# define KEY(desc) (&(desc)->key)
-#endif
-
-#ifndef MAX
-# define MAX(x, y) (((x) > (y)) ? (x) : (y))
-#endif
-
-#ifdef FILENAMES_16BIT
-#ifdef USE_VM_PROBES
-#error 16bit characters in filenames and dtrace in combination is not supported.
-#endif
-# define FILENAME_BYTELEN(Str) filename_len_16bit(Str)
-# define FILENAME_COPY(To,From) filename_cpy_16bit((To),(From))
-# define FILENAME_CHARSIZE 2
-
- static int filename_len_16bit(char *str)
- {
- char *p = str;
- while(*p != '\0' || p[1] != '\0') {
- p += 2;
- }
- return (p - str);
- }
-
- static void filename_cpy_16bit(char *to, char *from)
- {
- while(*from != '\0' || from[1] != '\0') {
- *to++ = *from++;
- *to++ = *from++;
- }
- *to++ = *from++;
- *to++ = *from++;
- }
-
-#else
-# define FILENAME_BYTELEN(Str) strlen(Str)
-# define FILENAME_COPY(To,From) strcpy(To,From)
-# define FILENAME_CHARSIZE 1
-#endif
-
-#if (MAXPATHLEN+1)*FILENAME_CHARSIZE+1 > BUFSIZ
-# define RESBUFSIZE ((MAXPATHLEN+1)*FILENAME_CHARSIZE+1)
-#else
-# define RESBUFSIZE BUFSIZ
-#endif
-
-#define READDIR_CHUNKS (5)
-
-
-
-#if ALWAYS_READ_LINE_AHEAD
-#define DEFAULT_LINEBUF_SIZE 2048
-#else
-#define DEFAULT_LINEBUF_SIZE 512 /* Small, it's usually discarded anyway */
-#endif
-
-typedef unsigned char uchar;
-
-static ErlDrvData file_start(ErlDrvPort port, char* command);
-static int file_init(void);
-static void file_stop(ErlDrvData);
-static void file_output(ErlDrvData, char* buf, ErlDrvSizeT len);
-static ErlDrvSSizeT file_control(ErlDrvData, unsigned int command,
- char* buf, ErlDrvSizeT len,
- char **rbuf, ErlDrvSizeT rlen);
-static void file_timeout(ErlDrvData);
-static void file_outputv(ErlDrvData, ErlIOVec*);
-static void file_async_ready(ErlDrvData, ErlDrvThreadData);
-static void file_flush(ErlDrvData);
-
-#ifdef HAVE_SENDFILE
-static void file_ready_output(ErlDrvData data, ErlDrvEvent event);
-static void file_stop_select(ErlDrvEvent event, void* _);
-#endif /* HAVE_SENDFILE */
-
-
-enum e_timer {timer_idle, timer_again, timer_write};
-#ifdef HAVE_SENDFILE
-enum e_sendfile {sending, not_sending};
-#define SENDFILE_USE_THREADS (1 << 0)
-#endif /* HAVE_SENDFILE */
-
-struct t_data;
-
-typedef struct {
- SWord fd;
- ErlDrvPort port;
- unsigned int key; /* Async queue key */
- unsigned flags; /* Original flags from FILE_OPEN. */
- void (*invoke)(void *);
- struct t_data *d;
- void (*free)(void *);
- struct t_data *cq_head; /* Queue of incoming commands */
- struct t_data *cq_tail; /* -""- */
- enum e_timer timer_state;
-#ifdef HAVE_SENDFILE
- enum e_sendfile sendfile_state;
-#endif /* HAVE_SENDFILE */
- size_t read_bufsize;
- ErlDrvBinary *read_binp;
- size_t read_offset;
- size_t read_size;
- size_t write_bufsize;
- unsigned long write_delay;
- int write_error;
- Efile_error write_errInfo;
- ErlDrvPDL q_mtx; /* Mutex for the driver queue, known by the emulator. Also used for
- mutual exclusion when accessing field(s) below. */
- size_t write_buffered;
-#ifdef USE_VM_PROBES
- int idnum; /* Unique ID # for this driver thread/desc */
- char port_str[DTRACE_TERM_BUF_SIZE];
-#endif
-} file_descriptor;
-
-
-static int reply_error(file_descriptor*, Efile_error* errInfo);
-
-struct erl_drv_entry efile_driver_entry = {
- file_init,
- file_start,
- file_stop,
- file_output,
- NULL,
-#ifdef HAVE_SENDFILE
- file_ready_output,
-#else
- NULL,
-#endif /* HAVE_SENDFILE */
- "efile",
- NULL,
- NULL,
- file_control,
- file_timeout,
- file_outputv,
- file_async_ready,
- file_flush,
- NULL,
- NULL,
- ERL_DRV_EXTENDED_MARKER,
- ERL_DRV_EXTENDED_MAJOR_VERSION,
- ERL_DRV_EXTENDED_MINOR_VERSION,
- ERL_DRV_FLAG_USE_PORT_LOCKING,
- NULL,
- NULL,
-#ifdef HAVE_SENDFILE
- file_stop_select
-#else
- NULL
-#endif /* HAVE_SENDFILE */
-};
-
-
-
-static int thread_short_circuit;
-
-#define DRIVER_ASYNC(level, desc, f_invoke, data, f_free) \
-if (thread_short_circuit >= (level)) { \
- (*(f_invoke))(data); \
- file_async_ready((ErlDrvData)(desc), (data)); \
-} else { \
- driver_async((desc)->port, KEY(desc), (f_invoke), (data), (f_free)); \
-}
-
-
-
-struct t_pbuf_spec {
- Sint64 offset;
- size_t size;
-};
-
-struct t_pwritev {
- ErlDrvPort port;
- ErlDrvPDL q_mtx;
- size_t size;
- unsigned cnt;
- unsigned n;
- struct t_pbuf_spec specs[1];
-};
-
-struct t_preadv {
- ErlIOVec eiov;
- unsigned n;
- unsigned cnt;
- size_t size;
- Sint64 offsets[1];
-};
-
-#define READDIR_BUFSIZE (8*1024)*READDIR_CHUNKS
-#if READDIR_BUFSIZE < (1 + (2 + MAXPATHLEN)*FILENAME_CHARSIZE*READDIR_CHUNKS)
-# undef READDIR_BUFSIZE
-# define READDIR_BUFSIZE (1 + (2 + MAXPATHLEN)*FILENAME_CHARSIZE*READDIR_CHUNKS)
-#endif
-
-struct t_readdir_buf {
- struct t_readdir_buf *next;
- size_t n;
- char buf[READDIR_BUFSIZE];
-};
-
-struct t_data
-{
- struct t_data *next;
- int command;
- int level;
- void (*invoke)(void *);
- void (*free)(void *);
- void *data_to_free; /* used by FILE_CLOSE_ON_PORT_EXIT only */
- int again;
- int reply;
-#ifdef USE_VM_PROBES
- int sched_i1;
- Uint64 sched_i2;
- char sched_utag[DTRACE_EFILE_BUFSIZ+1];
-#endif
- int result_ok;
- Efile_error errInfo;
- int flags;
- SWord fd;
- int is_fd_unused;
- /**/
- Efile_info info;
- EFILE_DIR_HANDLE dir_handle; /* Handle to open directory. */
- ErlDrvBinary *bin;
- int drive;
- size_t n;
- /*off_t offset;*/
- /*size_t bytesRead; Bytes read from the file. */
- /**/
- union {
- struct {
- Sint64 offset;
- int origin;
- Sint64 location;
- } lseek;
- struct {
- ErlDrvPort port;
- ErlDrvPDL q_mtx;
- size_t size;
- size_t reply_size;
- } writev;
- struct t_pwritev pwritev;
- struct t_preadv preadv;
- struct {
- ErlDrvBinary *binp;
- size_t bin_offset;
- size_t bin_size;
- size_t size;
- } read;
- struct {
- ErlDrvBinary *binp; /* in - out */
- size_t read_offset; /* in - out */
- size_t read_size; /* in - out */
- size_t nl_pos; /* out */
- short nl_skip; /* out, 0 or 1 */
-#if !ALWAYS_READ_LINE_AHEAD
- short read_ahead; /* in, bool */
-#endif
- } read_line;
- struct {
- ErlDrvBinary *binp;
- int size;
- int offset;
- } read_file;
- struct {
- struct t_readdir_buf *first_buf;
- struct t_readdir_buf *last_buf;
- } read_dir;
- struct {
- Sint64 offset;
- Sint64 length;
- int advise;
- } fadvise;
-#ifdef HAVE_SENDFILE
- struct {
- ErlDrvPort port;
- ErlDrvPDL q_mtx;
- int out_fd;
- off_t offset;
- Uint64 nbytes;
- Uint64 written;
- } sendfile;
-#endif /* HAVE_SENDFILE */
- struct {
- Sint64 offset;
- Sint64 length;
- } fallocate;
- } c;
- char b[1];
-};
-
-#define EF_ALLOC(S) driver_alloc((S))
-#define EF_REALLOC(P, S) driver_realloc((P), (S))
-#define EF_SAFE_ALLOC(S) ef_safe_alloc((S))
-#define EF_SAFE_REALLOC(P, S) ef_safe_realloc((P), (S))
-#define EF_FREE(P) do { if((P)) driver_free((P)); } while(0)
-
-static void *ef_safe_alloc(Uint s)
-{
- void *p = EF_ALLOC(s);
- if (!p) erts_exit(ERTS_ERROR_EXIT, "efile drv: Can't allocate %lu bytes of memory\n", (unsigned long)s);
- return p;
-}
-
-/*********************************************************************
- * ErlIOVec manipulation functions.
- */
-
-/* char EV_CHAR_P(ErlIOVec *ev, int p, int q) */
-#define EV_CHAR_P(ev, p, q) \
- (((char *)(ev)->iov[q].iov_base) + (p))
-
-/* int EV_GET_CHAR(ErlIOVec *ev, char *p, int *pp, int *qp) */
-#define EV_GET_CHAR(ev, p, pp, qp) efile_ev_get_char(ev, p ,pp, qp)
-static int
-efile_ev_get_char(ErlIOVec *ev, char *p, size_t *pp, size_t *qp) {
- if (*pp + 1 <= ev->iov[*qp].iov_len) {
- *p = *EV_CHAR_P(ev, *pp, *qp);
- if (*pp + 1 < ev->iov[*qp].iov_len)
- *pp += 1;
- else {
- *qp += 1;
- *pp = 0;
- }
- return !0;
- }
- return 0;
-}
-
-/* Uint32 EV_UINT32(ErlIOVec *ev, int p, int q)*/
-#define EV_UINT32(ev, p, q) \
- ((Uint32) ((unsigned char *)(ev)->iov[q].iov_base)[p])
-
-/* int EV_GET_UINT32(ErlIOVec *ev, Uint32 *p, int *pp, int *qp) */
-#define EV_GET_UINT32(ev, p, pp, qp) efile_ev_get_uint32(ev, p, pp, qp)
-static int
-efile_ev_get_uint32(ErlIOVec *ev, Uint32 *p, size_t *pp, size_t *qp) {
- if (*pp + 4 <= ev->iov[*qp].iov_len) {
- *p = (EV_UINT32(ev, *pp, *qp) << 24)
- | (EV_UINT32(ev, *pp + 1, *qp) << 16)
- | (EV_UINT32(ev, *pp + 2, *qp) << 8)
- | (EV_UINT32(ev, *pp + 3, *qp));
- if (*pp + 4 < ev->iov[*qp].iov_len)
- *pp += 4;
- else {
- *qp += 1;
- *pp = 0;
- }
- return !0;
- }
- return 0;
-}
-
-/* Uint64 EV_UINT64(ErlIOVec *ev, int p, int q)*/
-#define EV_UINT64(ev, p, q) \
- ((Uint64) ((unsigned char *)(ev)->iov[q].iov_base)[p])
-
-/* int EV_GET_UINT64(ErlIOVec *ev, Uint64 *p, int *pp, int *qp) */
-#define EV_GET_UINT64(ev, p, pp, qp) efile_ev_get_uint64(ev, p, pp, qp)
-static int
-efile_ev_get_uint64(ErlIOVec *ev, Uint64 *p, size_t *pp, size_t *qp) {
- if (*pp + 8 <= ev->iov[*qp].iov_len) {
- *p = (EV_UINT64(ev, *pp, *qp) << 56)
- | (EV_UINT64(ev, *pp + 1, *qp) << 48)
- | (EV_UINT64(ev, *pp + 2, *qp) << 40)
- | (EV_UINT64(ev, *pp + 3, *qp) << 32)
- | (EV_UINT64(ev, *pp + 4, *qp) << 24)
- | (EV_UINT64(ev, *pp + 5, *qp) << 16)
- | (EV_UINT64(ev, *pp + 6, *qp) << 8)
- | (EV_UINT64(ev, *pp + 7, *qp));
- if (*pp + 8 < ev->iov[*qp].iov_len)
- *pp += 8;
- else {
- *qp += 1;
- *pp = 0;
- }
- return !0;
- }
- return 0;
-}
-
-/* int EV_GET_SINT64(ErlIOVec *ev, Uint64 *p, int *pp, int *qp) */
-#define EV_GET_SINT64(ev, p, pp, qp) efile_ev_get_sint64(ev, p, pp, qp)
-static int
-efile_ev_get_sint64(ErlIOVec *ev, Sint64 *p, size_t *pp, size_t *qp) {
- Uint64 *tmp = (Uint64*)p;
- return EV_GET_UINT64(ev, tmp, pp, qp);
-}
-
-#if 0
-
-static void ev_clear(ErlIOVec *ev) {
- ASSERT(ev);
- ev->size = 0;
- ev->vsize = 0;
- ev->iov = NULL;
- ev->binv = NULL;
-}
-
-/* Assumes that ->iov and ->binv were allocated with sys_alloc().
- */
-static void ev_free(ErlIOVec *ev) {
- if (! ev) {
- return;
- }
- if (ev->vsize > 0) {
- int i;
- ASSERT(ev->iov);
- ASSERT(ev->binv);
- for (i = 0; i < ev->vsize; i++) {
- if (ev->binv[i]) {
- driver_free_binary(ev->binv[i]);
- }
- }
- EF_FREE(ev->iov);
- EF_FREE(ev->binv);
- }
-}
-
-/* Copy the contents from source to dest.
- * Data in binaries is not copied, just the pointers;
- * and refc is incremented.
- */
-static ErlIOVec *ev_copy(ErlIOVec *dest, ErlIOVec *source) {
- int *ip;
- ASSERT(dest);
- ASSERT(source);
- if (source->vsize == 0) {
- /* Empty source */
- ev_clear(dest);
- return dest;
- }
- /* Allocate ->iov and ->binv */
- dest->iov = EF_ALLOC(sizeof(*dest->iov) * source->vsize);
- if (! dest->iov) {
- return NULL;
- }
- dest->binv = EF_ALLOC(sizeof(*dest->binv) * source->vsize);
- if (! dest->binv) {
- EF_FREE(dest->iov);
- return NULL;
- }
- dest->size = source->size;
- /* Copy one vector element at the time.
- * Use *ip as an alias for dest->vsize to improve readabiliy.
- * Keep dest consistent in every iteration by using
- * dest->vsize==*ip as loop variable.
- */
- for (ip = &dest->vsize, *ip = 0; *ip < source->vsize; (*ip)++) {
- if (source->iov[*ip].iov_len == 0) {
- /* Empty vector element */
- dest->iov[*ip].iov_len = 0;
- dest->iov[*ip].iov_base = NULL;
- dest->binv[*ip] = NULL;
- } else {
- /* Non empty vector element */
- if (source->binv[*ip]) {
- /* Contents in binary - copy pointers and increment refc */
- dest->iov[*ip] = source->iov[*ip];
- dest->binv[*ip] = source->binv[*ip];
- driver_binary_inc_refc(source->binv[*ip]);
- } else {
- /* Contents not in binary - allocate new binary and copy data */
- if (! (dest->binv[*ip] =
- driver_alloc_binary(source->iov[*ip].iov_len))) {
- goto failed;
- }
- sys_memcpy(dest->binv[*ip]->orig_bytes,
- source->iov[*ip].iov_base,
- source->iov[*ip].iov_len);
- dest->iov[*ip].iov_base = dest->binv[*ip]->orig_bytes;
- dest->iov[*ip].iov_len = source->iov[*ip].iov_len;
- }
- }
- }
- return dest;
- failed:
- ev_free(dest);
- return NULL;
-}
-
-#endif
-
-
-
-/*********************************************************************
- * Command queue functions
- */
-
-static void cq_enq(file_descriptor *desc, struct t_data *d) {
- ASSERT(d);
- if (desc->cq_head) {
- ASSERT(desc->cq_tail);
- ASSERT(!desc->cq_tail->next);
- desc->cq_tail = desc->cq_tail->next = d;
- } else {
- ASSERT(desc->cq_tail == NULL);
- desc->cq_head = desc->cq_tail = d;
- }
- d->next = NULL;
-}
-
-static struct t_data *cq_deq(file_descriptor *desc) {
- struct t_data *d = desc->cq_head;
- ASSERT(d || (!d && !desc->cq_tail));
- if (d) {
- ASSERT(!d->next || (d->next && desc->cq_tail != d));
- if ((desc->cq_head = d->next) == NULL) {
- ASSERT(desc->cq_tail == d);
- desc->cq_tail = NULL;
- }
- }
- return d;
-}
-
-
-/*********************************************************************
- * Driver entry point -> init
- */
-static int
-file_init(void)
-{
- char buf[21]; /* enough to hold any 64-bit integer */
- size_t bufsz = sizeof(buf);
- thread_short_circuit = (erl_drv_getenv("ERL_EFILE_THREAD_SHORT_CIRCUIT",
- buf,
- &bufsz) == 0
- ? atoi(buf)
- : 0);
- driver_system_info(&sys_info, sizeof(ErlDrvSysInfo));
-
- /* run initiation of efile_driver if needed */
- efile_init();
-
-#ifdef USE_VM_PROBES
- erts_mtx_init(&dt_driver_mutex, "efile_drv dtrace mutex", NIL,
- ERTS_LOCK_FLAGS_PROPERTY_STATIC | ERTS_LOCK_FLAGS_CATEGORY_IO);
- pthread_key_create(&dt_driver_key, NULL);
-#endif /* USE_VM_PROBES */
-
- return 0;
-}
-
-
-/*********************************************************************
- * Driver entry point -> start
- */
-static ErlDrvData
-file_start(ErlDrvPort port, char* command)
-
-{
- file_descriptor* desc;
-
- if ((desc = (file_descriptor*) EF_ALLOC(sizeof(file_descriptor)))
- == NULL) {
- errno = ENOMEM;
- return ERL_DRV_ERROR_ERRNO;
- }
- desc->fd = FILE_FD_INVALID;
- desc->port = port;
- desc->key = driver_async_port_key(port);
- desc->flags = 0;
- desc->invoke = NULL;
- desc->d = NULL;
- desc->free = NULL;
- desc->cq_head = NULL;
- desc->cq_tail = NULL;
- desc->timer_state = timer_idle;
-#ifdef HAVE_SENDFILE
- desc->sendfile_state = not_sending;
-#endif
- desc->read_bufsize = 0;
- desc->read_binp = NULL;
- desc->read_offset = 0;
- desc->read_size = 0;
- desc->write_delay = 0L;
- desc->write_bufsize = 0;
- desc->write_error = 0;
- MUTEX_INIT(desc->q_mtx, port); /* Refc is one, referenced by emulator now */
- desc->write_buffered = 0;
-#ifdef USE_VM_PROBES
- dtrace_drvport_str(port, desc->port_str);
- get_dt_private(0); /* throw away return value */
-#endif /* USE_VM_PROBES */
- return (ErlDrvData) desc;
-}
-
-static void do_close(int flags, SWord fd) {
- if (flags & EFILE_COMPRESSED) {
- erts_gzclose((ErtsGzFile)(fd));
- } else {
- efile_closefile((int) fd);
- }
-}
-
-static void invoke_close(void *data)
-{
- struct t_data *d = (struct t_data *) data;
- DTRACE_INVOKE_SETUP(FILE_CLOSE);
- d->again = 0;
- do_close(d->flags, d->fd);
- DTRACE_INVOKE_RETURN(FILE_CLOSE);
-}
-
-static void free_data(void *data)
-{
- struct t_data *d = (struct t_data *) data;
-
- switch (d->command) {
- case FILE_OPEN:
- if (d->is_fd_unused && d->fd != FILE_FD_INVALID) {
- /* This is OK to do in scheduler thread because there can be no async op
- ongoing for this fd here, as we exited during async open.
- Ideally, this close should happen in an async thread too, but that would
- require a substantial rewrite, as we are here because of a dead port and
- cannot schedule async jobs for that port any more... */
- do_close(d->flags, d->fd);
- }
- break;
- case FILE_CLOSE_ON_PORT_EXIT:
- EF_FREE(d->data_to_free);
- break;
- }
-
- EF_FREE(data);
-}
-
-
-/*
- * Sends back an error reply to Erlang.
- */
-
-static void reply_posix_error(file_descriptor *desc, int posix_errno) {
- char response[256]; /* Response buffer. */
- char* s;
- char* t;
-
- /*
- * Contents of buffer sent back:
- *
- * +-----------------------------------------+
- * | FILE_RESP_ERROR | Posix error id string |
- * +-----------------------------------------+
- */
-
- TRACE_C('E');
-
- response[0] = FILE_RESP_ERROR;
- for (s = erl_errno_id(posix_errno), t = response+1; *s; s++, t++)
- *t = tolower(*s);
- driver_output2(desc->port, response, t-response, NULL, 0);
-}
-
-static void reply_Uint_posix_error(file_descriptor *desc, Uint num,
- int posix_errno) {
- char response[256]; /* Response buffer. */
- char* s;
- char* t;
-
- /*
- * Contents of buffer sent back:
- *
- * +----------------------------------------------------------------------+
- * | FILE_RESP_NUMERR | 64-bit number (big-endian) | Posix error id string |
- * +----------------------------------------------------------------------+
- */
-
- TRACE_C('N');
-
- response[0] = FILE_RESP_NUMERR;
-#if SIZEOF_VOID_P == 4
- put_int32(0, response+1);
-#else
- put_int32(num>>32, response+1);
-#endif
- put_int32((Uint32)num, response+1+4);
- for (s = erl_errno_id(posix_errno), t = response+1+4+4; *s; s++, t++)
- *t = tolower(*s);
- driver_output2(desc->port, response, t-response, NULL, 0);
-}
-
-#ifdef HAVE_SENDFILE
-static void reply_string_error(file_descriptor *desc, char* str) {
- char response[256]; /* Response buffer. */
- char* s;
- char* t;
-
- response[0] = FILE_RESP_ERROR;
- for (s = str, t = response+1; *s; s++, t++)
- *t = tolower(*s);
- driver_output2(desc->port, response, t-response, NULL, 0);
-}
-#endif
-
-static int reply_error(file_descriptor *desc,
- Efile_error *errInfo) /* The error codes. */
-{
- reply_posix_error(desc, errInfo->posix_errno);
- return 0;
-}
-
-static int reply_Uint_error(file_descriptor *desc, Uint num,
- Efile_error *errInfo) /* The error codes. */
-{
- reply_Uint_posix_error(desc, num, errInfo->posix_errno);
- return 0;
-}
-
-static int reply_ok(file_descriptor *desc) {
- char c = FILE_RESP_OK;
-
- driver_output2(desc->port, &c, 1, NULL, 0);
- return 0;
-}
-
-static int reply(file_descriptor *desc, int ok, Efile_error *errInfo) {
- if (!ok) {
- reply_error(desc, errInfo);
- } else {
- TRACE_C('K');
- reply_ok(desc);
- }
- return 0;
-}
-
-static int reply_Uint(file_descriptor *desc, Uint result) {
- char tmp[1+4+4];
-
- /*
- * Contents of buffer sent back:
- *
- * +-----------------------------------------------+
- * | FILE_RESP_NUMBER | 64-bit number (big-endian) |
- * +-----------------------------------------------+
- */
-
- TRACE_C('R');
-
- tmp[0] = FILE_RESP_NUMBER;
-#if SIZEOF_VOID_P == 4
- put_int32(0, tmp+1);
-#else
- put_int32(result>>32, tmp+1);
-#endif
- put_int32((Uint32)result, tmp+1+4);
- driver_output2(desc->port, tmp, sizeof(tmp), NULL, 0);
- return 0;
-}
-
-static int reply_Sint64(file_descriptor *desc, Sint64 result) {
- char tmp[1+4+4];
-
- /*
- * Contents of buffer sent back:
- *
- * +-----------------------------------------------+
- * | FILE_RESP_NUMBER | 64-bit number (big-endian) |
- * +-----------------------------------------------+
- */
-
- TRACE_C('R');
-
- tmp[0] = FILE_RESP_NUMBER;
- put_int64(result, tmp+1);
- driver_output2(desc->port, tmp, sizeof(tmp), NULL, 0);
- return 0;
-}
-
-#if 0
-static void reply_again(file_descriptor *desc) {
- char tmp[1];
- tmp[0] = FILE_RESP_AGAIN;
- driver_output2(desc->port, tmp, sizeof(tmp), NULL, 0);
-}
-#endif
-
-static void reply_ev(file_descriptor *desc, char response, ErlIOVec *ev) {
- char tmp[1];
- /* Data arriving at the Erlang process:
- * [Response, Binary0, Binary1, .... | BinaryN-1]
- */
- tmp[0] = response;
- driver_outputv(desc->port, tmp, sizeof(tmp), ev, 0);
-}
-
-static void reply_data(file_descriptor *desc,
- ErlDrvBinary *binp, size_t offset, size_t len) {
- char header[1+4+4];
- /* Data arriving at the Erlang process:
- * [?FILE_RESP_DATA, 64-bit length (big-endian) | Data]
- */
- header[0] = FILE_RESP_DATA;
-#if SIZEOF_SIZE_T == 4
- put_int32(0, header+1);
-#else
- put_int32(len>>32, header+1);
-#endif
- put_int32((Uint32)len, header+1+4);
- driver_output_binary(desc->port, header, sizeof(header),
- binp, offset, len);
-}
-
-static void reply_buf(file_descriptor *desc, char *buf, size_t len) {
- char header[1+4+4];
- /* Data arriving at the Erlang process:
- * [?FILE_RESP_DATA, 64-bit length (big-endian) | Data]
- */
- header[0] = FILE_RESP_DATA;
-#if SIZEOF_SIZE_T == 4
- put_int32(0, header+1);
-#else
- put_int32(len>>32, header+1);
-#endif
- put_int32((Uint32)len, header+1+4);
- driver_output2(desc->port, header, sizeof(header), buf, len);
-}
-
-static int reply_eof(file_descriptor *desc) {
- char c = FILE_RESP_EOF;
-
- driver_output2(desc->port, &c, 1, NULL, 0);
- return 0;
-}
-
-static void invoke_name(void *data, int (*f)(Efile_error *, char *))
-{
- struct t_data *d = (struct t_data *) data;
- char *name = (char *) d->b;
-
- d->again = 0;
- d->result_ok = (*f)(&d->errInfo, name);
-}
-
-static void invoke_mkdir(void *data)
-{
- DTRACE_INVOKE_SETUP_BY_NAME(FILE_MKDIR);
- invoke_name(data, efile_mkdir);
- DTRACE_INVOKE_RETURN(FILE_MKDIR);
-}
-
-static void invoke_rmdir(void *data)
-{
- DTRACE_INVOKE_SETUP_BY_NAME(FILE_RMDIR);
- invoke_name(data, efile_rmdir);
- DTRACE_INVOKE_RETURN(FILE_RMDIR);
-}
-
-static void invoke_delete_file(void *data)
-{
- DTRACE_INVOKE_SETUP_BY_NAME(FILE_DELETE);
- invoke_name(data, efile_delete_file);
- DTRACE_INVOKE_RETURN(FILE_DELETE);
-}
-
-static void invoke_chdir(void *data)
-{
- DTRACE_INVOKE_SETUP_BY_NAME(FILE_CHDIR);
- invoke_name(data, efile_chdir);
- DTRACE_INVOKE_RETURN(FILE_CHDIR);
-}
-
-static void invoke_fdatasync(void *data)
-{
- struct t_data *d = (struct t_data *) data;
- int fd = (int) d->fd;
- DTRACE_INVOKE_SETUP(FILE_FDATASYNC);
-
- d->again = 0;
- d->result_ok = efile_fdatasync(&d->errInfo, fd);
- DTRACE_INVOKE_RETURN(FILE_FDATASYNC);
-}
-
-static void invoke_fsync(void *data)
-{
- struct t_data *d = (struct t_data *) data;
- int fd = (int) d->fd;
- DTRACE_INVOKE_SETUP(FILE_FSYNC);
-
- d->again = 0;
- d->result_ok = efile_fsync(&d->errInfo, fd);
- DTRACE_INVOKE_RETURN(FILE_FSYNC);
-}
-
-static void invoke_truncate(void *data)
-{
- struct t_data *d = (struct t_data *) data;
- int fd = (int) d->fd;
- DTRACE_INVOKE_SETUP(FILE_TRUNCATE);
-
- d->again = 0;
- d->result_ok = efile_truncate_file(&d->errInfo, &fd, d->flags);
- DTRACE_INVOKE_RETURN(FILE_TRUNCATE);
-}
-
-static void invoke_read(void *data)
-{
- struct t_data *d = (struct t_data *) data;
- int status, segment;
- size_t size, read_size;
- DTRACE_INVOKE_SETUP(FILE_READ);
-
- segment = d->again && d->c.read.bin_size >= 2*FILE_SEGMENT_READ;
- if (segment) {
- size = FILE_SEGMENT_READ;
- } else {
- size = d->c.read.bin_size;
- }
- read_size = size;
- if (d->flags & EFILE_COMPRESSED) {
- read_size = erts_gzread((ErtsGzFile)d->fd,
- d->c.read.binp->orig_bytes + d->c.read.bin_offset,
- size);
- status = (read_size != (size_t) -1);
- if (!status) {
- d->errInfo.posix_errno = EIO;
- }
- } else {
- status = efile_read(&d->errInfo, d->flags, (int) d->fd,
- d->c.read.binp->orig_bytes + d->c.read.bin_offset,
- size,
- &read_size);
- }
- if ( (d->result_ok = status)) {
- ASSERT(read_size <= size);
- d->c.read.bin_offset += read_size;
- if (read_size < size || !segment) {
- d->c.read.bin_size = 0;
- d->again = 0;
- } else {
- d->c.read.bin_size -= read_size;
- }
- } else {
- d->again = 0;
- }
- DTRACE_INVOKE_RETURN(FILE_READ);
-}
-
-static void free_read(void *data)
-{
- struct t_data *d = (struct t_data *) data;
-
- driver_free_binary(d->c.read.binp);
- EF_FREE(d);
-}
-
-static void invoke_read_line(void *data)
-{
- struct t_data *d = (struct t_data *) data;
- int status;
- size_t read_size = 0;
- int local_loop = (d->again == 0);
- DTRACE_INVOKE_SETUP(FILE_READ_LINE);
-
- do {
- size_t size = (d->c.read_line.binp)->orig_size -
- d->c.read_line.read_offset - d->c.read_line.read_size;
- if (size == 0) {
- /* Need more place */
- ErlDrvSizeT need = (d->c.read_line.read_size >= DEFAULT_LINEBUF_SIZE) ?
- d->c.read_line.read_size + DEFAULT_LINEBUF_SIZE : DEFAULT_LINEBUF_SIZE;
- ErlDrvBinary *newbin;
-#if !ALWAYS_READ_LINE_AHEAD
- /* Use read_ahead size if need does not exceed it */
- if (need < (d->c.read_line.binp)->orig_size &&
- d->c.read_line.read_ahead)
- need = (d->c.read_line.binp)->orig_size;
-#endif
- newbin = driver_alloc_binary(need);
- if (newbin == NULL) {
- d->result_ok = 0;
- d->errInfo.posix_errno = ENOMEM;
- d->again = 0;
- break;
- }
- memcpy(newbin->orig_bytes, (d->c.read_line.binp)->orig_bytes + d->c.read_line.read_offset,
- d->c.read_line.read_size);
- driver_free_binary(d->c.read_line.binp);
- d->c.read_line.binp = newbin;
- d->c.read_line.read_offset = 0;
- size = need - d->c.read_line.read_size;
- }
- if (d->flags & EFILE_COMPRESSED) {
- read_size = erts_gzread((ErtsGzFile)d->fd,
- d->c.read_line.binp->orig_bytes +
- d->c.read_line.read_offset + d->c.read_line.read_size,
- size);
- status = (read_size != (size_t) -1);
- if (!status) {
- d->errInfo.posix_errno = EIO;
- }
- } else {
- status = efile_read(&d->errInfo, d->flags, (int) d->fd,
- d->c.read_line.binp->orig_bytes +
- d->c.read_line.read_offset + d->c.read_line.read_size,
- size,
- &read_size);
- }
- if ( (d->result_ok = status)) {
- void *nl_ptr = memchr((d->c.read_line.binp)->orig_bytes +
- d->c.read_line.read_offset + d->c.read_line.read_size,'\n',read_size);
- ASSERT(read_size <= size);
- d->c.read_line.read_size += read_size;
- if (nl_ptr != NULL) {
- /* If found, we're done */
- d->c.read_line.nl_pos = ((char *) nl_ptr) -
- ((char *) ((d->c.read_line.binp)->orig_bytes)) + 1;
- if (d->c.read_line.nl_pos > 1 &&
- *(((char *) nl_ptr) - 1) == '\r') {
- --d->c.read_line.nl_pos;
- *(((char *) nl_ptr) - 1) = '\n';
- d->c.read_line.nl_skip = 1;
- } else {
- d->c.read_line.nl_skip = 0;
- }
- d->again = 0;
-#if !ALWAYS_READ_LINE_AHEAD
- if (!(d->c.read_line.read_ahead)) {
- /* Ouch! Undo buffering... */
- size_t too_much = d->c.read_line.read_size - d->c.read_line.nl_skip -
- (d->c.read_line.nl_pos - d->c.read_line.read_offset);
- d->c.read_line.read_size -= too_much;
- ASSERT(d->c.read_line.read_size >= 0);
- if (d->flags & EFILE_COMPRESSED) {
- Sint64 location = erts_gzseek((ErtsGzFile)d->fd,
- -((Sint64) too_much), EFILE_SEEK_CUR);
- if (location == -1) {
- d->result_ok = 0;
- d->errInfo.posix_errno = errno;
- }
- } else {
- Sint64 location;
- d->result_ok = efile_seek(&d->errInfo, (int) d->fd,
- -((Sint64) too_much), EFILE_SEEK_CUR,
- &location);
- }
- }
-#endif
- break;
- } else if (read_size == 0) {
- d->c.read_line.nl_pos =
- d->c.read_line.read_offset + d->c.read_line.read_size;
- d->c.read_line.nl_skip = 0;
- d->again = 0;
- break;
- }
- } else {
- d->again = 0;
- break;
- }
- } while (local_loop);
- DTRACE_INVOKE_RETURN(FILE_READ_LINE);
-}
-
-static void free_read_line(void *data)
-{
- struct t_data *d = (struct t_data *) data;
-
- driver_free_binary(d->c.read_line.binp);
- EF_FREE(d);
-}
-
-static void invoke_read_file(void *data)
-{
- struct t_data *d = (struct t_data *) data;
- size_t read_size;
- int chop;
- DTRACE_INVOKE_SETUP(FILE_READ_FILE);
-
- if (! d->c.read_file.binp) { /* First invocation only */
- int fd;
- Sint64 size;
-
- if (! (d->result_ok =
- efile_openfile(&d->errInfo, d->b,
- EFILE_MODE_READ, &fd, &size))) {
- goto done;
- }
- d->fd = fd;
- d->c.read_file.size = (int) size;
- if (size < 0 || size != d->c.read_file.size ||
- ! (d->c.read_file.binp =
- driver_alloc_binary(d->c.read_file.size))) {
- d->result_ok = 0;
- d->errInfo.posix_errno = ENOMEM;
- goto close;
- }
- d->c.read_file.offset = 0;
- }
- /* Invariant: d->c.read_file.size >= d->c.read_file.offset */
-
- read_size = (size_t) (d->c.read_file.size - d->c.read_file.offset);
- if (! read_size) goto close;
- chop = d->again && read_size >= FILE_SEGMENT_READ*2;
- if (chop) read_size = FILE_SEGMENT_READ;
- d->result_ok =
- efile_read(&d->errInfo,
- EFILE_MODE_READ,
- (int) d->fd,
- d->c.read_file.binp->orig_bytes + d->c.read_file.offset,
- read_size,
- &read_size);
- if (d->result_ok) {
- d->c.read_file.offset += read_size;
- if (chop) goto chop_done; /* again */
- }
- close:
- efile_closefile((int) d->fd);
- done:
- d->again = 0;
- chop_done:
- DTRACE_INVOKE_RETURN(FILE_READ_FILE);
-}
-
-static void free_read_file(void *data)
-{
- struct t_data *d = (struct t_data *) data;
-
- if (d->c.read_file.binp) driver_free_binary(d->c.read_file.binp);
- EF_FREE(d);
-}
-
-
-
-static void invoke_preadv(void *data)
-{
- struct t_data *d = (struct t_data *) data;
- struct t_preadv *c = &d->c.preadv;
- ErlIOVec *ev = &c->eiov;
- size_t bytes_read_so_far = 0;
- unsigned char *p = (unsigned char *)ev->iov[0].iov_base + 4+4+8*c->cnt;
- DTRACE_INVOKE_SETUP(FILE_PREADV);
-
- while (c->cnt < c->n) {
- size_t read_size = ev->iov[1 + c->cnt].iov_len - c->size;
- size_t bytes_read = 0;
- int chop = d->again
- && bytes_read_so_far + read_size >= 2*FILE_SEGMENT_READ;
- if (chop) {
- ASSERT(bytes_read_so_far < FILE_SEGMENT_READ);
- read_size = FILE_SEGMENT_READ + FILE_SEGMENT_READ/2
- - bytes_read_so_far;
- }
- if ( (d->result_ok
- = efile_pread(&d->errInfo,
- (int) d->fd,
- c->offsets[c->cnt] + c->size,
- ((char *)ev->iov[1 + c->cnt].iov_base) + c->size,
- read_size,
- &bytes_read))) {
- bytes_read_so_far += bytes_read;
- if (chop && bytes_read == read_size) {
- c->size += bytes_read;
- goto done;
- }
- ASSERT(bytes_read <= read_size);
- ev->iov[1 + c->cnt].iov_len = bytes_read + c->size;
- ev->size += bytes_read + c->size;
- put_int64(bytes_read + c->size, p); p += 8;
- c->size = 0;
- c->cnt++;
- if (d->again
- && bytes_read_so_far >= FILE_SEGMENT_READ
- && c->cnt < c->n) {
- goto done;
- }
- } else {
- /* In case of a read error, ev->size will not be correct,
- * which does not matter since no read data is returned
- * to Erlang.
- */
- break;
- }
- }
- d->again = 0;
- done:
- DTRACE_INVOKE_RETURN(FILE_PREADV);
-}
-
-static void free_preadv(void *data) {
- struct t_data *d = data;
- int i;
- ErlIOVec *ev = &d->c.preadv.eiov;
-
- for(i = 0; i < ev->vsize; i++) {
- driver_free_binary(ev->binv[i]);
- }
- EF_FREE(d);
-}
-
-static void invoke_ipread(void *data)
-{
- struct t_data *d = data;
- struct t_preadv *c = &d->c.preadv;
- ErlIOVec *ev = &c->eiov;
- size_t bytes_read = 0;
- char buf[2*sizeof(Uint32)];
- Uint32 offset, size;
- DTRACE_INVOKE_SETUP(FILE_IPREAD);
-
- /* Read indirection header */
- if (! efile_pread(&d->errInfo, (int) d->fd, c->offsets[0],
- buf, sizeof(buf), &bytes_read)) {
- goto error;
- }
- if (bytes_read != sizeof(buf)) goto done; /* eof */
- size = get_int32(buf);
- offset = get_int32(buf+4);
- if (size > c->size) goto done; /* eof */
- c->n = 1;
- c->cnt = 0;
- c->size = 0;
- c->offsets[0] = offset;
- if (! (ev->binv[0] = driver_alloc_binary(3*8))) {
- d->errInfo.posix_errno = ENOMEM;
- goto error;
- }
- ev->vsize = 1;
- ev->iov[0].iov_len = 3*8;
- ev->iov[0].iov_base = ev->binv[0]->orig_bytes;
- ev->size = ev->iov[0].iov_len;
- put_int64(offset, ev->iov[0].iov_base);
- put_int64(size, ((char *)ev->iov[0].iov_base) + 2*8);
- if (size == 0) {
- put_int64(size, ((char *)ev->iov[0].iov_base) + 8);
- goto done;
- }
- if (! (ev->binv[1] = driver_alloc_binary(size))) {
- d->errInfo.posix_errno = ENOMEM;
- goto error;
- }
- ev->vsize = 2;
- ev->iov[1].iov_len = size;
- ev->iov[1].iov_base = ev->binv[1]->orig_bytes;
- /* Read data block */
- d->invoke = invoke_preadv;
- invoke_preadv(data);
- DTRACE_INVOKE_RETURN(FILE_IPREAD);
- return;
- error:
- d->result_ok = 0;
- d->again = 0;
- DTRACE_INVOKE_RETURN(FILE_IPREAD);
- return;
- done:
- d->result_ok = !0;
- d->again = 0;
- DTRACE_INVOKE_RETURN(FILE_IPREAD);
-}
-
-/* invoke_writev and invoke_pwritev are the only thread functions that
- * access non-thread data i.e the port queue and a mutex in the port
- * structure that is used to lock the port queue.
- *
- * The port will normally not be terminated until the port queue is
- * empty, but if the port is killed, i.e., exit(Port, kill) is called,
- * it will terminate regardless of the port queue state. When the
- * port is invalid driver_peekq() returns NULL and set the size to -1,
- * and driver_sizeq() returns -1.
- */
-
-static void invoke_writev(void *data) {
- struct t_data *d = (struct t_data *) data;
- SysIOVec *iov0;
- SysIOVec *iov;
- int iovlen;
- int iovcnt;
- size_t size;
- size_t p;
- int segment;
- DTRACE_INVOKE_SETUP(FILE_WRITE);
-
- segment = d->again && d->c.writev.size >= 2*FILE_SEGMENT_WRITE;
- if (segment) {
- size = FILE_SEGMENT_WRITE;
- } else {
- size = d->c.writev.size;
- }
-
- /* Copy the io vector to avoid locking the port que while writing,
- * also, both we and efile_writev might/will change the SysIOVec
- * when segmenting or due to partial write and we do not want to
- * tamper with the actual queue that we get from driver_peekq
- */
- MUTEX_LOCK(d->c.writev.q_mtx); /* Lock before accessing the port queue */
- iov0 = driver_peekq(d->c.writev.port, &iovlen);
-
- /* Calculate iovcnt */
- for (p = 0, iovcnt = 0;
- p < size && iovcnt < iovlen;
- p += iov0[iovcnt++].iov_len)
- ;
- iov = EF_SAFE_ALLOC(sizeof(SysIOVec)*iovcnt);
- memcpy(iov,iov0,iovcnt*sizeof(SysIOVec));
- MUTEX_UNLOCK(d->c.writev.q_mtx);
- /* Let go of lock until we deque from original vector */
-
- if (iovlen > 0) {
- ASSERT(iov[iovcnt-1].iov_len > p - size);
- iov[iovcnt-1].iov_len -= p - size;
- if (d->flags & EFILE_COMPRESSED) {
- int i, status = 1;
- for (i = 0; i < iovcnt; i++) {
- if (iov[i].iov_base && iov[i].iov_len > 0) {
- /* Just in case, I do not know what gzwrite does
- * with errno.
- */
- errno = EINVAL;
- status = erts_gzwrite((ErtsGzFile)d->fd,
- iov[i].iov_base,
- iov[i].iov_len) == iov[i].iov_len;
- if (! status) {
- d->errInfo.posix_errno =
- d->errInfo.os_errno = errno; /* XXX Correct? */
- break;
- }
- }
- }
- d->result_ok = status;
- } else {
- d->result_ok = efile_writev(&d->errInfo,
- d->flags, (int) d->fd,
- iov, iovcnt);
- }
- } else if (iovlen == 0) {
- d->result_ok = 1;
- }
- else { /* Port has terminated */
- d->result_ok = 0;
- d->errInfo.posix_errno = d->errInfo.os_errno = EINVAL;
- }
- EF_FREE(iov);
-
- if (! d->result_ok) {
- d->again = 0;
- MUTEX_LOCK(d->c.writev.q_mtx);
- driver_deq(d->c.writev.port, d->c.writev.size);
- MUTEX_UNLOCK(d->c.writev.q_mtx);
- } else {
- if (! segment) {
- d->again = 0;
- }
- d->c.writev.size -= size;
- TRACE_F(("w%lu", (unsigned long)size));
- MUTEX_LOCK(d->c.writev.q_mtx);
- driver_deq(d->c.writev.port, size);
- MUTEX_UNLOCK(d->c.writev.q_mtx);
- }
-
-
- DTRACE_INVOKE_RETURN(FILE_WRITE);
-}
-
-static void invoke_pwd(void *data)
-{
- struct t_data *d = (struct t_data *) data;
- DTRACE_INVOKE_SETUP(FILE_PWD);
-
- d->again = 0;
- d->result_ok = efile_getdcwd(&d->errInfo,d->drive, d->b+1,
- RESBUFSIZE-1);
- DTRACE_INVOKE_RETURN(FILE_PWD);
-}
-
-static void invoke_readlink(void *data)
-{
- struct t_data *d = (struct t_data *) data;
- char resbuf[RESBUFSIZE]; /* Result buffer. */
- DTRACE_INVOKE_SETUP(FILE_READLINK);
-
- d->again = 0;
- d->result_ok = efile_readlink(&d->errInfo, d->b, resbuf+1,
- RESBUFSIZE-1);
- if (d->result_ok != 0)
- FILENAME_COPY((char *) d->b + 1, resbuf+1);
- DTRACE_INVOKE_RETURN(FILE_READLINK);
-}
-
-static void invoke_altname(void *data)
-{
- struct t_data *d = (struct t_data *) data;
- char resbuf[RESBUFSIZE]; /* Result buffer. */
- DTRACE_INVOKE_SETUP(FILE_ALTNAME);
-
- d->again = 0;
- d->result_ok = efile_altname(&d->errInfo, d->b, resbuf+1,
- RESBUFSIZE-1);
- if (d->result_ok != 0)
- FILENAME_COPY((char *) d->b + 1, resbuf+1);
- DTRACE_INVOKE_RETURN(FILE_ALTNAME);
-}
-
-static void invoke_pwritev(void *data) {
- struct t_data* const d = (struct t_data *) data;
- struct t_pwritev * const c = &d->c.pwritev;
- SysIOVec *iov0;
- SysIOVec *iov;
- int iovlen;
- int iovcnt;
- size_t p;
- int segment;
- size_t size, write_size, written;
- DTRACE_INVOKE_SETUP(FILE_PWRITEV);
-
- segment = d->again && c->size >= 2*FILE_SEGMENT_WRITE;
- if (segment) {
- size = FILE_SEGMENT_WRITE;
- } else {
- size = c->size;
- }
- d->result_ok = !0;
- p = 0;
- /* Lock the queue just for a while, we don't want it locked during write */
- MUTEX_LOCK(c->q_mtx);
- iov0 = driver_peekq(c->port, &iovlen);
- iov = EF_SAFE_ALLOC(sizeof(SysIOVec)*iovlen);
- memcpy(iov,iov0,sizeof(SysIOVec)*iovlen);
- MUTEX_UNLOCK(c->q_mtx);
-
- if (iovlen < 0)
- goto error; /* Port terminated */
- for (iovcnt = 0, written = 0;
- c->cnt < c->n && iovcnt < iovlen && written < size;
- c->cnt++) {
- int chop;
- write_size = c->specs[c->cnt].size;
- if (iov[iovcnt].iov_len - p < write_size) {
- goto error;
- }
- chop = segment && written + write_size >= 2*FILE_SEGMENT_WRITE;
- if (chop) {
- ASSERT(written < FILE_SEGMENT_WRITE);
- write_size = FILE_SEGMENT_WRITE + FILE_SEGMENT_WRITE/2
- - written;
- }
- d->result_ok = efile_pwrite(&d->errInfo, (int) d->fd,
- (char *)(iov[iovcnt].iov_base) + p,
- write_size,
- c->specs[c->cnt].offset);
- if (! d->result_ok) {
- d->again = 0;
- goto deq_error;
- }
- written += write_size;
- c->size -= write_size;
- if (chop) {
- c->specs[c->cnt].offset += write_size;
- c->specs[c->cnt].size -= write_size;
- /* Schedule out (d->again != 0) */
- break;
- }
- /* Move forward in buffer */
- p += write_size;
- ASSERT(iov[iovcnt].iov_len >= p);
- if (iov[iovcnt].iov_len == p) {
- /* Move to next iov[], we trust that it is not a
- * zero length vector, and thereby depend on that
- * such are not queued.
- */
- iovcnt++; p = 0;
- }
- }
- if (! segment) {
- if (c->cnt != c->n) {
- /* Mismatch between number of
- * pos/size specs vs number of queued buffers .
- */
- error:
- d->errInfo.posix_errno = EINVAL;
- d->result_ok = 0;
- d->again = 0;
- deq_error:
- MUTEX_LOCK(c->q_mtx);
- driver_deq(c->port, c->size);
- MUTEX_UNLOCK(c->q_mtx);
-
- goto done;
- } else {
- ASSERT(written == size);
- d->again = 0;
- }
- } else {
- ASSERT(written >= FILE_SEGMENT_WRITE);
- }
-
- MUTEX_LOCK(c->q_mtx);
- driver_deq(c->port, written);
- MUTEX_UNLOCK(c->q_mtx);
- done:
- EF_FREE(iov); /* Free our copy of the vector, nothing to restore */
-
- DTRACE_INVOKE_RETURN(FILE_PWRITEV);
-}
-
-static void invoke_flstat(void *data)
-{
- struct t_data *d = (struct t_data *) data;
-
- DTRACE3(efile_drv_int_entry, d->sched_i1, d->sched_i2,
- d->command == FILE_LSTAT ? FILE_LSTAT : FILE_FSTAT);
- d->again = 0;
- d->result_ok = efile_fileinfo(&d->errInfo, &d->info,
- d->b, d->command == FILE_LSTAT);
- DTRACE3(efile_drv_int_entry, d->sched_i1, d->sched_i2,
- d->command == FILE_LSTAT ? FILE_LSTAT : FILE_FSTAT);
- gcc_optimizer_hack++;
-}
-
-static void invoke_link(void *data)
-{
- struct t_data *d = (struct t_data *) data;
- char *name = d->b;
- char *new_name;
- DTRACE_INVOKE_SETUP(FILE_LINK);
-
- d->again = 0;
- new_name = name+FILENAME_BYTELEN(name)+FILENAME_CHARSIZE;
- d->result_ok = efile_link(&d->errInfo, name, new_name);
- DTRACE_INVOKE_RETURN(FILE_LINK);
-}
-
-static void invoke_symlink(void *data)
-{
- struct t_data *d = (struct t_data *) data;
- char *name = d->b;
- char *new_name;
- DTRACE_INVOKE_SETUP(FILE_SYMLINK);
-
- d->again = 0;
- new_name = name+FILENAME_BYTELEN(name)+FILENAME_CHARSIZE;
- d->result_ok = efile_symlink(&d->errInfo, name, new_name);
- DTRACE_INVOKE_RETURN(FILE_SYMLINK);
-}
-
-static void invoke_rename(void *data)
-{
- struct t_data *d = (struct t_data *) data;
- char *name = d->b;
- char *new_name;
- DTRACE_INVOKE_SETUP(FILE_RENAME);
-
- d->again = 0;
- new_name = name+FILENAME_BYTELEN(name)+FILENAME_CHARSIZE;
- d->result_ok = efile_rename(&d->errInfo, name, new_name);
- DTRACE_INVOKE_RETURN(FILE_RENAME);
-}
-
-static void invoke_write_info(void *data)
-{
- struct t_data *d = (struct t_data *) data;
- DTRACE_INVOKE_SETUP(FILE_WRITE_INFO);
-
- d->again = 0;
- d->result_ok = efile_write_info(&d->errInfo, &d->info, d->b);
- DTRACE_INVOKE_RETURN(FILE_WRITE_INFO);
-}
-
-static void invoke_lseek(void *data)
-{
- struct t_data *d = (struct t_data *) data;
- int status;
- DTRACE_INVOKE_SETUP(FILE_LSEEK);
-
- d->again = 0;
- if (d->flags & EFILE_COMPRESSED) {
- int offset = (int) d->c.lseek.offset;
-
- if (offset != d->c.lseek.offset) {
- d->errInfo.posix_errno = EINVAL;
- status = 0;
- } else {
- d->c.lseek.location = erts_gzseek((ErtsGzFile)d->fd,
- offset, d->c.lseek.origin);
- if (d->c.lseek.location == -1) {
- d->errInfo.posix_errno = errno;
- status = 0;
- } else {
- status = 1;
- }
- }
- } else {
- status = efile_seek(&d->errInfo, (int) d->fd,
- d->c.lseek.offset, d->c.lseek.origin,
- &d->c.lseek.location);
- }
- d->result_ok = status;
- DTRACE_INVOKE_RETURN(FILE_LSEEK);
-}
-
-static void invoke_readdir(void *data)
-{
- struct t_data *d = (struct t_data *) data;
- char *p = NULL;
- size_t file_bs;
- size_t n = 0, total = 0;
- struct t_readdir_buf *b = NULL;
- int res = 0;
- DTRACE_INVOKE_SETUP(FILE_READDIR);
-
- d->again = 0;
- d->errInfo.posix_errno = 0;
-
- do {
- total = READDIR_BUFSIZE;
- n = 1;
- b = EF_SAFE_ALLOC(sizeof(struct t_readdir_buf));
- b->next = NULL;
-
- if (d->c.read_dir.last_buf) {
- d->c.read_dir.last_buf->next = b;
- } else {
- d->c.read_dir.first_buf = b;
- }
- d->c.read_dir.last_buf = b;
-
- p = &b->buf[0];
- p[0] = FILE_RESP_LFNAME;
- file_bs = READDIR_BUFSIZE - n;
-
- do {
- res = efile_readdir(&d->errInfo, d->b, &d->dir_handle, p + n + 2, &file_bs);
-
- if (res) {
- put_int16((Uint16)file_bs, p + n);
- n += 2 + file_bs;
- file_bs = READDIR_BUFSIZE - n;
- }
- } while( res && ((total - n - 2) >= MAXPATHLEN*FILENAME_CHARSIZE));
-
- b->n = n;
- } while(res);
-
- d->result_ok = (d->errInfo.posix_errno == 0);
- DTRACE_INVOKE_RETURN(FILE_READDIR);
-}
-
-static void invoke_open(void *data)
-{
- struct t_data *d = (struct t_data *) data;
- int status = 1; /* Status of open call. */
- DTRACE_INVOKE_SETUP(FILE_OPEN);
-
- d->again = 0;
- if ((d->flags & EFILE_COMPRESSED) == 0) {
- int fd;
- status = efile_openfile(&d->errInfo, d->b, d->flags, &fd, NULL);
- d->fd = fd;
- } else {
- char* mode = NULL;
-
- if (((d->flags & (EFILE_MODE_READ_WRITE)) == EFILE_MODE_READ_WRITE) ||
- (d->flags & EFILE_MODE_APPEND)) {
- status = 0;
- d->errInfo.posix_errno = EINVAL;
- } else {
- status = efile_may_openfile(&d->errInfo, d->b);
- if (status || (d->errInfo.posix_errno != EISDIR)) {
- mode = (d->flags & EFILE_MODE_READ) ? "rb" : "wb";
- d->fd = (SWord) erts_gzopen(d->b, mode);
- if ((ErtsGzFile)d->fd) {
- status = 1;
- } else {
- if (errno == 0) {
- errno = ENOMEM;
- }
- d->errInfo.posix_errno = errno;
- status = 0;
- }
- }
- }
- }
-
- d->result_ok = status;
- if (!status) {
- d->fd = FILE_FD_INVALID;
- }
- DTRACE_INVOKE_RETURN(FILE_OPEN);
-}
-
-static void invoke_fadvise(void *data)
-{
- struct t_data *d = (struct t_data *) data;
- int fd = (int) d->fd;
- off_t offset = (off_t) d->c.fadvise.offset;
- off_t length = (off_t) d->c.fadvise.length;
- int advise = (int) d->c.fadvise.advise;
- DTRACE_INVOKE_SETUP(FILE_FADVISE);
-
- d->again = 0;
- d->result_ok = efile_fadvise(&d->errInfo, fd, offset, length, advise);
- DTRACE_INVOKE_RETURN(FILE_FADVISE);
-}
-
-#ifdef HAVE_SENDFILE
-static void invoke_sendfile(void *data)
-{
- struct t_data *d = (struct t_data *)data;
- int fd = d->fd;
- int out_fd = (int)d->c.sendfile.out_fd;
- Uint64 nbytes = d->c.sendfile.nbytes;
- int result = 0;
- d->again = 0;
-
- result = efile_sendfile(&d->errInfo, fd, out_fd, &d->c.sendfile.offset, &nbytes, NULL);
-
- d->c.sendfile.written += nbytes;
-
- if (result == 1 || (result == 0 && USE_THRDS_FOR_SENDFILE(d))) {
- d->result_ok = 0;
- } else if (result == 0 && (d->errInfo.posix_errno == EAGAIN
- || d->errInfo.posix_errno == EINTR)) {
- if ((d->c.sendfile.nbytes - nbytes) != 0) {
- d->result_ok = 1;
- if (d->c.sendfile.nbytes != 0)
- d->c.sendfile.nbytes -= nbytes;
- } else if (nbytes == 0 && d->c.sendfile.nbytes == 0) {
- d->result_ok = 1;
- } else
- d->result_ok = 0;
- } else {
- d->result_ok = -1;
- }
-}
-
-static void free_sendfile(void *data) {
- struct t_data *d = (struct t_data *)data;
- if (USE_THRDS_FOR_SENDFILE(d)) {
- SET_NONBLOCKING(d->c.sendfile.out_fd);
- } else {
- MUTEX_LOCK(d->c.sendfile.q_mtx);
- driver_deq(d->c.sendfile.port,1);
- MUTEX_UNLOCK(d->c.sendfile.q_mtx);
- driver_select(d->c.sendfile.port, (ErlDrvEvent)(long)d->c.sendfile.out_fd,
- ERL_DRV_USE_NO_CALLBACK|ERL_DRV_WRITE, 0);
- }
- EF_FREE(data);
-}
-
-static void file_ready_output(ErlDrvData data, ErlDrvEvent event)
-{
- file_descriptor* fd = (file_descriptor*) data;
-
- switch (fd->d->command) {
- case FILE_SENDFILE:
- driver_select(fd->d->c.sendfile.port, event,
- (int)ERL_DRV_WRITE,(int) 0);
- invoke_sendfile((void *)fd->d);
- file_async_ready(data, (ErlDrvThreadData)fd->d);
- break;
- default:
- break;
- }
-}
-
-static void file_stop_select(ErlDrvEvent event, void* _)
-{
-
-}
-
-static int flush_sendfile(file_descriptor *desc,void *_) {
- if (desc->sendfile_state == sending) {
- desc->d->result_ok = -1;
- desc->d->errInfo.posix_errno = ECONNABORTED;
- file_async_ready((ErlDrvData)desc,(ErlDrvThreadData)desc->d);
- }
- return 1;
-}
-#endif /* HAVE_SENDFILE */
-
-
-static void invoke_fallocate(void *data)
-{
- struct t_data *d = (struct t_data *) data;
- int fd = (int) d->fd;
- Sint64 offset = d->c.fallocate.offset;
- Sint64 length = d->c.fallocate.length;
-
- d->again = 0;
- d->result_ok = efile_fallocate(&d->errInfo, fd, offset, length);
-}
-
-static void free_readdir(void *data)
-{
- struct t_data *d = (struct t_data *) data;
- struct t_readdir_buf *b1 = d->c.read_dir.first_buf;
-
- while (b1) {
- struct t_readdir_buf *b2 = b1;
- b1 = b1->next;
- EF_FREE(b2);
- }
- EF_FREE(d);
-}
-
-
-
-static void try_free_read_bin(file_descriptor *desc) {
- if ((desc->read_size == 0)
- && (desc->read_offset >= desc->read_binp->orig_size)) {
- ASSERT(desc->read_offset == desc->read_binp->orig_size);
- driver_free_binary(desc->read_binp);
- desc->read_binp = NULL;
- desc->read_offset = 0;
- desc->read_size = 0;
- }
-}
-
-
-
-static int try_again(file_descriptor *desc, struct t_data *d) {
- if (! d->again)
- return 0;
- if (desc->timer_state != timer_idle) {
- driver_cancel_timer(desc->port);
- }
- desc->timer_state = timer_again;
- desc->invoke = d->invoke;
- desc->d = d;
- desc->free = d->free;
- driver_set_timer(desc->port, 0L);
- return !0;
-}
-
-
-
-static void cq_execute(file_descriptor *desc) {
- struct t_data *d;
- register void *void_ptr; /* Soft cast variable */
- if (desc->timer_state == timer_again)
- return;
-#ifdef HAVE_SENDFILE
- if (desc->sendfile_state == sending)
- return;
-#endif
- if (! (d = cq_deq(desc)))
- return;
- TRACE_F(("x%i", (int) d->command));
- d->again = sys_info.async_threads == 0;
- DRIVER_ASYNC(d->level, desc, d->invoke, void_ptr=d, d->free);
-}
-
-static struct t_data *async_write(file_descriptor *desc, int *errp,
- int reply, Uint32 reply_size
-#ifdef USE_VM_PROBES
- ,Sint64 *dt_i1, Sint64 *dt_i2, Sint64 *dt_i3
-#endif
-) {
- struct t_data *d;
- if (! (d = EF_ALLOC(sizeof(struct t_data) - 1))) {
- if (errp) *errp = ENOMEM;
- return NULL;
- }
- TRACE_F(("w%lu", (unsigned long)desc->write_buffered));
- d->command = FILE_WRITE;
- d->fd = desc->fd;
- d->flags = desc->flags;
- d->c.writev.port = desc->port;
- d->c.writev.q_mtx = desc->q_mtx;
- d->c.writev.size = desc->write_buffered;
-#ifdef USE_VM_PROBES
- if (dt_i1 != NULL) {
- *dt_i1 = d->fd;
- *dt_i2 = d->flags;
- *dt_i3 = d->c.writev.size;
- }
-#endif
- d->reply = reply;
- d->c.writev.reply_size = reply_size;
- d->invoke = invoke_writev;
- d->free = free_data;
- d->level = 1;
- cq_enq(desc, d);
- desc->write_buffered = 0;
- return d;
-}
-
-static int flush_write(file_descriptor *desc, int *errp
-#ifdef USE_VM_PROBES
- , dt_private *dt_priv, char *dt_utag
-#endif
-) {
- int result = 0;
-#ifdef USE_VM_PROBES
- Sint64 dt_i1 = 0, dt_i2 = 0, dt_i3 = 0;
-#endif
- struct t_data *d = NULL;
-
- MUTEX_LOCK(desc->q_mtx);
- if (desc->write_buffered > 0) {
- if ((d = async_write(desc, errp, 0, 0
-#ifdef USE_VM_PROBES
- ,&dt_i1, &dt_i2, &dt_i3
-#endif
- )) == NULL) {
- result = -1;
- }
- }
- MUTEX_UNLOCK(desc->q_mtx);
-#ifdef USE_VM_PROBES
- if (d != NULL) {
- d->sched_i1 = dt_priv->thread_num;
- d->sched_i2 = dt_priv->tag;
- d->sched_utag[0] = '\0';
- if (dt_utag != NULL) {
- if (dt_utag[0] == '\0') {
- dt_utag = NULL;
- } else {
- strncpy(d->sched_utag, dt_utag, sizeof(d->sched_utag) - 1);
- d->sched_utag[sizeof(d->sched_utag) - 1] = '\0';
- }
- }
- DTRACE11(efile_drv_entry, dt_priv->thread_num, dt_priv->tag++,
- dt_utag, FILE_WRITE,
- NULL, NULL, dt_i1, dt_i2, dt_i3, 0, desc->port_str);
- }
-#endif /* USE_VM_PROBES */
- return result;
-}
-
-static int check_write_error(file_descriptor *desc, int *errp) {
- if (desc->write_error) {
- if (errp) *errp = desc->write_errInfo.posix_errno;
- desc->write_error = 0;
- return -1;
- }
- return 0;
-}
-
-static int flush_write_check_error(file_descriptor *desc, int *errp
-#ifdef USE_VM_PROBES
- , dt_private *dt_priv, char *dt_utag
-#endif
- ) {
- int r;
- if ( (r = flush_write(desc, errp
-#ifdef USE_VM_PROBES
- , dt_priv, dt_utag
-#endif
- )) != 0) {
- check_write_error(desc, NULL);
- return r;
- } else {
- return check_write_error(desc, errp);
- }
-}
-
-static struct t_data *async_lseek(file_descriptor *desc, int *errp, int reply,
- Sint64 offset, int origin
-#ifdef USE_VM_PROBES
- , Sint64 *dt_i1, Sint64 *dt_i2, Sint64 *dt_i3
-#endif
- ) {
- struct t_data *d;
- if (! (d = EF_ALLOC(sizeof(struct t_data)))) {
- *errp = ENOMEM;
- return NULL;
- }
- d->flags = desc->flags;
- d->fd = desc->fd;
- d->command = FILE_LSEEK;
- d->reply = reply;
- d->c.lseek.offset = offset;
- d->c.lseek.origin = origin;
-#ifdef USE_VM_PROBES
- if (dt_i1 != NULL) {
- *dt_i1 = d->fd;
- *dt_i2 = d->c.lseek.offset;
- *dt_i3 = d->c.lseek.origin;
- }
-#endif
- d->invoke = invoke_lseek;
- d->free = free_data;
- d->level = 1;
- cq_enq(desc, d);
- return d;
-}
-
-static void flush_read(file_descriptor *desc) {
- desc->read_offset = 0;
- desc->read_size = 0;
- if (desc->read_binp) {
- driver_free_binary(desc->read_binp);
- desc->read_binp = NULL;
- }
-}
-
-static int lseek_flush_read(file_descriptor *desc, int *errp
-#ifdef USE_VM_PROBES
- ,dt_private *dt_priv, char *dt_utag
-#endif
- ) {
- int r = 0;
- size_t read_size = desc->read_size;
-#ifdef USE_VM_PROBES
- Sint64 dt_i1 = 0, dt_i2 = 0, dt_i3 = 0;
-#endif
- struct t_data *d;
-
- flush_read(desc);
- if (read_size != 0) {
- if ((d = async_lseek(desc, errp, 0,
- -((ssize_t)read_size), EFILE_SEEK_CUR
-#ifdef USE_VM_PROBES
- , &dt_i1, &dt_i2, &dt_i3
-#endif
- )) == NULL) {
- r = -1;
- } else {
-#ifdef USE_VM_PROBES
- d->sched_i1 = dt_priv->thread_num;
- d->sched_i2 = dt_priv->tag;
- d->sched_utag[0] = '\0';
- if (dt_utag != NULL) {
- if (dt_utag[0] == '\0') {
- dt_utag = NULL;
- } else {
- strncpy(d->sched_utag, dt_utag, sizeof(d->sched_utag) - 1);
- d->sched_utag[sizeof(d->sched_utag) - 1] = '\0';
- }
- }
- DTRACE11(efile_drv_entry, dt_priv->thread_num, dt_priv->tag++,
- dt_utag, FILE_LSEEK,
- NULL, NULL, dt_i1, dt_i2, dt_i3, 0, desc->port_str);
-#endif /* USE_VM_PROBES */
- }
- }
- return r;
-}
-
-
-/*********************************************************************
- * Driver entry point -> stop
- * The close has to be scheduled on async thread, so that currently active
- * async operation does not suddenly have the ground disappearing under their feet...
- */
-static void
-file_stop(ErlDrvData e)
-{
- file_descriptor* desc = (file_descriptor*)e;
-
- TRACE_C('p');
-
- IF_THRDS {
- flush_read(desc);
- if (desc->fd != FILE_FD_INVALID) {
- struct t_data *d = EF_SAFE_ALLOC(sizeof(struct t_data));
- d->command = FILE_CLOSE_ON_PORT_EXIT;
- d->reply = !0;
- d->fd = desc->fd;
- d->flags = desc->flags;
- d->invoke = invoke_close;
- d->free = free_data;
- d->level = 2;
- d->data_to_free = (void *) desc;
- cq_enq(desc, d);
- desc->fd = FILE_FD_INVALID;
- desc->flags = 0;
- cq_execute(desc);
- } else {
- EF_FREE(desc);
- }
- } else {
- if (desc->fd != FILE_FD_INVALID) {
- do_close(desc->flags, desc->fd);
- desc->fd = FILE_FD_INVALID;
- desc->flags = 0;
- }
- if (desc->read_binp) {
- driver_free_binary(desc->read_binp);
- }
- EF_FREE(desc);
- }
-}
-
-/*********************************************************************
- * Driver entry point -> ready_async
- */
-static void
-file_async_ready(ErlDrvData e, ErlDrvThreadData data)
-{
- file_descriptor *desc = (file_descriptor*)e;
- struct t_data *d = (struct t_data *) data;
- char header[5]; /* result code + count */
- char resbuf[RESBUFSIZE]; /* Result buffer. */
-#ifdef USE_VM_PROBES
- int sched_i1 = d->sched_i1, sched_i2 = d->sched_i2, command = d->command,
- result_ok = d->result_ok,
- posix_errno = d->result_ok ? 0 : d->errInfo.posix_errno;
- DTRACE_CHARBUF(sched_utag, DTRACE_EFILE_BUFSIZ+1);
-
- sched_utag[0] = '\0';
- if (DTRACE_ENABLED(efile_drv_return)) {
- strncpy(sched_utag, d->sched_utag, DTRACE_EFILE_BUFSIZ);
- sched_utag[DTRACE_EFILE_BUFSIZ] = '\0';
- }
-#endif /* USE_VM_PROBES */
-
- TRACE_C('r');
-
- if (try_again(desc, d)) {
- /* DTRACE TODO: what kind of probe makes sense here? */
- return;
- }
-
- switch (d->command)
- {
- case FILE_READ:
- if (!d->result_ok) {
- reply_error(desc, &d->errInfo);
- } else {
- size_t available_bytes =
- d->c.read.bin_offset + d->c.read.bin_size - desc->read_offset;
- if (available_bytes < d->c.read.size) {
- d->c.read.size = available_bytes;
- }
- TRACE_C('D');
- reply_data(desc, d->c.read.binp,
- desc->read_offset, d->c.read.size);
- desc->read_offset += d->c.read.size;
- desc->read_size =
- d->c.read.bin_offset + d->c.read.bin_size - desc->read_offset;
- try_free_read_bin(desc);
- }
- free_read(data);
- break;
- case FILE_READ_LINE:
- /* The read_line structure differs from the read structure.
- The data->read_offset and d->c.read_line.read_offset are copies, as are
- data->read_size and d->c.read_line.read_size
- The read_line function does not kniow in advance how large the binary has to be,
- why new allocation (but not reallocation of the old binary, for obvious reasons)
- may happen in the worker thread. */
- if (!d->result_ok) {
- reply_error(desc, &d->errInfo);
- } else {
- size_t len = d->c.read_line.nl_pos - d->c.read_line.read_offset;
- TRACE_C('L');
- reply_data(desc, d->c.read_line.binp,
- d->c.read_line.read_offset, len);
- desc->read_offset = d->c.read_line.read_offset + d->c.read_line.nl_skip + len;
- desc->read_size =
- d->c.read_line.read_size - d->c.read_line.nl_skip - len;
- if (desc->read_binp != d->c.read_line.binp) { /* New binary allocated */
- driver_free_binary(desc->read_binp);
- desc->read_binp = d->c.read_line.binp;
- driver_binary_inc_refc(desc->read_binp);
- }
-#if !ALWAYS_READ_LINE_AHEAD
- ASSERT(desc->read_bufsize > 0 || desc->read_size == 0);
- if (desc->read_bufsize == 0) {
- desc->read_offset = desc->read_binp->orig_size; /* triggers cleanup */
- }
-#endif
- try_free_read_bin(desc);
- }
- free_read_line(data);
- break;
- case FILE_READ_FILE:
- if (!d->result_ok)
- reply_error(desc, &d->errInfo);
- else {
- header[0] = FILE_RESP_ALL_DATA;
- TRACE_C('R');
- driver_output_binary(desc->port, header, 1,
- d->c.read_file.binp,
- 0, d->c.read_file.offset);
- }
- free_read_file(data);
- break;
- case FILE_WRITE:
- if (d->reply) {
- if (! d->result_ok) {
- reply_error(desc, &d->errInfo);
- } else {
- reply_Uint(desc, d->c.writev.reply_size);
- }
- } else {
- if (! d->result_ok) {
- desc->write_error = !0;
- desc->write_errInfo = d->errInfo;
- }
- }
- free_data(data);
- break;
- case FILE_LSEEK:
- if (d->reply) {
- if (d->result_ok)
- reply_Sint64(desc, d->c.lseek.location);
- else
- reply_error(desc, &d->errInfo);
- }
- free_data(data);
- break;
- case FILE_MKDIR:
- case FILE_RMDIR:
- case FILE_CHDIR:
- case FILE_DELETE:
- case FILE_FDATASYNC:
- case FILE_FSYNC:
- case FILE_TRUNCATE:
- case FILE_LINK:
- case FILE_SYMLINK:
- case FILE_RENAME:
- case FILE_WRITE_INFO:
- case FILE_FADVISE:
- case FILE_FALLOCATE:
- reply(desc, d->result_ok, &d->errInfo);
- free_data(data);
- break;
- case FILE_ALTNAME:
- case FILE_PWD:
- case FILE_READLINK:
- {
- int length;
- char *resbuf = d->b;
-
- if (!d->result_ok)
- reply_error(desc, &d->errInfo);
- else {
- resbuf[0] = FILE_RESP_FNAME;
- length = 1+FILENAME_BYTELEN((char*) resbuf+1);
- TRACE_C('R');
- driver_output2(desc->port, resbuf, 1, resbuf+1, length-1);
- }
- free_data(data);
- break;
- }
- case FILE_OPEN:
- if (!d->result_ok) {
- reply_error(desc, &d->errInfo);
- } else {
- ASSERT(d->is_fd_unused);
- desc->fd = d->fd;
- desc->flags = d->flags;
- d->is_fd_unused = 0;
- reply_Uint(desc, d->fd);
- }
- free_data(data);
- break;
- case FILE_FSTAT:
- case FILE_LSTAT:
- {
- if (d->result_ok) {
- resbuf[0] = FILE_RESP_INFO;
-
- put_int32(d->info.size_high, &resbuf[1 + ( 0 * 4)]);
- put_int32(d->info.size_low, &resbuf[1 + ( 1 * 4)]);
- put_int32(d->info.type, &resbuf[1 + ( 2 * 4)]);
-
- /* Note 64 bit indexing in resbuf here */
- put_int64(d->info.accessTime, &resbuf[1 + ( 3 * 4)]);
- put_int64(d->info.modifyTime, &resbuf[1 + ( 5 * 4)]);
- put_int64(d->info.cTime, &resbuf[1 + ( 7 * 4)]);
-
- put_int32(d->info.mode, &resbuf[1 + ( 9 * 4)]);
- put_int32(d->info.links, &resbuf[1 + (10 * 4)]);
- put_int32(d->info.major_device, &resbuf[1 + (11 * 4)]);
- put_int32(d->info.minor_device, &resbuf[1 + (12 * 4)]);
- put_int32(d->info.inode, &resbuf[1 + (13 * 4)]);
- put_int32(d->info.uid, &resbuf[1 + (14 * 4)]);
- put_int32(d->info.gid, &resbuf[1 + (15 * 4)]);
- put_int32(d->info.access, &resbuf[1 + (16 * 4)]);
-
-#define RESULT_SIZE (1 + (17 * 4))
- TRACE_C('R');
- driver_output2(desc->port, resbuf, RESULT_SIZE, NULL, 0);
-#undef RESULT_SIZE
- } else
- reply_error(desc, &d->errInfo);
- }
- free_data(data);
- break;
- case FILE_READDIR:
- if (!d->result_ok) {
- reply_error(desc, &d->errInfo);
- } else {
- struct t_readdir_buf *b1 = d->c.read_dir.first_buf;
- char op = FILE_RESP_LFNAME;
-
- TRACE_C('R');
- ASSERT(b1);
-
- while (b1) {
- struct t_readdir_buf *b2 = b1;
- char *p = &b1->buf[0];
- driver_output2(desc->port, p, 1, p + 1, b1->n - 1);
- b1 = b1->next;
- EF_FREE(b2);
- }
- driver_output2(desc->port, &op, 1, NULL, 0);
-
- d->c.read_dir.first_buf = NULL;
- d->c.read_dir.last_buf = NULL;
- }
- free_readdir(data);
- break;
- case FILE_CLOSE:
- if (d->reply) {
- TRACE_C('K');
- reply_ok(desc);
-#ifdef USE_VM_PROBES
- result_ok = 1;
-#endif
- }
- free_data(data);
- break;
- case FILE_PWRITEV:
- if (!d->result_ok) {
- reply_Uint_error(desc, d->c.pwritev.cnt, &d->errInfo);
- } else {
- reply_Uint(desc, d->c.pwritev.n);
- }
- free_data(data);
- break;
- case FILE_PREADV:
- if (!d->result_ok) {
- reply_error(desc, &d->errInfo);
- } else {
- reply_ev(desc, FILE_RESP_LDATA, &d->c.preadv.eiov);
- }
- free_preadv(data);
- break;
- case FILE_IPREAD:
- if (!d->result_ok) {
- reply_error(desc, &d->errInfo);
- } else if (!d->c.preadv.eiov.vsize) {
- reply_eof(desc);
- } else {
- reply_ev(desc, FILE_RESP_N2DATA, &d->c.preadv.eiov);
- }
- free_preadv(data);
- break;
-#ifdef HAVE_SENDFILE
- case FILE_SENDFILE:
- if (d->result_ok == -1) {
- if (d->errInfo.posix_errno == ECONNRESET ||
- d->errInfo.posix_errno == ENOTCONN ||
- d->errInfo.posix_errno == EPIPE)
- reply_string_error(desc,"closed");
- else
- reply_error(desc, &d->errInfo);
- desc->sendfile_state = not_sending;
- free_sendfile(data);
- } else if (d->result_ok == 0) {
- reply_Sint64(desc, d->c.sendfile.written);
- desc->sendfile_state = not_sending;
- free_sendfile(data);
- } else if (d->result_ok == 1) { /* If we are using select to send the rest of the data */
- desc->sendfile_state = sending;
- desc->d = d;
- driver_select(desc->port, (ErlDrvEvent)(long)d->c.sendfile.out_fd,
- ERL_DRV_USE|ERL_DRV_WRITE, 1);
- }
- break;
-#endif
- case FILE_CLOSE_ON_PORT_EXIT:
- /* See file_stop. However this is never invoked after the port is killed. */
- free_data(data);
- desc = NULL;
- /* This is it for this port, so just send dtrace and return, avoid doing anything to the freed data */
- DTRACE6(efile_drv_return, sched_i1, sched_i2, sched_utag,
- command, result_ok, posix_errno);
- return;
- default:
- abort();
- }
- DTRACE6(efile_drv_return, sched_i1, sched_i2, sched_utag,
- command, result_ok, posix_errno);
- if (desc->write_buffered != 0 && desc->timer_state == timer_idle ) {
- desc->timer_state = timer_write;
- driver_set_timer(desc->port, desc->write_delay);
- }
- cq_execute(desc);
-
-}
-
-
-/*********************************************************************
- * Driver entry point -> output
- */
-static void
-file_output(ErlDrvData e, char* buf, ErlDrvSizeT count)
-{
- file_descriptor* desc = (file_descriptor*)e;
- Efile_error errInfo; /* The error codes for the last operation. */
- Sint fd; /* The file descriptor for this port, if any,
- * -1 if none.
- */
- char* name; /* Points to the filename in buf. */
- int command;
- struct t_data *d = NULL;
-#ifdef USE_VM_PROBES
- char *dt_utag = NULL;
- char *dt_s1 = NULL, *dt_s2 = NULL;
- Sint64 dt_i1 = 0;
- Sint64 dt_i2 = 0;
- Sint64 dt_i3 = 0;
- Sint64 dt_i4 = 0;
- dt_private *dt_priv = get_dt_private(0);
-#endif /* USE_VM_PROBES */
-
- TRACE_C('o');
-
- fd = desc->fd;
- name = buf+1;
- command = *(uchar*)buf++;
-
- switch(command) {
-
- case FILE_MKDIR:
- {
- d = EF_SAFE_ALLOC(sizeof(struct t_data) - 1 + FILENAME_BYTELEN(name) + FILENAME_CHARSIZE);
-
- FILENAME_COPY(d->b, name);
-#ifdef USE_VM_PROBES
- dt_s1 = d->b;
- dt_utag = name + FILENAME_BYTELEN(name) + FILENAME_CHARSIZE;
-#endif
- d->command = command;
- d->invoke = invoke_mkdir;
- d->free = free_data;
- d->level = 2;
- goto done;
- }
- case FILE_RMDIR:
- {
- d = EF_SAFE_ALLOC(sizeof(struct t_data) - 1 + FILENAME_BYTELEN(name) + FILENAME_CHARSIZE);
-
- FILENAME_COPY(d->b, name);
-#ifdef USE_VM_PROBES
- dt_s1 = d->b;
- dt_utag = name + FILENAME_BYTELEN(name) + FILENAME_CHARSIZE;
-#endif
- d->command = command;
- d->invoke = invoke_rmdir;
- d->free = free_data;
- d->level = 2;
- goto done;
- }
- case FILE_DELETE:
- {
- d = EF_SAFE_ALLOC(sizeof(struct t_data) - 1 + FILENAME_BYTELEN(name) + FILENAME_CHARSIZE);
-
- FILENAME_COPY(d->b, name);
-#ifdef USE_VM_PROBES
- dt_s1 = d->b;
- dt_utag = name + FILENAME_BYTELEN(name) + FILENAME_CHARSIZE;
-#endif
- d->command = command;
- d->invoke = invoke_delete_file;
- d->free = free_data;
- d->level = 2;
- goto done;
- }
- case FILE_RENAME:
- {
- char* new_name;
- int namelen = FILENAME_BYTELEN(name)+FILENAME_CHARSIZE;
- new_name = name+namelen;
- d = EF_SAFE_ALLOC(sizeof(struct t_data) - 1
- + namelen
- + FILENAME_BYTELEN(new_name) + FILENAME_CHARSIZE);
-
- FILENAME_COPY(d->b, name);
- FILENAME_COPY(d->b + namelen, new_name);
-#ifdef USE_VM_PROBES
- dt_s1 = d->b;
- dt_s2 = d->b + namelen;
- dt_utag = buf + namelen + FILENAME_BYTELEN(new_name) + FILENAME_CHARSIZE;
-#endif
- d->flags = desc->flags;
- d->fd = fd;
- d->command = command;
- d->invoke = invoke_rename;
- d->free = free_data;
- d->level = 2;
- goto done;
- }
- case FILE_CHDIR:
- {
- d = EF_SAFE_ALLOC(sizeof(struct t_data) - 1 + FILENAME_BYTELEN(name) + FILENAME_CHARSIZE);
-
- FILENAME_COPY(d->b, name);
-#ifdef USE_VM_PROBES
- dt_s1 = d->b;
- dt_utag = name + FILENAME_BYTELEN(name) + FILENAME_CHARSIZE;
-#endif
- d->command = command;
- d->invoke = invoke_chdir;
- d->free = free_data;
- d->level = 2;
- goto done;
- }
- case FILE_PWD:
- {
- d = EF_SAFE_ALLOC(sizeof(struct t_data) - 1 + RESBUFSIZE + 1);
-
- d->drive = *(uchar*)buf;
-#ifdef USE_VM_PROBES
- dt_utag = buf + 1;
-#endif
- d->command = command;
- d->invoke = invoke_pwd;
- d->free = free_data;
- d->level = 2;
- goto done;
- }
-
- case FILE_READDIR:
- if (sys_info.async_threads > 0)
- {
- d = EF_SAFE_ALLOC(sizeof(struct t_data) - 1 + FILENAME_BYTELEN(name) +
- FILENAME_CHARSIZE);
-
- FILENAME_COPY(d->b, name);
-#ifdef USE_VM_PROBES
- dt_s1 = d->b;
- dt_utag = name + FILENAME_BYTELEN(name) + FILENAME_CHARSIZE;
-#endif
- d->dir_handle = NULL;
- d->command = command;
- d->invoke = invoke_readdir;
- d->free = free_readdir;
- d->level = 2;
- d->c.read_dir.first_buf = NULL;
- d->c.read_dir.last_buf = NULL;
- goto done;
- }
- else
- {
- size_t resbufsize;
- size_t n = 0, total = 0;
- int res = 0;
- char resbuf[READDIR_BUFSIZE];
-
- EFILE_DIR_HANDLE dir_handle; /* Handle to open directory. */
-
- total = READDIR_BUFSIZE;
- errInfo.posix_errno = 0;
- dir_handle = NULL;
- resbuf[0] = FILE_RESP_LFNAME;
-
-#ifdef USE_VM_PROBES
- dt_s1 = name;
- dt_utag = name + FILENAME_BYTELEN(name) + FILENAME_CHARSIZE;
-#endif
- /* Fill the buffer with multiple directory listings before sending it to the
- * receiving process. READDIR_CHUNKS is minimum number of files sent to the
- * receiver.
- * Format for each driver_output2:
- * ------------------------------------
- * | Type | Len | Filename | ...
- * | 1 byte | 2 bytes | Len bytes | ...
- * ------------------------------------
- */
-
- do {
- n = 1;
- resbufsize = READDIR_BUFSIZE - n;
-
- do {
- res = efile_readdir(&errInfo, name, &dir_handle, resbuf + n + 2, &resbufsize);
-
- if (res) {
- put_int16((Uint16)resbufsize, resbuf + n);
- n += 2 + resbufsize;
- resbufsize = READDIR_BUFSIZE - n;
- }
- } while( res && ((total - n - 2) >= MAXPATHLEN*FILENAME_CHARSIZE));
-
- if (n > 1) {
- driver_output2(desc->port, resbuf, 1, resbuf + 1, n - 1);
- }
- } while(res);
-
- if (errInfo.posix_errno != 0) {
- reply_error(desc, &errInfo);
- return;
- }
-#ifdef USE_VM_PROBES
- if (dt_utag != NULL && dt_utag[0] == '\0') {
- dt_utag = NULL;
- }
-
- DTRACE11(efile_drv_entry, dt_priv->thread_num, dt_priv->tag,
- dt_utag, command, name, dt_s2,
- dt_i1, dt_i2, dt_i3, dt_i4, desc->port_str);
- DTRACE6(efile_drv_return, dt_priv->thread_num, dt_priv->tag++,
- dt_utag, command, 1, 0);
-#endif
- TRACE_C('R');
- driver_output2(desc->port, resbuf, 1, NULL, 0);
- return;
- }
- case FILE_OPEN:
- {
- d = EF_SAFE_ALLOC(sizeof(struct t_data) - 1 + FILENAME_BYTELEN(buf+4) +
- FILENAME_CHARSIZE);
-
- d->flags = get_int32((uchar*)buf);
- name = buf+4;
- FILENAME_COPY(d->b, name);
-#ifdef USE_VM_PROBES
- dt_i1 = d->flags;
- dt_s1 = d->b;
- dt_utag = name + FILENAME_BYTELEN(d->b) + FILENAME_CHARSIZE;
-#endif
- d->command = command;
- d->invoke = invoke_open;
- d->free = free_data;
- d->level = 2;
- d->is_fd_unused = 1;
- goto done;
- }
-
- case FILE_FDATASYNC:
- {
- d = EF_SAFE_ALLOC(sizeof(struct t_data));
-
- d->fd = fd;
-#ifdef USE_VM_PROBES
- dt_utag = name;
- dt_i1 = fd;
-#endif
- d->command = command;
- d->invoke = invoke_fdatasync;
- d->free = free_data;
- d->level = 2;
- goto done;
- }
-
- case FILE_FSYNC:
- {
- d = EF_SAFE_ALLOC(sizeof(struct t_data));
-
- d->fd = fd;
-#ifdef USE_VM_PROBES
- dt_utag = name;
- dt_i1 = fd;
-#endif
- d->command = command;
- d->invoke = invoke_fsync;
- d->free = free_data;
- d->level = 2;
- goto done;
- }
-
-
- case FILE_FSTAT:
- case FILE_LSTAT:
- {
- d = EF_SAFE_ALLOC(sizeof(struct t_data) - 1 + FILENAME_BYTELEN(name) +
- FILENAME_CHARSIZE);
-
- FILENAME_COPY(d->b, name);
- d->fd = fd;
-#ifdef USE_VM_PROBES
- dt_utag = name + FILENAME_BYTELEN(d->b) + FILENAME_CHARSIZE;
- if (command == FILE_LSTAT) {
- dt_s1 = d->b;
- } else {
- dt_i1 = fd;
- }
-#endif
- d->command = command;
- d->invoke = invoke_flstat;
- d->free = free_data;
- d->level = 2;
- goto done;
- }
-
- case FILE_TRUNCATE:
- {
- d = EF_SAFE_ALLOC(sizeof(struct t_data));
-
- d->flags = desc->flags;
- d->fd = fd;
-#ifdef USE_VM_PROBES
- dt_utag = name;
- dt_i1 = fd;
- dt_i2 = d->flags;
-#endif
- d->command = command;
- d->invoke = invoke_truncate;
- d->free = free_data;
- d->level = 2;
- goto done;
- }
-
- case FILE_WRITE_INFO:
- {
- d = EF_SAFE_ALLOC(sizeof(struct t_data) - 1
- + FILENAME_BYTELEN(buf + 9*4) + FILENAME_CHARSIZE);
-
- d->info.mode = get_int32(buf + 0 * 4);
- d->info.uid = get_int32(buf + 1 * 4);
- d->info.gid = get_int32(buf + 2 * 4);
- d->info.accessTime = get_int64(buf + 3 * 4);
- d->info.modifyTime = get_int64(buf + 5 * 4);
- d->info.cTime = get_int64(buf + 7 * 4);
-
- FILENAME_COPY(d->b, buf + 9*4);
-#ifdef USE_VM_PROBES
- dt_i1 = d->info.mode;
- dt_i2 = d->info.uid;
- dt_i3 = d->info.gid;
- dt_s1 = d->b;
- dt_utag = buf + 9 * 4 + FILENAME_BYTELEN(d->b) + FILENAME_CHARSIZE;
-#endif
- d->command = command;
- d->invoke = invoke_write_info;
- d->free = free_data;
- d->level = 2;
- goto done;
- }
-
- case FILE_READLINK:
- {
- d = EF_SAFE_ALLOC(sizeof(struct t_data) - 1 +
- MAX(RESBUFSIZE, (FILENAME_BYTELEN(name) +
- FILENAME_CHARSIZE)) + 1);
- FILENAME_COPY(d->b, name);
-#ifdef USE_VM_PROBES
- dt_s1 = d->b;
- dt_utag = name + FILENAME_BYTELEN(d->b) + FILENAME_CHARSIZE;
-#endif
- d->command = command;
- d->invoke = invoke_readlink;
- d->free = free_data;
- d->level = 2;
- goto done;
- }
-
- case FILE_ALTNAME:
- {
- d = EF_SAFE_ALLOC(sizeof(struct t_data) - 1 +
- MAX(RESBUFSIZE, (FILENAME_BYTELEN(name) +
- FILENAME_CHARSIZE)) + 1);
- FILENAME_COPY(d->b, name);
-#ifdef USE_VM_PROBES
- dt_s1 = d->b;
- dt_utag = name + FILENAME_BYTELEN(d->b) + FILENAME_CHARSIZE;
-#endif
- d->command = command;
- d->invoke = invoke_altname;
- d->free = free_data;
- d->level = 2;
- goto done;
- }
-
-
- case FILE_LINK:
- {
- char* new_name;
- int namelen = FILENAME_BYTELEN(name) + FILENAME_CHARSIZE;
-
- new_name = name+namelen;
- d = EF_SAFE_ALLOC(sizeof(struct t_data) - 1
- + namelen
- + FILENAME_BYTELEN(new_name) + FILENAME_CHARSIZE);
-
- FILENAME_COPY(d->b, name);
- FILENAME_COPY(d->b + namelen, new_name);
-#ifdef USE_VM_PROBES
- dt_s1 = d->b;
- dt_s2 = d->b + namelen;
- dt_utag = buf + namelen + FILENAME_BYTELEN(dt_s2) + FILENAME_CHARSIZE;
-#endif
- d->flags = desc->flags;
- d->fd = fd;
- d->command = command;
- d->invoke = invoke_link;
- d->free = free_data;
- d->level = 2;
- goto done;
- }
-
- case FILE_SYMLINK:
- {
- char* new_name;
- int namelen = FILENAME_BYTELEN(name) + FILENAME_CHARSIZE;
-
- new_name = name+namelen;
- d = EF_SAFE_ALLOC(sizeof(struct t_data) - 1
- + namelen
- + FILENAME_BYTELEN(new_name) + FILENAME_CHARSIZE);
-
- FILENAME_COPY(d->b, name);
- FILENAME_COPY(d->b + namelen, new_name);
-#ifdef USE_VM_PROBES
- dt_s1 = d->b;
- dt_s2 = d->b + namelen;
- dt_utag = buf + namelen + FILENAME_BYTELEN(dt_s2) + FILENAME_CHARSIZE;
-#endif
- d->flags = desc->flags;
- d->fd = fd;
- d->command = command;
- d->invoke = invoke_symlink;
- d->free = free_data;
- d->level = 2;
- goto done;
- }
-
- case FILE_FADVISE:
- {
- d = EF_SAFE_ALLOC(sizeof(struct t_data));
-
- d->fd = fd;
- d->command = command;
- d->invoke = invoke_fadvise;
- d->free = free_data;
- d->level = 2;
- d->c.fadvise.offset = get_int64((uchar*) buf);
- d->c.fadvise.length = get_int64(((uchar*) buf) + sizeof(Sint64));
- d->c.fadvise.advise = get_int32(((uchar*) buf) + 2 * sizeof(Sint64));
-#ifdef USE_VM_PROBES
- dt_i1 = d->fd;
- dt_i2 = d->c.fadvise.offset;
- dt_i3 = d->c.fadvise.length;
- dt_i4 = d->c.fadvise.advise;
- dt_utag = buf + 3 * sizeof(Sint64);
-#endif
- goto done;
- }
-
- case FILE_FALLOCATE:
- {
- d = EF_SAFE_ALLOC(sizeof(struct t_data));
-
- d->fd = fd;
- d->command = command;
- d->invoke = invoke_fallocate;
- d->free = free_data;
- d->level = 2;
- d->c.fallocate.offset = get_int64((uchar*) buf);
- d->c.fallocate.length = get_int64(((uchar*) buf) + sizeof(Sint64));
- goto done;
- }
-
- }
-
- /*
- * Ignore anything else -- let the caller hang.
- */
-
- return;
-
- done:
- if (d) {
-#ifdef USE_VM_PROBES
- d->sched_i1 = dt_priv->thread_num;
- d->sched_i2 = dt_priv->tag;
- d->sched_utag[0] = '\0';
- if (dt_utag != NULL) {
- if (dt_utag[0] == '\0') {
- dt_utag = NULL;
- } else {
- strncpy(d->sched_utag, dt_utag, sizeof(d->sched_utag) - 1);
- d->sched_utag[sizeof(d->sched_utag) - 1] = '\0';
- }
- }
- DTRACE11(efile_drv_entry, dt_priv->thread_num, dt_priv->tag++,
- dt_utag, command, dt_s1, dt_s2,
- dt_i1, dt_i2, dt_i3, dt_i4, desc->port_str);
-#endif
- cq_enq(desc, d);
- }
-}
-
-/*********************************************************************
- * Driver entry point -> flush
- */
-static void
-file_flush(ErlDrvData e) {
- file_descriptor *desc = (file_descriptor *)e;
-#ifdef DEBUG
- int r;
-#endif
-#ifdef USE_VM_PROBES
- dt_private *dt_priv = get_dt_private(dt_driver_io_worker_base);
-#endif
-
- TRACE_C('f');
-
-#ifdef HAVE_SENDFILE
- flush_sendfile(desc, NULL);
-#endif
-
-#ifdef DEBUG
- r =
-#endif
- flush_write(desc, NULL
-#ifdef USE_VM_PROBES
- , dt_priv, (desc->d == NULL) ? NULL : desc->d->sched_utag
-#endif
- );
- /* Only possible reason for bad return value is ENOMEM, and
- * there is nobody to tell...
- */
-#ifdef DEBUG
- ASSERT(r == 0);
-#endif
- cq_execute(desc);
-}
-
-
-
-/*********************************************************************
- * Driver entry point -> control
- * Only debug functionality...
- */
-static ErlDrvSSizeT
-file_control(ErlDrvData e, unsigned int command,
- char* buf, ErlDrvSizeT len, char **rbuf, ErlDrvSizeT rlen) {
- file_descriptor *desc = (file_descriptor *)e;
- switch (command) {
- case 'K' :
- if (rlen < 4) {
- *rbuf = EF_ALLOC(4);
- }
- (*rbuf)[0] = ((desc->key) >> 24) & 0xFF;
- (*rbuf)[1] = ((desc->key) >> 16) & 0xFF;
- (*rbuf)[2] = ((desc->key) >> 8) & 0xFF;
- (*rbuf)[3] = (desc->key) & 0xFF;
- return 4;
- default:
- return 0;
- }
-}
-
-/*********************************************************************
- * Driver entry point -> timeout
- */
-static void
-file_timeout(ErlDrvData e) {
- file_descriptor *desc = (file_descriptor *)e;
- enum e_timer timer_state = desc->timer_state;
-#ifdef USE_VM_PROBES
- dt_private *dt_priv = get_dt_private(dt_driver_io_worker_base);
-#endif
-
- TRACE_C('t');
-
- desc->timer_state = timer_idle;
- switch (timer_state) {
- case timer_idle:
- ASSERT(0);
- break;
- case timer_again:
- ASSERT(desc->invoke);
- ASSERT(desc->free);
- driver_async(desc->port, KEY(desc), desc->invoke, desc->d, desc->free);
- break;
- case timer_write: {
-#ifdef DEBUG
- int r =
-#endif
- flush_write(desc, NULL
-#ifdef USE_VM_PROBES
- , dt_priv, (desc->d == NULL) ? NULL : desc->d->sched_utag
-#endif
- );
- /* Only possible reason for bad return value is ENOMEM, and
- * there is nobody to tell...
- */
- ASSERT(r == 0);
- cq_execute(desc);
- } break;
- } /* case */
-}
-
-
-
-/*********************************************************************
- * Driver entry point -> outputv
- */
-static void
-file_outputv(ErlDrvData e, ErlIOVec *ev) {
- file_descriptor* desc = (file_descriptor*)e;
- char command;
- size_t p, q;
- int err;
- struct t_data *d = NULL;
-#ifdef USE_VM_PROBES
- Sint64 dt_i1 = 0, dt_i2 = 0, dt_i3 = 0;
- Sint64 dt_i4 = 0;
- char *dt_utag = NULL;
- char *dt_s1 = NULL;
- dt_private *dt_priv = get_dt_private(dt_driver_io_worker_base);
-#endif
-
- TRACE_C('v');
-
- p = 0; q = 1;
- if (! EV_GET_CHAR(ev, &command, &p, &q)) {
- /* Empty command */
- reply_posix_error(desc, EINVAL);
- goto done;
- }
- /* 'command' contains the decoded command number,
- * 'p' and 'q' point out the next byte in the command:
- * ((char *)ev->iov[q].iov_base) + p;
- */
-
- TRACE_F(("%i", (int) command));
-
- switch (command) {
-
- case FILE_CLOSE: {
-#ifdef USE_VM_PROBES
- dt_utag = EV_CHAR_P(ev, p, q);
-#endif
- flush_read(desc);
- if (flush_write_check_error(desc, &err
-#ifdef USE_VM_PROBES
- , dt_priv, dt_utag
-#endif
- ) < 0) {
- reply_posix_error(desc, err);
- goto done;
- }
- if (desc->fd != FILE_FD_INVALID) {
- if (! (d = EF_ALLOC(sizeof(struct t_data)))) {
- reply_posix_error(desc, ENOMEM);
- } else {
- d->command = command;
- d->reply = !0;
- d->fd = desc->fd;
- d->flags = desc->flags;
-#ifdef USE_VM_PROBES
- dt_i1 = d->fd;
- dt_i2 = d->flags;
-#endif
- d->invoke = invoke_close;
- d->free = free_data;
- d->level = 2;
- cq_enq(desc, d);
- desc->fd = FILE_FD_INVALID;
- desc->flags = 0;
- }
- } else {
- reply_posix_error(desc, EBADF);
- }
- } goto done;
-
- case FILE_READ: {
- Uint32 sizeH, sizeL;
- size_t size, alloc_size;
-
- if (!EV_GET_UINT32(ev, &sizeH, &p, &q)
- || !EV_GET_UINT32(ev, &sizeL, &p, &q)) {
- /* Wrong buffer length to contain the read count */
- reply_posix_error(desc, EINVAL);
- goto done;
- }
-#ifdef USE_VM_PROBES
- dt_utag = EV_CHAR_P(ev, p, q);
-#endif
- if (flush_write_check_error(desc, &err
-#ifdef USE_VM_PROBES
- , dt_priv, dt_utag
-#endif
- ) < 0) {
- reply_posix_error(desc, err);
- goto done;
- }
-#if ALWAYS_READ_LINE_AHEAD
- if (desc->read_bufsize == 0 && desc->read_binp != NULL && desc->read_size > 0) {
- /* We have allocated a buffer for line mode but should not really have a
- read-ahead buffer... */
- if (lseek_flush_read(desc, &err
-#ifdef USE_VM_PROBES
- , dt_priv, dt_utag
-#endif
- ) < 0) {
- reply_posix_error(desc, err);
- goto done;
- }
- }
-#endif
-#if SIZEOF_SIZE_T == 4
- if (sizeH != 0) {
- reply_posix_error(desc, EINVAL);
- goto done;
- }
- size = sizeL;
-#else
- size = ((size_t)sizeH << 32) | sizeL;
-#endif
- if ((desc->fd == FILE_FD_INVALID)
- || (! (desc->flags & EFILE_MODE_READ)) ) {
- reply_posix_error(desc, EBADF);
- goto done;
- }
- if (size == 0) {
- reply_buf(desc, &command, 0);
- goto done;
- }
- if (desc->read_size >= size) {
- /* We already have all data */
- TRACE_C('D');
- reply_data(desc, desc->read_binp, desc->read_offset, size);
- desc->read_offset += size;
- desc->read_size -= size;
- try_free_read_bin(desc);
- goto done;
- }
- /* We may have some of the data
- */
- /* Justification for the following strange formula:
- * If the read request is for such a large block as more than
- * half the buffer size it may lead to a lot of unnecessary copying,
- * since the tail of the old buffer is copied to the head of the
- * new, and if the tail is almost half the buffer it is a lot
- * to copy. Therefore allocate the exact amount needed in
- * this case, giving no lingering tail. */
- alloc_size =
- size > (desc->read_bufsize>>1) ?
- size : desc->read_bufsize;
- if (! desc->read_binp) {
- /* Need to allocate a new binary for the result */
- if (! (desc->read_binp = driver_alloc_binary(alloc_size))) {
- reply_posix_error(desc, ENOMEM);
- goto done;
- }
- } else {
- /* We already have a buffer */
- if (desc->read_binp->orig_size - desc->read_offset < size) {
- /* Need to allocate a new binary for the result */
- ErlDrvBinary *binp;
- if (! (binp = driver_alloc_binary(alloc_size))) {
- reply_posix_error(desc, ENOMEM);
- goto done;
- }
- /* Move data we already have to the new binary */
- sys_memcpy(binp->orig_bytes,
- desc->read_binp->orig_bytes + desc->read_offset,
- desc->read_size);
- driver_free_binary(desc->read_binp);
- desc->read_offset = 0;
- desc->read_binp = binp;
- }
- }
- if (! (d = EF_ALLOC(sizeof(struct t_data)))) {
- reply_posix_error(desc, ENOMEM);
- goto done;
- }
- d->command = command;
- d->reply = !0;
- d->fd = desc->fd;
- d->flags = desc->flags;
- d->c.read.binp = desc->read_binp;
- d->c.read.bin_offset = desc->read_offset + desc->read_size;
- d->c.read.bin_size = desc->read_binp->orig_size - d->c.read.bin_offset;
- d->c.read.size = size;
-#ifdef USE_VM_PROBES
- dt_i1 = d->fd;
- dt_i2 = d->flags;
- dt_i3 = d->c.read.size;
-#endif
- driver_binary_inc_refc(d->c.read.binp);
- d->invoke = invoke_read;
- d->free = free_read;
- d->level = 1;
- cq_enq(desc, d);
- } goto done; /* case FILE_READ: */
-
- case FILE_READ_LINE: {
- /*
- * Icky little creature... We do mostly as ordinary file read, but with a few differences.
- * 1) We have to scan for proper newline sequence if there is a buffer already, we cannot know
- * in advance if the buffer contains a whole line without scanning.
- * 2) We do not know how large the buffer needs to be in advance. We give a default buffer,
- * but the worker may need to allocate a new one. Freeing the old and rereferencing a newly
- * allocated binary + dealing with offsets and lengts are done in file_async ready
- * for this OP.
- */
-#ifdef USE_VM_PROBES
- dt_utag = EV_CHAR_P(ev, p, q);
-#endif
- if (flush_write_check_error(desc, &err
-#ifdef USE_VM_PROBES
- , dt_priv, dt_utag
-#endif
- ) < 0) {
- reply_posix_error(desc, err);
- goto done;
- }
- if (ev->size != 1
-#ifdef USE_VM_PROBES
- + FILENAME_BYTELEN(dt_utag) + FILENAME_CHARSIZE
-#endif
- ) {
- /* Wrong command length */
- reply_posix_error(desc, EINVAL);
- goto done;
- }
- if ((desc->fd == FILE_FD_INVALID)
- || (! (desc->flags & EFILE_MODE_READ)) ) {
- reply_posix_error(desc, EBADF);
- goto done;
- }
- if (desc->read_size > 0) {
- /* look for '\n' in what we'we already got */
- void *nl_ptr = memchr(desc->read_binp->orig_bytes + desc->read_offset,'\n',desc->read_size);
- if (nl_ptr != NULL) {
- /* If found, we're done */
- int skip = 0;
- size_t size = ((char *) nl_ptr) -
- ((char *) (desc->read_binp->orig_bytes + desc->read_offset)) + 1;
- if (size > 1 &&
- *(((char *) nl_ptr) - 1) == '\r') {
- *(((char *) nl_ptr) - 1) = '\n';
- skip = 1;
- --size;
- }
- reply_data(desc, desc->read_binp, desc->read_offset, size);
- desc->read_offset += (size + skip);
- desc->read_size -= (size + skip);
- try_free_read_bin(desc);
- goto done;
- }
- }
- /* Now, it's up to the thread to work out the need for more buffers and such, it's
- no use doing it in this thread as we do not have the information required anyway.
- Even a NULL buffer could be handled by the thread, but code is simplified by us
- allocating it */
- if (! desc->read_binp) {
- int alloc_size = (desc->read_bufsize > DEFAULT_LINEBUF_SIZE) ? desc->read_bufsize :
- DEFAULT_LINEBUF_SIZE;
- /* Allocate a new binary for the result */
- if (! (desc->read_binp = driver_alloc_binary(alloc_size))) {
- reply_posix_error(desc, ENOMEM);
- goto done;
- }
- }
- if (! (d = EF_ALLOC(sizeof(struct t_data)))) {
- reply_posix_error(desc, ENOMEM);
- goto done;
- }
-
- d->command = command;
- d->reply = !0;
- d->fd = desc->fd;
- d->flags = desc->flags;
- d->c.read_line.binp = desc->read_binp;
- d->c.read_line.read_offset = desc->read_offset;
- d->c.read_line.read_size = desc->read_size;
-#ifdef USE_VM_PROBES
- dt_i1 = d->fd;
- dt_i2 = d->flags;
- dt_i3 = d->c.read_line.read_offset;
-#endif
-#if !ALWAYS_READ_LINE_AHEAD
- d->c.read_line.read_ahead = (desc->read_bufsize > 0);
-#ifdef USE_VM_PROBES
- dt_i4 = d->c.read_line.read_ahead;
-#endif
-#endif
- driver_binary_inc_refc(d->c.read.binp);
- d->invoke = invoke_read_line;
- d->free = free_read_line;
- d->level = 1;
- cq_enq(desc, d);
- } goto done;
- case FILE_WRITE: { /* Dtrace: The dtrace user tag is not last in message,
- but follows the message tag directly.
- This is handled specially in prim_file.erl */
- ErlDrvSizeT skip = 1;
- ErlDrvSizeT size = ev->size - skip;
-
-#ifdef USE_VM_PROBES
- dt_utag = EV_CHAR_P(ev, p, q);
- skip += FILENAME_BYTELEN(dt_utag) + FILENAME_CHARSIZE;
- size = ev->size - skip;
-#endif
- if (lseek_flush_read(desc, &err
-#ifdef USE_VM_PROBES
- , dt_priv, dt_utag
-#endif
- ) < 0) {
- reply_posix_error(desc, err);
- goto done;
- }
- if (! (desc->flags & EFILE_MODE_WRITE)) {
- reply_posix_error(desc, EBADF);
- goto done;
- }
- if (size == 0) {
- reply_Uint(desc, size);
- goto done;
- }
- MUTEX_LOCK(desc->q_mtx);
- if (driver_enqv(desc->port, ev, skip)) {
- MUTEX_UNLOCK(desc->q_mtx);
- reply_posix_error(desc, ENOMEM);
- goto done;
- }
- desc->write_buffered += size;
- if (desc->write_buffered < desc->write_bufsize) {
- MUTEX_UNLOCK(desc->q_mtx);
- reply_Uint(desc, size);
- if (desc->timer_state == timer_idle) {
- desc->timer_state = timer_write;
- driver_set_timer(desc->port, desc->write_delay);
- }
- } else {
- if ((d = async_write(desc, &err, !0, size
-#ifdef USE_VM_PROBES
- , &dt_i1, &dt_i2, &dt_i3
-#endif
- )) == NULL) {
- MUTEX_UNLOCK(desc->q_mtx);
- reply_posix_error(desc, err);
- goto done;
- } else {
- MUTEX_UNLOCK(desc->q_mtx);
- }
- }
- } goto done; /* case FILE_WRITE */
-
- case FILE_PWRITEV: { /* Dtrace: The dtrace user tag is not last in message,
- but follows the message tag directly.
- This is handled specially in prim_file.erl */
- Uint32 i, j, n;
- size_t total;
-#ifdef USE_VM_PROBES
- char dt_tmp;
- int dt_utag_bytes = 1;
-
- dt_utag = EV_CHAR_P(ev, p, q);
- /* This will work for UTF-8, but not for UTF-16 - extra reminder here */
-#ifdef FILENAMES_16BIT
-#error 16bit characters in filenames and dtrace in combination is not supported.
-#endif
- while (EV_GET_CHAR(ev, &dt_tmp, &p, &q) && dt_tmp != '\0') {
- dt_utag_bytes++;
- }
-#endif
- if (ev->size < 1+4
-#ifdef USE_VM_PROBES
- + dt_utag_bytes
-#endif
- || !EV_GET_UINT32(ev, &n, &p, &q)) {
- /* Buffer too short to contain even the number of pos/size specs */
- reply_Uint_posix_error(desc, 0, EINVAL);
- goto done;
- }
- if (lseek_flush_read(desc, &err
-#ifdef USE_VM_PROBES
- , dt_priv, dt_utag
-#endif
- ) < 0) {
- reply_Uint_posix_error(desc, 0, err);
- goto done;
- }
- if (flush_write_check_error(desc, &err
-#ifdef USE_VM_PROBES
- , dt_priv, dt_utag
-#endif
- ) < 0) {
- reply_Uint_posix_error(desc, 0, err);
- goto done;
- }
- if (n == 0) {
- /* Trivial case - nothing to write */
- if (ev->size != 1+4) {
- reply_posix_error(desc, err);
- } else {
- reply_Uint(desc, 0);
- }
- goto done;
- }
- if (ev->size < 1+4+8*(2*n)
-#ifdef USE_VM_PROBES
- + dt_utag_bytes
-#endif
- ) {
- /* Buffer too short to contain even the pos/size specs */
- reply_Uint_posix_error(desc, 0, EINVAL);
- goto done;
- }
- d = EF_ALLOC(sizeof(struct t_data)
- + (n * sizeof(struct t_pbuf_spec)));
- if (! d) {
- reply_Uint_posix_error(desc, 0, ENOMEM);
- goto done;
- }
- d->command = command;
- d->reply = !0;
- d->fd = desc->fd;
- d->flags = desc->flags;
-#ifdef USE_VM_PROBES
- dt_i1 = d->fd;
- dt_i2 = d->flags;
-#endif
- d->c.pwritev.port = desc->port;
- d->c.pwritev.q_mtx = desc->q_mtx;
- d->c.pwritev.n = n;
- d->c.pwritev.cnt = 0;
- total = 0;
- j = 0;
- /* Create pos/size specs in the thread data structure
- * for all non-zero size binaries. Calculate total size.
- */
- for(i = 0; i < n; i++) {
- Uint32 sizeH, sizeL;
- size_t size;
- if ( !EV_GET_SINT64(ev, &d->c.pwritev.specs[i].offset, &p, &q)
- || !EV_GET_UINT32(ev, &sizeH, &p, &q)
- || !EV_GET_UINT32(ev, &sizeL, &p, &q)) {
- /* Misalignment in buffer */
- reply_Uint_posix_error(desc, 0, EINVAL);
- EF_FREE(d);
- goto done;
- }
-#if SIZEOF_SIZE_T == 4
- if (sizeH != 0) {
- reply_Uint_posix_error(desc, 0, EINVAL);
- EF_FREE(d);
- goto done;
- }
- size = sizeL;
-#else
- size = ((size_t)sizeH<<32) | sizeL;
-#endif
- if (size > 0) {
- total += size;
- d->c.pwritev.specs[j].size = size;
- j++;
- }
- }
- d->c.pwritev.size = total;
-#ifdef USE_VM_PROBES
- dt_i3 = d->c.pwritev.size;
-#endif
- if (j == 0) {
- /* Trivial case - nothing to write */
- EF_FREE(d);
- reply_Uint(desc, 0);
- } else {
- ErlDrvSizeT skip = 1 + 4 + 8 * (2*n)
-#ifdef USE_VM_PROBES
- + dt_utag_bytes
-#endif
- ;
- if (skip + total != ev->size) {
- /* Actual amount of data does not match
- * total of all pos/size specs
- */
- EF_FREE(d);
- reply_Uint_posix_error(desc, 0, EINVAL);
- } else {
- /* Enqueue the data */
- MUTEX_LOCK(desc->q_mtx);
- driver_enqv(desc->port, ev, skip);
- MUTEX_UNLOCK(desc->q_mtx);
- /* Execute the command */
- d->invoke = invoke_pwritev;
- d->free = free_data;
- d->level = 1;
- cq_enq(desc, d);
- }
- }
- } goto done; /* case FILE_PWRITEV: */
-
- case FILE_PREADV: { /* Dtrace: The dtrace user tag is not last in message,
- but follows the message tag directly.
- This is handled specially in prim_file.erl */
- register void * void_ptr;
- Uint32 i, n;
- ErlIOVec *res_ev;
-#ifdef USE_VM_PROBES
- char dt_tmp;
- int dt_utag_bytes = 1;
- /* This will work for UTF-8, but not for UTF-16 - extra reminder here */
-#ifdef FILENAMES_16BIT
-#error 16bit characters in filenames and dtrace in combination is not supported.
-#endif
- dt_utag = EV_CHAR_P(ev, p, q);
- while (EV_GET_CHAR(ev, &dt_tmp, &p, &q) && dt_tmp != '\0') {
- dt_utag_bytes++;
- }
-#endif
- if (lseek_flush_read(desc, &err
-#ifdef USE_VM_PROBES
- , dt_priv, dt_utag
-#endif
- ) < 0) {
- reply_posix_error(desc, err);
- goto done;
- }
- if (flush_write_check_error(desc, &err
-#ifdef USE_VM_PROBES
- , dt_priv, dt_utag
-#endif
- ) < 0) {
- reply_posix_error(desc, err);
- goto done;
- }
- if (ev->size < 1+8
-#ifdef USE_VM_PROBES
- + dt_utag_bytes
-#endif
- || !EV_GET_UINT32(ev, &n, &p, &q)
- || !EV_GET_UINT32(ev, &n, &p, &q)) {
- /* Buffer too short to contain even the number of pos/size specs */
- reply_posix_error(desc, EINVAL);
- goto done;
- }
- if (ev->size < 1+8+8*(2*n)
-#ifdef USE_VM_PROBES
- + dt_utag_bytes
-#endif
- ) {
- /* Buffer wrong length to contain the pos/size specs */
- reply_posix_error(desc, EINVAL);
- goto done;
- }
- /* Create the thread data structure with the contained ErlIOVec
- * and corresponding binaries for the response
- */
- d = EF_ALLOC(sizeof(*d)
- + (n * sizeof(*d->c.preadv.offsets))
- + ((1+n) * (sizeof(*res_ev->iov)
- + sizeof(*res_ev->binv))));
- if (! d) {
- reply_posix_error(desc, ENOMEM);
- goto done;
- }
- d->command = command;
- d->reply = !0;
- d->fd = desc->fd;
- d->flags = desc->flags;
-#ifdef USE_VM_PROBES
- dt_i1 = d->fd;
- dt_i2 = d->flags;
-#endif
- d->c.preadv.n = n;
- d->c.preadv.cnt = 0;
- d->c.preadv.size = 0;
- res_ev = &d->c.preadv.eiov;
- /* XXX possible alignment problems here for weird machines */
- res_ev->vsize = 1+d->c.preadv.n;
- res_ev->iov = void_ptr = &d->c.preadv.offsets[d->c.preadv.n];
- res_ev->binv = void_ptr = &res_ev->iov[res_ev->vsize];
- /* Read in the pos/size specs and allocate binaries for the results */
- for (i = 1; i < 1+n; i++) {
- Uint32 sizeH, sizeL;
- size_t size;
- if ( !EV_GET_SINT64(ev, &d->c.preadv.offsets[i-1], &p, &q)
- || !EV_GET_UINT32(ev, &sizeH, &p, &q)
- || !EV_GET_UINT32(ev, &sizeL, &p, &q)) {
- reply_posix_error(desc, EINVAL);
- break;
- }
-#if SIZEOF_SIZE_T == 4
- if (sizeH != 0) {
- reply_posix_error(desc, EINVAL);
- break;
- }
- size = sizeL;
-#else
- size = ((size_t)sizeH<<32) | sizeL;
-#endif
-#ifdef USE_VM_PROBES
- dt_i3 += size;
-#endif
- if (! (res_ev->binv[i] = driver_alloc_binary(size))) {
- reply_posix_error(desc, ENOMEM);
- break;
- } else {
- res_ev->iov[i].iov_len = size;
- res_ev->iov[i].iov_base = res_ev->binv[i]->orig_bytes;
- }
- }
- if (i < 1+n) {
- for (i--; i > 0; i--) {
- driver_free_binary(res_ev->binv[i]);
- }
- EF_FREE(d);
- goto done;
- }
- /* Allocate the header binary (index 0) */
- res_ev->binv[0] = driver_alloc_binary(4+4+8*n);
- if (! res_ev->binv[0]) {
- reply_posix_error(desc, ENOMEM);
- for (i = 1; i < 1+n; i++) {
- driver_free_binary(res_ev->binv[i]);
- }
- EF_FREE(d);
- goto done;
- }
- res_ev->iov[0].iov_len = 4+4+8*n;
- res_ev->iov[0].iov_base = res_ev->binv[0]->orig_bytes;
- /* Fill in the number of buffers in the header */
- put_int32(0, res_ev->iov[0].iov_base);
- put_int32(n, (char *)(res_ev->iov[0].iov_base) + 4);
- /**/
- res_ev->size = res_ev->iov[0].iov_len;
- if (n == 0) {
- /* Trivial case - nothing to read */
- reply_ev(desc, FILE_RESP_LDATA, res_ev);
- free_preadv(d);
- goto done;
- } else {
- d->invoke = invoke_preadv;
- d->free = free_preadv;
- d->level = 1;
- cq_enq(desc, d);
- }
- } goto done; /* case FILE_PREADV: */
-
- case FILE_LSEEK: {
- Sint64 offset; /* Offset for seek */
- Uint32 origin; /* Origin of seek. */
-
- if (ev->size < 1+8+4
- || !EV_GET_SINT64(ev, &offset, &p, &q)
- || !EV_GET_UINT32(ev, &origin, &p, &q)) {
- /* Wrong length of buffer to contain offset and origin */
- reply_posix_error(desc, EINVAL);
- goto done;
- }
-#ifdef USE_VM_PROBES
- dt_utag = EV_CHAR_P(ev, p, q);
-#endif
- if (lseek_flush_read(desc, &err
-#ifdef USE_VM_PROBES
- , dt_priv, dt_utag
-#endif
- ) < 0) {
- reply_posix_error(desc, err);
- goto done;
- }
- if (flush_write_check_error(desc, &err
-#ifdef USE_VM_PROBES
- , dt_priv, dt_utag
-#endif
- ) < 0) {
- reply_posix_error(desc, err);
- goto done;
- }
- if ((d = async_lseek(desc, &err, !0, offset, origin
-#ifdef USE_VM_PROBES
- , &dt_i1, &dt_i2, &dt_i3
-#endif
- )) == NULL) {
- reply_posix_error(desc, err);
- goto done;
- }
- } goto done;
-
- case FILE_READ_FILE: {
- char *filename;
- if (ev->size < 1+1) {
- /* Buffer contains empty name */
- reply_posix_error(desc, ENOENT);
- goto done;
- }
-#ifndef USE_VM_PROBES
- /* In the dtrace case, the iov has an extra element, the dtrace utag - we will need
- another test to see that
- the filename is in a single buffer: */
- if (ev->size-1 != ev->iov[q].iov_len-p) {
- /* Name not in one single buffer */
- reply_posix_error(desc, EINVAL);
- goto done;
- }
-#else
- if (((byte *)ev->iov[q].iov_base)[ev->iov[q].iov_len-1] != '\0') {
- /* Name not in one single buffer */
- reply_posix_error(desc, EINVAL);
- goto done;
- }
-#endif
- filename = EV_CHAR_P(ev, p, q);
- d = EF_ALLOC(sizeof(struct t_data) -1 + FILENAME_BYTELEN(filename) + FILENAME_CHARSIZE);
- if (! d) {
- reply_posix_error(desc, ENOMEM);
- goto done;
- }
- d->command = command;
- d->reply = !0;
- /* Copy name */
- FILENAME_COPY(d->b, filename);
-#ifdef USE_VM_PROBES
- {
- char dt_tmp;
-
- /* This will work for UTF-8, but not for UTF-16 - extra reminder here */
-#ifdef FILENAMES_16BIT
-#error 16bit characters in filenames and dtrace in combination is not supported.
-#endif
- while (EV_GET_CHAR(ev, &dt_tmp, &p, &q) && dt_tmp != '\0')
- ;
- dt_s1 = d->b;
- dt_utag = EV_CHAR_P(ev, p, q);
- }
-#endif
- d->c.read_file.binp = NULL;
- d->invoke = invoke_read_file;
- d->free = free_read_file;
- d->level = 2;
- cq_enq(desc, d);
- } goto done;
-
- case FILE_IPREAD: {
- /* This operation cheets by using invoke_preadv() and free_preadv()
- * plus its own invoke_ipread. Therefore the result format is
- * a bit awkward - the header binary contains one extra 64 bit
- * field that invoke_preadv() fortunately ignores,
- * and the first 64 bit field does not contain the number of
- * data binaries which invoke_preadv() also ignores.
- */
- register void * void_ptr;
- char mode;
- Sint64 hdr_offset;
- Uint32 max_size;
- ErlIOVec *res_ev;
- int vsize;
- if (! EV_GET_CHAR(ev, &mode, &p, &q)) {
- /* Empty command */
- reply_posix_error(desc, EINVAL);
- goto done;
- }
- if (mode != IPREAD_S32BU_P32BU) {
- reply_posix_error(desc, EINVAL);
- goto done;
- }
- if (ev->size < 1+1+8+4
- || !EV_GET_SINT64(ev, &hdr_offset, &p, &q)
- || !EV_GET_UINT32(ev, &max_size, &p, &q)) {
- /* Buffer too short to contain
- * the header offset and max size spec */
- reply_posix_error(desc, EINVAL);
- goto done;
- }
-#ifdef USE_VM_PROBES
- dt_utag = EV_CHAR_P(ev, p, q);
-#endif
- if (lseek_flush_read(desc, &err
-#ifdef USE_VM_PROBES
- , dt_priv, dt_utag
-#endif
- ) < 0) {
- reply_posix_error(desc, err);
- goto done;
- }
- if (flush_write_check_error(desc, &err
-#ifdef USE_VM_PROBES
- , dt_priv, dt_utag
-#endif
- ) < 0) {
- reply_posix_error(desc, err);
- goto done;
- }
- /* Create the thread data structure with the contained ErlIOVec
- * and corresponding binaries for the response
- */
- vsize = 2;
- d = EF_ALLOC(sizeof(*d) +
- vsize*(sizeof(*res_ev->iov) + sizeof(*res_ev->binv)));
- if (! d) {
- reply_posix_error(desc, ENOMEM);
- goto done;
- }
- d->command = command;
- d->reply = !0;
- d->fd = desc->fd;
- d->flags = desc->flags;
- d->c.preadv.offsets[0] = hdr_offset;
- d->c.preadv.size = max_size;
-#ifdef USE_VM_PROBES
- dt_i1 = d->fd;
- dt_i2 = d->flags;
- dt_i3 = d->c.preadv.offsets[0];
- dt_i4 = d->c.preadv.size;
-#endif
- res_ev = &d->c.preadv.eiov;
- /* XXX possible alignment problems here for weird machines */
- res_ev->iov = void_ptr = d + 1;
- res_ev->binv = void_ptr = res_ev->iov + vsize;
- res_ev->size = 0;
- res_ev->vsize = 0;
- d->invoke = invoke_ipread;
- d->free = free_preadv;
- d->level = 1;
- cq_enq(desc, d);
- } goto done; /* case FILE_IPREAD: */
-
- case FILE_SETOPT: {
- char opt;
-
- if (ev->size < 1+1
- || !EV_GET_CHAR(ev, &opt, &p, &q)) {
- /* Buffer too short to contain even the option type */
- reply_posix_error(desc, EINVAL);
- goto done;
- }
-#ifdef USE_VM_PROBES
- dt_i1 = opt;
- dt_utag = EV_CHAR_P(ev, p, q);
-#endif
- switch (opt) {
- case FILE_OPT_DELAYED_WRITE: {
- Uint32 sizeH, sizeL, delayH, delayL;
- if (ev->size != 1+1+4*sizeof(Uint32)
-#ifdef USE_VM_PROBES
- + FILENAME_BYTELEN(dt_utag) + FILENAME_CHARSIZE
-#endif
- || !EV_GET_UINT32(ev, &sizeH, &p, &q)
- || !EV_GET_UINT32(ev, &sizeL, &p, &q)
- || !EV_GET_UINT32(ev, &delayH, &p, &q)
- || !EV_GET_UINT32(ev, &delayL, &p, &q)) {
- /* Buffer has wrong length to contain the option values */
- reply_posix_error(desc, EINVAL);
- goto done;
- }
-#if SIZEOF_SIZE_T == 4
- if (sizeH != 0) {
- reply_posix_error(desc, EINVAL);
- goto done;
- }
- desc->write_bufsize = sizeL;
-#else
- desc->write_bufsize = ((size_t)sizeH << 32) | sizeL;
-#endif
-#if SIZEOF_LONG == 4
- if (delayH != 0) {
- reply_posix_error(desc, EINVAL);
- goto done;
- }
- desc->write_delay = delayL;
-#else
- desc->write_delay = ((unsigned long)delayH << 32) | delayL;
-#endif
-#ifdef USE_VM_PROBES
- dt_i2 = desc->write_delay;
-#endif
- TRACE_C('K');
- reply_ok(desc);
- } goto done;
- case FILE_OPT_READ_AHEAD: {
- Uint32 sizeH, sizeL;
- if (ev->size != 1+1+2*sizeof(Uint32)
-#ifdef USE_VM_PROBES
- + FILENAME_BYTELEN(dt_utag)+FILENAME_CHARSIZE
-#endif
- || !EV_GET_UINT32(ev, &sizeH, &p, &q)
- || !EV_GET_UINT32(ev, &sizeL, &p, &q)) {
- /* Buffer has wrong length to contain the option values */
- reply_posix_error(desc, EINVAL);
- goto done;
- }
-#if SIZEOF_SIZE_T == 4
- if (sizeH != 0) {
- reply_posix_error(desc, EINVAL);
- goto done;
- }
- desc->read_bufsize = sizeL;
-#else
- desc->read_bufsize = ((size_t)sizeH << 32) | sizeL;
-#endif
-#ifdef USE_VM_PROBES
- dt_i2 = desc->read_bufsize;
-#endif
- TRACE_C('K');
- reply_ok(desc);
- } goto done;
- default:
- reply_posix_error(desc, EINVAL);
- goto done;
- } /* case FILE_OPT_DELAYED_WRITE: */
- } ASSERT(0); goto done; /* case FILE_SETOPT: */
-
- case FILE_SENDFILE: {
-
-#ifdef HAVE_SENDFILE
- struct t_data *d;
- Uint32 out_fd, offsetH, offsetL, hd_len, tl_len;
- Uint64 nbytes;
- char flags;
-
- if (ev->size < 1 + 7 * sizeof(Uint32) + sizeof(char)
- || !EV_GET_UINT32(ev, &out_fd, &p, &q)
- || !EV_GET_CHAR(ev, &flags, &p, &q)
- || !EV_GET_UINT32(ev, &offsetH, &p, &q)
- || !EV_GET_UINT32(ev, &offsetL, &p, &q)
- || !EV_GET_UINT64(ev, &nbytes, &p, &q)
- || !EV_GET_UINT32(ev, &hd_len, &p, &q)
- || !EV_GET_UINT32(ev, &tl_len, &p, &q)) {
- /* Buffer has wrong length to contain all the needed values */
- reply_posix_error(desc, EINVAL);
- goto done;
- }
-
- if (hd_len != 0 || tl_len != 0) {
- /* We do not allow header, trailers */
- reply_posix_error(desc, EINVAL);
- goto done;
- }
-
-
- if (flags & SENDFILE_FLGS_USE_THREADS && !THRDS_AVAILABLE) {
- /* We do not allow use_threads flag on a system where
- no threads are available. */
- reply_posix_error(desc, EINVAL);
- goto done;
- }
-
- d = EF_SAFE_ALLOC(sizeof(struct t_data));
- d->fd = desc->fd;
- d->command = command;
- d->invoke = invoke_sendfile;
- d->free = free_sendfile;
- d->flags = flags;
- d->level = 2;
-
- d->c.sendfile.out_fd = (int) out_fd;
- d->c.sendfile.written = 0;
- d->c.sendfile.port = desc->port;
- d->c.sendfile.q_mtx = desc->q_mtx;
-
- #if SIZEOF_OFF_T == 4
- if (offsetH != 0) {
- reply_posix_error(desc, EINVAL);
- goto done;
- }
- d->c.sendfile.offset = (off_t) offsetL;
- #else
- d->c.sendfile.offset = ((off_t) offsetH << 32) | offsetL;
- #endif
-
- d->c.sendfile.nbytes = nbytes;
-
- if (USE_THRDS_FOR_SENDFILE(d)) {
- SET_BLOCKING(d->c.sendfile.out_fd);
- } else {
- /**
- * Write a place holder to queue in order to force file_flush
- * to be called before the driver is closed.
- */
- char tmp[1] = "";
- MUTEX_LOCK(d->c.sendfile.q_mtx);
- if (driver_enq(d->c.sendfile.port, tmp, 1)) {
- MUTEX_UNLOCK(d->c.sendfile.q_mtx);
- reply_posix_error(desc, ENOMEM);
- goto done;
- }
- MUTEX_UNLOCK(d->c.sendfile.q_mtx);
- }
-
- cq_enq(desc, d);
-#else
- reply_posix_error(desc, ENOTSUP);
-#endif
- goto done;
- } /* case FILE_SENDFILE: */
-
- } /* switch(command) */
-
- if (lseek_flush_read(desc, &err
-#ifdef USE_VM_PROBES
- , dt_priv, dt_utag
-#endif
- ) < 0) {
- reply_posix_error(desc, err);
- goto done;
- }
- if (flush_write_check_error(desc, &err
-#ifdef USE_VM_PROBES
- , dt_priv, dt_utag
-#endif
- ) < 0) {
- reply_posix_error(desc, err);
- goto done;
- } else {
- /* Flatten buffer and send it to file_output(desc, buf, len) */
- int len = ev->size;
- char *buf = EF_ALLOC(len);
- if (! buf) {
- reply_posix_error(desc, ENOMEM);
- goto done;
- }
- driver_vec_to_buf(ev, buf, len);
- file_output((ErlDrvData) desc, buf, len);
- EF_FREE(buf);
- goto done;
- }
-
- done:
- if (d != NULL) {
-#ifdef USE_VM_PROBES
- /*
- * If d == NULL, then either:
- * 1). There was an error of some sort, or
- * 2). The command given to us is actually implemented
- * by file_output() instead.
- *
- * Case #1 is probably a TODO item, perhaps?
- * Case #2 we definitely don't want to activate a probe.
- */
- d->sched_i1 = dt_priv->thread_num;
- d->sched_i2 = dt_priv->tag;
- d->sched_utag[0] = '\0';
- if (dt_utag != NULL) {
- if (dt_utag[0] == '\0') {
- dt_utag = NULL;
- } else {
- strncpy(d->sched_utag, dt_utag, sizeof(d->sched_utag) - 1);
- d->sched_utag[sizeof(d->sched_utag) - 1] = '\0';
- }
- }
- DTRACE11(efile_drv_entry, dt_priv->thread_num, dt_priv->tag++,
- dt_utag, command, dt_s1, NULL, dt_i1, dt_i2, dt_i3, dt_i4,
- desc->port_str);
-#endif
- }
- cq_execute(desc);
-}
-
-#ifdef USE_VM_PROBES
-dt_private *
-get_dt_private(int base)
-{
- dt_private *dt_priv = (dt_private *) pthread_getspecific(dt_driver_key);
-
- if (dt_priv == NULL) {
- dt_priv = EF_SAFE_ALLOC(sizeof(dt_private));
- erts_mtx_lock(&dt_driver_mutex);
- dt_priv->thread_num = (base + dt_driver_idnum++);
- erts_mtx_unlock(&dt_driver_mutex);
- dt_priv->tag = 0;
- pthread_setspecific(dt_driver_key, dt_priv);
- }
- return dt_priv;
-}
-#endif /* USE_VM_PROBES */
diff --git a/erts/emulator/drivers/common/erl_efile.h b/erts/emulator/drivers/common/erl_efile.h
deleted file mode 100644
index b7f063b4f2..0000000000
--- a/erts/emulator/drivers/common/erl_efile.h
+++ /dev/null
@@ -1,176 +0,0 @@
-/*
- * %CopyrightBegin%
- *
- * Copyright Ericsson AB 1997-2016. All Rights Reserved.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * 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%
- */
-/*
- * Defines the interfaces between the generic efile driver and its
- * operating-system dependent helpers.
- */
-
-#include "sys.h"
-#include "erl_driver.h"
-
-/*
- * Open modes for efile_openfile().
- */
-#define EFILE_MODE_READ 1
-#define EFILE_MODE_WRITE 2 /* Implies truncating file when used alone. */
-#define EFILE_MODE_READ_WRITE 3
-#define EFILE_MODE_APPEND 4
-#define EFILE_COMPRESSED 8
-#define EFILE_MODE_EXCL 16
-#define EFILE_NO_TRUNCATE 32 /* Special for reopening on VxWorks */
-#define EFILE_MODE_SYNC 64
-
-/*
- * Seek modes for efile_seek().
- */
-#define EFILE_SEEK_SET 0
-#define EFILE_SEEK_CUR 1
-#define EFILE_SEEK_END 2
-
-/*
- * File types returned by efile_fileinfo().
- */
-#define FT_DEVICE 1
-#define FT_DIRECTORY 2
-#define FT_REGULAR 3
-#define FT_SYMLINK 4
-#define FT_OTHER 5
-
-/*
- * Access attributes returned by efile_fileinfo() (the bits can be ORed
- * together).
- */
-#define FA_NONE 0
-#define FA_WRITE 1
-#define FA_READ 2
-
-/* Some OS'es (i.e. Windows) has filenames in wide charaqcters. That requires special handling */
-/* Note that we do *not* honor alignment in the communication to the OS specific driver, */
-/* which is not a problem on x86, but might be on other platforms. The OS specific efile */
-/* implementation is expected to align if needed */
-#ifdef __WIN32__
-#define FILENAMES_16BIT 1
-#endif
-
-/* We use sendfilev if it exist on solaris */
-#if !defined(HAVE_SENDFILE) && defined(HAVE_SENDFILEV)
-#define HAVE_SENDFILE
-#endif
-
-/*
- * An handle to an open directory. To be cast to the correct type
- * in the system-dependent directory functions.
- */
-
-typedef struct _Efile_Dir_Handle* EFILE_DIR_HANDLE;
-
-/*
- * Error information from the last call.
- */
-typedef struct _Efile_error {
- int posix_errno; /* Posix error number, as in <errno.h>. */
- int os_errno; /* Os-dependent error number (not used). */
-} Efile_error;
-
-/*
- * Describes what is returned by file:file_info/1.
- */
-
-typedef struct _Efile_info {
- Uint32 size_low; /* Size of file, lower 32 bits.. */
- Uint32 size_high; /* Size of file, higher 32 bits. */
- Uint32 type; /* Type of file -- one of FT_*. */
- Uint32 access; /* Access to file -- one of FA_*. */
- Uint32 mode; /* Access permissions -- bit field. */
- Uint32 links; /* Number of links to file. */
- Uint32 major_device; /* Major device or file system. */
- Uint32 minor_device; /* Minor device (for devices). */
- Uint32 inode; /* Inode number. */
- Uint32 uid; /* User id of owner. */
- Uint32 gid; /* Group id of owner. */
- Sint64 accessTime; /* Last time the file was accessed. */
- Sint64 modifyTime; /* Last time the file was modified. */
- Sint64 cTime; /* Creation time (Windows) or last
- * inode change (Unix).
- */
-} Efile_info;
-
-
-#ifdef HAVE_SENDFILE
-/*
- * Describes the structure of headers/trailers for sendfile
- */
-struct t_sendfile_hdtl {
- SysIOVec *headers;
- int hdr_cnt;
- SysIOVec *trailers;
- int trl_cnt;
-};
-#endif /* HAVE_SENDFILE */
-
-/*
- * Functions.
- */
-int efile_init(void);
-int efile_mkdir(Efile_error* errInfo, char* name);
-int efile_rmdir(Efile_error* errInfo, char* name);
-int efile_delete_file(Efile_error* errInfo, char* name);
-int efile_rename(Efile_error* errInfo, char* src, char* dst);
-int efile_chdir(Efile_error* errInfo, char* name);
-int efile_getdcwd(Efile_error* errInfo, int drive,
- char* buffer, size_t size);
-int efile_readdir(Efile_error* errInfo, char* name,
- EFILE_DIR_HANDLE* dir_handle,
- char* buffer, size_t *size);
-int efile_openfile(Efile_error* errInfo, char* name, int flags,
- int* pfd, Sint64* pSize);
-void efile_closefile(int fd);
-int efile_fdatasync(Efile_error* errInfo, int fd);
-int efile_fsync(Efile_error* errInfo, int fd);
-int efile_fileinfo(Efile_error* errInfo, Efile_info* pInfo,
- char *name, int info_for_link);
-int efile_write_info(Efile_error* errInfo, Efile_info* pInfo, char *name);
-int efile_write(Efile_error* errInfo, int flags, int fd,
- char* buf, size_t count);
-int efile_writev(Efile_error* errInfo, int flags, int fd,
- SysIOVec* iov, int iovcnt);
-int efile_read(Efile_error* errInfo, int flags, int fd,
- char* buf, size_t count, size_t* pBytesRead);
-int efile_seek(Efile_error* errInfo, int fd,
- Sint64 offset, int origin, Sint64* new_location);
-int efile_truncate_file(Efile_error* errInfo, int *fd, int flags);
-int efile_pwrite(Efile_error* errInfo, int fd,
- char* buf, size_t count, Sint64 offset);
-int efile_pread(Efile_error* errInfo, int fd,
- Sint64 offset, char* buf, size_t count, size_t* pBytesRead);
-int efile_readlink(Efile_error* errInfo, char *name,
- char* buffer, size_t size);
-int efile_altname(Efile_error* errInfo, char *name,
- char* buffer, size_t size);
-int efile_link(Efile_error* errInfo, char* old, char* new);
-int efile_symlink(Efile_error* errInfo, char* old, char* new);
-int efile_may_openfile(Efile_error* errInfo, char *name);
-int efile_fadvise(Efile_error* errInfo, int fd, Sint64 offset, Sint64 length,
- int advise);
-#ifdef HAVE_SENDFILE
-int efile_sendfile(Efile_error* errInfo, int in_fd, int out_fd,
- off_t *offset, Uint64 *nbytes, struct t_sendfile_hdtl *hdtl);
-#endif /* HAVE_SENDFILE */
-int efile_fallocate(Efile_error* errInfo, int fd, Sint64 offset, Sint64 length);
diff --git a/erts/emulator/drivers/common/gzio.c b/erts/emulator/drivers/common/gzio.c
index f60c781894..86c3b07cea 100644
--- a/erts/emulator/drivers/common/gzio.c
+++ b/erts/emulator/drivers/common/gzio.c
@@ -19,726 +19,16 @@
#include <unistd.h>
#endif
#include <ctype.h>
+
#include "erl_driver.h"
-#include "erl_efile.h"
#include "sys.h"
-#ifdef __WIN32__
-#ifndef HAVE_CONFLICTING_FREAD_DECLARATION
-#define HAVE_CONFLICTING_FREAD_DECLARATION
-#endif
-#define FILENAMES_16BIT 1
-#endif
-
-#ifdef STDC
-# define zstrerror(errnum) strerror(errnum)
-#else
-# define zstrerror(errnum) ""
-#endif
-
#include "gzio_zutil.h"
#include "erl_zlib.h"
#include "gzio.h"
-/********struct internal_state {int dummy;}; / * for buggy compilers */
-
-#define Z_BUFSIZE 4096
-
-#define ALLOC(size) driver_alloc(size)
-#define TRYFREE(p) {if (p) driver_free(p);}
-
static int gz_magic[2] = {0x1f, 0x8b}; /* gzip magic header */
-/* gzip flag byte */
-#define ASCII_FLAG 0x01 /* bit 0 set: file probably ascii text */
-#define HEAD_CRC 0x02 /* bit 1 set: header CRC present */
-#define EXTRA_FIELD 0x04 /* bit 2 set: extra field present */
-#define ORIG_NAME 0x08 /* bit 3 set: original file name present */
-#define COMMENT 0x10 /* bit 4 set: file comment present */
-#define RESERVED 0xE0 /* bits 5..7: reserved */
-
-typedef struct gz_stream {
- z_stream stream;
- int z_err; /* error code for last stream operation */
- int z_eof; /* set if end of input file */
-#ifdef UNIX
- int file; /* .gz file descriptor */
-#else
- FILE *file; /* .gz file */
-#endif
- Byte *inbuf; /* input buffer */
- Byte *outbuf; /* output buffer */
- uLong crc; /* crc32 of uncompressed data */
- char *msg; /* error message */
- char *path; /* path name for debugging only */
- int transparent; /* 1 if input file is not a .gz file */
- char mode; /* 'w' or 'r' */
- int position; /* Position (for seek) */
- int (*destroy)(struct gz_stream*); /* Function to destroy
- * this structure. */
-} gz_stream;
-
-local ErtsGzFile gz_open (const char *path, const char *mode);
-local int get_byte (gz_stream *s);
-local void check_header (gz_stream *s);
-local int destroy (gz_stream *s);
-local uLong getLong (gz_stream *s);
-
-#ifdef UNIX
-/*
- * In Solaris 8 and earlier, fopen() and its friends cannot handle
- * file descriptors larger than 255. Therefore, we use read()/write()
- * on all Unix systems.
- */
-# define ERTS_GZWRITE(File, Buf, Count) write((File), (Buf), (Count))
-# define ERTS_GZREAD(File, Buf, Count) read((File), (Buf), (Count))
-#else
-/*
- * On all other operating systems, using fopen(), fread()/fwrite(), since
- * there is not guaranteed to exist any read()/write() (not part of
- * ANSI/ISO-C).
- */
-# define ERTS_GZWRITE(File, Buf, Count) fwrite((Buf), 1, (Count), (File))
-# define ERTS_GZREAD(File, Buf, Count) fread((Buf), 1, (Count), (File))
-#endif
-
-/*
- * Ripped from efile_drv.c
- */
-
-#ifdef FILENAMES_16BIT
-# define FILENAME_BYTELEN(Str) filename_len_16bit(Str)
-# define FILENAME_COPY(To,From) filename_cpy_16bit((To),(From))
-# define FILENAME_CHARSIZE 2
-
- static int filename_len_16bit(const char *str)
- {
- const char *p = str;
- while(*p != '\0' || p[1] != '\0') {
- p += 2;
- }
- return (p - str);
- }
-
- static void filename_cpy_16bit(char *to, const char *from)
- {
- while(*from != '\0' || from[1] != '\0') {
- *to++ = *from++;
- *to++ = *from++;
- }
- *to++ = *from++;
- *to++ = *from++;
- }
-
-#else
-# define FILENAME_BYTELEN(Str) strlen(Str)
-# define FILENAME_COPY(To,From) strcpy(To,From)
-# define FILENAME_CHARSIZE 1
-#endif
-
-/* ===========================================================================
- Opens a gzip (.gz) file for reading or writing. The mode parameter
- is as in fopen ("rb" or "wb"). The file is given either by file descriptor
- or path name (if fd == -1).
- gz_open return NULL if the file could not be opened or if there was
- insufficient memory to allocate the (de)compression state; errno
- can be checked to distinguish the two cases (if errno is zero, the
- zlib error is Z_MEM_ERROR).
-*/
-local ErtsGzFile gz_open (path, mode)
- const char *path;
- const char *mode;
-{
- int err;
- int level = Z_DEFAULT_COMPRESSION; /* compression level */
- char *p = (char*)mode;
- gz_stream *s;
- char fmode[80]; /* copy of mode, without the compression level */
- char *m = fmode;
-
- if (!path || !mode) return Z_NULL;
-
- s = (gz_stream *)ALLOC(sizeof(gz_stream));
- if (!s) return Z_NULL;
-
- erl_zlib_alloc_init(&s->stream);
- s->stream.next_in = s->inbuf = Z_NULL;
- s->stream.next_out = s->outbuf = Z_NULL;
- s->stream.avail_in = s->stream.avail_out = 0;
-#ifdef UNIX
- s->file = -1;
-#else
- s->file = NULL;
-#endif
- s->z_err = Z_OK;
- s->z_eof = 0;
- s->crc = crc32(0L, Z_NULL, 0);
- s->msg = NULL;
- s->transparent = 0;
- s->position = 0;
- s->destroy = destroy;
-
- s->path = (char*)ALLOC(FILENAME_BYTELEN(path)+FILENAME_CHARSIZE);
- if (s->path == NULL) {
- return s->destroy(s), (ErtsGzFile)Z_NULL;
- }
- FILENAME_COPY(s->path, path); /* do this early for debugging */
-
- s->mode = '\0';
- do {
- if (*p == 'r')
- s->mode = 'r';
- if (*p == 'w' || *p == 'a')
- s->mode = 'w';
- if (isdigit((int)*p)) {
- level = *p - '0';
- } else {
- *m++ = *p; /* Copy the mode */
- }
- } while (*p++ && m < fmode + sizeof(fmode) - 1);
- *m = '\0';
- if (s->mode == '\0')
- return s->destroy(s), (ErtsGzFile)Z_NULL;
-
- if (s->mode == 'w') {
- err = deflateInit2(&(s->stream), level,
- Z_DEFLATED, MAX_WBITS+16, DEF_MEM_LEVEL, 0);
- /* windowBits is passed < 0 to suppress zlib header */
-
- s->stream.next_out = s->outbuf = (Byte*)ALLOC(Z_BUFSIZE);
-
- if (err != Z_OK || s->outbuf == Z_NULL) {
- return s->destroy(s), (ErtsGzFile)Z_NULL;
- }
- } else {
- /*
- * It is tempting to use the built-in support in zlib
- * for handling GZIP headers, but unfortunately it
- * cannot handle multiple GZIP headers (which occur when
- * several GZIP files have been concatenated).
- */
-
- err = inflateInit2(&(s->stream), -MAX_WBITS);
- s->stream.next_in = s->inbuf = (Byte*)ALLOC(Z_BUFSIZE);
-
- if (err != Z_OK || s->inbuf == Z_NULL) {
- return s->destroy(s), (ErtsGzFile)Z_NULL;
- }
- }
- s->stream.avail_out = Z_BUFSIZE;
-
- errno = 0;
-#if defined(FILENAMES_16BIT)
- {
- FILE* efile_wfopen(const WCHAR* name, const WCHAR* mode);
- WCHAR wfmode[80];
- int i = 0;
- int j;
- for(j = 0; fmode[j] != '\0'; ++j) {
- wfmode[i++] = (WCHAR) fmode[j];
- }
- wfmode[i++] = L'\0';
- s->file = efile_wfopen((WCHAR *)path, wfmode);
- if (s->file == NULL) {
- return s->destroy(s), (ErtsGzFile)Z_NULL;
- }
- }
-#elif defined(UNIX)
- if (s->mode == 'r') {
- s->file = open(path, O_RDONLY);
- } else {
- s->file = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0666);
- }
- if (s->file == -1) {
- return s->destroy(s), (ErtsGzFile)Z_NULL;
- }
-#else
- s->file = fopen(path, fmode);
- if (s->file == NULL) {
- return s->destroy(s), (ErtsGzFile)Z_NULL;
- }
-#endif
- if (s->mode == 'r') {
- check_header(s); /* skip the .gz header */
- }
- return (ErtsGzFile)s;
-}
-
-/* ===========================================================================
- Rewind a gzfile back to the beginning.
-*/
-
-local int gz_rewind (gz_stream *s)
-{
- TRYFREE(s->msg);
-
-#ifdef UNIX
- lseek(s->file, 0L, SEEK_SET);
-#else
- fseek(s->file, 0L, SEEK_SET);
-#endif
- inflateReset(&(s->stream));
- s->stream.next_in = Z_NULL;
- s->stream.next_out = Z_NULL;
- s->stream.avail_in = s->stream.avail_out = 0;
- s->z_err = Z_OK;
- s->z_eof = 0;
- s->crc = crc32(0L, Z_NULL, 0);
- s->msg = NULL;
- s->position = 0;
- s->stream.next_in = s->inbuf;
-
- s->stream.avail_out = Z_BUFSIZE;
-
- check_header(s); /* skip the .gz header */
- return 1;
-}
-
-/* ===========================================================================
- Opens a gzip (.gz) file for reading or writing.
-*/
-ErtsGzFile erts_gzopen (path, mode)
- const char *path;
- const char *mode;
-{
- return gz_open (path, mode);
-}
-
-
-/* ===========================================================================
- Read a byte from a gz_stream; update next_in and avail_in. Return EOF
- for end of file.
- IN assertion: the stream s has been successfully opened for reading.
-*/
-local int get_byte(s)
- gz_stream *s;
-{
- if (s->z_eof) return EOF;
- if (s->stream.avail_in == 0) {
-#ifdef UNIX
- ssize_t res;
- errno = 0;
- res = ERTS_GZREAD(s->file, s->inbuf, Z_BUFSIZE);
- if (res == 0) {
- s->stream.avail_in = 0;
- s->z_eof = 1;
- return EOF;
- } else if (res < 0) {
- s->stream.avail_in = 0;
- s->z_eof = 1;
- s->z_err = Z_ERRNO;
- return EOF;
- } else {
- s->stream.avail_in = (uInt) res;
- }
-#else
- errno = 0;
- s->stream.avail_in = ERTS_GZREAD(s->file, s->inbuf, Z_BUFSIZE);
- if (s->stream.avail_in == 0) {
- s->z_eof = 1;
- if (s->file && ferror(s->file))
- s->z_err = Z_ERRNO;
- return EOF;
- }
-#endif
- s->stream.next_in = s->inbuf;
- }
- s->stream.avail_in--;
- return *(s->stream.next_in)++;
-}
-
-/* ===========================================================================
- Check the gzip header of a gz_stream opened for reading. Set the stream
- mode to transparent if the gzip magic header is not present; set s->err
- to Z_DATA_ERROR if the magic header is present but the rest of the header
- is incorrect.
- IN assertion: the stream s has already been created sucessfully;
- s->stream.avail_in is zero for the first time, but may be non-zero
- for concatenated .gz files.
-*/
-local void check_header(s)
- gz_stream *s;
-{
- int method; /* method byte */
- int flags; /* flags byte */
- uInt len;
- int c;
-
- /* Check the gzip magic header */
- for (len = 0; len < 2; len++) {
- c = get_byte(s);
- if (c != gz_magic[len]) {
- if (len != 0) s->stream.avail_in++, s->stream.next_in--;
- if (c != EOF) {
- s->stream.avail_in++, s->stream.next_in--;
- s->transparent = 1;
- }
- s->z_err = s->stream.avail_in != 0 ? Z_OK : Z_STREAM_END;
- return;
- }
- }
- method = get_byte(s);
- flags = get_byte(s);
- if (method != Z_DEFLATED || (flags & RESERVED) != 0) {
- s->z_err = Z_DATA_ERROR;
- return;
- }
-
- /* Discard time, xflags and OS code: */
- for (len = 0; len < 6; len++) (void)get_byte(s);
-
- if ((flags & EXTRA_FIELD) != 0) { /* skip the extra field */
- len = (uInt)get_byte(s);
- len += ((uInt)get_byte(s))<<8;
- /* len is garbage if EOF but the loop below will quit anyway */
- while (len-- != 0 && get_byte(s) != EOF) ;
- }
- if ((flags & ORIG_NAME) != 0) { /* skip the original file name */
- while ((c = get_byte(s)) != 0 && c != EOF) ;
- }
- if ((flags & COMMENT) != 0) { /* skip the .gz file comment */
- while ((c = get_byte(s)) != 0 && c != EOF) ;
- }
- if ((flags & HEAD_CRC) != 0) { /* skip the header crc */
- for (len = 0; len < 2; len++) (void)get_byte(s);
- }
- s->z_err = s->z_eof ? Z_DATA_ERROR : Z_OK;
-}
-
- /* ===========================================================================
- * Cleanup then free the given gz_stream. Return a zlib error code.
- Try freeing in the reverse order of allocations.
- */
-local int destroy (s)
- gz_stream *s;
-{
- int err = Z_OK;
-
- if (!s) return Z_STREAM_ERROR;
-
- TRYFREE(s->msg);
-
- if (s->stream.state != NULL) {
- if (s->mode == 'w') {
- err = deflateEnd(&(s->stream));
- } else if (s->mode == 'r') {
- err = inflateEnd(&(s->stream));
- }
- }
-#ifdef UNIX
- if (s->file != -1 && close(s->file)) {
- err = Z_ERRNO;
- }
-#else
- if (s->file != NULL && fclose(s->file)) {
- err = Z_ERRNO;
- }
-#endif
- if (s->z_err < 0) err = s->z_err;
-
- TRYFREE(s->inbuf);
- TRYFREE(s->outbuf);
- TRYFREE(s->path);
- TRYFREE(s);
- return err;
-}
-
-/* ===========================================================================
- Reads the given number of uncompressed bytes from the compressed file.
- gzread returns the number of bytes actually read (0 for end of file).
-*/
-int
-erts_gzread(ErtsGzFile file, voidp buf, unsigned len)
-{
- gz_stream *s = (gz_stream*)file;
- Bytef *start = buf; /* starting point for crc computation */
- Byte *next_out; /* == stream.next_out but not forced far (for MSDOS) */
-
- if (s == NULL || s->mode != 'r') return Z_STREAM_ERROR;
-
- if (s->z_err == Z_DATA_ERROR || s->z_err == Z_ERRNO) return -1;
- if (s->z_err == Z_STREAM_END) return 0; /* EOF */
-
- s->stream.next_out = next_out = buf;
- s->stream.avail_out = len;
-
- while (s->stream.avail_out != 0) {
-
- if (s->transparent) {
- /* Copy first the lookahead bytes: */
- uInt n = s->stream.avail_in;
- if (n > s->stream.avail_out) n = s->stream.avail_out;
- if (n > 0) {
- zmemcpy(s->stream.next_out, s->stream.next_in, n);
- next_out += n;
- s->stream.next_out = next_out;
- s->stream.next_in += n;
- s->stream.avail_out -= n;
- s->stream.avail_in -= n;
- }
- if (s->stream.avail_out > 0) {
- s->stream.avail_out -= ERTS_GZREAD(s->file, next_out,
- s->stream.avail_out);
- }
- len -= s->stream.avail_out;
- s->stream.total_in += (uLong)len;
- s->stream.total_out += (uLong)len;
- if (len == 0) s->z_eof = 1;
- s->position += (int)len;
- return (int)len;
- }
- if (s->stream.avail_in == 0 && !s->z_eof) {
-#ifdef UNIX
- ssize_t res;
- errno = 0;
- res = ERTS_GZREAD(s->file, s->inbuf, Z_BUFSIZE);
- if (res == 0) {
- s->stream.avail_in = 0;
- s->z_eof = 1;
- return EOF;
- } else if (res < 0) {
- s->stream.avail_in = 0;
- s->z_eof = 1;
- s->z_err = Z_ERRNO;
- return EOF;
- } else {
- s->stream.avail_in = (uInt) res;
- }
-#else
- errno = 0;
- s->stream.avail_in = ERTS_GZREAD(s->file, s->inbuf, Z_BUFSIZE);
- if (s->stream.avail_in == 0) {
- s->z_eof = 1;
- if (s->file && ferror(s->file)) {
- s->z_err = Z_ERRNO;
- break;
- }
- }
-#endif
- s->stream.next_in = s->inbuf;
- }
- s->z_err = inflate(&(s->stream), Z_NO_FLUSH);
-
- if (s->z_err == Z_STREAM_END) {
- /* Check CRC and original size */
- s->crc = crc32(s->crc, start, (uInt)(s->stream.next_out - start));
- start = s->stream.next_out;
-
- if (getLong(s) != s->crc) {
- s->z_err = Z_DATA_ERROR;
- } else {
- (void)getLong(s);
- /* The uncompressed length returned by above getlong() may
- * be different from s->stream.total_out) in case of
- * concatenated .gz files. Check for such files:
- */
- check_header(s);
- if (s->z_err == Z_OK) {
- uLong total_in = s->stream.total_in;
- uLong total_out = s->stream.total_out;
-
- inflateReset(&(s->stream));
- s->stream.total_in = total_in;
- s->stream.total_out = total_out;
- s->crc = crc32(0L, Z_NULL, 0);
- }
- }
- }
- if (s->z_err != Z_OK || s->z_eof) break;
- }
- s->crc = crc32(s->crc, start, (uInt)(s->stream.next_out - start));
-
- s->position += (int)(len - s->stream.avail_out);
-
- return (int)(len - s->stream.avail_out);
-}
-
-/* ===========================================================================
- Writes the given number of uncompressed bytes into the compressed file.
- gzwrite returns the number of bytes actually written (0 in case of error).
-*/
-int
-erts_gzwrite(ErtsGzFile file, voidp buf, unsigned len)
-{
- gz_stream *s = (gz_stream*)file;
-
- if (s == NULL || s->mode != 'w') return Z_STREAM_ERROR;
-
- s->stream.next_in = buf;
- s->stream.avail_in = len;
-
- while (s->stream.avail_in != 0) {
-
- if (s->stream.avail_out == 0) {
-
- s->stream.next_out = s->outbuf;
- if (ERTS_GZWRITE(s->file, s->outbuf, Z_BUFSIZE) != Z_BUFSIZE) {
- s->z_err = Z_ERRNO;
- break;
- }
- s->stream.avail_out = Z_BUFSIZE;
- }
- s->z_err = deflate(&(s->stream), Z_NO_FLUSH);
- if (s->z_err != Z_OK) break;
- }
- s->position += (int)(len - s->stream.avail_in);
- return (int)(len - s->stream.avail_in);
-}
-
-/*
- * For use by Erlang file driver.
- *
- * XXX Limitations:
- * - SEEK_END is not allowed (length of file is not known).
- * - When writing, only forward seek is supported.
- */
-
-int
-erts_gzseek(ErtsGzFile file, int offset, int whence)
-{
- int pos;
- gz_stream* s = (gz_stream *) file;
-
- switch (whence) {
- case EFILE_SEEK_SET: whence = SEEK_SET; break;
- case EFILE_SEEK_CUR: whence = SEEK_CUR; break;
- case EFILE_SEEK_END: whence = SEEK_END; break;
- default:
- errno = EINVAL;
- return -1;
- }
-
- if (s == NULL) {
- errno = EINVAL;
- return -1;
- }
- if (s->z_err == Z_DATA_ERROR || s->z_err == Z_ERRNO) {
- errno = EIO;
- return -1;
- }
-
- switch (whence) {
- case SEEK_SET: pos = offset; break;
- case SEEK_CUR: pos = s->position+offset; break;
- case SEEK_END:
- default:
- errno = EINVAL; return -1;
- }
-
- if (pos == s->position) {
- return pos;
- }
-
- if (pos < s->position) {
- if (s->mode == 'w') {
- errno = EINVAL;
- return -1;
- }
- gz_rewind(s);
- }
-
- while (s->position < pos) {
- char buf[512];
- int n;
- int save_pos = s->position;
-
- n = pos - s->position;
- if (n > sizeof(buf))
- n = sizeof(buf);
-
- if (s->mode == 'r') {
- erts_gzread(file, buf, n);
- } else {
- memset(buf, '\0', n);
- erts_gzwrite(file, buf, n);
- }
- if (save_pos == s->position) break;
- }
-
- return s->position;
-}
-
-/* ===========================================================================
- Flushes all pending output into the compressed file. The parameter
- flush is as in the deflate() function.
- gzflush should be called only when strictly necessary because it can
- degrade compression.
-*/
-int
-erts_gzflush(ErtsGzFile file, int flush)
-{
- uInt len;
- int done = 0;
- gz_stream *s = (gz_stream*)file;
-
- if (s == NULL || s->mode != 'w') return Z_STREAM_ERROR;
-
- s->stream.avail_in = 0; /* should be zero already anyway */
-
- for (;;) {
- len = Z_BUFSIZE - s->stream.avail_out;
-
- if (len != 0) {
- if ((uInt)ERTS_GZWRITE(s->file, s->outbuf, len) != len) {
- s->z_err = Z_ERRNO;
- return Z_ERRNO;
- }
- s->stream.next_out = s->outbuf;
- s->stream.avail_out = Z_BUFSIZE;
- }
- if (done) break;
- s->z_err = deflate(&(s->stream), flush);
-
- /* deflate has finished flushing only when it hasn't used up
- * all the available space in the output buffer:
- */
- done = (s->stream.avail_out != 0 || s->z_err == Z_STREAM_END);
-
- if (s->z_err != Z_OK && s->z_err != Z_STREAM_END) break;
- }
-#ifndef UNIX
- fflush(s->file);
-#endif
- return s->z_err == Z_STREAM_END ? Z_OK : s->z_err;
-}
-
-/* ===========================================================================
- Reads a long in LSB order from the given gz_stream. Sets
-*/
-local uLong getLong (s)
- gz_stream *s;
-{
- uLong x = (uLong)get_byte(s);
- int c;
-
- x += ((uLong)get_byte(s))<<8;
- x += ((uLong)get_byte(s))<<16;
- c = get_byte(s);
- if (c == EOF) s->z_err = Z_DATA_ERROR;
- x += ((uLong)c)<<24;
- return x;
-}
-
-/* ===========================================================================
- Flushes all pending output if necessary, closes the compressed file
- and deallocates all the (de)compression state.
-*/
-int
-erts_gzclose(ErtsGzFile file)
-{
- int err;
- gz_stream *s = (gz_stream*)file;
-
- if (s == NULL) return Z_STREAM_ERROR;
-
- if (s->mode == 'w') {
- err = erts_gzflush (file, Z_FINISH);
- if (err != Z_OK) return s->destroy(s);
- }
- return s->destroy(s);
-}
-
-
/* ===========================================================================
Uncompresses the buffer given and returns a pointer to a binary.
If the buffer was not compressed with gzip, the buffer contents
diff --git a/erts/emulator/drivers/common/gzio.h b/erts/emulator/drivers/common/gzio.h
index ee0ebe7bd8..e331b5208b 100644
--- a/erts/emulator/drivers/common/gzio.h
+++ b/erts/emulator/drivers/common/gzio.h
@@ -20,13 +20,5 @@
#include "zlib.h"
-typedef struct erts_gzFile* ErtsGzFile;
-
-ErtsGzFile erts_gzopen (const char *path, const char *mode);
-int erts_gzread(ErtsGzFile file, voidp buf, unsigned len);
-int erts_gzwrite(ErtsGzFile file, voidp buf, unsigned len);
-int erts_gzseek(ErtsGzFile, int, int);
-int erts_gzflush(ErtsGzFile file, int flush);
-int erts_gzclose(ErtsGzFile file);
ErlDrvBinary* erts_gzinflate_buffer(char*, uLong);
ErlDrvBinary* erts_gzdeflate_buffer(char*, uLong);
diff --git a/erts/emulator/drivers/common/inet_drv.c b/erts/emulator/drivers/common/inet_drv.c
index 7b1f4a0e9c..1a68f65b52 100644
--- a/erts/emulator/drivers/common/inet_drv.c
+++ b/erts/emulator/drivers/common/inet_drv.c
@@ -63,6 +63,20 @@
#include <sys/un.h>
#endif
+#ifdef HAVE_SENDFILE
+#if defined(__linux__) || (defined(__sun) && defined(__SVR4))
+ #include <sys/sendfile.h>
+#elif defined(__FreeBSD__) || defined(__DragonFly__)
+ /* Need to define __BSD_VISIBLE in order to expose prototype of sendfile */
+ #define __BSD_VISIBLE 1
+ #include <sys/socket.h>
+#endif
+#endif
+
+#if defined(__APPLE__) && defined(__MACH__) && !defined(__DARWIN__)
+ #define __DARWIN__ 1
+#endif
+
/* All platforms fail on malloc errors. */
#define FATAL_MALLOC
@@ -701,6 +715,7 @@ static size_t my_strnlen(const char *s, size_t maxlen)
#define TCP_REQ_RECV 42
#define TCP_REQ_UNRECV 43
#define TCP_REQ_SHUTDOWN 44
+#define TCP_REQ_SENDFILE 45
/* UDP and SCTP requests */
#define PACKET_REQ_RECV 60 /* Common for UDP and SCTP */
/* #define SCTP_REQ_LISTEN 61 MERGED Different from TCP; not for UDP */
@@ -723,6 +738,7 @@ static size_t my_strnlen(const char *s, size_t maxlen)
#define TCP_ADDF_DELAYED_ECONNRESET 128 /* An ECONNRESET error occurred on send or shutdown */
#define TCP_ADDF_SHUTDOWN_WR_DONE 256 /* A shutdown(sock, SHUT_WR) or SHUT_RDWR was made */
#define TCP_ADDF_LINGER_ZERO 512 /* Discard driver queue on port close */
+#define TCP_ADDF_SENDFILE 1024 /* Send from an fd instead of the driver queue */
/* *_REQ_* replies */
#define INET_REP_ERROR 0
@@ -1136,7 +1152,6 @@ static int packet_inet_init(void);
static void packet_inet_stop(ErlDrvData);
static void packet_inet_command(ErlDrvData, char*, ErlDrvSizeT);
static void packet_inet_drv_input(ErlDrvData data, ErlDrvEvent event);
-static void packet_inet_drv_output(ErlDrvData data, ErlDrvEvent event);
static ErlDrvData udp_inet_start(ErlDrvPort, char* command);
#ifdef HAVE_SCTP
static ErlDrvData sctp_inet_start(ErlDrvPort, char* command);
@@ -1161,7 +1176,7 @@ static struct erl_drv_entry udp_inet_driver_entry =
NULL,
#else
packet_inet_drv_input,
- packet_inet_drv_output,
+ NULL,
#endif
"udp_inet",
NULL,
@@ -1196,7 +1211,7 @@ static struct erl_drv_entry sctp_inet_driver_entry =
NULL,
#else
packet_inet_drv_input,
- packet_inet_drv_output,
+ NULL,
#endif
"sctp_inet",
NULL,
@@ -1236,6 +1251,21 @@ typedef struct {
inet_async_multi_op *multi_first;/* NULL == no multi-accept-queue, op is in ordinary queue */
inet_async_multi_op *multi_last;
MultiTimerData *mtd; /* Timer structures for multiple accept */
+#ifdef HAVE_SENDFILE
+ struct {
+ ErlDrvSizeT ioq_skip; /* The number of bytes in the queue at the time
+ * sendfile was issued, which must be sent
+ * before issuing the sendfile call itself. */
+ int dup_file_fd; /* The file handle to send from; this is
+ * duplicated when sendfile is issued to
+ * reduce (but not eliminate) the impact of a
+ * nasty race, so we have to remember to close
+ * it. */
+ Uint64 bytes_sent;
+ Uint64 offset;
+ Uint64 length;
+ } sendfile;
+#endif
} tcp_descriptor;
/* send function */
@@ -1246,6 +1276,8 @@ static int tcp_deliver(tcp_descriptor* desc, int len);
static int tcp_shutdown_error(tcp_descriptor* desc, int err);
+static int tcp_inet_sendfile(tcp_descriptor* desc);
+
static int tcp_inet_output(tcp_descriptor* desc, HANDLE event);
static int tcp_inet_input(tcp_descriptor* desc, HANDLE event);
@@ -1262,7 +1294,6 @@ typedef struct {
static int packet_inet_input(udp_descriptor* udesc, HANDLE event);
-static int packet_inet_output(udp_descriptor* udesc, HANDLE event);
#endif
/* convert descriptor pointer to inet_descriptor pointer */
@@ -1331,6 +1362,9 @@ static ErlDrvTermData am_ipv6_v6only;
static ErlDrvTermData am_netns;
static ErlDrvTermData am_bind_to_device;
#endif
+#ifdef HAVE_SENDFILE
+static ErlDrvTermData am_sendfile;
+#endif
static char str_eafnosupport[] = "eafnosupport";
static char str_einval[] = "einval";
@@ -2199,13 +2233,16 @@ static int inet_reply_ok(inet_descriptor* desc)
ErlDrvTermData caller = desc->caller;
int i = 0;
+ desc->caller = 0;
+ if (is_not_internal_pid(caller))
+ return 0;
+
i = LOAD_ATOM(spec, i, am_inet_reply);
i = LOAD_PORT(spec, i, desc->dport);
i = LOAD_ATOM(spec, i, am_ok);
i = LOAD_TUPLE(spec, i, 3);
ASSERT(i == sizeof(spec)/sizeof(*spec));
- desc->caller = 0;
return erl_drv_send_term(desc->dport, caller, spec, i);
}
@@ -3874,6 +3911,10 @@ static int inet_init()
INIT_ATOM(https);
INIT_ATOM(scheme);
+#ifdef HAVE_SENDFILE
+ INIT_ATOM(sendfile);
+#endif
+
/* add TCP, UDP and SCTP drivers */
add_driver_entry(&tcp_inet_driver_entry);
#ifdef HAVE_UDP
@@ -9269,6 +9310,13 @@ static void tcp_inet_stop(ErlDrvData e)
* will be freed through tcp_inet_stop later on. */
static void tcp_desc_close(tcp_descriptor* desc)
{
+#ifdef HAVE_SENDFILE
+ if(desc->tcp_add_flags & TCP_ADDF_SENDFILE) {
+ desc->tcp_add_flags &= ~TCP_ADDF_SENDFILE;
+ close(desc->sendfile.dup_file_fd);
+ }
+#endif
+
tcp_clear_input(desc);
tcp_clear_output(desc);
@@ -9607,6 +9655,60 @@ static ErlDrvSSizeT tcp_inet_ctl(ErlDrvData e, unsigned int cmd,
return ctl_reply(INET_REP_OK, NULL, 0, rbuf, rsize);
}
}
+
+ case TCP_REQ_SENDFILE: {
+#ifdef HAVE_SENDFILE
+ const ErlDrvSizeT required_len =
+ sizeof(desc->sendfile.dup_file_fd) +
+ sizeof(Uint64) * 2;
+
+ int raw_file_fd;
+
+ DEBUGF(("tcp_inet_ctl(%ld): SENDFILE\r\n", (long)desc->inet.port));
+
+ if (len != required_len) {
+ return ctl_error(EINVAL, rbuf, rsize);
+ } else if (!IS_CONNECTED(INETP(desc))) {
+ return ctl_error(ENOTCONN, rbuf, rsize);
+ }
+
+ sys_memcpy(&raw_file_fd, buf, sizeof(raw_file_fd));
+ buf += sizeof(raw_file_fd);
+
+ desc->sendfile.dup_file_fd = dup(raw_file_fd);
+
+ if(desc->sendfile.dup_file_fd == -1) {
+ return ctl_error(errno, rbuf, rsize);
+ }
+
+ desc->sendfile.offset = get_int64(buf);
+ buf += sizeof(Uint64);
+
+ desc->sendfile.length = get_int64(buf);
+ buf += sizeof(Uint64);
+
+ ASSERT(desc->sendfile.offset >= 0);
+ ASSERT(desc->sendfile.length >= 0);
+
+ desc->sendfile.ioq_skip = driver_sizeq(desc->inet.port);
+ desc->sendfile.bytes_sent = 0;
+
+ desc->inet.caller = driver_caller(desc->inet.port);
+ desc->tcp_add_flags |= TCP_ADDF_SENDFILE;
+
+ /* See if we can finish sending without selecting & rescheduling. */
+ tcp_inet_sendfile(desc);
+
+ if(desc->sendfile.length > 0) {
+ sock_select(INETP(desc), FD_WRITE, 1);
+ }
+
+ return ctl_reply(INET_REP_OK, NULL, 0, rbuf, rsize);
+#else
+ return ctl_error(ENOTSUP, rbuf, rsize);
+#endif
+ }
+
default:
DEBUGF(("tcp_inet_ctl(%ld): %u\r\n", (long)desc->inet.port, cmd));
return inet_ctl(INETP(desc), cmd, buf, len, rbuf, rsize);
@@ -9746,12 +9848,27 @@ static void tcp_inet_commandv(ErlDrvData e, ErlIOVec* ev)
static void tcp_inet_flush(ErlDrvData e)
{
tcp_descriptor* desc = (tcp_descriptor*)e;
- if (!(desc->inet.event_mask & FD_WRITE)) {
- /* Discard send queue to avoid hanging port (OTP-7615) */
- tcp_clear_output(desc);
+ int discard_output;
+
+ /* Discard send queue to avoid hanging port (OTP-7615) */
+ discard_output = !(desc->inet.event_mask & FD_WRITE);
+
+ discard_output |= desc->tcp_add_flags & TCP_ADDF_LINGER_ZERO;
+
+#ifdef HAVE_SENDFILE
+ /* The old file driver aborted when it was stopped during sendfile, so
+ * we'll clear the flag and discard all output. */
+ if(desc->tcp_add_flags & TCP_ADDF_SENDFILE) {
+ desc->tcp_add_flags &= ~TCP_ADDF_SENDFILE;
+ close(desc->sendfile.dup_file_fd);
+
+ discard_output = 1;
+ }
+#endif
+
+ if (discard_output) {
+ tcp_clear_output(desc);
}
- if (desc->tcp_add_flags & TCP_ADDF_LINGER_ZERO)
- tcp_clear_output(desc);
}
static void tcp_inet_process_exit(ErlDrvData e, ErlDrvMonitor *monitorp)
@@ -9813,6 +9930,12 @@ static int tcp_recv_closed(tcp_descriptor* desc)
set_busy_port(desc->inet.port, 0);
inet_reply_error_am(INETP(desc), am_closed);
DEBUGF(("tcp_recv_closed(%ld): busy reply 'closed'\r\n", port));
+ } else {
+ /* No blocking send op to reply to right now.
+ * If next op is a send, make sure it returns {error,closed}
+ * rather than {error,enotconn}.
+ */
+ desc->tcp_add_flags |= TCP_ADDF_DELAYED_CLOSE_SEND;
}
if (!desc->inet.active) {
/* We must cancel any timer here ! */
@@ -10646,7 +10769,9 @@ static int tcp_sendv(tcp_descriptor* desc, ErlIOVec* ev)
ev->size += h_len;
}
- if ((sz = driver_sizeq(ix)) > 0) {
+ sz = driver_sizeq(ix);
+
+ if ((desc->tcp_add_flags & TCP_ADDF_SENDFILE) || sz > 0) {
driver_enqv(ix, ev, 0);
if (sz+ev->size >= desc->high) {
DEBUGF(("tcp_sendv(%ld): s=%d, sender forced busy\r\n",
@@ -10740,8 +10865,9 @@ static int tcp_send(tcp_descriptor* desc, char* ptr, ErlDrvSizeT len)
inet_output_count(INETP(desc), len+h_len);
+ sz = driver_sizeq(ix);
- if ((sz = driver_sizeq(ix)) > 0) {
+ if ((desc->tcp_add_flags & TCP_ADDF_SENDFILE) || sz > 0) {
if (h_len > 0)
driver_enq(ix, buf, h_len);
driver_enq(ix, ptr, len);
@@ -10831,6 +10957,246 @@ static void tcp_inet_drv_input(ErlDrvData data, ErlDrvEvent event)
(void)tcp_inet_input((tcp_descriptor*)data, (HANDLE)event);
}
+#ifdef HAVE_SENDFILE
+static int tcp_sendfile_completed(tcp_descriptor* desc) {
+ ErlDrvTermData spec[LOAD_PORT_CNT + LOAD_TUPLE_CNT * 2 +
+ LOAD_ATOM_CNT * 2 + LOAD_UINT_CNT * 2];
+ Uint32 sent_low, sent_high;
+ int i;
+
+ desc->tcp_add_flags &= ~TCP_ADDF_SENDFILE;
+ close(desc->sendfile.dup_file_fd);
+
+ /* While we flushed the output queue prior to sending the file, we've
+ * deferred clearing busy status until now as there's no point in doing so
+ * while we still have a file to send.
+ *
+ * The watermark is checked since more data may have been added while we
+ * were sending the file. */
+
+ if (driver_sizeq(desc->inet.port) <= desc->low) {
+ if (IS_BUSY(INETP(desc))) {
+ desc->inet.caller = desc->inet.busy_caller;
+ desc->inet.state &= ~INET_F_BUSY;
+
+ set_busy_port(desc->inet.port, 0);
+
+ /* if we have a timer then cancel and send ok to client */
+ if (desc->busy_on_send) {
+ driver_cancel_timer(desc->inet.port);
+ desc->busy_on_send = 0;
+ }
+
+ inet_reply_ok(INETP(desc));
+ }
+ }
+
+ if (driver_sizeq(desc->inet.port) == 0) {
+ sock_select(INETP(desc), FD_WRITE, 0);
+ send_empty_out_q_msgs(INETP(desc));
+
+ if (desc->tcp_add_flags & TCP_ADDF_PENDING_SHUTDOWN) {
+ tcp_shutdown_async(desc);
+ }
+ }
+
+ sent_low = ((Uint64)desc->sendfile.bytes_sent >> 0) & 0xFFFFFFFF;
+ sent_high = ((Uint64)desc->sendfile.bytes_sent >> 32) & 0xFFFFFFFF;
+
+ i = LOAD_ATOM(spec, 0, am_sendfile);
+ i = LOAD_PORT(spec, i, desc->inet.dport);
+ i = LOAD_ATOM(spec, i, am_ok);
+ i = LOAD_UINT(spec, i, sent_low);
+ i = LOAD_UINT(spec, i, sent_high);
+ i = LOAD_TUPLE(spec, i, 3);
+ i = LOAD_TUPLE(spec, i, 3);
+
+ ASSERT(i == sizeof(spec)/sizeof(*spec));
+
+ return erl_drv_output_term(desc->inet.dport, spec, i);
+}
+
+static int tcp_sendfile_aborted(tcp_descriptor* desc, int socket_error) {
+ ErlDrvTermData spec[LOAD_PORT_CNT + LOAD_TUPLE_CNT * 2 + LOAD_ATOM_CNT * 3];
+ int i;
+
+ /* We don't clean up sendfile state here, as that's done in tcp_desc_close
+ * following normal error handling. All we do here is report the failure. */
+
+ i = LOAD_ATOM(spec, 0, am_sendfile);
+ i = LOAD_PORT(spec, i, desc->inet.dport);
+ i = LOAD_ATOM(spec, i, am_error);
+
+ switch (socket_error) {
+ case ECONNRESET:
+ case ENOTCONN:
+ case EPIPE:
+ i = LOAD_ATOM(spec, i, am_closed);
+ break;
+ default:
+ i = LOAD_ATOM(spec, i, error_atom(socket_error));
+ }
+
+ i = LOAD_TUPLE(spec, i, 2);
+ i = LOAD_TUPLE(spec, i, 3);
+
+ ASSERT(i == sizeof(spec)/sizeof(*spec));
+
+ return erl_drv_output_term(desc->inet.dport, spec, i);
+}
+
+static int tcp_inet_sendfile(tcp_descriptor* desc) {
+ ErlDrvPort ix = desc->inet.port;
+ int result = 0;
+ ssize_t n;
+
+ DEBUGF(("tcp_inet_sendfile(%ld) {s=%d\r\n", (long)ix, desc->inet.s));
+
+ /* If there was any data in the queue by the time sendfile was issued,
+ * we'll need to skip it first. Note that we don't clear busy status until
+ * we're finished sending the file. */
+ while (desc->sendfile.ioq_skip > 0) {
+ ssize_t bytes_to_send;
+ SysIOVec* iov;
+ int vsize;
+
+ ASSERT(driver_sizeq(ix) >= desc->sendfile.ioq_skip);
+
+ if ((iov = driver_peekq(ix, &vsize)) == NULL) {
+ ERTS_INTERNAL_ERROR("ioq empty when sendfile.ioq_skip > 0");
+ }
+
+ bytes_to_send = MIN(desc->sendfile.ioq_skip, iov[0].iov_len);
+ n = sock_send(desc->inet.s, iov[0].iov_base, bytes_to_send, 0);
+
+ if (!IS_SOCKET_ERROR(n)) {
+ desc->sendfile.ioq_skip -= n;
+ driver_deq(ix, n);
+ } else if (sock_errno() == ERRNO_BLOCK) {
+#ifdef __WIN32__
+ desc->inet.send_would_block = 1;
+#endif
+ goto done;
+ } else if (sock_errno() != EINTR) {
+ goto socket_error;
+ }
+ }
+
+ while (desc->sendfile.length > 0) {
+ /* For some reason the maximum ssize_t cannot be used as the max size.
+ * 1GB seems to work on all platforms */
+ const Sint64 SENDFILE_CHUNK_SIZE = ((1UL << 30) - 1);
+
+ ssize_t bytes_to_send = MIN(SENDFILE_CHUNK_SIZE, desc->sendfile.length);
+ off_t offset = desc->sendfile.offset;
+
+#if defined(__linux__)
+ n = sendfile(desc->inet.s, desc->sendfile.dup_file_fd, &offset,
+ bytes_to_send);
+#elif defined(__FreeBSD__) || defined(__DragonFly__) || defined(__DARWIN__)
+ {
+ off_t bytes_sent;
+ int error;
+
+ #if defined(__DARWIN__)
+ bytes_sent = bytes_to_send;
+
+ error = sendfile(desc->sendfile.dup_file_fd, desc->inet.s, offset,
+ &bytes_sent, NULL, 0);
+ n = bytes_sent;
+ #else
+ error = sendfile(desc->sendfile.dup_file_fd, desc->inet.s, offset,
+ bytes_to_send, NULL, &bytes_sent, 0);
+ n = bytes_sent;
+ #endif
+
+ if(error < 0) {
+ /* EAGAIN/EINTR report partial success by setting bytes_sent,
+ * so we have to skip error handling if nonzero, and skip EOF
+ * handling if zero, as it's possible that we didn't manage to
+ * send anything at all before being interrupted by a
+ * signal. */
+ if((errno != EAGAIN && errno != EINTR) || bytes_sent == 0) {
+ n = -1;
+ }
+ }
+ }
+#elif defined(__sun) && defined(__SVR4) && defined(HAVE_SENDFILEV)
+ {
+ sendfilevec_t sfvec[1];
+ size_t bytes_sent;
+ ssize_t error;
+
+ sfvec[0].sfv_fd = desc->sendfile.dup_file_fd;
+ sfvec[0].sfv_len = bytes_to_send;
+ sfvec[0].sfv_off = offset;
+ sfvec[0].sfv_flag = 0;
+
+ error = sendfilev(desc->inet.s, sfvec, 1, &bytes_sent);
+ n = bytes_sent;
+
+ if(error < 0) {
+ if(errno == EINVAL) {
+ /* On some solaris versions (I've seen it on SunOS 5.10),
+ * using a sfv_len larger than the filesize will result in
+ * a (-1 && errno == EINVAL). We translate this to a
+ * successful send of the data.*/
+ } else {
+ /* EAGAIN/EINTR behavior is identical to *BSD. */
+ if((errno != EAGAIN && errno != EINTR) || bytes_sent == 0) {
+ n = -1;
+ }
+ }
+ }
+ }
+#else
+ #error "Unsupported sendfile syscall; update configure test."
+#endif
+
+ if (n > 0) {
+ desc->sendfile.bytes_sent += n;
+ desc->sendfile.offset += n;
+ desc->sendfile.length -= n;
+ } else if (n == 0) {
+ /* EOF. */
+ desc->sendfile.length = 0;
+ break;
+ } else if (IS_SOCKET_ERROR(n) && sock_errno() != EINTR) {
+ if (sock_errno() != ERRNO_BLOCK) {
+ goto socket_error;
+ }
+
+#ifdef __WIN32__
+ desc->inet.send_would_block = 1;
+#endif
+ break;
+ }
+ }
+
+ if (desc->sendfile.length == 0) {
+ tcp_sendfile_completed(desc);
+ }
+
+ goto done;
+
+socket_error: {
+ int socket_errno = sock_errno();
+
+ DEBUGF(("tcp_inet_sendfile(%ld): send errno = %d (errno %d)\r\n",
+ (long)desc->inet.port, socket_errno, errno));
+
+ result = tcp_send_error(desc, socket_errno);
+ tcp_sendfile_aborted(desc, socket_errno);
+
+ goto done;
+ }
+
+done:
+ DEBUGF(("tcp_inet_sendfile(%ld) }\r\n", (long)desc->inet.port));
+ return result;
+}
+#endif /* HAVE_SENDFILE */
+
/* socket ready for ouput:
** 1. INET_STATE_CONNECTING => non block connect ?
** 2. INET_STATE_CONNECTED => write output
@@ -10891,7 +11257,14 @@ static int tcp_inet_output(tcp_descriptor* desc, HANDLE event)
async_ok(INETP(desc));
}
else if (IS_CONNECTED(INETP(desc))) {
- for (;;) {
+
+#ifdef HAVE_SENDFILE
+ if(desc->tcp_add_flags & TCP_ADDF_SENDFILE) {
+ return tcp_inet_sendfile(desc);
+ }
+#endif
+
+ for (;;) {
int vsize;
ssize_t n;
SysIOVec* iov;
@@ -11297,24 +11670,20 @@ static ErlDrvSSizeT packet_inet_ctl(ErlDrvData e, unsigned int cmd, char* buf,
(desc->sfamily, &remote, &buf, &len)) != NULL)
return ctl_xerror(xerror, rbuf, rsize);
- sock_select(desc, FD_CONNECT, 1);
code = sock_connect(desc->s, &remote.sa, len);
if (IS_SOCKET_ERROR(code) && (sock_errno() == EINPROGRESS)) {
/* XXX: Unix only -- WinSock would have a different cond! */
- desc->state = INET_STATE_CONNECTING;
if (timeout != INET_INFINITY)
driver_set_timer(desc->port, timeout);
enq_async(desc, tbuf, INET_REQ_CONNECT);
+ async_ok(desc);
}
else if (code == 0) { /* OK we are connected */
- sock_select(desc, FD_CONNECT, 0);
- desc->state = INET_STATE_CONNECTED;
enq_async(desc, tbuf, INET_REQ_CONNECT);
async_ok(desc);
}
else {
- sock_select(desc, FD_CONNECT, 0);
return ctl_error(sock_errno(), rbuf, rsize);
}
return ctl_reply(INET_REP_OK, tbuf, 2, rbuf, rsize);
@@ -11829,77 +12198,6 @@ static int packet_inet_input(udp_descriptor* udesc, HANDLE event)
return count;
}
-static void packet_inet_drv_output(ErlDrvData e, ErlDrvEvent event)
-{
- (void) packet_inet_output((udp_descriptor*)e, (HANDLE)event);
-}
-
-/* UDP/SCTP socket ready for output:
-** This is a Back-End for Non-Block SCTP Connect (INET_STATE_CONNECTING)
-*/
-static int packet_inet_output(udp_descriptor* udesc, HANDLE event)
-{
- inet_descriptor* desc = INETP(udesc);
- int ret = 0;
- ErlDrvPort ix = desc->port;
-
- DEBUGF(("packet_inet_output(%ld) {s=%d\r\n",
- (long)desc->port, desc->s));
-
- if (desc->state == INET_STATE_CONNECTING) {
- sock_select(desc, FD_CONNECT, 0);
-
- driver_cancel_timer(ix); /* posssibly cancel a timer */
-#ifndef __WIN32__
- /*
- * XXX This is strange. This *should* work on Windows NT too,
- * but doesn't. An bug in Winsock 2.0 for Windows NT?
- *
- * See "Unix Netwok Programming", W.R.Stevens, p 412 for a
- * discussion about Unix portability and non blocking connect.
- */
-
-#ifndef SO_ERROR
- {
- int sz = sizeof(desc->remote);
- int code = sock_peer(desc->s,
- (struct sockaddr*) &desc->remote, &sz);
-
- if (IS_SOCKET_ERROR(code)) {
- desc->state = INET_STATE_OPEN; /* restore state */
- ret = async_error(desc, sock_errno());
- goto done;
- }
- }
-#else
- {
- int error = 0; /* Has to be initiated, we check it */
- unsigned int sz = sizeof(error); /* even if we get -1 */
- int code = sock_getopt(desc->s, SOL_SOCKET, SO_ERROR,
- (void *)&error, &sz);
-
- if ((code < 0) || error) {
- desc->state = INET_STATE_OPEN; /* restore state */
- ret = async_error(desc, error);
- goto done;
- }
- }
-#endif /* SO_ERROR */
-#endif /* !__WIN32__ */
-
- desc->state = INET_STATE_CONNECTED;
- async_ok(desc);
- }
- else {
- sock_select(desc,FD_CONNECT,0);
-
- DEBUGF(("packet_inet_output(%ld): bad state: %04x\r\n",
- (long)desc->port, desc->state));
- }
- done:
- DEBUGF(("packet_inet_output(%ld) }\r\n", (long)desc->port));
- return ret;
-}
#endif
/*---------------------------------------------------------------------------*/
diff --git a/erts/emulator/drivers/unix/unix_efile.c b/erts/emulator/drivers/unix/unix_efile.c
deleted file mode 100644
index f8341f788a..0000000000
--- a/erts/emulator/drivers/unix/unix_efile.c
+++ /dev/null
@@ -1,1102 +0,0 @@
-/*
- * %CopyrightBegin%
- *
- * Copyright Ericsson AB 1997-2017. All Rights Reserved.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- * %CopyrightEnd%
- */
-/*
- * Purpose: Provides file and directory operations for Unix.
- */
-#ifdef HAVE_CONFIG_H
-# include "config.h"
-#endif
-#if defined(HAVE_POSIX_FALLOCATE) && !defined(__sun) && !defined(__sun__)
-#define _XOPEN_SOURCE 600
-#endif
-#if !defined(_GNU_SOURCE) && defined(HAVE_LINUX_FALLOC_H)
-#define _GNU_SOURCE
-#endif
-#include "sys.h"
-#include "erl_driver.h"
-#include "erl_efile.h"
-#include <utime.h>
-#ifdef HAVE_UNISTD_H
-#include <unistd.h>
-#endif
-#ifdef HAVE_SYS_UIO_H
-#include <sys/types.h>
-#include <sys/uio.h>
-#if defined(HAVE_SENDFILE) && (defined(__FreeBSD__) || defined(__DragonFly__))
-/* Need to define __BSD_VISIBLE in order to expose prototype of sendfile */
-#define __BSD_VISIBLE 1
-#include <sys/socket.h>
-#endif
-#endif
-#if defined(HAVE_SENDFILE) && (defined(__linux__) || (defined(__sun) && defined(__SVR4)))
-#include <sys/sendfile.h>
-#endif
-
-#if defined(__APPLE__) && defined(__MACH__) && !defined(__DARWIN__)
-#define __DARWIN__ 1
-#endif
-
-#if defined(__DARWIN__) || defined(HAVE_LINUX_FALLOC_H) || defined(HAVE_POSIX_FALLOCATE)
-#include <fcntl.h>
-#endif
-
-#ifdef HAVE_LINUX_FALLOC_H
-#include <linux/falloc.h>
-#endif
-
-#ifdef SUNOS4
-# define getcwd(buf, size) getwd(buf)
-#endif
-
-/* Find a definition of MAXIOV, that is used in the code later. */
-#if defined IOV_MAX
-#define MAXIOV IOV_MAX
-#elif defined UIO_MAXIOV
-#define MAXIOV UIO_MAXIOV
-#else
-#define MAXIOV 16
-#endif
-
-
-/*
- * Macros for testing file types.
- */
-
-#define ISDIR(st) (S_ISDIR((st).st_mode))
-#define ISREG(st) (S_ISREG((st).st_mode))
-#define ISDEV(st) (S_ISCHR((st).st_mode) || S_ISBLK((st).st_mode))
-#define ISLNK(st) (S_ISLNK((st).st_mode))
-#ifdef NO_UMASK
-#define FILE_MODE 0644
-#define DIR_MODE 0755
-#else
-#define FILE_MODE 0666
-#define DIR_MODE 0777
-#endif
-
-#define IS_DOT_OR_DOTDOT(s) \
- (s[0] == '.' && (s[1] == '\0' || (s[1] == '.' && s[2] == '\0')))
-
-static int check_error(int result, Efile_error* errInfo);
-
-static int
-check_error(int result, Efile_error *errInfo)
-{
- if (result < 0) {
- errInfo->posix_errno = errInfo->os_errno = errno;
- return 0;
- }
- return 1;
-}
-
-int
-efile_init() {
- return 1;
-}
-
-int
-efile_mkdir(Efile_error* errInfo, /* Where to return error codes. */
- char* name) /* Name of directory to create. */
-{
-#ifdef NO_MKDIR_MODE
- return check_error(mkdir(name), errInfo);
-#else
- return check_error(mkdir(name, DIR_MODE), errInfo);
-#endif
-}
-
-int
-efile_rmdir(Efile_error* errInfo, /* Where to return error codes. */
- char* name) /* Name of directory to delete. */
-{
- if (rmdir(name) == 0) {
- return 1;
- }
- if (errno == ENOTEMPTY) {
- errno = EEXIST;
- }
- if (errno == EEXIST) {
- int saved_errno = errno;
- struct stat file_stat;
- struct stat cwd_stat;
-
- /*
- * The error code might be wrong if this is the current directory.
- */
-
- if (stat(name, &file_stat) == 0 && stat(".", &cwd_stat) == 0 &&
- file_stat.st_ino == cwd_stat.st_ino &&
- file_stat.st_dev == cwd_stat.st_dev) {
- saved_errno = EINVAL;
- }
- errno = saved_errno;
- }
- return check_error(-1, errInfo);
-}
-
-int
-efile_delete_file(Efile_error* errInfo, /* Where to return error codes. */
- char* name) /* Name of file to delete. */
-{
- if (unlink(name) == 0) {
- return 1;
- }
- if (errno == EISDIR) { /* Linux sets the wrong error code. */
- errno = EPERM;
- }
- return check_error(-1, errInfo);
-}
-
-/*
- *---------------------------------------------------------------------------
- *
- * Changes the name of an existing file or directory, from src to dst.
- * If src and dst refer to the same file or directory, does nothing
- * and returns success. Otherwise if dst already exists, it will be
- * deleted and replaced by src subject to the following conditions:
- * If src is a directory, dst may be an empty directory.
- * If src is a file, dst may be a file.
- * In any other situation where dst already exists, the rename will
- * fail.
- *
- * Results:
- * If the directory was successfully created, returns 1.
- * Otherwise the return value is 0 and errno is set to
- * indicate the error. Some possible values for errno are:
- *
- * EACCES: src or dst parent directory can't be read and/or written.
- * EEXIST: dst is a non-empty directory.
- * EINVAL: src is a root directory or dst is a subdirectory of src.
- * EISDIR: dst is a directory, but src is not.
- * ENOENT: src doesn't exist, or src or dst is "".
- * ENOTDIR: src is a directory, but dst is not.
- * EXDEV: src and dst are on different filesystems.
- *
- * Side effects:
- * The implementation of rename may allow cross-filesystem renames,
- * but the caller should be prepared to emulate it with copy and
- * delete if errno is EXDEV.
- *
- *---------------------------------------------------------------------------
- */
-
-int
-efile_rename(Efile_error* errInfo, /* Where to return error codes. */
- char* src, /* Original name. */
- char* dst) /* New name. */
-{
- if (rename(src, dst) == 0) {
- return 1;
- }
- if (errno == ENOTEMPTY) {
- errno = EEXIST;
- }
-#if defined (sparc)
- /*
- * SunOS 4.1.4 reports overwriting a non-empty directory with a
- * directory as EINVAL instead of EEXIST (first rule out the correct
- * EINVAL result code for moving a directory into itself). Must be
- * conditionally compiled because realpath() is only defined on SunOS.
- */
-
- if (errno == EINVAL) {
- char srcPath[MAXPATHLEN], dstPath[MAXPATHLEN];
- DIR *dirPtr;
- struct dirent *dirEntPtr;
-
-#ifdef PURIFY
- memset(srcPath, '\0', sizeof(srcPath));
- memset(dstPath, '\0', sizeof(dstPath));
-#endif
-
- if ((realpath(src, srcPath) != NULL)
- && (realpath(dst, dstPath) != NULL)
- && (strncmp(srcPath, dstPath, strlen(srcPath)) != 0)) {
- dirPtr = opendir(dst);
- if (dirPtr != NULL) {
- while ((dirEntPtr = readdir(dirPtr)) != NULL) {
- if ((strcmp(dirEntPtr->d_name, ".") != 0) &&
- (strcmp(dirEntPtr->d_name, "..") != 0)) {
- errno = EEXIST;
- closedir(dirPtr);
- return check_error(-1, errInfo);
- }
- }
- closedir(dirPtr);
- }
- }
- errno = EINVAL;
- }
-#endif /* sparc */
-
- if (strcmp(src, "/") == 0) {
- /*
- * Alpha reports renaming / as EBUSY and Linux reports it as EACCES,
- * instead of EINVAL.
- */
-
- errno = EINVAL;
- }
-
- /*
- * DEC Alpha OSF1 V3.0 returns EACCES when attempting to move a
- * file across filesystems and the parent directory of that file is
- * not writable. Most other systems return EXDEV. Does nothing to
- * correct this behavior.
- */
-
- return check_error(-1, errInfo);
-}
-
-int
-efile_chdir(Efile_error* errInfo, /* Where to return error codes. */
- char* name) /* Name of directory to make current. */
-{
- return check_error(chdir(name), errInfo);
-}
-
-
-int
-efile_getdcwd(Efile_error* errInfo, /* Where to return error codes. */
- int drive, /* 0 - current, 1 - A, 2 - B etc. */
- char* buffer, /* Where to return the current
- directory. */
- size_t size) /* Size of buffer. */
-{
- if (drive == 0) {
- if (getcwd(buffer, size) == NULL)
- return check_error(-1, errInfo);
-
-#ifdef SIMSPARCSOLARIS
- /* We get "host:" prepended to the dirname - remove!. */
- {
- int i = 0;
- int j = 0;
- while ((buffer[i] != ':') && (buffer[i] != '\0')) i++;
- if (buffer[i] == ':') {
- i++;
- while ((buffer[j++] = buffer[i++]) != '\0');
- }
- }
-#endif
- return 1;
- }
-
- /*
- * Drives other than 0 is not supported on Unix.
- */
-
- errno = ENOTSUP;
- return check_error(-1, errInfo);
-}
-
-int
-efile_readdir(Efile_error* errInfo, /* Where to return error codes. */
- char* name, /* Name of directory to open. */
- EFILE_DIR_HANDLE* p_dir_handle, /* Pointer to directory
- handle of
- open directory.*/
- char* buffer, /* Pointer to buffer for
- one filename. */
- size_t *size) /* in-out Size of buffer, length
- of name. */
-{
- DIR *dp; /* Pointer to directory structure. */
- struct dirent* dirp; /* Pointer to directory entry. */
-
- /*
- * If this is the first call, we must open the directory.
- */
-
- if (*p_dir_handle == NULL) {
- dp = opendir(name);
- if (dp == NULL)
- return check_error(-1, errInfo);
- *p_dir_handle = (EFILE_DIR_HANDLE) dp;
- }
-
- /*
- * Retrieve the name of the next file using the directory handle.
- */
-
- dp = *((DIR **)((void *)p_dir_handle));
- for (;;) {
- dirp = readdir(dp);
- if (dirp == NULL) {
- closedir(dp);
- return 0;
- }
- if (IS_DOT_OR_DOTDOT(dirp->d_name))
- continue;
- buffer[0] = '\0';
- strncat(buffer, dirp->d_name, (*size)-1);
- *size = strlen(dirp->d_name);
- return 1;
- }
-}
-
-int
-efile_openfile(Efile_error* errInfo, /* Where to return error codes. */
- char* name, /* Name of directory to open. */
- int flags, /* Flags to user for opening. */
- int* pfd, /* Where to store the file
- descriptor. */
- Sint64 *pSize) /* Where to store the size of the
- file. */
-{
- struct stat statbuf;
- int fd;
- int mode; /* Open mode. */
-
- switch (flags & (EFILE_MODE_READ|EFILE_MODE_WRITE)) {
- case EFILE_MODE_READ:
- mode = O_RDONLY;
- break;
- case EFILE_MODE_WRITE:
- if (flags & EFILE_NO_TRUNCATE)
- mode = O_WRONLY | O_CREAT;
- else
- mode = O_WRONLY | O_CREAT | O_TRUNC;
- break;
- case EFILE_MODE_READ_WRITE:
- mode = O_RDWR | O_CREAT;
- break;
- default:
- errno = EINVAL;
- return check_error(-1, errInfo);
- }
-
- if (flags & EFILE_MODE_APPEND) {
- mode &= ~O_TRUNC;
- mode |= O_APPEND;
- }
- if (flags & EFILE_MODE_EXCL) {
- mode |= O_EXCL;
- }
- if (flags & EFILE_MODE_SYNC) {
-#ifdef O_SYNC
- mode |= O_SYNC;
-#else
- errno = ENOTSUP;
- return check_error(-1, errInfo);
-#endif
- }
-
-#ifdef HAVE_FSTAT
- while (((fd = open(name, mode, FILE_MODE)) < 0) && (errno == EINTR));
- if (!check_error(fd, errInfo)) return 0;
-#endif
-
- if (
-#ifdef HAVE_FSTAT
- fstat(fd, &statbuf) < 0
-#else
- stat(name, &statbuf) < 0
-#endif
- ) {
- /* statbuf is undefined: if the caller depends on it,
- i.e. invoke_read_file(), fail the call immediately */
- if (pSize && flags == EFILE_MODE_READ) {
- check_error(-1, errInfo);
-#ifdef HAVE_FSTAT
- efile_closefile(fd);
-#endif
- return 0;
- }
- }
- else if (! ISREG(statbuf)) {
- struct stat nullstatbuf;
- /*
- * For UNIX only, here is some ugly code to allow
- * /dev/null to be opened as a file.
- */
- if ( (stat("/dev/null", &nullstatbuf) < 0)
- || (statbuf.st_ino != nullstatbuf.st_ino)
- || (statbuf.st_dev != nullstatbuf.st_dev) ) {
-#ifdef HAVE_FSTAT
- efile_closefile(fd);
-#endif
- errno = EISDIR;
- return check_error(-1, errInfo);
- }
- }
-
-#ifndef HAVE_FSTAT
- while (((fd = open(name, mode, FILE_MODE)) < 0) && (errno == EINTR));
- if (!check_error(fd, errInfo)) return 0;
-#endif
-
- *pfd = fd;
- if (pSize) *pSize = statbuf.st_size;
- return 1;
-}
-
-int
-efile_may_openfile(Efile_error* errInfo, char *name) {
- struct stat statbuf; /* Information about the file */
- int result;
-
- result = stat(name, &statbuf);
- if (!check_error(result, errInfo))
- return 0;
- if (!ISREG(statbuf)) {
- errno = EISDIR;
- return check_error(-1, errInfo);
- }
- return 1;
-}
-
-void
-efile_closefile(int fd)
-{
- while((close(fd) < 0) && (errno == EINTR));
-}
-
-int
-efile_fdatasync(Efile_error *errInfo, /* Where to return error codes. */
- int fd) /* File descriptor for file to sync data. */
-{
-#if defined(HAVE_FDATASYNC) && !defined(__DARWIN__)
- return check_error(fdatasync(fd), errInfo);
-#else
- return efile_fsync(errInfo, fd);
-#endif
-}
-
-int
-efile_fsync(Efile_error *errInfo, /* Where to return error codes. */
- int fd) /* File descriptor for file to sync. */
-{
-#ifdef NO_FSYNC
- undefined fsync /* XXX: Really? */
-#else
-#if defined(__DARWIN__) && defined(F_FULLFSYNC)
- return check_error(fcntl(fd, F_FULLFSYNC), errInfo);
-#else
- return check_error(fsync(fd), errInfo);
-#endif /* __DARWIN__ */
-#endif /* NO_FSYNC */
-}
-
-int
-efile_fileinfo(Efile_error* errInfo, Efile_info* pInfo,
- char* name, int info_for_link)
-{
- struct stat statbuf; /* Information about the file */
- int result;
-
- if (info_for_link) {
- result = lstat(name, &statbuf);
- } else {
- result = stat(name, &statbuf);
- }
- if (!check_error(result, errInfo)) {
- return 0;
- }
-
-#if SIZEOF_OFF_T == 4
- pInfo->size_high = 0;
-#else
- pInfo->size_high = (Uint32)(statbuf.st_size >> 32);
-#endif
- pInfo->size_low = (Uint32)statbuf.st_size;
-
-#ifdef NO_ACCESS
- /* Just look at read/write access for owner. */
-
- pInfo->access = ((statbuf.st_mode >> 6) & 07) >> 1;
-
-#else
- pInfo->access = FA_NONE;
- if (access(name, R_OK) == 0)
- pInfo->access |= FA_READ;
- if (access(name, W_OK) == 0)
- pInfo->access |= FA_WRITE;
-
-#endif
-
- if (ISDEV(statbuf))
- pInfo->type = FT_DEVICE;
- else if (ISDIR(statbuf))
- pInfo->type = FT_DIRECTORY;
- else if (ISREG(statbuf))
- pInfo->type = FT_REGULAR;
- else if (ISLNK(statbuf))
- pInfo->type = FT_SYMLINK;
- else
- pInfo->type = FT_OTHER;
-
- pInfo->accessTime = (Sint64)statbuf.st_atime;
- pInfo->modifyTime = (Sint64)statbuf.st_mtime;
- pInfo->cTime = (Sint64)statbuf.st_ctime;
-
- pInfo->mode = statbuf.st_mode;
- pInfo->links = statbuf.st_nlink;
- pInfo->major_device = statbuf.st_dev;
- pInfo->minor_device = statbuf.st_rdev;
- pInfo->inode = statbuf.st_ino;
- pInfo->uid = statbuf.st_uid;
- pInfo->gid = statbuf.st_gid;
-
- return 1;
-}
-
-int
-efile_write_info(Efile_error *errInfo, Efile_info *pInfo, char *name)
-{
- struct utimbuf tval;
-
- /*
- * On some systems chown will always fail for a non-root user unless
- * POSIX_CHOWN_RESTRICTED is not set. Others will succeed as long as
- * you don't try to chown a file to someone besides youself.
- */
-
- if (chown(name, pInfo->uid, pInfo->gid) && errno != EPERM) {
- return check_error(-1, errInfo);
- }
-
- if (pInfo->mode != -1) {
- mode_t newMode = pInfo->mode & (S_ISUID | S_ISGID |
- S_IRWXU | S_IRWXG | S_IRWXO);
- if (chmod(name, newMode)) {
- newMode &= ~(S_ISUID | S_ISGID);
- if (chmod(name, newMode)) {
- return check_error(-1, errInfo);
- }
- }
- }
-
- tval.actime = (time_t)pInfo->accessTime;
- tval.modtime = (time_t)pInfo->modifyTime;
-
- return check_error(utime(name, &tval), errInfo);
-}
-
-
-int
-efile_write(Efile_error* errInfo, /* Where to return error codes. */
- int flags, /* Flags given when file was
- opened. */
- int fd, /* File descriptor to write to. */
- char* buf, /* Buffer to write. */
- size_t count) /* Number of bytes to write. */
-{
- ssize_t written; /* Bytes written in last operation. */
-
- while (count > 0) {
- if ((written = write(fd, buf, count)) < 0) {
- if (errno != EINTR)
- return check_error(-1, errInfo);
- else
- written = 0;
- }
- ASSERT(written <= count);
- buf += written;
- count -= written;
- }
- return 1;
-}
-
-int
-efile_writev(Efile_error* errInfo, /* Where to return error codes */
- int flags, /* Flags given when file was
- * opened */
- int fd, /* File descriptor to write to */
- SysIOVec* iov, /* Vector of buffer structs.
- * The structs may be changed i.e.
- * due to incomplete writes */
- int iovcnt) /* Number of structs in vector */
-{
- int cnt = 0; /* Buffers so far written */
-
- ASSERT(iovcnt >= 0);
-
- while (cnt < iovcnt) {
- if ((! iov[cnt].iov_base) || (iov[cnt].iov_len <= 0)) {
- /* Empty buffer - skip */
- cnt++;
- } else { /* Non-empty buffer */
- ssize_t w; /* Bytes written in this call */
-#ifdef HAVE_WRITEV
- int b = iovcnt - cnt; /* Buffers to write */
- /* Use as many buffers as MAXIOV allows */
- if (b > MAXIOV)
- b = MAXIOV;
- if (b > 1) {
- do {
- w = writev(fd, &iov[cnt], b);
- } while (w < 0 && errno == EINTR);
- if (w < 0 && errno == EINVAL) {
- goto single_write;
- }
- } else
- single_write:
- /* Degenerated io vector - use regular write */
-#endif
- {
- do {
- size_t iov_len = iov[cnt].iov_len;
- size_t limit = 1024*1024*1024; /* 1GB */
- if (iov_len > limit) {
- iov_len = limit;
- }
- w = write(fd, iov[cnt].iov_base, iov_len);
- } while (w < 0 && errno == EINTR);
- ASSERT(w <= iov[cnt].iov_len ||
- (w == -1 && errno != EINTR));
- }
- if (w < 0) return check_error(-1, errInfo);
- /* Move forward to next buffer to write */
- for (; cnt < iovcnt && w > 0; cnt++) {
- if (iov[cnt].iov_base && iov[cnt].iov_len > 0) {
- if (w < iov[cnt].iov_len) {
- /* Adjust the buffer for next write */
- iov[cnt].iov_len -= w;
- iov[cnt].iov_base = ((char *)iov[cnt].iov_base) + w;
- w = 0;
- break;
- } else {
- w -= iov[cnt].iov_len;
- }
- }
- }
- ASSERT(w == 0);
- } /* else Non-empty buffer */
- } /* while (cnt< iovcnt) */
- return 1;
-}
-
-int
-efile_read(Efile_error* errInfo, /* Where to return error codes. */
- int flags, /* Flags given when file was opened. */
- int fd, /* File descriptor to read from. */
- char* buf, /* Buffer to read into. */
- size_t count, /* Number of bytes to read. */
- size_t *pBytesRead) /* Where to return number of
- bytes read. */
-{
- ssize_t n;
-
- for (;;) {
- if ((n = read(fd, buf, count)) >= 0)
- break;
- else if (errno != EINTR)
- return check_error(-1, errInfo);
- }
- *pBytesRead = (size_t) n;
- return 1;
-}
-
-
-/* pread() and pwrite() */
-/* Some unix systems, notably Solaris has these syscalls */
-/* It is especially nice for i.e. the dets module to have support */
-/* for this, even if the underlying OS dosn't support it, it is */
-/* reasonably easy to work around by first calling seek, and then */
-/* calling read(). */
-/* This later strategy however changes the file pointer, which pread() */
-/* does not do. We choose to ignore this and say that the location */
-/* of the file pointer is undefined after a call to any of the p functions*/
-
-
-int
-efile_pread(Efile_error* errInfo, /* Where to return error codes. */
- int fd, /* File descriptor to read from. */
- Sint64 offset, /* Offset in bytes from BOF. */
- char* buf, /* Buffer to read into. */
- size_t count, /* Number of bytes to read. */
- size_t *pBytesRead) /* Where to return
- number of bytes read. */
-{
-#if defined(HAVE_PREAD) && defined(HAVE_PWRITE)
- ssize_t n;
- off_t off = (off_t) offset;
- if (off != offset) {
- errno = EINVAL;
- return check_error(-1, errInfo);
- }
- for (;;) {
- if ((n = pread(fd, buf, count, offset)) >= 0)
- break;
- else if (errno != EINTR)
- return check_error(-1, errInfo);
- }
- *pBytesRead = (size_t) n;
- return 1;
-#else
- {
- int res = efile_seek(errInfo, fd, offset, EFILE_SEEK_SET, NULL);
- if (res) {
- return efile_read(errInfo, 0, fd, buf, count, pBytesRead);
- } else {
- return res;
- }
- }
-#endif
-}
-
-
-
-int
-efile_pwrite(Efile_error* errInfo, /* Where to return error codes. */
- int fd, /* File descriptor to write to. */
- char* buf, /* Buffer to write. */
- size_t count, /* Number of bytes to write. */
- Sint64 offset) /* where to write it */
-{
-#if defined(HAVE_PREAD) && defined(HAVE_PWRITE)
- ssize_t written; /* Bytes written in last operation. */
- off_t off = (off_t) offset;
- if (off != offset) {
- errno = EINVAL;
- return check_error(-1, errInfo);
- }
-
- while (count > 0) {
- if ((written = pwrite(fd, buf, count, offset)) < 0) {
- if (errno != EINTR)
- return check_error(-1, errInfo);
- else
- written = 0;
- }
- ASSERT(written <= count);
- buf += written;
- count -= written;
- offset += written;
- }
- return 1;
-#else /* For unix systems that don't support pread() and pwrite() */
- {
- int res = efile_seek(errInfo, fd, offset, EFILE_SEEK_SET, NULL);
-
- if (res) {
- return efile_write(errInfo, 0, fd, buf, count);
- } else {
- return res;
- }
- }
-#endif
-}
-
-
-int
-efile_seek(Efile_error* errInfo, /* Where to return error codes. */
- int fd, /* File descriptor to do the seek on. */
- Sint64 offset, /* Offset in bytes from the given
- origin. */
- int origin, /* Origin of seek (SEEK_SET, SEEK_CUR,
- SEEK_END). */
- Sint64 *new_location) /* Resulting new location in file. */
-{
- off_t off, result;
-
- switch (origin) {
- case EFILE_SEEK_SET: origin = SEEK_SET; break;
- case EFILE_SEEK_CUR: origin = SEEK_CUR; break;
- case EFILE_SEEK_END: origin = SEEK_END; break;
- default:
- errno = EINVAL;
- return check_error(-1, errInfo);
- }
- off = (off_t) offset;
- if (off != offset) {
- errno = EINVAL;
- return check_error(-1, errInfo);
- }
-
- errno = 0;
- result = lseek(fd, off, origin);
-
- /*
- * Note that the man page for lseek (on SunOs 5) says:
- *
- * "if fildes is a remote file descriptor and offset is
- * negative, lseek() returns the file pointer even if it is
- * negative."
- */
-
- if (result < 0 && errno == 0)
- errno = EINVAL;
- if (result < 0)
- return check_error(-1, errInfo);
- if (new_location) {
- *new_location = result;
- }
- return 1;
-}
-
-
-int
-efile_truncate_file(Efile_error* errInfo, int *fd, int flags)
-{
-#ifndef NO_FTRUNCATE
- off_t offset;
-
- return check_error((offset = lseek(*fd, 0, 1)) >= 0 &&
- ftruncate(*fd, offset) == 0 ? 1 : -1,
- errInfo);
-#else
- return 1;
-#endif
-}
-
-int
-efile_readlink(Efile_error* errInfo, char* name, char* buffer, size_t size)
-{
- int len;
- ASSERT(size > 0);
- len = readlink(name, buffer, size-1);
- if (len == -1) {
- return check_error(-1, errInfo);
- }
- buffer[len] = '\0';
- return 1;
-}
-
-int
-efile_altname(Efile_error* errInfo, char* name, char* buffer, size_t size)
-{
- errno = ENOTSUP;
- return check_error(-1, errInfo);
-}
-
-int
-efile_link(Efile_error* errInfo, char* old, char* new)
-{
- return check_error(link(old, new), errInfo);
-}
-
-int
-efile_symlink(Efile_error* errInfo, char* old, char* new)
-{
- return check_error(symlink(old, new), errInfo);
-}
-
-int
-efile_fadvise(Efile_error* errInfo, int fd, Sint64 offset,
- Sint64 length, int advise)
-{
-#ifdef HAVE_POSIX_FADVISE
- return check_error(posix_fadvise(fd, offset, length, advise), errInfo);
-#else
- return check_error(0, errInfo);
-#endif
-}
-
-#ifdef HAVE_SENDFILE
-/* For some reason the maximum size_t cannot be used as the max size
- 3GB seems to work on all platforms */
-#define SENDFILE_CHUNK_SIZE ((1UL << 30) -1)
-
-/*
- * sendfile: The implementation of the sendfile system call varies
- * a lot on different *nix platforms so to make the api similar in all
- * we have to emulate some things in linux and play with variables on
- * bsd/darwin.
- *
- * All of the calls will split a command which tries to send more than
- * SENDFILE_CHUNK_SIZE of data at once.
- *
- * On platforms where *nbytes of 0 does not mean the entire file, this is
- * simulated.
- *
- * It could be possible to implement header/trailer in sendfile. Though
- * you would have to emulate it in linux and on BSD/Darwin some complex
- * calculations have to be made when using a non blocking socket to figure
- * out how much of the header/file/trailer was sent in each command.
- *
- * The semantics of the API is this:
- * Return value: 1 if all data was sent and the function does not need to
- * be called again. 0 if an error occures OR if there is more data which
- * has to be sent (EAGAIN or EINTR will be set appropriately)
- *
- * The amount of data written in a call is returned through nbytes.
- *
- */
-
-int
-efile_sendfile(Efile_error* errInfo, int in_fd, int out_fd,
- off_t *offset, Uint64 *nbytes, struct t_sendfile_hdtl* hdtl)
-{
- Uint64 written = 0;
-#if defined(__linux__)
- ssize_t retval;
- do {
- /* check if *nbytes is 0 or greater than chunk size */
- if (*nbytes == 0 || *nbytes > SENDFILE_CHUNK_SIZE)
- retval = sendfile(out_fd, in_fd, offset, SENDFILE_CHUNK_SIZE);
- else
- retval = sendfile(out_fd, in_fd, offset, *nbytes);
- if (retval > 0) {
- written += retval;
- *nbytes -= retval;
- }
- } while (retval == SENDFILE_CHUNK_SIZE);
- if (written != 0) {
- /* -1 is not returned by the linux API so we have to simulate it */
- retval = -1;
- errno = EAGAIN;
- }
-#elif defined(__sun) && defined(__SVR4) && defined(HAVE_SENDFILEV)
- ssize_t retval;
- size_t len;
- sendfilevec_t fdrec;
- fdrec.sfv_fd = in_fd;
- fdrec.sfv_flag = 0;
- do {
- fdrec.sfv_off = *offset;
- len = 0;
- /* check if *nbytes is 0 or greater than chunk size */
- if (*nbytes == 0 || *nbytes > SENDFILE_CHUNK_SIZE)
- fdrec.sfv_len = SENDFILE_CHUNK_SIZE;
- else
- fdrec.sfv_len = *nbytes;
-
- retval = sendfilev(out_fd, &fdrec, 1, &len);
-
- if (retval == -1 && errno == EINVAL) {
- /* On some solaris versions (I've seen it on SunOS 5.10),
- using a sfv_len larger then a filesize will result in
- a -1 && errno == EINVAL return. We translate this so
- a successful send of the data.*/
- retval = len;
- }
-
- if (retval != -1 || errno == EAGAIN || errno == EINTR) {
- *offset += len;
- *nbytes -= len;
- written += len;
- }
- } while (len == SENDFILE_CHUNK_SIZE);
-#elif defined(__DARWIN__)
- int retval;
- off_t len;
- do {
- /* check if *nbytes is 0 or greater than chunk size */
- if(*nbytes > SENDFILE_CHUNK_SIZE)
- len = SENDFILE_CHUNK_SIZE;
- else
- len = *nbytes;
- retval = sendfile(in_fd, out_fd, *offset, &len, NULL, 0);
- if (retval != -1 || errno == EAGAIN || errno == EINTR) {
- *offset += len;
- *nbytes -= len;
- written += len;
- }
- } while (len == SENDFILE_CHUNK_SIZE);
-#elif defined(__FreeBSD__) || defined(__DragonFly__)
- off_t len;
- int retval;
- do {
- if (*nbytes > SENDFILE_CHUNK_SIZE)
- retval = sendfile(in_fd, out_fd, *offset, SENDFILE_CHUNK_SIZE,
- NULL, &len, 0);
- else
- retval = sendfile(in_fd, out_fd, *offset, *nbytes, NULL, &len, 0);
- if (retval != -1 || errno == EAGAIN || errno == EINTR) {
- *offset += len;
- *nbytes -= len;
- written += len;
- }
- } while(len == SENDFILE_CHUNK_SIZE);
-#endif
- *nbytes = written;
- return check_error(retval, errInfo);
-}
-#endif /* HAVE_SENDFILE */
-
-#ifdef HAVE_POSIX_FALLOCATE
-static int
-call_posix_fallocate(int fd, Sint64 offset, Sint64 length)
-{
- int ret;
-
- /*
- * On Linux and Solaris for example, posix_fallocate() returns
- * a positive error number on error and it does not set errno.
- * On FreeBSD however (9.0 at least), it returns -1 on error
- * and it sets errno.
- */
- do {
- ret = posix_fallocate(fd, (off_t) offset, (off_t) length);
- if (ret > 0) {
- errno = ret;
- ret = -1;
- }
- } while (ret != 0 && errno == EINTR);
-
- return ret;
-}
-#endif /* HAVE_POSIX_FALLOCATE */
-
-int
-efile_fallocate(Efile_error* errInfo, int fd, Sint64 offset, Sint64 length)
-{
-#if defined HAVE_FALLOCATE
- /* Linux specific, more efficient than posix_fallocate. */
- int ret;
-
- do {
- ret = fallocate(fd, FALLOC_FL_KEEP_SIZE, (off_t) offset, (off_t) length);
- } while (ret != 0 && errno == EINTR);
-
-#if defined HAVE_POSIX_FALLOCATE
- /* Fallback to posix_fallocate if available. */
- if (ret != 0) {
- ret = call_posix_fallocate(fd, offset, length);
- }
-#endif
-
- return check_error(ret, errInfo);
-#elif defined F_PREALLOCATE
- /* Mac OS X specific, equivalent to posix_fallocate. */
- int ret;
- fstore_t fs;
-
- memset(&fs, 0, sizeof(fs));
- fs.fst_flags = F_ALLOCATECONTIG;
- fs.fst_posmode = F_VOLPOSMODE;
- fs.fst_offset = (off_t) offset;
- fs.fst_length = (off_t) length;
-
- ret = fcntl(fd, F_PREALLOCATE, &fs);
-
- if (-1 == ret) {
- fs.fst_flags = F_ALLOCATEALL;
- ret = fcntl(fd, F_PREALLOCATE, &fs);
-
-#if defined HAVE_POSIX_FALLOCATE
- /* Fallback to posix_fallocate if available. */
- if (-1 == ret) {
- ret = call_posix_fallocate(fd, offset, length);
- }
-#endif
- }
-
- return check_error(ret, errInfo);
-#elif defined HAVE_POSIX_FALLOCATE
- /* Other Unixes, use posix_fallocate if available. */
- return check_error(call_posix_fallocate(fd, offset, length), errInfo);
-#else
- errno = ENOTSUP;
- return check_error(-1, errInfo);
-#endif
-}
diff --git a/erts/emulator/drivers/win32/win_efile.c b/erts/emulator/drivers/win32/win_efile.c
deleted file mode 100644
index 2d366b5833..0000000000
--- a/erts/emulator/drivers/win32/win_efile.c
+++ /dev/null
@@ -1,2058 +0,0 @@
-/*
- * %CopyrightBegin%
- *
- * Copyright Ericsson AB 1997-2016. All Rights Reserved.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- * %CopyrightEnd%
- */
-/*
- * Purpose: Provides file and directory operations for Windows.
- */
-
-#include <windows.h>
-#ifdef HAVE_CONFIG_H
-# include "config.h"
-#endif
-#include "sys.h"
-#include <ctype.h>
-#include <wchar.h>
-#include "erl_efile.h"
-
-#define DBG_TRACE_MASK 0
-/* 1 = file name ops
- * 2 = file descr ops
- * 4 = errors
- * 8 = path name conversion
- */
-#if !DBG_TRACE_MASK
-# define DBG_TRACE(M,S)
-# define DBG_TRACE1(M,FMT,A)
-# define DBG_TRACE2(M,FMT,A,B)
-#else
-# define DBG_TRACE(M,S) do { if ((M)&DBG_TRACE_MASK) fwprintf(stderr, L"DBG_TRACE %d: %s\r\n", __LINE__, (WCHAR*)(S)); }while(0)
-# define DBG_TRACE1(M,FMT,A) do { if ((M)&DBG_TRACE_MASK) fwprintf(stderr, L"DBG_TRACE %d: " L##FMT L"\r\n", __LINE__, (A)); }while(0)
-# define DBG_TRACE2(M,FMT,A,B) do { if ((M)&DBG_TRACE_MASK) fwprintf(stderr, L"DBG_TRACE %d: " L##FMT L"\r\n", __LINE__, (A), (B)); }while(0)
-#endif
-
-/*
- * Microsoft-specific function to map a WIN32 error code to a Posix errno.
- */
-
-#define ISSLASH(a) ((a) == L'\\' || (a) == L'/')
-#define ISDIR(st) (((st).st_mode&S_IFMT) == S_IFDIR)
-#define ISREG(st) (((st).st_mode&S_IFMT) == S_IFREG)
-
-#define IS_DOT_OR_DOTDOT(s) \
- ((s)[0] == L'.' && ((s)[1] == L'\0' || ((s)[1] == L'.' && (s)[2] == L'\0')))
-
-#define FILE_SHARE_FLAGS (FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE)
-
-#ifndef INVALID_FILE_ATTRIBUTES
-#define INVALID_FILE_ATTRIBUTES ((DWORD) 0xFFFFFFFF)
-#endif
-
-#define TICKS_PER_SECOND (10000000ULL)
-#define EPOCH_DIFFERENCE (11644473600LL)
-
-#define FILETIME_TO_EPOCH(epoch, ft) \
- do { \
- ULARGE_INTEGER ull; \
- ull.LowPart = (ft).dwLowDateTime; \
- ull.HighPart = (ft).dwHighDateTime; \
- (epoch) = ((ull.QuadPart / TICKS_PER_SECOND) - EPOCH_DIFFERENCE); \
- } while(0)
-
-#define EPOCH_TO_FILETIME(ft, epoch) \
- do { \
- ULARGE_INTEGER ull; \
- ull.QuadPart = (((epoch) + EPOCH_DIFFERENCE) * TICKS_PER_SECOND); \
- (ft).dwLowDateTime = ull.LowPart; \
- (ft).dwHighDateTime = ull.HighPart; \
- } while(0)
-
-
-static int check_error(int result, Efile_error* errInfo);
-static int set_error(Efile_error* errInfo);
-static int set_os_errno(Efile_error* errInfo, DWORD os_errno);
-static int is_root_unc_name(const WCHAR *path);
-static int extract_root(WCHAR *name);
-static unsigned short dos_to_posix_mode(int attr, const WCHAR *name);
-
-
-struct wpath_tmp_buffer {
- struct wpath_tmp_buffer* next;
- WCHAR buffer[1];
-};
-
-typedef struct {
- Efile_error* errInfo;
- struct wpath_tmp_buffer* buf_list;
-}Efile_call_state;
-
-static void call_state_init(Efile_call_state* state, Efile_error* errInfo)
-{
- state->errInfo = errInfo;
- state->buf_list = NULL;
-}
-static WCHAR* wpath_tmp_alloc(Efile_call_state* state, size_t len)
-{
- size_t sz = offsetof(struct wpath_tmp_buffer, buffer)
- + (len+1)*sizeof(WCHAR);
- struct wpath_tmp_buffer* p = driver_alloc(sz);
- p->next = state->buf_list;
- state->buf_list = p;
- return p->buffer;
-}
-static void call_state_free(Efile_call_state* state)
-{
- while(state->buf_list) {
- struct wpath_tmp_buffer* next = state->buf_list->next;
- driver_free(state->buf_list);
- state->buf_list = next;
- }
-}
-static WCHAR* get_cwd_wpath_tmp(Efile_call_state* state)
-{
- WCHAR dummy;
- DWORD size = GetCurrentDirectoryW(0, &dummy);
- WCHAR* ret = NULL;
-
- if (size) {
- ret = wpath_tmp_alloc(state, size);
- if (!GetCurrentDirectoryW(size, ret)) {
- ret = NULL;
- }
- }
- return ret;
-}
-static WCHAR* get_full_wpath_tmp(Efile_call_state* state,
- const WCHAR* file,
- WCHAR** file_part,
- DWORD extra)
-{
- WCHAR dummy;
- DWORD size = GetFullPathNameW(file, 0, &dummy, NULL);
- WCHAR* ret = NULL;
-
- if (size) {
- int ok;
- ret = wpath_tmp_alloc(state, size + extra);
- if (file_part) {
- ok = (GetFullPathNameW(file, size, ret, file_part) != 0);
- }
- else {
- ok = (_wfullpath(ret, file, size) != NULL);
- }
- if (!ok) {
- ret = NULL;
- }
- }
- return ret;
-}
-
-static void ensure_wpath_max(Efile_call_state* state, WCHAR** pathp, size_t max);
-static int do_rmdir(Efile_call_state*, char* name);
-static int do_rename(Efile_call_state*, char* src, char* dst);
-static int do_readdir(Efile_call_state*, char* name, EFILE_DIR_HANDLE*, char* buffer, size_t *size);
-static int do_fileinfo(Efile_call_state*, Efile_info*, char* orig_name, int info_for_link);
-static char* do_readlink(Efile_call_state*, char* name, char* buffer, size_t size);
-static int do_altname(Efile_call_state*, char* orig_name, char* buffer, size_t size);
-
-
-static int errno_map(DWORD last_error) {
-
- switch (last_error) {
- case ERROR_SUCCESS:
- return 0;
- case ERROR_INVALID_FUNCTION:
- case ERROR_INVALID_DATA:
- case ERROR_INVALID_PARAMETER:
- case ERROR_INVALID_TARGET_HANDLE:
- case ERROR_INVALID_CATEGORY:
- case ERROR_NEGATIVE_SEEK:
- return EINVAL;
- case ERROR_DIR_NOT_EMPTY:
- return EEXIST;
- case ERROR_BAD_FORMAT:
- return ENOEXEC;
- case ERROR_PATH_NOT_FOUND:
- case ERROR_FILE_NOT_FOUND:
- case ERROR_NO_MORE_FILES:
- return ENOENT;
- case ERROR_TOO_MANY_OPEN_FILES:
- return EMFILE;
- case ERROR_ACCESS_DENIED:
- case ERROR_INVALID_ACCESS:
- case ERROR_CURRENT_DIRECTORY:
- case ERROR_SHARING_VIOLATION:
- case ERROR_LOCK_VIOLATION:
- case ERROR_INVALID_PASSWORD:
- case ERROR_DRIVE_LOCKED:
- return EACCES;
- case ERROR_INVALID_HANDLE:
- return EBADF;
- case ERROR_NOT_ENOUGH_MEMORY:
- case ERROR_OUTOFMEMORY:
- case ERROR_OUT_OF_STRUCTURES:
- return ENOMEM;
- case ERROR_INVALID_DRIVE:
- case ERROR_BAD_UNIT:
- case ERROR_NOT_READY:
- case ERROR_REM_NOT_LIST:
- case ERROR_DUP_NAME:
- case ERROR_BAD_NETPATH:
- case ERROR_NETWORK_BUSY:
- case ERROR_DEV_NOT_EXIST:
- case ERROR_BAD_NET_NAME:
- return ENXIO;
- case ERROR_NOT_SAME_DEVICE:
- return EXDEV;
- case ERROR_WRITE_PROTECT:
- return EROFS;
- case ERROR_BAD_LENGTH:
- case ERROR_BUFFER_OVERFLOW:
- return E2BIG;
- case ERROR_SEEK:
- case ERROR_SECTOR_NOT_FOUND:
- return ESPIPE;
- case ERROR_NOT_DOS_DISK:
- return ENODEV;
- case ERROR_GEN_FAILURE:
- return ENODEV;
- case ERROR_SHARING_BUFFER_EXCEEDED:
- case ERROR_NO_MORE_SEARCH_HANDLES:
- return EMFILE;
- case ERROR_HANDLE_EOF:
- case ERROR_BROKEN_PIPE:
- return EPIPE;
- case ERROR_HANDLE_DISK_FULL:
- case ERROR_DISK_FULL:
- return ENOSPC;
- case ERROR_NOT_SUPPORTED:
- return ENOTSUP;
- case ERROR_FILE_EXISTS:
- case ERROR_ALREADY_EXISTS:
- case ERROR_CANNOT_MAKE:
- return EEXIST;
- case ERROR_ALREADY_ASSIGNED:
- return EBUSY;
- case ERROR_NO_PROC_SLOTS:
- return EAGAIN;
- case ERROR_CANT_RESOLVE_FILENAME:
- return EMLINK;
- case ERROR_PRIVILEGE_NOT_HELD:
- return EPERM;
- case ERROR_ARENA_TRASHED:
- case ERROR_INVALID_BLOCK:
- case ERROR_BAD_ENVIRONMENT:
- case ERROR_BAD_COMMAND:
- case ERROR_CRC:
- case ERROR_OUT_OF_PAPER:
- case ERROR_READ_FAULT:
- case ERROR_WRITE_FAULT:
- case ERROR_WRONG_DISK:
- case ERROR_NET_WRITE_FAULT:
- return EIO;
- default: /* not to do with files I expect. */
- return EIO;
- }
-}
-
-static int
-check_error(int result, Efile_error* errInfo)
-{
- if (result < 0) {
- errInfo->posix_errno = errno;
- errInfo->os_errno = GetLastError();
- DBG_TRACE2(4, "ERROR os_error=%d errno=%d @@@@@@@@@@@@@@@@@@@@@@@@@@@@",
- errInfo->os_errno, errInfo->posix_errno);
- return 0;
- }
- return 1;
-}
-
-static void
-save_last_error(Efile_error* errInfo)
-{
- errInfo->posix_errno = errno;
- errInfo->os_errno = GetLastError();
- DBG_TRACE2(4, "ERROR os_error=%d errno=%d $$$$$$$$$$$$$$$$$$$$$$$$$$$$$",
- errInfo->os_errno, errInfo->posix_errno);
-}
-
-
-/*
- * Fills the provided error information structure with information
- * with the error code given by GetLastError() and its corresponding
- * Posix error number.
- *
- * Returns 0.
- */
-
-static int
-set_error(Efile_error* errInfo)
-{
- set_os_errno(errInfo, GetLastError());
- return 0;
-}
-
-
-static int
-set_os_errno(Efile_error* errInfo, DWORD os_errno)
-{
- errInfo->os_errno = os_errno;
- errInfo->posix_errno = errno_map(os_errno);
- DBG_TRACE2(4, "ERROR os_error=%d errno=%d ############################",
- errInfo->os_errno, errInfo->posix_errno);
- return 0;
-}
-
-int
-efile_init() {
- return 1;
-}
-
-/*
- * A writev with Unix semantics, but with Windows arguments
- */
-static int
-win_writev(Efile_error* errInfo,
- HANDLE fd, /* handle to file */
- FILE_SEGMENT_ELEMENT iov[], /* array of buffer pointers */
- DWORD *size) /* number of bytes to write */
-{
- OVERLAPPED ov;
- ov.Offset = 0L;
- ov.OffsetHigh = 0L;
- ov.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
- if (ov.hEvent == NULL)
- return set_error(errInfo);
- if (! write_file_gather(fd, iov, *size, NULL, &ov))
- return set_error(errInfo);
- if (WaitForSingleObject(ov.hEvent, INFINITE) != WAIT_OBJECT_0)
- return set_error(errInfo);
- if (! GetOverlappedResult(fd, &ov, size, FALSE))
- return set_error(errInfo);
- return 1;
-}
-
-
-/* Check '*pathp' and convert it if needed to something that windows will accept.
- * Typically use UNC path with \\?\ prefix if absolute path is longer than 260.
- */
-static void ensure_wpath(Efile_call_state* state, WCHAR** pathp)
-{
- ensure_wpath_max(state, pathp, MAX_PATH);
-}
-
-static void ensure_wpath_max(Efile_call_state* state, WCHAR** pathp, size_t max)
-{
- WCHAR* path = *pathp;
- WCHAR* p;
- size_t len = wcslen(path);
- int unc_fixup = 0;
-
- if (path[0] == 0) {
- DBG_TRACE(8, L"Let empty path pass through");
- return;
- }
-
- DBG_TRACE1(8,"IN: %s", path);
-
- if (path[1] == L':' && ISSLASH(path[2])) { /* absolute path */
- if (len >= max) {
- WCHAR *src, *dst;
-
- *pathp = wpath_tmp_alloc(state, 4+len+1);
- dst = *pathp;
- wcscpy(dst, L"\\\\?\\");
- for (src=path,dst+=4; *src; src++) {
- if (*src == L'/') {
- if (dst[-1] != L'\\') {
- *dst++ = L'\\';
- }
- /*else ignore redundant slashes */
- }
- else
- *dst++ = *src;
- }
- *dst = 0;
- unc_fixup = 1;
- }
- }
- else if (!(ISSLASH(path[0]) && ISSLASH(path[1]))) { /* relative path */
- DWORD cwdLen = GetCurrentDirectoryW(0, NULL);
- DWORD absLen = cwdLen + 1 + len;
- if (absLen >= max) {
- WCHAR *fullPath = wpath_tmp_alloc(state, 4+4+absLen);
- DWORD fullLen;
-
- fullLen = GetFullPathNameW(path, 4 + absLen, fullPath+4, NULL);
- if (fullLen >= 4+absLen) {
- *pathp = path;
- DBG_TRACE2(8,"ensure_wpath FAILED absLen=%u %s", (int)absLen, path);
- return;
- }
- /* GetFullPathNameW can return paths longer than MAX_PATH without the \\?\ prefix.
- * At least seen on Windows 7. Go figure...
- */
- if (fullLen >= max && wcsncmp(fullPath+4, L"\\\\?\\", 4) != 0) {
- wcsncpy(fullPath, L"\\\\?\\", 4);
- *pathp = fullPath;
- }
- else {
- *pathp = fullPath + 4;
- }
- }
- }
-
- if (unc_fixup) {
- WCHAR* endp;
-
- p = *pathp;
- len = wcslen(p);
- endp = p + len;
- if (len > 4) {
- p += 4;
- while (*p) {
- if (p[0] == L'\\' && p[1] == L'.') {
- if (p[2] == L'\\' || !p[2]) { /* single dot */
- wmemmove(p, p+2, (&endp[1] - &p[2]));
- endp -= 2;
- }
- else if (p[2] == L'.' && (p[3] == L'\\' || !p[3])) { /* double dot */
- WCHAR* r;
- for (r=p-1; *r == L'\\'; --r)
- /*skip redundant slashes*/;
- for (; *r != L'\\'; --r)
- /*find start of prev directory*/;
- if (r < *pathp + 6)
- break;
- wmemmove(r, p+3, (&endp[1] - &p[3]));
- p = r;
- }
- else p += 3;
- }
- else ++p;
- }
- }
- }
- DBG_TRACE1(8,"OUT: %s", *pathp);
-}
-
-int
-efile_mkdir(Efile_error* errInfo, /* Where to return error codes. */
- char* name) /* Name of directory to create. */
-{
- Efile_call_state state;
- WCHAR* wname = (WCHAR*)name;
- int ret;
-
- DBG_TRACE(1, name);
- call_state_init(&state, errInfo);
- ensure_wpath_max(&state, &wname, 248); /* Yes, 248 limit for normal paths */
-
- ret = (int) CreateDirectoryW(wname, NULL);
- if (!ret)
- set_error(errInfo);
-
- call_state_free(&state);
- return ret;
-}
-
-int
-efile_rmdir(Efile_error* errInfo, /* Where to return error codes. */
- char* name) /* Name of directory to delete. */
-{
- Efile_call_state state;
- int ret;
-
- DBG_TRACE(1, name);
- call_state_init(&state, errInfo);
- ret = do_rmdir(&state, name);
- call_state_free(&state);
- return ret;
-}
-
-static int do_rmdir(Efile_call_state* state, char* name)
-{
- OSVERSIONINFO os;
- DWORD attr;
- WCHAR *wname = (WCHAR *) name;
- WCHAR *buffer = NULL;
-
- ensure_wpath(state, &wname);
-
- if (RemoveDirectoryW(wname) != FALSE) {
- return 1;
- }
- errno = errno_map(GetLastError());
- if (errno == EACCES) {
- attr = GetFileAttributesW(wname);
- if (attr != (DWORD) -1) {
- if ((attr & FILE_ATTRIBUTE_DIRECTORY) == 0) {
- /*
- * Windows 95 reports calling RemoveDirectory on a file as an
- * EACCES, not an ENOTDIR.
- */
-
- errno = ENOTDIR;
- goto end;
- }
-
- /*
- * Windows 95 reports removing a non-empty directory as
- * an EACCES, not an EEXIST. If the directory is not empty,
- * change errno so caller knows what's going on.
- */
-
- os.dwOSVersionInfoSize = sizeof(os);
- GetVersionEx(&os);
- if (os.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) {
- HANDLE handle;
- WIN32_FIND_DATAW data;
- int len = wcslen(wname);
-
- buffer = wpath_tmp_alloc(state, len + 4);
- wcscpy(buffer, wname);
- if (buffer[0] && buffer[len-1] != L'\\' && buffer[len-1] != L'/') {
- wcscat(buffer, L"\\");
- }
- wcscat(buffer, L"*.*");
- handle = FindFirstFileW(buffer, &data);
- if (handle != INVALID_HANDLE_VALUE) {
- while (1) {
- if ((wcscmp(data.cFileName, L".") != 0)
- && (wcscmp(data.cFileName, L"..") != 0)) {
- /*
- * Found something in this directory.
- */
-
- errno = EEXIST;
- break;
- }
- if (FindNextFileW(handle, &data) == FALSE) {
- break;
- }
- }
- FindClose(handle);
- }
- }
- }
- }
-
- if (errno == ENOTEMPTY) {
- /*
- * Posix allows both EEXIST or ENOTEMPTY, but we'll always
- * return EEXIST to allow easy matching in Erlang code.
- */
-
- errno = EEXIST;
- }
-
- end:
- save_last_error(state->errInfo);
- return 0;
-}
-
-int
-efile_delete_file(Efile_error* errInfo, /* Where to return error codes. */
- char* name) /* Name of file to delete. */
-{
- Efile_call_state state;
- int ret;
- DBG_TRACE(1, name);
- call_state_init(&state, errInfo);
- ret = do_delete_file(&state, name);
- call_state_free(&state);
- return ret;
-}
-
-static int do_delete_file(Efile_call_state* state, char* name)
-{
- DWORD attr;
- WCHAR *wname = (WCHAR *) name;
-
- ensure_wpath(state, &wname);
-
- if (DeleteFileW(wname) != FALSE) {
- return 1;
- }
-
- errno = errno_map(GetLastError());
- if (errno == EACCES) {
- attr = GetFileAttributesW(wname);
- if (attr != (DWORD) -1) {
- if (attr & FILE_ATTRIBUTE_DIRECTORY) {
- /*
- * Windows NT reports removing a directory as EACCES instead
- * of EPERM.
- */
-
- errno = EPERM;
- }
- }
- } else if (errno == ENOENT) {
- attr = GetFileAttributesW(wname);
- if (attr != (DWORD) -1) {
- if (attr & FILE_ATTRIBUTE_DIRECTORY) {
- /*
- * Windows 95 reports removing a directory as ENOENT instead
- * of EPERM.
- */
-
- errno = EPERM;
- }
- }
- } else if (errno == EINVAL) {
- /*
- * Windows NT reports removing a char device as EINVAL instead of
- * EACCES.
- */
-
- errno = EACCES;
- }
-
- return check_error(-1, state->errInfo);
-}
-
-/*
- *---------------------------------------------------------------------------
- *
- * Changes the name of an existing file or directory, from src to dst.
- * If src and dst refer to the same file or directory, does nothing
- * and returns success. Otherwise if dst already exists, it will be
- * deleted and replaced by src subject to the following conditions:
- * If src is a directory, dst may be an empty directory.
- * If src is a file, dst may be a file.
- * In any other situation where dst already exists, the rename will
- * fail.
- *
- * Some possible error codes:
- *
- * EACCES: src or dst parent directory can't be read and/or written.
- * EEXIST: dst is a non-empty directory.
- * EINVAL: src is a root directory or dst is a subdirectory of src.
- * EISDIR: dst is a directory, but src is not.
- * ENOENT: src doesn't exist, or src or dst is "".
- * ENOTDIR: src is a directory, but dst is not.
- * EXDEV: src and dst are on different filesystems.
- *
- * Side effects:
- * The implementation of rename may allow cross-filesystem renames,
- * but the caller should be prepared to emulate it with copy and
- * delete if errno is EXDEV.
- *
- *---------------------------------------------------------------------------
- */
-
-int
-efile_rename(Efile_error* errInfo, char* src, char* dst)
-{
- Efile_call_state state;
- int ret;
- DBG_TRACE(1, src);
- call_state_init(&state, errInfo);
- ret = do_rename(&state, src, dst);
- call_state_free(&state);
- return ret;
-}
-
-static int
-do_rename(Efile_call_state* state,
- char* src, /* Original name. */
- char* dst) /* New name. */
-{
- DWORD srcAttr, dstAttr;
- WCHAR *wsrc = (WCHAR *) src;
- WCHAR *wdst = (WCHAR *) dst;
-
- ensure_wpath(state, &wsrc);
- ensure_wpath(state, &wdst);
-
- if (MoveFileW(wsrc, wdst) != FALSE) {
- return 1;
- }
-
- errno = errno_map(GetLastError());
- srcAttr = GetFileAttributesW(wsrc);
- dstAttr = GetFileAttributesW(wdst);
- if (srcAttr == (DWORD) -1) {
- srcAttr = 0;
- }
- if (dstAttr == (DWORD) -1) {
- dstAttr = 0;
- }
-
- if (errno == EBADF) {
- errno = EACCES;
- return check_error(-1, state->errInfo);
- }
- if (errno == EACCES) {
- decode:
- if (srcAttr & FILE_ATTRIBUTE_DIRECTORY) {
- WCHAR *srcPath, *dstPath;
- WCHAR *srcRest, *dstRest;
- int size;
-
- srcPath = get_full_wpath_tmp(state, wsrc, &srcRest, 0);
- if (!srcPath) {
- save_last_error(state->errInfo);
- return 0;
- }
-
- dstPath = get_full_wpath_tmp(state, wdst, &dstRest, 0);
- if (!dstPath) {
- save_last_error(state->errInfo);
- return 0;
- }
-
- if (srcRest == NULL) {
- srcRest = srcPath + wcslen(srcPath);
- }
- if (_wcsnicmp(srcPath, dstPath, srcRest - srcPath) == 0) {
- /*
- * Trying to move a directory into itself.
- */
-
- errno = EINVAL;
- }
- if (extract_root(srcPath)) {
- /*
- * Attempt to move a root directory. Never allowed.
- */
- errno = EINVAL;
- }
-
- (void) extract_root(dstPath);
- if (dstPath[0] == L'\0') {
- /*
- * The filename was invalid. (Don't know why,
- * but play it safe.)
- */
- errno = EINVAL;
- }
- if (_wcsicmp(srcPath, dstPath) != 0) {
- /*
- * If src is a directory and dst filesystem != src
- * filesystem, errno should be EXDEV. It is very
- * important to get this behavior, so that the caller
- * can respond to a cross filesystem rename by
- * simulating it with copy and delete. The MoveFile
- * system call already handles the case of moving a
- * *file* between filesystems.
- */
-
- errno = EXDEV;
- }
- }
-
- /*
- * Other types of access failure is that dst is a read-only
- * filesystem, that an open file referred to src or dest, or that
- * src or dest specified the current working directory on the
- * current filesystem. EACCES is returned for those cases.
- */
-
- } else if (errno == EEXIST) {
- /*
- * Reports EEXIST any time the target already exists. If it makes
- * sense, remove the old file and try renaming again.
- */
-
- if (srcAttr & FILE_ATTRIBUTE_DIRECTORY) {
- if (dstAttr & FILE_ATTRIBUTE_DIRECTORY) {
- /*
- * Overwrite empty dst directory with src directory. The
- * following call will remove an empty directory. If it
- * fails, it's because it wasn't empty.
- */
-
- if (RemoveDirectoryW(wdst)) {
- /*
- * Now that that empty directory is gone, we can try
- * renaming again. If that fails, we'll put this empty
- * directory back, for completeness.
- */
-
- if (MoveFileW(wsrc, wdst) != FALSE) {
- return 1;
- }
-
- /*
- * Some new error has occurred. Don't know what it
- * could be, but report this one.
- */
-
- errno = errno_map(GetLastError());
- CreateDirectoryW(wdst, NULL);
- SetFileAttributesW(wdst, dstAttr);
- if (errno == EACCES) {
- /*
- * Decode the EACCES to a more meaningful error.
- */
-
- goto decode;
- }
- }
- } else { /* (dstAttr & FILE_ATTRIBUTE_DIRECTORY) == 0 */
- errno = ENOTDIR;
- }
- } else { /* (srcAttr & FILE_ATTRIBUTE_DIRECTORY) == 0 */
- if (dstAttr & FILE_ATTRIBUTE_DIRECTORY) {
- errno = EISDIR;
- } else {
- /*
- * Overwrite existing file by:
- *
- * 1. Rename existing file to temp name.
- * 2. Rename old file to new name.
- * 3. If success, delete temp file. If failure,
- * put temp file back to old name.
- */
-
- WCHAR *tempName;
- int result;
- WCHAR *rest;
-
- tempName = get_full_wpath_tmp(state, wdst, &rest, 14);
- if (!tempName || !rest) {
- save_last_error(state->errInfo);
- return 0;
- }
-
- *rest = L'\0';
- result = -1;
- if (GetTempFileNameW(tempName, L"erlr", 0, tempName) != 0) {
- /*
- * Strictly speaking, need the following DeleteFile and
- * MoveFile to be joined as an atomic operation so no
- * other app comes along in the meantime and creates the
- * same temp file.
- */
-
- DeleteFileW(tempName);
- if (MoveFileW(wdst, tempName) != FALSE) {
- if (MoveFileW(wsrc, wdst) != FALSE) {
- SetFileAttributesW(tempName, FILE_ATTRIBUTE_NORMAL);
- DeleteFileW(tempName);
- return 1;
- } else {
- DeleteFileW(wdst);
- MoveFileW(tempName, wdst);
- }
- }
-
- /*
- * Can't backup dst file or move src file. Return that
- * error. Could happen if an open file refers to dst.
- */
-
- errno = errno_map(GetLastError());
- if (errno == EACCES) {
- /*
- * Decode the EACCES to a more meaningful error.
- */
- goto decode;
- }
- }
- return result;
- }
- }
- }
- return check_error(-1, state->errInfo);
-}
-
-int
-efile_chdir(Efile_error* errInfo, /* Where to return error codes. */
- char* name) /* Name of directory to make current. */
-{
- /* We don't even try to handle long paths here
- * as current working directory is always limited to MAX_PATH
- * even if we use UNC paths and SetCurrentDirectoryW()
- */
- int success = check_error(_wchdir((WCHAR *) name), errInfo);
- if (!success && errInfo->posix_errno == EINVAL)
- /* POSIXification of errno */
- errInfo->posix_errno = ENOENT;
- return success;
-}
-
-int
-efile_getdcwd(Efile_error* errInfo, /* Where to return error codes. */
- int drive, /* 0 - current, 1 - A, 2 - B etc. */
- char* buffer, /* Where to return the current directory. */
- size_t size) /* Size of buffer. */
-{
- WCHAR *wbuffer = (WCHAR *) buffer;
- size_t wbuffer_size = size / 2;
- DBG_TRACE(1, L"#getdcwd#");
- if (_wgetdcwd(drive, wbuffer, wbuffer_size) == NULL) {
- return check_error(-1, errInfo);
- }
- DBG_TRACE1(8, "getdcwd OS=%s", wbuffer);
- if (wcsncmp(wbuffer, L"\\\\?\\", 4) == 0) {
- wmemmove(wbuffer, wbuffer+4, wcslen(wbuffer+4)+1);
- }
- for ( ; *wbuffer; wbuffer++)
- if (*wbuffer == L'\\')
- *wbuffer = L'/';
- DBG_TRACE1(8, "getdcwd ERLANG=%s", (WCHAR*)buffer);
- return 1;
-}
-
-int
-efile_readdir(Efile_error* errInfo, char* name, EFILE_DIR_HANDLE* dir_handle,
- char* buffer, size_t *size)
-{
- Efile_call_state state;
- int ret;
- DBG_TRACE(dir_handle?2:1, name);
- call_state_init(&state, errInfo);
- ret = do_readdir(&state, name, dir_handle, buffer, size);
- call_state_free(&state);
- return ret;
-}
-
-static int do_readdir(Efile_call_state* state,
- char* name, /* Name of directory to list */
- EFILE_DIR_HANDLE* dir_handle, /* Handle of opened directory or NULL */
- char* buffer, /* Buffer to put one filename in */
- size_t *size) /* in-out size of buffer/size of filename excluding zero
- termination in bytes*/
-{
- HANDLE dir; /* Handle to directory. */
- WIN32_FIND_DATAW findData; /* Data found by FindFirstFile() or FindNext(). */
- /* Alignment is not honored, this works on x86 because of alignment fixup by processor.
- Not perfect, but faster than alinging by hand (really) */
- WCHAR *wbuffer = (WCHAR *) buffer;
-
- /*
- * First time we must setup everything.
- */
-
- if (*dir_handle == NULL) {
- WCHAR *wname = (WCHAR *) name;
- WCHAR* wildcard;
- int length;
- WCHAR* s;
-
- ensure_wpath_max(state, &wname, MAX_PATH-2);
- length = wcslen(wname);
-
- wildcard = wpath_tmp_alloc(state, length+3);
-
- wcscpy(wildcard, wname);
- s = wildcard+length-1;
- if (*s != L'/' && *s != L'\\')
- *++s = L'\\';
- *++s = L'*';
- *++s = L'\0';
- DEBUGF(("Reading %ws\n", wildcard));
- dir = FindFirstFileW(wildcard, &findData);
- if (dir == INVALID_HANDLE_VALUE) {
- set_error(state->errInfo);
- return 0;
- }
- *dir_handle = (EFILE_DIR_HANDLE) dir;
-
- if (!IS_DOT_OR_DOTDOT(findData.cFileName)) {
- wcscpy(wbuffer, findData.cFileName);
- *size = wcslen(wbuffer)*2;
- return 1;
- }
- }
-
- /*
- * Retrieve the name of the next file using the directory handle.
- */
-
- dir = (HANDLE) *dir_handle;
-
- for (;;) {
- if (FindNextFileW(dir, &findData)) {
- if (IS_DOT_OR_DOTDOT(findData.cFileName))
- continue;
- wcscpy(wbuffer, findData.cFileName);
- *size = wcslen(wbuffer)*2;
- return 1;
- }
-
- if (GetLastError() == ERROR_NO_MORE_FILES) {
- state->errInfo->posix_errno = state->errInfo->os_errno = 0;
- }
- else {
- set_error(state->errInfo);
- }
- FindClose(dir);
- return 0;
- }
-}
-
-int
-efile_openfile(Efile_error* errInfo, char* name, int flags, int* pfd, Sint64* pSize)
-{
- Efile_call_state state;
- int ret;
- DBG_TRACE1(1, "openfile(%s)", name);
- call_state_init(&state, errInfo);
- ret = do_openfile(&state, name, flags, pfd, pSize);
- call_state_free(&state);
- return ret;
-}
-
-static
-int do_openfile(Efile_call_state* state, /* Where to return error codes. */
- char* name, /* Name of directory to open. */
- int flags, /* Flags to use for opening. */
- int* pfd, /* Where to store the file descriptor. */
- Sint64* pSize) /* Where to store the size of the file. */
-{
- Efile_error* errInfo = state->errInfo;
- BY_HANDLE_FILE_INFORMATION fileInfo; /* File information from a handle. */
- HANDLE fd; /* Handle to open file. */
- DWORD access; /* Access mode: GENERIC_READ, GENERIC_WRITE. */
- DWORD crFlags;
- DWORD flagsAndAttrs = FILE_ATTRIBUTE_NORMAL;
- WCHAR *wname = (WCHAR *) name;
-
- switch (flags & (EFILE_MODE_READ|EFILE_MODE_WRITE)) {
- case EFILE_MODE_READ:
- access = GENERIC_READ;
- crFlags = OPEN_EXISTING;
- break;
- case EFILE_MODE_WRITE:
- access = GENERIC_WRITE;
- crFlags = CREATE_ALWAYS;
- break;
- case EFILE_MODE_READ_WRITE:
- access = GENERIC_READ|GENERIC_WRITE;
- crFlags = OPEN_ALWAYS;
- break;
- default:
- errno = EINVAL;
- check_error(-1, errInfo);
- return 0;
- }
-
- if (flags & EFILE_MODE_SYNC) {
- flagsAndAttrs = FILE_FLAG_WRITE_THROUGH;
- }
-
- if (flags & EFILE_MODE_APPEND) {
- crFlags = OPEN_ALWAYS;
- }
- if (flags & EFILE_MODE_EXCL) {
- crFlags = CREATE_NEW;
- }
- ensure_wpath(state, &wname);
- fd = CreateFileW(wname, access,
- FILE_SHARE_FLAGS,
- NULL, crFlags, flagsAndAttrs, NULL);
-
- /*
- * Check for errors.
- */
-
- if (fd == INVALID_HANDLE_VALUE) {
- DWORD attr;
-
- set_error(errInfo);
-
- /*
- * If the error is EACESS, the reason could be that we tried to
- * open a directory. In that case, we'll change the error code
- * to EISDIR.
- */
- if (errInfo->posix_errno &&
- (attr = GetFileAttributesW(wname)) != INVALID_FILE_ATTRIBUTES &&
- (attr & FILE_ATTRIBUTE_DIRECTORY)) {
- errInfo->posix_errno = EISDIR;
- }
- return 0;
- }
-
- /*
- * Get and return the length of the open file.
- */
-
- if (!GetFileInformationByHandle(fd, &fileInfo))
- return set_error(errInfo);
- *pfd = (int) fd;
- if (pSize) {
- *pSize = (Sint64)
- (((Uint64)fileInfo.nFileSizeHigh << 32) |
- (Uint64)fileInfo.nFileSizeLow);
- }
- return 1;
-}
-
-int
-efile_may_openfile(Efile_error* errInfo, char *name)
-{
- Efile_call_state state;
- WCHAR *wname = (WCHAR *) name;
- DWORD attr;
- int ret;
-
- DBG_TRACE(1, name);
- call_state_init(&state, errInfo);
- ensure_wpath(&state, &wname);
- if ((attr = GetFileAttributesW(wname)) == INVALID_FILE_ATTRIBUTES) {
- errno = ENOENT;
- ret = check_error(-1, errInfo);
- }
- else if (attr & FILE_ATTRIBUTE_DIRECTORY) {
- errno = EISDIR;
- ret = check_error(-1, errInfo);
- }
- else ret = 1;
-
- call_state_free(&state);
- return ret;
-}
-
-void
-efile_closefile(fd)
-int fd; /* File descriptor for file to close. */
-{
- DBG_TRACE(2, L"");
- CloseHandle((HANDLE) fd);
-}
-
-FILE* efile_wfopen(const WCHAR* name, const WCHAR* mode)
-{
- Efile_call_state state;
- Efile_error dummy;
- FILE* f;
- call_state_init(&state, &dummy);
- ensure_wpath(&state, (WCHAR**)&name);
- f = _wfopen(name, mode);
- call_state_free(&state);
- return f;
-}
-
-int
-efile_fdatasync(errInfo, fd)
-Efile_error* errInfo; /* Where to return error codes. */
-int fd; /* File descriptor for file to sync. */
-{
- DBG_TRACE(2, L"");
- /* Not available in Windows, just call regular fsync */
- return efile_fsync(errInfo, fd);
-}
-
-int
-efile_fsync(errInfo, fd)
-Efile_error* errInfo; /* Where to return error codes. */
-int fd; /* File descriptor for file to sync. */
-{
- DBG_TRACE(2, L"");
- if (!FlushFileBuffers((HANDLE) fd)) {
- return check_error(-1, errInfo);
- }
- return 1;
-}
-
-int
-efile_fileinfo(Efile_error* errInfo, Efile_info* pInfo,
- char* orig_name, int info_for_link)
-{
- Efile_call_state state;
- int ret;
- DBG_TRACE(1, L"");
- call_state_init(&state, errInfo);
- ret = do_fileinfo(&state, pInfo, orig_name, info_for_link);
- call_state_free(&state);
- return ret;
-}
-
-static int
-do_fileinfo(Efile_call_state* state, Efile_info* pInfo,
- char* orig_name, int info_for_link)
-{
- Efile_error* errInfo = state->errInfo;
- HANDLE findhandle; /* Handle returned by FindFirstFile(). */
- WIN32_FIND_DATAW findbuf; /* Data return by FindFirstFile(). */
- WCHAR* name = NULL;
- WCHAR* win_path;
- int name_len;
- int drive; /* Drive for filename (1 = A:, 2 = B: etc). */
- WCHAR *worig_name = (WCHAR *) orig_name;
-
- ensure_wpath(state, &worig_name);
- /* Don't allow wildcards to be interpreted by system */
-
-
- /*
- * Move the name to a buffer and make sure to remove a trailing
- * slash, because it causes FindFirstFile() to fail on Win95.
- */
-
- name_len = wcslen(worig_name);
-
- name = wpath_tmp_alloc(state, name_len+1);
- wcscpy(name, worig_name);
- if (name_len > 2 && ISSLASH(name[name_len-1]) &&
- name[name_len-2] != L':') {
- name[name_len-1] = L'\0';
- }
-
- win_path = name;
- if (wcsncmp(name, L"\\\\?\\", 4) == 0) {
- win_path += 4;
- }
-
- if (wcspbrk(win_path, L"?*")) {
- enoent:
- errInfo->posix_errno = ENOENT;
- errInfo->os_errno = ERROR_FILE_NOT_FOUND;
- return 0;
- }
-
- /* Try to get disk from name. If none, get current disk. */
-
- if (win_path[1] != L':') {
- WCHAR* cwd_path = get_cwd_wpath_tmp(state);
- drive = 0;
- if (cwd_path[1] == L':') {
- drive = towlower(cwd_path[0]) - L'a' + 1;
- }
- } else if (*win_path && win_path[2] == L'\0') {
- /*
- * X: and nothing more is an error.
- */
- errInfo->posix_errno = ENOENT;
- errInfo->os_errno = ERROR_FILE_NOT_FOUND;
- return 0;
- } else {
- drive = towlower(*win_path) - L'a' + 1;
- }
-
- findhandle = FindFirstFileW(name, &findbuf);
- if (findhandle == INVALID_HANDLE_VALUE) {
- WCHAR* path = NULL;
-
- if (!(wcspbrk(name, L"./\\") &&
- (path = get_full_wpath_tmp(state, name, NULL, 0)) &&
- /* root dir. ('C:\') or UNC root dir. ('\\server\share\') */
- ((wcslen(path) == 3) || is_root_unc_name(path)) &&
- (GetDriveTypeW(path) > 1) ) ) {
-
- errInfo->posix_errno = ENOENT;
- errInfo->os_errno = ERROR_FILE_NOT_FOUND;
- return 0;
- }
-
- /*
- * Root directories (such as C:\ or \\server\share\ are fabricated.
- */
-
- findbuf.dwFileAttributes = FILE_ATTRIBUTE_DIRECTORY;
- findbuf.nFileSizeHigh = 0;
- findbuf.nFileSizeLow = 0;
- findbuf.cFileName[0] = L'\0';
-
- pInfo->links = 1;
- pInfo->cTime = pInfo->accessTime = pInfo->modifyTime = 0;
- } else {
- SYSTEMTIME SystemTime;
- FILETIME LocalFTime;
-
- /*first check if we are a symlink */
- if (!info_for_link && (findbuf.dwFileAttributes &
- FILE_ATTRIBUTE_REPARSE_POINT)){
- /*
- * given that we know this is a symlink,
- we should be able to find its target */
- WCHAR* target_name = (WCHAR*) do_readlink(state, (char *) name, NULL, 0);
- if (target_name) {
- FindClose(findhandle);
- return do_fileinfo(state, pInfo,
- (char *) target_name, info_for_link);
- }
- }
-
- /* number of links: */
- {
- HANDLE handle; /* Handle returned by CreateFile() */
- BY_HANDLE_FILE_INFORMATION fileInfo; /* from CreateFile() */
-
- /* We initialise nNumberOfLinks as GetFileInformationByHandle
- does not always initialise this field */
- fileInfo.nNumberOfLinks = 1;
- if (handle = CreateFileW(name, GENERIC_READ, FILE_SHARE_FLAGS, NULL,
- OPEN_EXISTING, 0, NULL)) {
- GetFileInformationByHandle(handle, &fileInfo);
- pInfo->links = fileInfo.nNumberOfLinks;
- CloseHandle(handle);
- } else {
- pInfo->links = 1;
- }
- }
-
- FILETIME_TO_EPOCH(pInfo->modifyTime, findbuf.ftLastWriteTime);
-
- if (findbuf.ftLastAccessTime.dwLowDateTime == 0 &&
- findbuf.ftLastAccessTime.dwHighDateTime == 0) {
- pInfo->accessTime = pInfo->modifyTime;
- } else {
- FILETIME_TO_EPOCH(pInfo->accessTime, findbuf.ftLastAccessTime);
- }
-
- if (findbuf.ftCreationTime.dwLowDateTime == 0 &&
- findbuf.ftCreationTime.dwHighDateTime == 0) {
- pInfo->cTime = pInfo->modifyTime;
- } else {
- FILETIME_TO_EPOCH(pInfo->cTime ,findbuf.ftCreationTime);
- }
- FindClose(findhandle);
- }
-
- pInfo->size_low = findbuf.nFileSizeLow;
- pInfo->size_high = findbuf.nFileSizeHigh;
-
- if (info_for_link && (findbuf.dwFileAttributes &
- FILE_ATTRIBUTE_REPARSE_POINT))
- pInfo->type = FT_SYMLINK;
- else if (findbuf.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
- pInfo->type = FT_DIRECTORY;
- else
- pInfo->type = FT_REGULAR;
-
- if (findbuf.dwFileAttributes & FILE_ATTRIBUTE_READONLY)
- pInfo->access = FA_READ;
- else
- pInfo->access = FA_READ|FA_WRITE;
-
- pInfo->mode = dos_to_posix_mode(findbuf.dwFileAttributes, name);
- pInfo->major_device = drive;
- pInfo->minor_device = 0;
- pInfo->inode = 0;
- pInfo->uid = 0;
- pInfo->gid = 0;
-
- return 1;
-}
-
-int
-efile_write_info(Efile_error* errInfo,
- Efile_info* pInfo,
- char* name)
-{
- Efile_call_state state;
- int ret;
- call_state_init(&state, errInfo);
- ret = do_write_info(&state, pInfo, name);
- call_state_free(&state);
- return ret;
-}
-
-static int
-do_write_info(Efile_call_state* state,
- Efile_info* pInfo,
- char* name)
-{
- Efile_error* errInfo = state->errInfo;
- SYSTEMTIME timebuf;
- FILETIME ModifyFileTime;
- FILETIME AccessFileTime;
- FILETIME CreationFileTime;
- HANDLE fd;
- DWORD attr;
- DWORD tempAttr;
- WCHAR *wname = (WCHAR *) name;
-
- DBG_TRACE(1, name);
-
- ensure_wpath(state, &wname);
-
- /*
- * Get the attributes for the file.
- */
-
- tempAttr = attr = GetFileAttributesW(wname);
- if (attr == 0xffffffff) {
- return set_error(errInfo);
- }
- if (pInfo->mode != -1) {
- if (pInfo->mode & _S_IWRITE) {
- /* clear read only bit */
- attr &= ~FILE_ATTRIBUTE_READONLY;
- } else {
- /* set read only bit */
- attr |= FILE_ATTRIBUTE_READONLY;
- }
- }
-
- /*
- * Construct all file times.
- */
-
- EPOCH_TO_FILETIME(ModifyFileTime, pInfo->modifyTime);
- EPOCH_TO_FILETIME(AccessFileTime, pInfo->accessTime);
- EPOCH_TO_FILETIME(CreationFileTime, pInfo->cTime);
-
- /*
- * If necessary, set the file times.
- */
-
- /*
- * If the has read only access, we must temporarily turn on
- * write access (this is necessary for native filesystems,
- * but not for NFS filesystems).
- */
-
- if (tempAttr & FILE_ATTRIBUTE_READONLY) {
- tempAttr &= ~FILE_ATTRIBUTE_READONLY;
- if (!SetFileAttributesW(wname, tempAttr)) {
- return set_error(errInfo);
- }
- }
-
- fd = CreateFileW(wname, GENERIC_READ|GENERIC_WRITE,
- FILE_SHARE_FLAGS,
- NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
- if (fd != INVALID_HANDLE_VALUE) {
- BOOL result = SetFileTime(fd, &CreationFileTime, &AccessFileTime, &ModifyFileTime);
- if (!result) {
- return set_error(errInfo);
- }
- CloseHandle(fd);
- }
-
- /*
- * If the file doesn't have the correct attributes, set them now.
- * (It could have been done before setting the file times, above).
- */
-
- if (tempAttr != attr) {
- if (!SetFileAttributesW(wname, attr)) {
- return set_error(errInfo);
- }
- }
- return 1;
-}
-
-
-int
-efile_pwrite(errInfo, fd, buf, count, offset)
-Efile_error* errInfo; /* Where to return error codes. */
-int fd; /* File descriptor to write to. */
-char* buf; /* Buffer to write. */
-size_t count; /* Number of bytes to write. */
-Sint64 offset; /* where to write it */
-{
- int res;
- DBG_TRACE(2, L"");
- res = efile_seek(errInfo, fd, offset, EFILE_SEEK_SET, NULL);
- if (res) {
- return efile_write(errInfo, EFILE_MODE_WRITE, fd, buf, count);
- } else {
- return res;
- }
-}
-
-/* position and read/write as a single atomic op */
-int
-efile_pread(errInfo, fd, offset, buf, count, pBytesRead)
-Efile_error* errInfo; /* Where to return error codes. */
-int fd; /* File descriptor to read from. */
-Sint64 offset; /* Offset in bytes from BOF. */
-char* buf; /* Buffer to read into. */
-size_t count; /* Number of bytes to read. */
-size_t* pBytesRead; /* Where to return number of bytes read. */
-{
- int res;
- DBG_TRACE(2, L"");
- res = efile_seek(errInfo, fd, offset, EFILE_SEEK_SET, NULL);
- if (res) {
- return efile_read(errInfo, EFILE_MODE_READ, fd, buf, count, pBytesRead);
- } else {
- return res;
- }
-}
-
-
-
-int
-efile_write(errInfo, flags, fd, buf, count)
-Efile_error* errInfo; /* Where to return error codes. */
-int flags; /* Flags given when file was opened. */
-int fd; /* File descriptor to write to. */
-char* buf; /* Buffer to write. */
-size_t count; /* Number of bytes to write. */
-{
- DWORD written; /* Bytes written in last operation. */
- OVERLAPPED overlapped;
- OVERLAPPED* pOverlapped = NULL;
-
- DBG_TRACE(2, L"");
- if (flags & EFILE_MODE_APPEND) {
- memset(&overlapped, 0, sizeof(overlapped));
- overlapped.Offset = 0xffffffff;
- overlapped.OffsetHigh = 0xffffffff;
- pOverlapped = &overlapped;
- }
- while (count > 0) {
- if (!WriteFile((HANDLE) fd, buf, count, &written, pOverlapped))
- return set_error(errInfo);
- buf += written;
- count -= written;
- }
- return 1;
-}
-
-int
-efile_writev(Efile_error* errInfo, /* Where to return error codes */
- int flags, /* Flags given when file was
- * opened */
- int fd, /* File descriptor to write to */
- SysIOVec* iov, /* Vector of buffer structs.
- * The structs are unchanged
- * after the call */
- int iovcnt) /* Number of structs in vector */
-{
- int cnt; /* Buffers so far written */
- OVERLAPPED overlapped;
- OVERLAPPED* pOverlapped = NULL;
-
- DBG_TRACE(2, L"");
- ASSERT(iovcnt >= 0);
-
- if (flags & EFILE_MODE_APPEND) {
- memset(&overlapped, 0, sizeof(overlapped));
- overlapped.Offset = 0xffffffff;
- overlapped.OffsetHigh = 0xffffffff;
- pOverlapped = &overlapped;
- }
- for (cnt = 0; cnt < iovcnt; cnt++) {
- if (iov[cnt].iov_base && iov[cnt].iov_len > 0) {
- /* Non-empty buffer */
- int p; /* Position in buffer */
- int w = iov[cnt].iov_len;/* Bytes written in this call */
- for (p = 0; p < iov[cnt].iov_len; p += w) {
- if (!WriteFile((HANDLE) fd,
- iov[cnt].iov_base + p,
- iov[cnt].iov_len - p,
- &w,
- pOverlapped))
- return set_error(errInfo);
- }
- }
- }
- return 1;
-}
-
-int
-efile_read(errInfo, flags, fd, buf, count, pBytesRead)
-Efile_error* errInfo; /* Where to return error codes. */
-int flags; /* Flags given when file was opened. */
-int fd; /* File descriptor to read from. */
-char* buf; /* Buffer to read into. */
-size_t count; /* Number of bytes to read. */
-size_t* pBytesRead; /* Where to return number of bytes read. */
-{
- DWORD nbytes = 0;
-
- DBG_TRACE(2, L"");
- if (!ReadFile((HANDLE) fd, buf, count, &nbytes, NULL))
- return set_error(errInfo);
-
- *pBytesRead = nbytes;
- return 1;
-}
-
-int
-efile_seek(errInfo, fd, offset, origin, new_location)
-Efile_error* errInfo; /* Where to return error codes. */
-int fd; /* File descriptor to do the seek on. */
-Sint64 offset; /* Offset in bytes from the given origin. */
-int origin; /* Origin of seek (SEEK_SET, SEEK_CUR,
- * SEEK_END).
- */
-Sint64* new_location; /* Resulting new location in file. */
-{
- LARGE_INTEGER off, new_loc;
-
- DBG_TRACE(2, L"");
- switch (origin) {
- case EFILE_SEEK_SET: origin = FILE_BEGIN; break;
- case EFILE_SEEK_CUR: origin = FILE_CURRENT; break;
- case EFILE_SEEK_END: origin = FILE_END; break;
- default:
- errno = EINVAL;
- check_error(-1, errInfo);
- break;
- }
-
- off.QuadPart = offset;
- if (! SetFilePointerEx((HANDLE) fd, off,
- new_location ? &new_loc : NULL, origin)) {
- return set_error(errInfo);
- }
- if (new_location) {
- *new_location = new_loc.QuadPart;
- DEBUGF(("efile_seek(offset=%ld, origin=%d) -> %ld\n",
- (long) offset, origin, (long) *new_location));
- } else {
- DEBUGF(("efile_seek(offset=%ld, origin=%d)\n", (long) offset, origin));
- }
- return 1;
-}
-
-int
-efile_truncate_file(errInfo, fd, flags)
-Efile_error* errInfo; /* Where to return error codes. */
-int *fd; /* File descriptor for file to truncate. */
-int flags;
-{
- DBG_TRACE(2, L"");
- if (!SetEndOfFile((HANDLE) (*fd)))
- return set_error(errInfo);
- return 1;
-}
-
-
-/*
- * is_root_unc_name - returns TRUE if the argument is a UNC name specifying
- * a root share. That is, if it is of the form \\server\share\.
- * This routine will also return true if the argument is of the
- * form \\server\share (no trailing slash) but Win32 currently
- * does not like that form.
- *
- * Forward slashes ('/') may be used instead of backslashes ('\').
- */
-
-static int
-is_root_unc_name(const WCHAR *path)
-{
- /*
- * If a root UNC name, path will start with 2 (but not 3) slashes
- */
-
- if ((wcslen(path) >= 5) /* minimum string is "//x/y" */
- && ISSLASH(path[0]) && ISSLASH(path[1]))
- {
- const WCHAR *p = path + 2;
-
- /*
- * find the slash between the server name and share name
- */
- while ( * ++ p )
- if ( ISSLASH(*p) )
- break ;
-
- if ( *p && p[1] )
- {
- /*
- * is there a further slash?
- */
- while ( * ++ p )
- if ( ISSLASH(*p) )
- break ;
-
- /*
- * just final slash (or no final slash)
- */
- if ( !*p || !p[1])
- return 1;
- }
- }
-
- return 0 ;
-}
-
-/*
- * Extracts the root part of an absolute filename (by modifying the string
- * pointed to by the name argument). The name can start
- * with either a driver letter (for example, C:\), or a UNC name
- * (for example, \\guinness\bjorn).
- *
- * If the name is invalid, the buffer will be modified to point to
- * an empty string.
- *
- * Returns: 1 if the name consists of just the root part, 0 if
- * the name was longer.
- */
-
-static int
-extract_root(WCHAR* name)
-{
- int len = wcslen(name);
-
- if (iswalpha(name[0]) && name[1] == L':' && ISSLASH(name[2])) {
- WCHAR c = name[3];
- name[3] = L'\0';
- return c == L'\0';
- } else if (len < 5 || !ISSLASH(name[0]) || !ISSLASH(name[1])) {
- goto error;
- } else { /* Try to find the end of the UNC name. */
- WCHAR* p;
- WCHAR c;
-
- /*
- * Find the slash between the server name and share name.
- */
-
- for (p = name + 2; *p; p++)
- if (ISSLASH(*p))
- break;
- if (*p == L'\0')
- goto error;
-
- /*
- * Find the slash after the share name.
- */
-
- for (p++; *p; p++)
- if (ISSLASH(*p))
- break;
- c = *p;
- *p = L'\0';
- return c == L'\0' || p[1] == L'\0';
- }
-
- error:
- *name = L'\0';
- return 1;
-}
-
-static unsigned short
-dos_to_posix_mode(int attr, const WCHAR *name)
-{
- register unsigned short uxmode;
- unsigned dosmode;
- register const WCHAR *p;
-
- dosmode = attr & 0xff;
- if ((p = name)[1] == L':')
- p += 2;
-
- /* check to see if this is a directory - note we must make a special
- * check for the root, which DOS thinks is not a directory
- */
-
- uxmode = (unsigned short)
- (((ISSLASH(*p) && !p[1]) || (dosmode & FILE_ATTRIBUTE_DIRECTORY) ||
- *p == L'\0') ? _S_IFDIR|_S_IEXEC : _S_IFREG);
-
- /* If attribute byte does not have read-only bit, it is read-write */
-
- uxmode |= (dosmode & FILE_ATTRIBUTE_READONLY) ?
- _S_IREAD : (_S_IREAD|_S_IWRITE);
-
- /* see if file appears to be executable - check extension of name */
-
- if (p = wcsrchr(name, L'.')) {
- if (!_wcsicmp(p, L".exe") ||
- !_wcsicmp(p, L".cmd") ||
- !_wcsicmp(p, L".bat") ||
- !_wcsicmp(p, L".com"))
- uxmode |= _S_IEXEC;
- }
-
- /* propagate user read/write/execute bits to group/other fields */
-
- uxmode |= (uxmode & 0700) >> 3;
- uxmode |= (uxmode & 0700) >> 6;
-
- return uxmode;
-}
-
-
-int
-efile_readlink(Efile_error* errInfo, char* name, char* buffer, size_t size)
-{
- Efile_call_state state;
- int ret;
- DBG_TRACE(1, name);
- call_state_init(&state, errInfo);
- ret = !!do_readlink(&state, name, buffer, size);
- call_state_free(&state);
- return ret;
-}
-
-/* If buffer==0, return buffer allocated by wpath_tmp_allocate
-*/
-static char*
-do_readlink(Efile_call_state* state, char* name, char* buffer, size_t size)
-{
- /*
- * load dll and see if we have CreateSymbolicLink at runtime:
- * (Vista only)
- */
- HINSTANCE hModule = NULL;
- WCHAR *wname = (WCHAR *) name;
- WCHAR *wbuffer = (WCHAR *) buffer;
- DWORD wsize = size / sizeof(WCHAR);
- char* ret = NULL;
-
- if ((hModule = LoadLibrary("kernel32.dll")) != NULL) {
- typedef DWORD (WINAPI * GETFINALPATHNAMEBYHANDLEPTR)(
- HANDLE hFile,
- LPCWSTR lpFilePath,
- DWORD cchFilePath,
- DWORD dwFlags);
-
- GETFINALPATHNAMEBYHANDLEPTR pGetFinalPathNameByHandle =
- (GETFINALPATHNAMEBYHANDLEPTR)GetProcAddress(hModule, "GetFinalPathNameByHandleW");
-
- if (pGetFinalPathNameByHandle != NULL) {
- DWORD fileAttributes;
- ensure_wpath(state, &wname);
- /* first check if file is a symlink; {error, einval} otherwise */
- fileAttributes = GetFileAttributesW(wname);
- if ((fileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)) {
- DWORD success = 0;
- HANDLE h = CreateFileW(wname, GENERIC_READ, FILE_SHARE_FLAGS, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
- int len;
- if(h != INVALID_HANDLE_VALUE) {
- if (!wbuffer) { /* dynamic allocation */
- WCHAR dummy;
- wsize = pGetFinalPathNameByHandle(h, &dummy, 0, 0);
- if (wsize) {
- wbuffer = wpath_tmp_alloc(state, wsize);
- }
- }
- if (wbuffer
- && (success = pGetFinalPathNameByHandle(h, wbuffer, wsize, 0))
- && success < wsize) {
- WCHAR* wp;
-
- /* GetFinalPathNameByHandle prepends path with "\\?\": */
- len = wcslen(wbuffer);
- wmemmove(wbuffer,wbuffer+4,len-3);
- if (len - 4 >= 2 && wbuffer[1] == L':' && wbuffer[0] >= L'A' &&
- wbuffer[0] <= L'Z') {
- wbuffer[0] = wbuffer[0] + L'a' - L'A';
- }
-
- for (wp=wbuffer ; *wp; wp++)
- if (*wp == L'\\')
- *wp = L'/';
- }
- CloseHandle(h);
- }
- if (success) {
- ret = (char*) wbuffer;
- } else {
- set_error(state->errInfo);
- }
- } else {
- errno = EINVAL;
- save_last_error(state->errInfo);
- }
- goto done;
- }
- }
- errno = ENOTSUP;
- save_last_error(state->errInfo);
-
-done:
- if (hModule)
- FreeLibrary(hModule);
- return ret;
-}
-
-
-int
-efile_altname(Efile_error* errInfo, char* orig_name, char* buffer, size_t size)
-{
- Efile_call_state state;
- int ret;
- DBG_TRACE(1, orig_name);
- call_state_init(&state, errInfo);
- ret = do_altname(&state, orig_name, buffer, size);
- call_state_free(&state);
- return ret;
-}
-
-static int
-do_altname(Efile_call_state* state, char* orig_name, char* buffer, size_t size)
-{
- WIN32_FIND_DATAW wfd;
- HANDLE fh;
- WCHAR* name;
- int name_len;
- WCHAR* full_path = NULL;
- WCHAR *worig_name = (WCHAR *) orig_name;
- WCHAR *wbuffer = (WCHAR *) buffer;
- int drive; /* Drive for filename (1 = A:, 2 = B: etc). */
-
- /* Don't allow wildcards to be interpreted by system */
-
- if (wcspbrk(worig_name, L"?*")) {
- enoent:
- state->errInfo->posix_errno = ENOENT;
- state->errInfo->os_errno = ERROR_FILE_NOT_FOUND;
- return 0;
- }
-
- /*
- * Move the name to a buffer and make sure to remove a trailing
- * slash, because it causes FindFirstFile() to fail on Win95.
- */
- ensure_wpath(state, &worig_name);
- name_len = wcslen(worig_name);
-
- name = wpath_tmp_alloc(state, name_len + 1);
- wcscpy(name, worig_name);
- if (name_len > 2 && ISSLASH(name[name_len-1]) &&
- name[name_len-2] != L':') {
- name[name_len-1] = L'\0';
- }
-
- /* Try to get disk from name. If none, get current disk. */
-
- if (name[1] != L':') {
- WCHAR* cwd_path = get_cwd_wpath_tmp(state);
- drive = 0;
- if (cwd_path[1] == L':') {
- drive = towlower(cwd_path[0]) - L'a' + 1;
- }
- } else if (*name && name[2] == L'\0') {
- /*
- * X: and nothing more is an error.
- */
- goto enoent;
- } else {
- drive = towlower(*name) - L'a' + 1;
- }
- fh = FindFirstFileW(name,&wfd);
- if (fh == INVALID_HANDLE_VALUE) {
- DWORD fff_error = GetLastError();
- if (!(wcspbrk(name, L"./\\") &&
- (full_path = get_full_wpath_tmp(state, name, NULL, 0)) &&
- /* root dir. ('C:\') or UNC root dir. ('\\server\share\') */
- ((wcslen(full_path) == 3) || is_root_unc_name(full_path)) &&
- (GetDriveTypeW(full_path) > 1) ) ) {
-
- set_os_errno(state->errInfo, fff_error);
- return 0;
- }
- /*
- * Root directories (such as C:\ or \\server\share\ are fabricated.
- */
- wcscpy(wbuffer,name);
- return 1;
- }
-
- wcscpy(wbuffer,wfd.cAlternateFileName);
- if (!*wbuffer) {
- wcscpy(wbuffer,wfd.cFileName);
- }
- FindClose(fh);
- return 1;
-}
-
-
-int
-efile_link(Efile_error* errInfo, char* old, char* new)
-{
- Efile_call_state state;
- WCHAR *wold = (WCHAR *) old;
- WCHAR *wnew = (WCHAR *) new;
- int ret;
- DBG_TRACE(1, old);
- call_state_init(&state, errInfo);
- ensure_wpath(&state, &wold);
- ensure_wpath(&state, &wnew);
- if(!CreateHardLinkW(wnew, wold, NULL)) {
- ret = set_error(errInfo);
- }
- else ret =1;
- call_state_free(&state);
- return ret;
-}
-
-int
-efile_symlink(Efile_error* errInfo, char* old, char* new)
-{
- Efile_call_state state;
- int ret;
- DBG_TRACE2(1, "symlink(%s <- %s)", old, new);
- call_state_init(&state, errInfo);
- ret = do_symlink(&state, old, new);
- call_state_free(&state);
- return ret;
-}
-
-static int
-do_symlink(Efile_call_state* state, char* old, char* new)
-{
- /*
- * Load dll and see if we have CreateSymbolicLink at runtime:
- * (Vista only)
- */
- HINSTANCE hModule = NULL;
- WCHAR *wold = (WCHAR *) old;
- WCHAR *wnew = (WCHAR *) new;
-
- DBG_TRACE(1, old);
- if ((hModule = LoadLibrary("kernel32.dll")) != NULL) {
- typedef BOOLEAN (WINAPI * CREATESYMBOLICLINKFUNCPTR) (
- LPCWSTR lpSymlinkFileName,
- LPCWSTR lpTargetFileName,
- DWORD dwFlags);
-
- CREATESYMBOLICLINKFUNCPTR pCreateSymbolicLink =
- (CREATESYMBOLICLINKFUNCPTR) GetProcAddress(hModule,
- "CreateSymbolicLinkW");
- /* A for MBCS, W for UNICODE... char* above implies 'W'! */
- if (pCreateSymbolicLink != NULL) {
- ensure_wpath(state, &wold);
- ensure_wpath(state, &wnew);
- {
- DWORD attr = GetFileAttributesW(wold);
- int flag = (attr != INVALID_FILE_ATTRIBUTES &&
- attr & FILE_ATTRIBUTE_DIRECTORY) ? 1 : 0;
- /* SYMBOLIC_LINK_FLAG_DIRECTORY = 1 */
- BOOLEAN success = pCreateSymbolicLink(wnew, wold, flag);
- FreeLibrary(hModule);
-
- if (success) {
- return 1;
- } else {
- return set_error(state->errInfo);
- }
- }
- } else
- FreeLibrary(hModule);
- }
- errno = ENOTSUP;
- return check_error(-1, state->errInfo);
-}
-
-int
-efile_fadvise(Efile_error* errInfo, int fd, Sint64 offset,
- Sint64 length, int advise)
-{
- DBG_TRACE(2, L"");
- /* posix_fadvise is not available on Windows, do nothing */
- errno = ERROR_SUCCESS;
- return check_error(0, errInfo);
-}
-
-int
-efile_fallocate(Efile_error* errInfo, int fd, Sint64 offset, Sint64 length)
-{
- DBG_TRACE(2, L"");
- /* No file preallocation method available in Windows. */
- errno = errno_map(ERROR_NOT_SUPPORTED);
- SetLastError(ERROR_NOT_SUPPORTED);
-
- return check_error(-1, errInfo);
-}
diff --git a/erts/emulator/hipe/hipe_amd64.c b/erts/emulator/hipe/hipe_amd64.c
index e3cff4a4ba..f23f341e6d 100644
--- a/erts/emulator/hipe/hipe_amd64.c
+++ b/erts/emulator/hipe/hipe_amd64.c
@@ -28,6 +28,7 @@
#include "error.h"
#include "bif.h"
#include "big.h" /* term_to_Sint() */
+#include "erl_binary.h"
#include "hipe_arch.h"
#include "hipe_bif0.h"
@@ -38,6 +39,8 @@
#undef ERL_FUN_SIZE
#include "hipe_literals.h"
+static void patch_trampoline(void *trampoline, void *destAddress);
+
const Uint sse2_fnegate_mask[2] = {0x8000000000000000,0};
void hipe_patch_load_fe(Uint64 *address, Uint64 value)
@@ -52,9 +55,9 @@ int hipe_patch_insn(void *address, Uint64 value, Eterm type)
switch (type) {
case am_closure:
case am_constant:
+ case am_c_const:
*(Uint64*)address = value;
break;
- case am_c_const:
case am_atom:
/* check that value fits in an unsigned imm32 */
/* XXX: are we sure it's not really a signed imm32? */
@@ -71,14 +74,18 @@ int hipe_patch_insn(void *address, Uint64 value, Eterm type)
int hipe_patch_call(void *callAddress, void *destAddress, void *trampoline)
{
- Sint rel32;
+ Sint64 destOffset = (Sint64)destAddress - (Sint64)callAddress - 4;
- ASSERT(trampoline == NULL);
+ if ((destOffset < -0x80000000L) || (destOffset >= 0x80000000L)) {
+ destOffset = (Sint64)trampoline - (Sint64)callAddress - 4;
- rel32 = (Sint)destAddress - (Sint)callAddress - 4;
- if ((Sint)(Sint32)rel32 != rel32)
- return -1;
- *(Uint32*)callAddress = (Uint32)rel32;
+ if ((destOffset < -0x80000000L) || (destOffset >= 0x80000000L))
+ return -1;
+
+ patch_trampoline(trampoline, destAddress);
+ }
+
+ *(Uint32*)callAddress = (Uint32)destOffset;
hipe_flush_icache_word(callAddress);
return 0;
}
@@ -96,12 +103,80 @@ static void *alloc_code(unsigned int alloc_bytes)
return erts_alloc(ERTS_ALC_T_HIPE_EXEC, alloc_bytes);
}
+static int check_callees(Eterm callees)
+{
+ Eterm *tuple;
+ Uint arity;
+ Uint i;
+
+ if (is_not_tuple(callees))
+ return -1;
+ tuple = tuple_val(callees);
+ arity = arityval(tuple[0]);
+ for (i = 1; i <= arity; ++i) {
+ Eterm mfa = tuple[i];
+ if (is_atom(mfa))
+ continue;
+ if (is_not_tuple(mfa) ||
+ tuple_val(mfa)[0] != make_arityval(3) ||
+ is_not_atom(tuple_val(mfa)[1]) ||
+ is_not_atom(tuple_val(mfa)[2]) ||
+ is_not_small(tuple_val(mfa)[3]) ||
+ unsigned_val(tuple_val(mfa)[3]) > 255)
+ return -1;
+ }
+ return arity;
+}
+
+#define TRAMPOLINE_BYTES 12
+
+static void generate_trampolines(unsigned char *address,
+ int nrcallees, Eterm callees,
+ unsigned char **trampvec)
+{
+ unsigned char *trampoline = address;
+ int i;
+
+ for(i = 0; i < nrcallees; ++i) {
+ trampoline[0] = 0x48; /* movabsq $..., %rax; */
+ trampoline[1] = 0xb8;
+ *(void**)(trampoline+2) = NULL; /* callee's address */
+ trampoline[10] = 0xff; /* jmpq *%rax */
+ trampoline[11] = 0xe0;
+ trampvec[i] = trampoline;
+ trampoline += TRAMPOLINE_BYTES;
+ }
+ hipe_flush_icache_range(address, nrcallees*TRAMPOLINE_BYTES);
+}
+
+static void patch_trampoline(void *trampoline, void *destAddress)
+{
+ unsigned char *tp = (unsigned char*) trampoline;
+
+ ASSERT(tp[0] == 0x48 && tp[1] == 0xb8);
+
+ *(void**)(tp+2) = destAddress; /* callee's address */
+ hipe_flush_icache_word(tp+2);
+}
+
void *hipe_alloc_code(Uint nrbytes, Eterm callees, Eterm *trampolines, Process *p)
{
- if (is_not_nil(callees))
+ int nrcallees;
+ Eterm trampvecbin;
+ unsigned char **trampvec;
+ unsigned char *address;
+
+ nrcallees = check_callees(callees);
+ if (nrcallees < 0)
return NULL;
- *trampolines = NIL;
- return alloc_code(nrbytes);
+
+ trampvecbin = new_binary(p, NULL, nrcallees*sizeof(unsigned char*));
+ trampvec = (unsigned char **)binary_bytes(trampvecbin);
+
+ address = alloc_code(nrbytes + nrcallees*TRAMPOLINE_BYTES);
+ generate_trampolines(address + nrbytes, nrcallees, callees, trampvec);
+ *trampolines = trampvecbin;
+ return address;
}
void hipe_free_code(void* code, unsigned int bytes)
@@ -129,10 +204,9 @@ void *hipe_make_native_stub(void *callee_exp, unsigned int beamArity)
*/
unsigned int codeSize;
unsigned char *code, *codep;
- unsigned int callEmuOffset;
- codeSize = /* 23, 26, 29, or 32 bytes */
- 23 + /* 23 when all offsets are 8-bit */
+ codeSize = /* 30, 33, 36, or 39 bytes */
+ 30 + /* 30 when all offsets are 8-bit */
(P_CALLEE_EXP >= 128 ? 3 : 0) +
((P_CALLEE_EXP + 4) >= 128 ? 3 : 0) +
(P_ARITY >= 128 ? 3 : 0);
@@ -197,14 +271,15 @@ void *hipe_make_native_stub(void *callee_exp, unsigned int beamArity)
codep[0] = beamArity;
codep += 1;
- /* jmp callemu; 5 bytes */
- callEmuOffset = (unsigned char*)nbif_callemu - (code + codeSize);
- codep[0] = 0xe9;
- codep[1] = callEmuOffset & 0xFF;
- codep[2] = (callEmuOffset >> 8) & 0xFF;
- codep[3] = (callEmuOffset >> 16) & 0xFF;
- codep[4] = (callEmuOffset >> 24) & 0xFF;
- codep += 5;
+ /* jmp callemu; 12 bytes */
+ codep[0] = 0x48;
+ codep[1] = 0xb8;
+ codep += 2;
+ *(Uint64*)codep = (Uint64)nbif_callemu;
+ codep += 8;
+ codep[0] = 0xff;
+ codep[1] = 0xe0;
+ codep += 2;
ASSERT(codep == code + codeSize);
diff --git a/erts/emulator/hipe/hipe_amd64_bifs.m4 b/erts/emulator/hipe/hipe_amd64_bifs.m4
index b3c9a460bb..cf4c59c9af 100644
--- a/erts/emulator/hipe/hipe_amd64_bifs.m4
+++ b/erts/emulator/hipe/hipe_amd64_bifs.m4
@@ -462,6 +462,7 @@ ASYM($1):
TYPE_FUNCTION(ASYM($1))
#endif')
+
/*
* noproc_primop_interface_0(nbif_name, cbif_name)
* noproc_primop_interface_1(nbif_name, cbif_name)
diff --git a/erts/emulator/hipe/hipe_bif0.c b/erts/emulator/hipe/hipe_bif0.c
index 05663648e9..a8be64e08d 100644
--- a/erts/emulator/hipe/hipe_bif0.c
+++ b/erts/emulator/hipe/hipe_bif0.c
@@ -53,8 +53,6 @@
#include "hipe_literals.h"
#endif
-#define BeamOpCode(Op) ((Uint)BeamOp(Op))
-
int term_to_Sint32(Eterm term, Sint *sp)
{
@@ -615,7 +613,7 @@ static ErtsCodeInfo* hipe_find_emu_address(Eterm mod, Eterm name, unsigned int a
n = code_hdr->num_functions;
for (i = 0; i < n; ++i) {
ErtsCodeInfo *ci = code_hdr->functions[i];
- ASSERT(ci->op == BeamOpCode(op_i_func_info_IaaI));
+ ASSERT(BeamIsOpCode(ci->op, op_i_func_info_IaaI));
if (ci->mfa.function == name && ci->mfa.arity == arity)
return ci;
}
@@ -1114,7 +1112,7 @@ static struct hipe_mfa_info* mod2mfa_put(struct hipe_mfa_info* mfa)
struct hipe_ref {
struct hipe_ref_head head; /* list of refs to same calleee */
void *address;
-#if defined(__arm__) || defined(__powerpc__) || defined(__ppc__) || defined(__powerpc64__)
+#if defined(__x86_64__) || defined(__arm__) || defined(__powerpc__) || defined(__ppc__) || defined(__powerpc64__)
void *trampoline;
#endif
unsigned int flags;
@@ -1545,7 +1543,7 @@ BIF_RETTYPE hipe_bifs_add_ref_2(BIF_ALIST_2)
ref = erts_alloc(ERTS_ALC_T_HIPE_LL, sizeof(struct hipe_ref));
ref->address = address;
-#if defined(__arm__) || defined(__powerpc__) || defined(__ppc__) || defined(__powerpc64__)
+#if defined(__x86_64__) || defined(__arm__) || defined(__powerpc__) || defined(__ppc__) || defined(__powerpc64__)
ref->trampoline = trampoline;
#endif
ref->flags = flags;
@@ -1773,7 +1771,8 @@ void hipe_redirect_to_module(Module* modp)
struct hipe_mfa_info *p;
struct hipe_ref_head* refh;
- ERTS_LC_ASSERT(erts_thr_progress_is_blocking());
+ ERTS_LC_ASSERT(erts_thr_progress_is_blocking() ||
+ erts_is_multi_scheduling_blocked());
for (p = mod2mfa_get(modp); p; p = p->next_in_mod) {
if (p->new_address) {
@@ -1820,7 +1819,7 @@ void hipe_redirect_to_module(Module* modp)
if (ref->flags & REF_FLAG_IS_LOAD_MFA)
res = hipe_patch_insn(ref->address, (Uint)p->remote_address, am_load_mfa);
else {
-#if defined(__arm__) || defined(__powerpc__) || defined(__ppc__) || defined(__powerpc64__)
+#if defined(__x86_64__) || defined(__arm__) || defined(__powerpc__) || defined(__ppc__) || defined(__powerpc64__)
void* trampoline = ref->trampoline;
#else
void* trampoline = NULL;
diff --git a/erts/emulator/hipe/hipe_bif0.tab b/erts/emulator/hipe/hipe_bif0.tab
index 4038ca7ef8..6728e20123 100644
--- a/erts/emulator/hipe/hipe_bif0.tab
+++ b/erts/emulator/hipe/hipe_bif0.tab
@@ -109,6 +109,7 @@ atom suspend_0
atom gc_1
atom hipe_apply
atom rethrow
+atom raw_raise
atom find_na_or_make_stub
atom nonclosure_address
atom atomic_inc
@@ -135,8 +136,8 @@ atom bs_utf16_size
atom bs_put_utf16be
atom bs_put_utf16le
atom bs_get_utf16
-atom bs_validate_unicode
atom bs_validate_unicode_retract
atom emulate_fpe
atom emasculate_binary
atom is_divisible
+atom is_unicode \ No newline at end of file
diff --git a/erts/emulator/hipe/hipe_bif1.c b/erts/emulator/hipe/hipe_bif1.c
index 3d3df4fd48..73d07f0ce5 100644
--- a/erts/emulator/hipe/hipe_bif1.c
+++ b/erts/emulator/hipe/hipe_bif1.c
@@ -32,11 +32,10 @@
#include "big.h"
#include "error.h"
#include "beam_load.h"
+#include "erl_vm.h"
#include "hipe_bif0.h"
#include "hipe_bif1.h"
-#define BeamOpCode(Op) ((Uint)BeamOp(Op))
-
BIF_RETTYPE hipe_bifs_call_count_on_1(BIF_ALIST_1)
{
ErtsCodeInfo *ci;
@@ -46,17 +45,17 @@ BIF_RETTYPE hipe_bifs_call_count_on_1(BIF_ALIST_1)
ci = hipe_bifs_find_pc_from_mfa(BIF_ARG_1);
if (!ci)
BIF_ERROR(BIF_P, BADARG);
- ASSERT(ci->op == BeamOpCode(op_i_func_info_IaaI));
+ ASSERT(BeamIsOpCode(ci->op, op_i_func_info_IaaI));
pc = erts_codeinfo_to_code(ci);
- if (pc[0] == BeamOpCode(op_hipe_trap_call))
+ if (BeamIsOpCode(pc[0], op_hipe_trap_call))
BIF_ERROR(BIF_P, BADARG);
- if (pc[0] == BeamOpCode(op_hipe_call_count))
+ if (BeamIsOpCode(pc[0], op_hipe_call_count))
BIF_RET(NIL);
hcc = erts_alloc(ERTS_ALC_T_HIPE_SL, sizeof(*hcc));
hcc->count = 0;
hcc->opcode = pc[0];
ci->u.hcc = hcc;
- pc[0] = BeamOpCode(op_hipe_call_count);
+ pc[0] = BeamOpCodeAddr(op_hipe_call_count);
BIF_RET(am_true);
}
@@ -70,9 +69,9 @@ BIF_RETTYPE hipe_bifs_call_count_off_1(BIF_ALIST_1)
ci = hipe_bifs_find_pc_from_mfa(BIF_ARG_1);
if (!ci)
BIF_ERROR(BIF_P, BADARG);
- ASSERT(ci->op == BeamOpCode(op_i_func_info_IaaI));
+ ASSERT(BeamIsOpCode(ci->op, op_i_func_info_IaaI));
pc = erts_codeinfo_to_code(ci);
- if (pc[0] != BeamOpCode(op_hipe_call_count))
+ if (! BeamIsOpCode(pc[0], op_hipe_call_count))
BIF_RET(am_false);
hcc = ci->u.hcc;
count = hcc->count;
@@ -91,9 +90,9 @@ BIF_RETTYPE hipe_bifs_call_count_get_1(BIF_ALIST_1)
ci = hipe_bifs_find_pc_from_mfa(BIF_ARG_1);
if (!ci)
BIF_ERROR(BIF_P, BADARG);
- ASSERT(ci->op == BeamOpCode(op_i_func_info_IaaI));
+ ASSERT(BeamIsOpCode(ci->op, op_i_func_info_IaaI));
pc = erts_codeinfo_to_code(ci);
- if (pc[0] != BeamOpCode(op_hipe_call_count))
+ if (! BeamIsOpCode(pc[0], op_hipe_call_count))
BIF_RET(am_false);
hcc = ci->u.hcc;
BIF_RET(make_small(hcc->count));
@@ -109,9 +108,9 @@ BIF_RETTYPE hipe_bifs_call_count_clear_1(BIF_ALIST_1)
ci = hipe_bifs_find_pc_from_mfa(BIF_ARG_1);
if (!ci)
BIF_ERROR(BIF_P, BADARG);
- ASSERT(ci->op == BeamOpCode(op_i_func_info_IaaI));
+ ASSERT(BeamIsOpCode(ci->op, op_i_func_info_IaaI));
pc = erts_codeinfo_to_code(ci);
- if (pc[0] != BeamOpCode(op_hipe_call_count))
+ if (! BeamIsOpCode(pc[0], op_hipe_call_count))
BIF_RET(am_false);
hcc = ci->u.hcc;
count = hcc->count;
diff --git a/erts/emulator/hipe/hipe_bif2.c b/erts/emulator/hipe/hipe_bif2.c
index 9ebbb22846..df377b2153 100644
--- a/erts/emulator/hipe/hipe_bif2.c
+++ b/erts/emulator/hipe/hipe_bif2.c
@@ -190,3 +190,8 @@ BIF_RETTYPE hipe_bifs_llvm_fix_pinned_regs_0(BIF_ALIST_0)
{
BIF_RET(am_ok);
}
+
+BIF_RETTYPE hipe_bifs_build_stacktrace_1(BIF_ALIST_1)
+{
+ BIF_RET(build_stacktrace(BIF_P, BIF_ARG_1));
+}
diff --git a/erts/emulator/hipe/hipe_bif2.tab b/erts/emulator/hipe/hipe_bif2.tab
index bbcb577be0..c4da44606a 100644
--- a/erts/emulator/hipe/hipe_bif2.tab
+++ b/erts/emulator/hipe/hipe_bif2.tab
@@ -32,3 +32,4 @@ bif hipe_bifs:modeswitch_debug_on/0
bif hipe_bifs:modeswitch_debug_off/0
bif hipe_bifs:debug_native_called/2
bif hipe_bifs:llvm_fix_pinned_regs/0
+bif hipe_bifs:build_stacktrace/1
diff --git a/erts/emulator/hipe/hipe_bif_list.m4 b/erts/emulator/hipe/hipe_bif_list.m4
index bebe20a18e..33b3cc1ee5 100644
--- a/erts/emulator/hipe/hipe_bif_list.m4
+++ b/erts/emulator/hipe/hipe_bif_list.m4
@@ -186,6 +186,7 @@ gc_bif_interface_1(nbif_erase_1, erase_1)
gc_bif_interface_1(nbif_erts_internal_garbage_collect_1, erts_internal_garbage_collect_1)
gc_nofail_primop_interface_1(nbif_gc_1, hipe_gc)
gc_bif_interface_2(nbif_put_2, put_2)
+gc_bif_interface_2(nbif_hipe_bifs_build_stacktrace, hipe_bifs_build_stacktrace_1)
/*
* Debug BIFs that need read access to the full state.
@@ -219,10 +220,12 @@ standard_bif_interface_1(nbif_bnot_1, bnot_1)
standard_bif_interface_1(nbif_set_timeout, hipe_set_timeout)
standard_bif_interface_1(nbif_conv_big_to_float, hipe_conv_big_to_float)
standard_bif_interface_2(nbif_rethrow, hipe_rethrow)
+standard_bif_interface_3(nbif_raw_raise, hipe_raw_raise)
standard_bif_interface_3(nbif_find_na_or_make_stub, hipe_find_na_or_make_stub)
standard_bif_interface_2(nbif_nonclosure_address, hipe_nonclosure_address)
nocons_nofail_primop_interface_0(nbif_fclearerror_error, hipe_fclearerror_error)
-standard_bif_interface_2(nbif_is_divisible, hipe_is_divisible)
+noproc_primop_interface_2(nbif_is_divisible, hipe_is_divisible)
+noproc_primop_interface_1(nbif_is_unicode, hipe_is_unicode)
/*
* Mbox primops with implicit P parameter.
@@ -244,10 +247,9 @@ noproc_primop_interface_2(nbif_eq_2, eq)
nofail_primop_interface_3(nbif_bs_get_integer_2, erts_bs_get_integer_2)
nofail_primop_interface_3(nbif_bs_get_binary_2, erts_bs_get_binary_2)
nofail_primop_interface_3(nbif_bs_get_float_2, erts_bs_get_float_2)
-standard_bif_interface_3(nbif_bs_put_utf8, hipe_bs_put_utf8)
+nocons_nofail_primop_interface_3(nbif_bs_put_utf8, hipe_bs_put_utf8)
standard_bif_interface_3(nbif_bs_put_utf16be, hipe_bs_put_utf16be)
standard_bif_interface_3(nbif_bs_put_utf16le, hipe_bs_put_utf16le)
-standard_bif_interface_1(nbif_bs_validate_unicode, hipe_bs_validate_unicode)
/*
* Bit-syntax primops without any P parameter.
diff --git a/erts/emulator/hipe/hipe_debug.c b/erts/emulator/hipe/hipe_debug.c
index 222a11db3d..929b2a9432 100644
--- a/erts/emulator/hipe/hipe_debug.c
+++ b/erts/emulator/hipe/hipe_debug.c
@@ -63,12 +63,13 @@ static void print_beam_pc(BeamInstr *pc)
printf("normal-process-exit");
} else {
ErtsCodeMFA *cmfa = find_function_from_pc(pc);
- if (cmfa)
+ if (cmfa) {
+ fflush(stdout);
erts_printf("%T:%T/%bpu + 0x%bpx",
cmfa->module, cmfa->function,
cmfa->arity,
pc - erts_codemfa_to_code(cmfa));
- else
+ } else
printf("?");
}
}
@@ -116,6 +117,7 @@ static void print_stack(Eterm *sp, Eterm *end)
printf(" | 0x%0*lx | 0x%0*lx | ",
2*(int)sizeof(long), (unsigned long)sp,
2*(int)sizeof(long), (unsigned long)val);
+ fflush(stdout);
erts_printf("%.30T", val);
printf("\r\n");
}
@@ -126,7 +128,9 @@ static void print_stack(Eterm *sp, Eterm *end)
void hipe_print_estack(Process *p)
{
- printf(" | BEAM STACK |\r\n");
+ printf(" | %*s BEAM STACK %*s |\r\n",
+ 2*(int)sizeof(long)-3, "",
+ 2*(int)sizeof(long)-4, "");
print_stack(p->stop, STACK_START(p));
}
@@ -135,7 +139,9 @@ static void print_heap(Eterm *pos, Eterm *end)
printf("From: 0x%0*lx to 0x%0*lx\n\r",
2*(int)sizeof(long), (unsigned long)pos,
2*(int)sizeof(long), (unsigned long)end);
- printf(" | H E A P |\r\n");
+ printf(" | %*s H E A P %*s |\r\n",
+ 2*(int)sizeof(long)-1, "",
+ 2*(int)sizeof(long)-1, "");
printf(" | %*s | %*s |\r\n",
2+2*(int)sizeof(long), "Address",
2+2*(int)sizeof(long), "Contents");
@@ -158,8 +164,10 @@ static void print_heap(Eterm *pos, Eterm *end)
++pos;
--ari;
}
- } else
+ } else {
+ fflush(stdout);
erts_printf("%.30T", val);
+ }
printf("\r\n");
}
printf(" |%s|%s|\r\n", dashes, dashes);
@@ -173,11 +181,15 @@ void hipe_print_heap(Process *p)
void hipe_print_pcb(Process *p)
{
printf("P: 0x%0*lx\r\n", 2*(int)sizeof(long), (unsigned long)p);
- printf("-----------------------------------------------\r\n");
- printf("Offset| Name | Value | *Value |\r\n");
+ printf("%.*s\r\n",
+ 6+1+13+1+2*(int)sizeof(long)+4+1+2*(int)sizeof(long)+4+1,
+ "---------------------------------------------------------------");
+ printf("Offset| Name | Value %*s | *Value %*s |\r\n",
+ 2*(int)sizeof(long)-4, "",
+ 2*(int)sizeof(long)-5, "");
#undef U
#define U(n,x) \
- printf(" % 4d | %s | 0x%0*lx | |\r\n", (int)offsetof(Process,x), n, 2*(int)sizeof(long), (unsigned long)p->x)
+ printf(" % 4d | %s | 0x%0*lx | %*s |\r\n", (int)offsetof(Process,x), n, 2*(int)sizeof(long), (unsigned long)p->x, 2*(int)sizeof(long)+2, "")
#undef P
#define P(n,x) \
printf(" % 4d | %s | 0x%0*lx | 0x%0*lx |\r\n", (int)offsetof(Process,x), n, 2*(int)sizeof(long), (unsigned long)p->x, 2*(int)sizeof(long), p->x ? (unsigned long)*(p->x) : -1UL)
@@ -241,5 +253,7 @@ void hipe_print_pcb(Process *p)
#endif /* HIPE */
#undef U
#undef P
- printf("-----------------------------------------------\r\n");
+ printf("%.*s\r\n",
+ 6+1+14+1+2*(int)sizeof(long)+4+1+2*(int)sizeof(long)+4+1,
+ "---------------------------------------------------------------");
}
diff --git a/erts/emulator/hipe/hipe_gc.c b/erts/emulator/hipe/hipe_gc.c
index 1a4a4c7952..aaedba1afd 100644
--- a/erts/emulator/hipe/hipe_gc.c
+++ b/erts/emulator/hipe/hipe_gc.c
@@ -91,7 +91,7 @@ Eterm *fullsweep_nstack(Process *p, Eterm *n_htop)
ASSERT(is_boxed(val));
*nsp_i = val;
} else if (!erts_is_literal(gval, ptr)) {
- move_boxed(&ptr, val, &n_htop, nsp_i);
+ move_boxed(ptr, val, &n_htop, nsp_i);
}
} else if (is_list(gval)) {
Eterm *ptr = list_val(gval);
@@ -100,7 +100,7 @@ Eterm *fullsweep_nstack(Process *p, Eterm *n_htop)
*nsp_i = ptr[1];
} else if (!erts_is_literal(gval, ptr)) {
ASSERT(erts_dbg_within_proc(ptr, p, NULL));
- move_cons(&ptr, val, &n_htop, nsp_i);
+ move_cons(ptr, val, &n_htop, nsp_i);
}
}
}
@@ -206,10 +206,10 @@ void gensweep_nstack(Process *p, Eterm **ptr_old_htop, Eterm **ptr_n_htop)
ASSERT(is_boxed(val));
*nsp_i = val;
} else if (ErtsInArea(ptr, mature, mature_size)) {
- move_boxed(&ptr, val, &old_htop, nsp_i);
+ move_boxed(ptr, val, &old_htop, nsp_i);
} else if (ErtsInYoungGen(gval, ptr, oh, oh_size)) {
ASSERT(erts_dbg_within_proc(ptr, p, NULL));
- move_boxed(&ptr, val, &n_htop, nsp_i);
+ move_boxed(ptr, val, &n_htop, nsp_i);
}
} else if (is_list(gval)) {
Eterm *ptr = list_val(gval);
@@ -217,10 +217,10 @@ void gensweep_nstack(Process *p, Eterm **ptr_old_htop, Eterm **ptr_n_htop)
if (IS_MOVED_CONS(val)) {
*nsp_i = ptr[1];
} else if (ErtsInArea(ptr, mature, mature_size)) {
- move_cons(&ptr, val, &old_htop, nsp_i);
+ move_cons(ptr, val, &old_htop, nsp_i);
} else if (ErtsInYoungGen(gval, ptr, oh, oh_size)) {
ASSERT(erts_dbg_within_proc(ptr, p, NULL));
- move_cons(&ptr, val, &n_htop, nsp_i);
+ move_cons(ptr, val, &n_htop, nsp_i);
}
}
}
@@ -278,7 +278,7 @@ Eterm *sweep_literals_nstack(Process *p, Eterm *old_htop, char *area,
ASSERT(is_boxed(val));
*nsp_i = val;
} else if (ErtsInArea(ptr, area, area_size)) {
- move_boxed(&ptr, val, &old_htop, nsp_i);
+ move_boxed(ptr, val, &old_htop, nsp_i);
}
} else if (is_list(gval)) {
Eterm *ptr = list_val(gval);
@@ -286,7 +286,7 @@ Eterm *sweep_literals_nstack(Process *p, Eterm *old_htop, char *area,
if (IS_MOVED_CONS(val)) {
*nsp_i = ptr[1];
} else if (ErtsInArea(ptr, area, area_size)) {
- move_cons(&ptr, val, &old_htop, nsp_i);
+ move_cons(ptr, val, &old_htop, nsp_i);
}
}
}
diff --git a/erts/emulator/hipe/hipe_instrs.tab b/erts/emulator/hipe/hipe_instrs.tab
index bcce196a1d..a01baebddf 100644
--- a/erts/emulator/hipe/hipe_instrs.tab
+++ b/erts/emulator/hipe/hipe_instrs.tab
@@ -45,7 +45,7 @@ hipe_trap.call() {
* ... remainder of original BEAM code
*/
ErtsCodeInfo *ci = erts_code_to_codeinfo(I);
- ASSERT(ci->op == (Uint) OpCode(i_func_info_IaaI));
+ ASSERT(IsOpCode(ci->op, i_func_info_IaaI));
c_p->hipe.u.ncallee = ci->u.ncallee;
++hipe_trap_count;
$HIPE_MODE_SWITCH(HIPE_MODE_SWITCH_CMD_CALL | (ci->mfa.arity << 8));
@@ -53,7 +53,7 @@ hipe_trap.call() {
hipe_trap.call_closure() {
ErtsCodeInfo *ci = erts_code_to_codeinfo(I);
- ASSERT(ci->op == (Uint) OpCode(i_func_info_IaaI));
+ ASSERT(IsOpCode(ci->op, i_func_info_IaaI));
c_p->hipe.u.ncallee = ci->u.ncallee;
++hipe_trap_count;
$HIPE_MODE_SWITCH(HIPE_MODE_SWITCH_CMD_CALL_CLOSURE | (ci->mfa.arity << 8));
@@ -132,7 +132,7 @@ hipe_call_count() {
*/
ErtsCodeInfo *ci = erts_code_to_codeinfo(I);
struct hipe_call_count *hcc = ci->u.hcc;
- ASSERT(ci->op == (Uint) OpCode(i_func_info_IaaI));
+ ASSERT(IsOpCode(ci->op, i_func_info_IaaI));
ASSERT(hcc != NULL);
ASSERT(VALID_INSTR(hcc->opcode));
++(hcc->count);
diff --git a/erts/emulator/hipe/hipe_mkliterals.c b/erts/emulator/hipe/hipe_mkliterals.c
index 6ea120c65c..74e793577c 100644
--- a/erts/emulator/hipe/hipe_mkliterals.c
+++ b/erts/emulator/hipe/hipe_mkliterals.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2001-2016. All Rights Reserved.
+ * Copyright Ericsson AB 2001-2018. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -462,7 +462,15 @@ static const struct rts_param rts_params[] = {
0
#endif
},
- /* This parameter is always defined, but its value depends on ERTS_SMP. */
+ /* This flag is always defined, but its value is configuration-dependent. */
+ { 17, "ERTS_USE_LITERAL_TAG",
+ 1,
+#if defined(TAG_LITERAL_PTR)
+ 1
+#else
+ 0
+#endif
+ },
{ 19, "MSG_MESSAGE",
1, offsetof(struct erl_mesg, m[0])
},
@@ -511,8 +519,8 @@ static const struct rts_param rts_params[] = {
1, offsetof(struct process, hipe.bif_callee)
#endif
},
- { 49, "P_MSG_FIRST", 1, offsetof(struct process, msg.first) },
- { 50, "P_MSG_SAVE", 1, offsetof(struct process, msg.save) },
+ { 49, "P_MSG_FIRST", 1, offsetof(struct process, sig_qs.first) },
+ { 50, "P_MSG_SAVE", 1, offsetof(struct process, sig_qs.save) },
{ 51, "P_CALLEE_EXP", 1, offsetof(struct process, hipe.u.callee_exp) },
{ 52, "THE_NON_VALUE", 1, (int)THE_NON_VALUE },
@@ -522,6 +530,9 @@ static const struct rts_param rts_params[] = {
1, offsetof(struct process, hipe.gc_is_unsafe)
#endif
},
+
+ { 54, "P_MSG_LAST", 1, offsetof(struct process, sig_qs.last) },
+ { 55, "P_MSG_SAVED_LAST", 1, offsetof(struct process, sig_qs.saved_last) },
};
#define NR_PARAMS ARRAY_SIZE(rts_params)
@@ -529,6 +540,11 @@ static const struct rts_param rts_params[] = {
static unsigned int literals_crc;
static unsigned int system_crc;
+/*
+ * Change this version value to detect incompatible changes in primop interface.
+ */
+#define PRIMOP_ABI_VSN 0x090300 /* erts-9.3 */
+
static void compute_crc(void)
{
unsigned int crc_value;
@@ -544,6 +560,8 @@ static void compute_crc(void)
for (i = 0; i < NR_PARAMS; ++i)
if (rts_params[i].is_defined)
crc_value = crc_update_int(crc_value, &rts_params[i].value);
+
+ crc_value ^= PRIMOP_ABI_VSN;
crc_value &= 0x07FFFFFF;
system_crc = crc_value;
}
diff --git a/erts/emulator/hipe/hipe_mode_switch.c b/erts/emulator/hipe/hipe_mode_switch.c
index b7f81fc4a6..bc9a700204 100644
--- a/erts/emulator/hipe/hipe_mode_switch.c
+++ b/erts/emulator/hipe/hipe_mode_switch.c
@@ -155,8 +155,6 @@ void hipe_check_pcb(Process *p, const char *file, unsigned line)
#include "hipe_arm_glue.h"
#endif
-#define BeamOpCode(Op) ((Uint)BeamOp(Op))
-
Uint hipe_beam_pc_return[1]; /* needed in hipe_debug.c */
Uint hipe_beam_pc_throw[1]; /* needed in hipe_debug.c */
Uint hipe_beam_pc_resume[1]; /* needed by hipe_set_timeout() */
@@ -166,9 +164,9 @@ void hipe_mode_switch_init(void)
{
hipe_arch_glue_init();
- hipe_beam_pc_return[0] = BeamOpCode(op_hipe_trap_return);
- hipe_beam_pc_throw[0] = BeamOpCode(op_hipe_trap_throw);
- hipe_beam_pc_resume[0] = BeamOpCode(op_hipe_trap_resume);
+ hipe_beam_pc_return[0] = BeamOpCodeAddr(op_hipe_trap_return);
+ hipe_beam_pc_throw[0] = BeamOpCodeAddr(op_hipe_trap_throw);
+ hipe_beam_pc_resume[0] = BeamOpCodeAddr(op_hipe_trap_resume);
hipe_beam_catch_throw =
make_catch(beam_catches_cons(hipe_beam_pc_throw, BEAM_CATCHES_NIL));
@@ -182,8 +180,8 @@ void hipe_set_call_trap(ErtsCodeInfo* ci, void *nfun, int is_closure)
HIPE_ASSERT(ci->op == BeamOpCode(op_i_func_info_IaaI));
bfun[0] =
is_closure
- ? BeamOpCode(op_hipe_trap_call_closure)
- : BeamOpCode(op_hipe_trap_call);
+ ? BeamOpCodeAddr(op_hipe_trap_call_closure)
+ : BeamOpCodeAddr(op_hipe_trap_call);
ci->u.ncallee = (void (*)(void)) nfun;
}
@@ -498,8 +496,10 @@ Process *hipe_mode_switch(Process *p, unsigned cmd, Eterm reg[])
erts_proc_lock(p, ERTS_PROC_LOCKS_MSG_RECEIVE);
p->i = hipe_beam_pc_resume;
p->arity = 0;
- erts_atomic32_read_band_relb(&p->state,
- ~ERTS_PSFLG_ACTIVE);
+ if (erts_atomic32_read_nob(&p->state) & ERTS_PSFLG_EXITING)
+ ASSERT(erts_atomic32_read_nob(&p->state) & ERTS_PSFLG_ACTIVE);
+ else
+ erts_atomic32_read_band_relb(&p->state, ~ERTS_PSFLG_ACTIVE);
erts_proc_unlock(p, ERTS_PROC_LOCKS_MSG_RECEIVE);
do_schedule:
{
@@ -664,7 +664,8 @@ void hipe_inc_nstack(Process *p)
Eterm *new_nstack = erts_alloc(ERTS_ALC_T_HIPE_STK, new_size*sizeof(Eterm));
unsigned used_size = p->hipe.nstend - p->hipe.nsp;
- sys_memcpy(new_nstack+new_size-used_size, p->hipe.nsp, used_size*sizeof(Eterm));
+ if (used_size)
+ sys_memcpy(new_nstack+new_size-used_size, p->hipe.nsp, used_size*sizeof(Eterm));
if (p->hipe.nstgraylim)
p->hipe.nstgraylim = new_nstack + new_size - (p->hipe.nstend - p->hipe.nstgraylim);
if (p->hipe.nstblacklim)
diff --git a/erts/emulator/hipe/hipe_native_bif.c b/erts/emulator/hipe/hipe_native_bif.c
index 23f64a6991..cf8c4139be 100644
--- a/erts/emulator/hipe/hipe_native_bif.c
+++ b/erts/emulator/hipe/hipe_native_bif.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2001-2017. All Rights Reserved.
+ * Copyright Ericsson AB 2001-2018. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -35,6 +35,7 @@
#include "hipe_native_bif.h"
#include "hipe_arch.h"
#include "hipe_stack.h"
+#include "erl_proc_sig_queue.h"
/*
* These are wrappers for BIFs that may trigger a native
@@ -254,6 +255,8 @@ void hipe_handle_exception(Process *c_p)
/* Synthesized to avoid having to generate code for it. */
c_p->def_arg_reg[0] = exception_tag[GET_EXC_CLASS(c_p->freason)];
+ ERTS_RECV_MARK_CLEAR(c_p); /* No longer safe to use this position */
+
hipe_find_handler(c_p);
}
@@ -312,6 +315,32 @@ BIF_RETTYPE nbif_impl_hipe_rethrow(NBIF_ALIST_2)
}
}
+/* Called via standard_bif_interface_3 */
+BIF_RETTYPE nbif_impl_hipe_raw_raise(NBIF_ALIST_3)
+{
+ Process *c_p = BIF_P;
+ Eterm class = BIF_ARG_1;
+ Eterm value = BIF_ARG_2;
+ Eterm stacktrace = BIF_ARG_3;
+ Eterm reason;
+
+ if (class == am_error) {
+ c_p->fvalue = value;
+ reason = EXC_ERROR;
+ } else if (class == am_exit) {
+ c_p->fvalue = value;
+ reason = EXC_EXIT;
+ } else if (class == am_throw) {
+ c_p->fvalue = value;
+ reason = EXC_THROWN;
+ } else {
+ return am_badarg;
+ }
+ reason &= ~EXF_SAVETRACE;
+ c_p->ftrace = stacktrace;
+ BIF_ERROR(c_p, reason);
+}
+
/*
* Support for compiled binary syntax operations.
*/
@@ -394,12 +423,8 @@ Eterm hipe_bs_utf8_size(Eterm arg)
return make_small(4);
}
-BIF_RETTYPE nbif_impl_hipe_bs_put_utf8(NBIF_ALIST_3)
+Eterm hipe_bs_put_utf8(Process* p, Eterm arg, byte* base, Uint offset)
{
- Process* p = BIF_P;
- Eterm arg = BIF_ARG_1;
- byte* base = (byte*) BIF_ARG_2;
- Uint offset = (Uint) BIF_ARG_3;
byte *save_bin_buf;
Uint save_bin_offset;
int res;
@@ -415,7 +440,8 @@ BIF_RETTYPE nbif_impl_hipe_bs_put_utf8(NBIF_ALIST_3)
erts_current_bin = save_bin_buf;
erts_bin_offset = save_bin_offset;
if (res == 0)
- BIF_ERROR(p, BADARG);
+ return 0;
+ ASSERT(new_offset != 0);
return new_offset;
}
@@ -482,15 +508,12 @@ static int validate_unicode(Eterm arg)
return 1;
}
-BIF_RETTYPE nbif_impl_hipe_bs_validate_unicode(NBIF_ALIST_1)
+Uint hipe_is_unicode(Eterm arg)
{
- Process *p = BIF_P;
- Eterm arg = BIF_ARG_1;
- if (!validate_unicode(arg))
- BIF_ERROR(p, BADARG);
- return NIL;
+ return (Uint) validate_unicode(arg);
}
+
int hipe_bs_validate_unicode_retract(ErlBinMatchBuffer* mb, Eterm arg)
{
if (!validate_unicode(arg)) {
@@ -500,16 +523,12 @@ int hipe_bs_validate_unicode_retract(ErlBinMatchBuffer* mb, Eterm arg)
return 1;
}
-/* Called via standard_bif_interface_2 */
-BIF_RETTYPE nbif_impl_hipe_is_divisible(NBIF_ALIST_2)
+Uint hipe_is_divisible(Uint dividend, Uint divisor)
{
- /* Arguments are Eterm-sized unsigned integers */
- Uint dividend = BIF_ARG_1;
- Uint divisor = BIF_ARG_2;
if (dividend % divisor) {
- BIF_ERROR(BIF_P, BADARG);
+ return 0;
} else {
- return NIL;
+ return 1;
}
}
@@ -526,38 +545,57 @@ Eterm hipe_check_get_msg(Process *c_p)
msgp = PEEK_MESSAGE(c_p);
if (!msgp) {
- erts_proc_lock(c_p, ERTS_PROC_LOCKS_MSG_RECEIVE);
- /* Make sure messages wont pass exit signals... */
- if (ERTS_PROC_PENDING_EXIT(c_p)) {
- erts_proc_unlock(c_p, ERTS_PROC_LOCKS_MSG_RECEIVE);
- return THE_NON_VALUE; /* Will be rescheduled for exit */
- }
- ERTS_MSGQ_MV_INQ2PRIVQ(c_p);
- msgp = PEEK_MESSAGE(c_p);
- if (msgp)
- erts_proc_unlock(c_p, ERTS_PROC_LOCKS_MSG_RECEIVE);
- else {
- /* XXX: BEAM doesn't need this */
- c_p->hipe_smp.have_receive_locks = 1;
- c_p->flags &= ~F_DELAY_GC;
- return THE_NON_VALUE;
- }
+ int get_out;
+ (void) erts_proc_sig_receive_helper(c_p, CONTEXT_REDS, 0,
+ &msgp, &get_out);
+ /* FIXME: Need to bump reductions... */
+ if (!msgp) {
+ if (get_out) {
+ if (get_out < 0) {
+ /*
+ * FIXME: We should get out yielding
+ * here...
+ */
+ goto next_message;
+ }
+ /* Go exit... */
+ return THE_NON_VALUE;
+ }
+
+ /*
+ * If there are no more messages in queue
+ * (and we are not yielding or exiting)
+ * erts_proc_sig_receive_helper()
+ * returns with message queue lock locked...
+ */
+
+ /* XXX: BEAM doesn't need this */
+ c_p->hipe_smp.have_receive_locks = 1;
+ c_p->flags &= ~F_DELAY_GC;
+ return THE_NON_VALUE;
+ }
}
- if (is_non_value(ERL_MESSAGE_TERM(msgp))
- && !erts_decode_dist_message(c_p, ERTS_PROC_LOCK_MAIN, msgp, 0)) {
- /*
- * A corrupt distribution message that we weren't able to decode;
- * remove it...
- */
- ASSERT(!msgp->data.attached);
- UNLINK_MESSAGE(c_p, msgp);
- msgp->next = NULL;
- erts_cleanup_messages(msgp);
- goto next_message;
+ ASSERT(msgp == PEEK_MESSAGE(c_p));
+ ASSERT(msgp && ERTS_SIG_IS_MSG(msgp));
+
+ if (ERTS_SIG_IS_EXTERNAL_MSG(msgp)) {
+ /* FIXME: bump appropriate amount... */
+ if (!erts_decode_dist_message(c_p, ERTS_PROC_LOCK_MAIN, msgp, 0)) {
+ /*
+ * A corrupt distribution message that we weren't able to decode;
+ * remove it...
+ */
+ /* TODO: Add DTrace probe for this bad message situation? */
+ UNLINK_MESSAGE(c_p, msgp);
+ msgp->next = NULL;
+ erts_cleanup_messages(msgp);
+ goto next_message;
+ }
}
- ASSERT(is_value(ERL_MESSAGE_TERM(msgp)));
+ ASSERT(msgp == PEEK_MESSAGE(c_p));
+ ASSERT(ERTS_SIG_IS_INTERNAL_MSG(msgp));
return ERL_MESSAGE_TERM(msgp);
}
diff --git a/erts/emulator/hipe/hipe_native_bif.h b/erts/emulator/hipe/hipe_native_bif.h
index cbc7ab8dc6..ba42b126be 100644
--- a/erts/emulator/hipe/hipe_native_bif.h
+++ b/erts/emulator/hipe/hipe_native_bif.h
@@ -36,6 +36,7 @@ AEXTERN(int,nbif_suspend_msg,(void));
AEXTERN(int,nbif_suspend_msg_timeout,(void));
AEXTERN(Eterm,nbif_rethrow,(Process*, Eterm, Eterm));
+AEXTERN(Eterm,nbif_raw_raise,(Process*, Eterm, Eterm, Eterm));
AEXTERN(Eterm,nbif_set_timeout,(Process*, Eterm));
AEXTERN(Eterm,nbif_gc_1,(void));
@@ -66,9 +67,9 @@ AEXTERN(Eterm,nbif_bs_utf16_size,(Eterm));
AEXTERN(Eterm,nbif_bs_put_utf16be,(Process*,Eterm,byte*,unsigned int));
AEXTERN(Eterm,nbif_bs_put_utf16le,(Process*,Eterm,byte*,unsigned int));
AEXTERN(Eterm,nbif_bs_get_utf16,(void));
-AEXTERN(Eterm,nbif_bs_validate_unicode,(Process*,Eterm));
+AEXTERN(Uint,nbif_is_unicode,(Eterm));
AEXTERN(Eterm,nbif_bs_validate_unicode_retract,(void));
-AEXTERN(void,nbif_is_divisible,(Process*,Uint,Uint));
+AEXTERN(Uint,nbif_is_divisible,(Uint,Uint));
AEXTERN(void,nbif_select_msg,(Process*));
AEXTERN(Eterm,nbif_cmp_2,(void));
@@ -82,19 +83,20 @@ void hipe_gc(Process*, Eterm);
BIF_RETTYPE nbif_impl_hipe_set_timeout(NBIF_ALIST_1);
void hipe_handle_exception(Process*);
BIF_RETTYPE nbif_impl_hipe_rethrow(NBIF_ALIST_2);
+BIF_RETTYPE nbif_impl_hipe_raw_raise(NBIF_ALIST_3);
char *hipe_bs_allocate(int);
Binary *hipe_bs_reallocate(Binary*, int);
int hipe_bs_put_small_float(Process*, Eterm, Uint, byte*, unsigned, unsigned);
void hipe_bs_put_bits(Eterm, Uint, byte*, unsigned, unsigned);
Eterm hipe_bs_utf8_size(Eterm);
-BIF_RETTYPE nbif_impl_hipe_bs_put_utf8(NBIF_ALIST_3);
+Eterm hipe_bs_put_utf8(Process*, Eterm arg, byte* base, Uint offset);
Eterm hipe_bs_utf16_size(Eterm);
BIF_RETTYPE nbif_impl_hipe_bs_put_utf16be(NBIF_ALIST_3);
BIF_RETTYPE nbif_impl_hipe_bs_put_utf16le(NBIF_ALIST_3);
-BIF_RETTYPE nbif_impl_hipe_bs_validate_unicode(NBIF_ALIST_1);
+Uint hipe_is_unicode(Eterm);
struct erl_bin_match_buffer;
int hipe_bs_validate_unicode_retract(struct erl_bin_match_buffer*, Eterm);
-BIF_RETTYPE nbif_impl_hipe_is_divisible(NBIF_ALIST_2);
+Uint hipe_is_divisible(Uint, Uint);
#ifdef NO_FPE_SIGNALS
AEXTERN(void,nbif_emulate_fpe,(Process*));
@@ -104,6 +106,9 @@ void hipe_emulate_fpe(Process*);
AEXTERN(void,nbif_emasculate_binary,(Eterm));
void hipe_emasculate_binary(Eterm);
+AEXTERN(BIF_RETTYPE,nbif_hipe_bifs_build_stacktrace,(Process*,Eterm));
+BIF_RETTYPE hipe_bifs_build_stacktrace_1(BIF_ALIST_1);
+
/*
* Stuff that is different in SMP and non-SMP.
*/
diff --git a/erts/emulator/hipe/hipe_primops.h b/erts/emulator/hipe/hipe_primops.h
index 6aac5e6205..c5f10672f3 100644
--- a/erts/emulator/hipe/hipe_primops.h
+++ b/erts/emulator/hipe/hipe_primops.h
@@ -46,6 +46,7 @@ PRIMOP_LIST(am_clear_timeout, &nbif_clear_timeout)
PRIMOP_LIST(am_select_msg, &nbif_select_msg)
PRIMOP_LIST(am_set_timeout, &nbif_set_timeout)
PRIMOP_LIST(am_rethrow, &nbif_rethrow)
+PRIMOP_LIST(am_raw_raise, &nbif_raw_raise)
PRIMOP_LIST(am_bs_get_integer_2, &nbif_bs_get_integer_2)
@@ -63,7 +64,7 @@ PRIMOP_LIST(am_bs_utf16_size, &nbif_bs_utf16_size)
PRIMOP_LIST(am_bs_put_utf16be, &nbif_bs_put_utf16be)
PRIMOP_LIST(am_bs_put_utf16le, &nbif_bs_put_utf16le)
PRIMOP_LIST(am_bs_get_utf16, &nbif_bs_get_utf16)
-PRIMOP_LIST(am_bs_validate_unicode, &nbif_bs_validate_unicode)
+PRIMOP_LIST(am_is_unicode, &nbif_is_unicode)
PRIMOP_LIST(am_bs_validate_unicode_retract, &nbif_bs_validate_unicode_retract)
PRIMOP_LIST(am_is_divisible, &nbif_is_divisible)
@@ -83,6 +84,7 @@ PRIMOP_LIST(am_emulate_fpe, &nbif_emulate_fpe)
#endif
PRIMOP_LIST(am_emasculate_binary, &nbif_emasculate_binary)
PRIMOP_LIST(am_debug_native_called, &nbif_hipe_bifs_debug_native_called)
+PRIMOP_LIST(am_build_stacktrace, &nbif_hipe_bifs_build_stacktrace)
#if defined(__sparc__)
#include "hipe_sparc_primops.h"
diff --git a/erts/emulator/hipe/hipe_risc_stack.c b/erts/emulator/hipe/hipe_risc_stack.c
index 4001bedeb6..bb93a918a2 100644
--- a/erts/emulator/hipe/hipe_risc_stack.c
+++ b/erts/emulator/hipe/hipe_risc_stack.c
@@ -47,8 +47,10 @@ static void print_slot(Eterm *sp, unsigned int live)
printf(" | 0x%0*lx | 0x%0*lx | ",
2*(int)sizeof(long), (unsigned long)sp,
2*(int)sizeof(long), val);
- if (live)
+ if (live) {
+ fflush(stdout);
erts_printf("%.30T", val);
+ }
printf("\r\n");
}
@@ -68,7 +70,9 @@ void hipe_print_nstack(Process *p)
[0 ... 2*sizeof(long)+3] = '-'
};
- printf(" | NATIVE STACK |\r\n");
+ printf(" | %*s NATIVE STACK %*s |\r\n",
+ 2*(int)sizeof(long)-5, "",
+ 2*(int)sizeof(long)-4, "");
printf(" |%s|%s|\r\n", dashes, dashes);
printf(" | %*s | 0x%0*lx |\r\n",
2+2*(int)sizeof(long), "heap",
diff --git a/erts/emulator/hipe/hipe_x86_stack.c b/erts/emulator/hipe/hipe_x86_stack.c
index 31582b3a2e..615e07917a 100644
--- a/erts/emulator/hipe/hipe_x86_stack.c
+++ b/erts/emulator/hipe/hipe_x86_stack.c
@@ -43,8 +43,10 @@ static void print_slot(Eterm *sp, unsigned int live)
printf(" | 0x%0*lx | 0x%0*lx | ",
2*(int)sizeof(long), (unsigned long)sp,
2*(int)sizeof(long), val);
- if (live)
+ if (live) {
+ fflush(stdout);
erts_printf("%.30T", val);
+ }
printf("\r\n");
}
@@ -74,7 +76,9 @@ void hipe_print_nstack(Process *p)
sdesc0.livebits[0] = ~1;
sdesc = &sdesc0;
- printf(" | NATIVE STACK |\r\n");
+ printf(" | %*s NATIVE STACK %*s |\r\n",
+ 2*(int)sizeof(long)-5, "",
+ 2*(int)sizeof(long)-4, "");
printf(" |%s|%s|\r\n", dashes, dashes);
printf(" | %*s | 0x%0*lx |\r\n",
2+2*(int)sizeof(long), "heap",
diff --git a/erts/emulator/internal_doc/CarrierMigration.md b/erts/emulator/internal_doc/CarrierMigration.md
index 2a9594db25..3a796d11b7 100644
--- a/erts/emulator/internal_doc/CarrierMigration.md
+++ b/erts/emulator/internal_doc/CarrierMigration.md
@@ -3,17 +3,17 @@ Carrier Migration
The ERTS memory allocators manage memory blocks in two types of raw
memory chunks. We call these chunks of raw memory
-*carriers*. Singleblock carriers which only contain one large block,
-and multiblock carriers which contain multiple blocks. A carrier is
+*carriers*. Single-block carriers which only contain one large block,
+and multi-block carriers which contain multiple blocks. A carrier is
typically created using `mmap()` on unix systems. However, how a
carrier is created is of minor importance. An allocator instance
-typically manages a mixture of single- and multiblock carriers.
+typically manages a mixture of single- and multi-block carriers.
Problem
-------
When a carrier is empty, i.e. contains only one large free block, it
-is deallocated. Since multiblock carriers can contain both allocated
+is deallocated. Since multi-block carriers can contain both allocated
blocks and free blocks at the same time, an allocator instance might
be stuck with a large amount of poorly utilized carriers if the memory
load decreases. After a peak in memory usage it is expected that not
@@ -23,9 +23,9 @@ can usually be reused if the memory load increases again. However,
since each scheduler thread manages its own set of allocator
instances, and memory load is not necessarily correlated to CPU load, we
might get into a situation where there are lots of poorly utilized
-multiblock carriers on some allocator instances while we need to
-allocate new multiblock carriers on other allocator instances. In
-scenarios like this, the demand for multiblock carriers in the system
+multi-block carriers on some allocator instances while we need to
+allocate new multi-block carriers on other allocator instances. In
+scenarios like this, the demand for multi-block carriers in the system
might increase at the same time as the actual memory demand in the
system has decreased which is both unwanted and quite unexpected for
the end user.
@@ -34,7 +34,7 @@ Solution
--------
In order to prevent scenarios like this we've implemented support for
-migration of multiblock carriers between allocator instances of the
+migration of multi-block carriers between allocator instances of the
same type.
### Management of Free Blocks ###
@@ -44,7 +44,7 @@ and add it to another we need to be able to move references to the
free blocks of the carrier between the allocator instances. The
allocator instance specific data structure referring to the free
blocks it manages often refers to the same carrier from multiple
-places. For example, when the address order bestfit strategy is used
+places. For example, when the address order best-fit strategy is used
this data structure is a binary search tree spanning all carriers that
the allocator instance manages. Free blocks in one specific carrier
can be referred to from potentially every other carrier that is
@@ -135,7 +135,7 @@ carriers between scheduler specific allocator instances of the same
allocator type.
Each allocator instance keeps track of the current utilization of its
-multiblock carriers. When the total utilization falls below the "abandon
+multi-block carriers. When the total utilization falls below the "abandon
carrier utilization limit" it starts to inspect the utilization of the
current carrier when deallocations are made. If also the utilization
of the carrier falls below the "abandon carrier utilization limit" it
@@ -144,31 +144,45 @@ and inserts the carrier into the pool.
Since the carrier has been unlinked from the data structure of
available free blocks, no more allocations will be made in the
-carrier. The allocator instance putting the carrier into the pool,
-however, still has the responsibility of performing deallocations in
-it while it remains in the pool. The allocator instance with this
-deallocation responsibility is here called the **employer**.
-
-Each carrier has a flag field containing information about the
-employing allocator instance, a flag indicating if the carrier is in
-the pool or not, and a flag indicating if it is busy or not. When the
-carrier is in the pool, the employing allocator instance needs to mark it
-as busy while operating on it. If another thread inspects it in order
-to try to fetch it from the pool, it will skip it if it is busy. When
-fetching the carrier from the pool, employment will change and further
+carrier.
+
+The allocator instance that created a carrier is called its **owner**.
+Ownership never changes.
+
+The allocator instance that has the responsibility to perform deallocations in a
+carrier is called its **employer**. The employer may also perform allocations if
+the carrier is not in the pool. Employment may change when a carrier is fetched from
+or inserted into the pool.
+
+Deallocations in a carrier, while it remains in the pool, is always performed
+the owner. That is, all pooled carriers are employed by their owners.
+
+Each carrier has an atomic word containing a pointer to the employing allocator
+instance and three bit flags; IN_POOL, BUSY and HOMECOMING.
+
+When fetching a carrier from the pool, employment may change and further
deallocations in the carrier will be redirected to the new
employer using the delayed dealloc functionality.
-If a carrier in the pool becomes empty, it will be withdrawn from the
-pool. All carriers that become empty are also always passed to its
-**owning** allocator instance for deallocation using the delayed
-dealloc functionality. Since carriers this way always will be
-deallocated by the owner that allocated the carrier, the
+When a foreign allocator instance abandons a carrier back into the pool, it will
+also pass it back to its **owner** using the delayed dealloc queue. When doing
+this it will set the HOMECOMING bit flag to mark it as "enqueued". The owner
+will later clear the HOMECOMING bit when the carrier is dequeued. This mechanism
+prevents a carrier from being enqueued again before it has been dequeued.
+
+When a carrier becomes empty, it will be deallocated. Carrier deallocation is
+always done by the owner that allocated the carrier. By doing this, the
underlying functionality of allocating and deallocating carriers can
remain simple and doesn't have to bother about multiple threads. In a
NUMA system we will also not mix carriers originating from multiple
NUMA nodes.
+If a carrier in the pool becomes empty, it will be withdrawn from the
+pool and be deallocated by the owner which already employs it.
+
+If a carrier employed by a foreign allocator becomes empty, it will be passed
+back to the owner for deallocation using the delayed dealloc functionality.
+
In short:
* The allocator instance that created a carrier **owns** it.
@@ -177,34 +191,31 @@ In short:
* The allocator instance that uses a carrier **employs** it.
* An **employer** can abandon a carrier into the pool.
* Pooled carriers are not allocated from.
-* Deallocation in a pooled carrier is still performed by its **employer**.
-* **Employment** can only change when a carrier is fetched from the pool.
+* Pooled carriers are always **employed** by their **owner**.
+* **Employment** can only change from **owner** to a foreign allocator
+ when a carrier is fetched from the pool.
+
### Searching the pool ###
+When an allocator instance needs more carrier space, it inspects the pool. If no
+carrier could be fetched from the pool, it will allocate a new
+carrier. Regardless of where the allocator instance gets the carrier from, it
+just links in the carrier into its data structure of free blocks.
+
To harbor real time characteristics, searching the pool is
limited. We only inspect a limited number of carriers. If none of
those carriers had a free block large enough to satisfy the allocation
-request, the search will fail. A carrier in the pool can also be busy
+request, the search will fail. A carrier in the pool can also be BUSY
if another thread is currently doing block deallocation work on the
-carrier. A busy carrier will also be skipped by the search as it can
+carrier. A BUSY carrier will also be skipped by the search as it can
not satisfy the request. The pool is lock-free and we do not want to
block, waiting for the other thread to finish.
-#### Before OTP 17.4 ####
+### The bad cluster problem ###
-When an allocator instance needs more carrier space, it always begins
-by inspecting its own carriers that are waiting for thread progress
-before they can be deallocated. If no such carrier could be found, it
-then inspects the pool. If no carrier could be fetched from the pool,
-it will allocate a new carrier. Regardless of where the allocator
-instance gets the carrier from it the just links in the carrier into
-its data structure of free blocks.
-
-#### After OTP 17.4 ####
-
-The old search algorithm had a problem as the search always started at
-the same position in the pool, the sentinel. This could lead to
+Before OTP-17.4 the search algorithm had a problem as the search always started
+at the same position in the pool, the sentinel. This could lead to
contention from concurrent searching processes. But even worse, it
could lead to a "bad" state when searches fail with a high rate
leading to new carriers instead being allocated. These new carriers
@@ -236,26 +247,27 @@ The result is that we prefer carriers created by the thread itself,
which is good for NUMA performance. And we get more entry points when
searching the pool, which will ease contention and clustering.
+### Our own pooled tree ###
+
To do the first search among own carriers, every allocator instance
-has two new lists: `pooled_list` and `traitor_list`. These lists are only
-accessed by the allocator itself and they only contain the allocator's
-own carriers. When an owned carrier is abandoned and put in the
-pool, it is also linked into `pooled_list`. When we search our
-`pooled_list` and find a carrier that is no longer in the pool, we
-move that carrier from `pooled_list` to `traitor_list` as it is now
-employed by another allocator. If searching `pooled_list` fails, we
-also do a limited search of `traitor_list`. When finding an abandoned
-carrier in `traitor_list` it is either employed or moved back to
-`pooled_list` if it could not satisfy the allocation request.
-
-When searching `pooled_list` and `traitor_list` we always start at the
-point where the last search ended. This to avoid clustering
-problems and increase the probability to find a "good" carrier. As
-`pooled_list` and `traitor_list` are only accessed by the owning
-allocator instance, they need no thread synchronization at all.
+has a `pooled_tree` of carriers. This tree is only accessed by the allocator
+itself and can only contain its own carriers. When a carrier is
+abandoned and put in the pool, it is also inserted into `pooled_tree`. This is
+either done direct, if the carrier was already employed by its owner, or by
+first passing it back to the owner via the delayed dealloc queue.
+
+When we search our `pooled_tree` and find a carrier that is no longer in the
+pool, we remove that carrier from `pooled_tree` and mark it as TRAITOR, as it is
+now employed by a foreign allocator. We will not find any carriers in
+`pooled_tree` that are marked as BUSY by other threads.
+
+If no carrier in `pooled_tree` had a large enough free block, we search it again
+to find any carrier that may act as an entry point into the shared list of all
+pooled carriers. This in order to, if possible, avoid starting at the sentinel
+and thereby ease the "bad clustering" problem.
Furthermore, the search for own carriers that are scheduled
-for deallocation is now done as the last search option. The idea is
+for deallocation is done as the last search option. The idea is
that it is better to reuse a poorly utilized carrier than to
resurrect an empty carrier that was just about to be released back to
the OS.
@@ -271,14 +283,14 @@ load did not.
When using the `aoffcaobf` or `aoff` strategies compared to `gf` or
`bf`, we loose some performance since we get more modifications in the
data structure of free blocks. This performance penalty is however
-reduced using the `aoffcbf` strategy. A tradeoff between memory
+reduced using the `aoffcbf` strategy. A trade off between memory
consumption and performance is however inevitable, and it is up to
the user to decide what is most important.
Further work
------------
-It would be quite easy to extend this to allow migration of multiblock
+It would be quite easy to extend this to allow migration of multi-block
carriers between all allocator types. More or less the only obstacle
is maintenance of the statistics information.
diff --git a/erts/emulator/internal_doc/GarbageCollection.md b/erts/emulator/internal_doc/GarbageCollection.md
new file mode 100644
index 0000000000..1d9e3f4160
--- /dev/null
+++ b/erts/emulator/internal_doc/GarbageCollection.md
@@ -0,0 +1,187 @@
+# Erlang Garbage Collector
+
+Erlang manages dynamic memory with a [tracing garbage collector](https://en.wikipedia.org/wiki/Tracing_garbage_collection). More precisely a per process generational semi-space copying collector using [Cheney's](#cheney) copy collection algorithm together with a global large object space.
+
+## Overview
+
+Each Erlang process has its own stack and heap which are allocated in the same memory block and grow towards each other. When the stack and the heap [meet](https://github.com/erlang/otp/blob/OTP-18.0/erts/emulator/beam/beam_emu.c#L387), the garbage collector is triggered and memory is reclaimed. If not enough memory was reclaimed, the heap will grow.
+
+### Creating Data
+
+Terms are created on the heap by evaluating expressions. There are two major types of terms: [immediate terms](https://github.com/erlang/otp/blob/OTP-18.0/erts/emulator/beam/erl_term.h#L88-L97) which require no heap space (small integers, atoms, pids, port ids etc) and cons or [boxed terms](https://github.com/erlang/otp/blob/OTP-18.0/erts/emulator/beam/erl_term.h#L106-L120) (tuple, big num, binaries etc) that do require heap space. Immediate terms do not need any heap space because they are embedded into the containing structure.
+
+Let's look at an example that returns a tuple with the newly created data.
+
+```erlang
+data(Foo) ->
+ Cons = [42|Foo],
+ Literal = {text, "hello world!"},
+ {tag, Cons, Literal}.
+```
+
+In this example we first create a new cons cell with an integer and a tuple with some text. Then a tuple of size three wrapping the other values with an atom tag is created and returned.
+
+On the heap tuples require a word size for each of its elements as well as for the header. Cons cells always require two words. Adding these things together, we get seven words for the tuples and 26 words for the cons cells. The string `"hello world!"` is a list of cons cells and thus requires 24 words. The atom `tag` and the integer `42` do not require any additional heap memory since it is an *immediate*. Adding all the terms together, the heap space required in this example should be 33 words.
+
+Compiling this code to beam assembly (`erlc -S`) shows exactly what is happening.
+
+```erlang
+ ...
+ {test_heap,6,1}.
+ {put_list,{integer,42},{x,0},{x,1}}.
+ {put_tuple,3,{x,0}}.
+ {put,{atom,tag}}.
+ {put,{x,1}}.
+ {put,{literal,{text,"hello world!"}}}.
+ return.
+```
+
+Looking at the assembler code we can see three things; The heap requirement in this function turns out to be only six words, as seen by the `{test_heap,6,1}` instruction. All the allocations are combined to a single instruction. The bulk of the data `{text, "hello world!"}` is a *literal*. Literals, sometimes referred to as constants, are not allocated in the function since they are a part of the module and allocated at load time.
+
+If there is not enough space available on the heap to satisfy the `test_heap` instructions request for memory, then a garbage collection is initiated. It may happen immediately in the `test_heap` instruction, or it can be delayed until a later time depending on what state the process is in. If the garbage collection is delayed, any memory needed will be allocated in heap fragments. Heap fragments are extra memory blocks that are a part of the young heap, but are not allocated in the contigious area where terms normally reside. See [The young heap](#the-young-heap) for more details.
+
+### The collector
+
+Erlang has a copying semi-space garbage collector. This means that when doing a garbage collection, the terms are copied from one distinct area, called the *from space*, to a new clean area, called the *to space*. The collector starts by [scanning the root-set](https://github.com/erlang/otp/blob/OTP-18.0/erts/emulator/beam/erl_gc.c#L1980) (stack, registers, etc).
+
+![Garbage collection: initial values](figures/gc-start.png)
+
+It follows all the pointers from the root-set to the heap and copies each term word by word to the *to space*.
+
+After the header word has been copied a [*move marker*](https://github.com/erlang/otp/blob/OTP-18.0/erts/emulator/beam/erl_gc.h#L45-L46) is destructively placed in it pointing to the term in the *to space*. Any other term that points to the already moved term will [see this move marker](https://github.com/erlang/otp/blob/OTP-18.0/erts/emulator/beam/erl_gc.c#L1125) and copy the referring pointer instead. For example, if the have the following Erlang code:
+
+```erlang
+foo(Arg) ->
+ T = {test, Arg},
+ {wrapper, T, T, T}.
+```
+
+Only one copy of T exists on the heap and during the garbage collection only the first time T is encountered will it be copied.
+
+![Garbage collection: root set scan](figures/gc-rootset-scan.png)
+
+After [all terms](https://github.com/erlang/otp/blob/OTP-18.0/erts/emulator/beam/erl_gc.c#L1089) referenced by the root-set have been copied, the collector scans the *to space* and copies all terms that these terms reference. When scanning, the collector steps through each term on the *to space* and any term still referencing the *from space* is copied over to the *to space*. Some terms contain non-term data (the payload of a on heap binary for instance). When encountered by the collector, these values are simply skipped.
+
+![Garbage collection: heap scan](figures/gc-heap-scan1.png)
+
+Every term object we can reach is copied to the *to space* and stored on top off the *scan stop* line, and then the scan stop is moved to the end of the last object.
+
+![Garbage collection: heap scan](figures/gc-heap-stop.png)
+
+When *scan stop* marker [catches up](https://github.com/erlang/otp/blob/OTP-18.0/erts/emulator/beam/erl_gc.c#L1103) to the *scan start* marker, the garbage collection is done. At this point we can [deallocate](https://github.com/erlang/otp/blob/OTP-18.0/erts/emulator/beam/erl_gc.c#L1206) the entire *from space* and therefore reclaim the entire young heap.
+
+## Generational Garbage Collection
+
+In addition to the collection algorithm described above, the Erlang garbage collector also provides generational garbage collection. An additional heap, called the old heap, is used where the long lived data is stored. The original heap is called the young heap, or sometimes the allocation heap.
+
+With this in mind we can look at the Erlang's garbage collection again. During the copy stage anything that should be copied to the young *to space* is instead copied to the old *to space* *if* it is [below the *high-watermark*](https://github.com/erlang/otp/blob/OTP-18.0/erts/emulator/beam/erl_gc.c#L1127).
+
+![Garbage collection: heap scan](figures/gc-watermark.png)
+
+The [*high-watermark*](https://github.com/erlang/otp/blob/OTP-18.0/erts/emulator/beam/erl_process.h#L1021) is placed where the previous garbage collection (described in [Overview](#overview)) ended and we have introduced a new area called the old heap. When doing the normal garbage collection pass, any term that is located below the high-watermark is copied to the old *to space* instead of the young.
+
+![Garbage collection: heap scan](figures/gc-watermark-2.png)
+
+In the next garbage collection, any pointers to the old heap will be ignored and not scanned. This way the garbage collector does not have to scan the long-lived terms.
+
+Generational garbage collection aims to increase performance at the expense of memory. This is achieved because only the young, smaller, heap is considered in most garbage collections.
+
+The generational [hypothesis](#ungar) predicts that most terms tend to die young, and for an immutable language such as Erlang, young terms die even faster than in other languages. So for most usage patterns the data in the new heap will die very soon after it is allocated. This is good because it limits the amount of data copied to the old heap and also because the garbage collection algorithm used is proportional to the amount of live data on the heap.
+
+One critical issue to note here is that any term on the young heap can reference terms on the old heap but *no* term on the old heap may refer to a term on the young heap. This is due to the nature of the copy algorithm. Anything referenced by an old heap term is not included in the reference tree, root-set and its followers, and hence is not copied. If it was, the data would be lost, fire and brimstone would rise to cover the earth. Fortunately, this comes naturally for Erlang because the terms are immutable and thus there can be no pointers modified on the old heap to point to the young heap.
+
+To reclaim data from the old heap, both young and old heaps are included during the collection and copied to a common *to space*. Both the *from space* of the young and old heap are then deallocated and the procedure will start over from the beginning. This type of garbage collection is called a full sweep and is triggered when the size of the area under the high-watermark is larger than the size of the free area of the old heap. It can also be triggered by doing a manual call to [erlang:garbage_collect()](http://erlang.org/doc/man/erlang.html#garbage_collect-0), or by running into the young garbage collection limit set by [spawn_opt(fun(),[{fullsweep_after, N}])](http://erlang.org/doc/man/erlang.html#spawn_opt-4) where N is the number of young garbage collections to do before forcing a garbage collection of both young and old heap.
+
+## The young heap
+
+The young heap, or the allocation heap, consists of the stack and heap as described in the Overview. However, it also includes any heap fragments that are attached to the heap. All of the heap fragments are considered to be above the high-watermark and part of the young generation. Heap fragments contain terms that either did not fit on the heap, or were created by another process and then attached to the heap. For instance if the bif binary_to_term created a term which does not fit on the current heap without doing a garbage collection, it will create a heap-fragment for the term and then schedule a garbage collection for later. Also if a message is sent to the process, the payload may be placed in a heap-fragment and that fragment is added to young heap when the message is matched in a receive clause.
+
+This procedure differs from how it worked prior to Erlang/OTP 19.0. Before 19.0, only a contiguous memory block where the young heap and stack resided was considered to be part of the young heap. Heap fragments and messages were immediately copied into the young heap before they could be inspected by the Erlang program. The behaviour introduced in 19.0 is superior in many ways - most significantly it reduces the number of necessary copy operations and the root set for garbage collection.
+
+## Sizing the heap
+
+As mentioned in the Overview the size of the heap [grows](https://github.com/erlang/otp/blob/OTP-18.0/erts/emulator/beam/erl_gc.c#L247) to accommodate more data. Heaps grow in two stages, first a [variation of the Fibonacci sequence](https://github.com/erlang/otp/blob/OTP-18.0/erts/emulator/beam/erl_gc.c#L199-L208) is used starting at 233 words. Then at about 1 mega words the heap only [grows in 20% increments](https://github.com/erlang/otp/blob/OTP-18.0/erts/emulator/beam/erl_gc.c#L215-L227).
+
+There are two occasions when the young heap grows:
+
+* if the total size of the heap + message and heap fragments exceeds the current heap size.
+* if after a fullsweep, the total amount of live objects is greater than 75%.
+
+There are two occasions when the young heap is shrunk:
+
+* if after a young collection, the total amount of live objects is less than 25% of the heap and the young heap is "big"
+* if after a fullsweep, the total amount of live objects is less than 25% of the heap.
+
+The old heap is always one step ahead in the heap growth stages than the young heap.
+
+## Literals
+
+When garbage collecting a heap (young or old) all literals are left in place and not copied. To figure out if a term should be copied or not when doing a garbage collection the following pseudo code is used:
+
+```c
+if (erts_is_literal(ptr) || (on_old_heap(ptr) && !fullsweep)) {
+ /* literal or non fullsweep - do not copy */
+} else {
+ copy(ptr);
+}
+```
+
+The [`erts_is_literal`](https://github.com/erlang/otp/blob/OTP-19.0/erts/emulator/beam/global.h#L1452-L1465) check works differently on different architectures and operating systems.
+
+On 64 bit systems that allow mapping of unreserved virtual memory areas (most operating systems except Windows), an area of size 1 GB (by default) is mapped and then all literals are placed within that area. Then all that has to be done to determine if something is a literal or not is [two quick pointer checks](https://github.com/erlang/otp/blob/OTP-19.0/erts/emulator/beam/erl_alloc.h#L322-L324). This system relies on the fact that a memory page that has not been touched yet does not take any actual space. So even if 1 GB of virtual memory is mapped, only the memory which is actually needed for literals is allocated in ram. The size of the literal area is configurable through the +MIscs erts_alloc option.
+
+On 32 bit systems, there is not enough virtual memory space to allocate 1 GB for just literals, so instead small 256 KB sized literal regions are created on demand and a card mark bit-array of the entire 32 bit memory space is then used to determine if a term is a literal or not. Since the total memory space is only 32 bits, the card mark bit-array is only 256 words large. On a 64 bit system the same bit-array would have to be 1 tera words large, so this technique is only viable on 32 bit systems. Doing [lookups in the array](https://github.com/erlang/otp/blob/OTP-19.0/erts/emulator/beam/erl_alloc.h#L316-L319) is a little more expensive then just doing the pointer checks that can be done in 64 bit systems, but not extremely so.
+
+On 64 bit windows, on which erts_alloc cannot do unreserved virtual memory mappings, a [special tag](https://github.com/erlang/otp/blob/OTP-19.0/erts/emulator/beam/erl_term.h#L59) within the Erlang term object is used to determine if something [is a literal or not](https://github.com/erlang/otp/blob/OTP-19.0/erts/emulator/beam/erl_term.h#L248-L252). This is very cheap, however, the tag is only available on 64 bit machines, and it is possible to do a great deal of other nice optimizations with this tag in the future (like for instance a more compact list implementation) so it is not used on operating systems where it is not needed.
+
+This behaviour is different from how it worked prior to Erlang/OTP 19.0. Before 19.0 the literal check was done by checking if the pointer pointed to the young or old heap block. If it did not, then it was considered a literal. This lead to considerable overhead and strange memory usage scenarios, so it was removed in 19.0.
+
+## Binary heap
+
+The binary heap works as a large object space for binary terms that are greater than 64 bytes (from now on called off-heap binaries). The binary heap is [reference counted](https://en.wikipedia.org/wiki/Reference_counting) and a pointer to the off-heap binary is stored on the process heap. To keep track of when to decrement the reference counter of the off-heap binary, a linked list (the MSO - mark and sweep object list) containing funs and externals as well as off-heap binaries is woven through the heap. After a garbage collection is done, the [MSO list is swept](https://github.com/erlang/otp/blob/OTP-18.0/erts/emulator/beam/erl_gc.c#L2299) and any off-heap binary that does not have a [move marker](https://github.com/erlang/otp/blob/OTP-18.0/erts/emulator/beam/erl_gc.c#L2325) written into the header words has its reference [decremented and is potentially freed](https://github.com/erlang/otp/blob/OTP-18.0/erts/emulator/beam/erl_gc.c#L2344-L2367).
+
+All items in the MSO list are ordered by the time they were added to the process heap, so when doing a minor garbage collection, the MSO sweeper only has to sweep until it [encounters an off-heap binary that is on the old heap](https://github.com/erlang/otp/blob/OTP-18.0/erts/emulator/beam/erl_gc.c#L2369).
+
+### Virtual Binary heap
+
+Each process has a virtual binary heap associated with it that has the size of all the current off-heap binaries that the process has references to. The virtual binary heap also has a limit and grows and shrinks depending on how off-heap binaries are used by the process. The same growth and shrink mechanisms are used for the binary heap and for the term heap, so first a Fibonacci like series and then 20% growth.
+
+The virtual binary heap exists in order to [trigger](https://github.com/erlang/otp/blob/OTP-18.0/erts/emulator/beam/beam_emu.c#L364) garbage collections earlier when potentially there is a very large amount of off-heap binary data that could be reclaimed. This approach does not catch all problems with binary memory not being released soon enough, but it does catch a lot of them.
+
+## Messages
+
+Messages can become a part of the process heap at different times. This depends on how the process is configured.
+We can configure the behaviour of each process using `process_flag(message_queue_data, off_heap | on_heap)` or we can set a default for all processes at start using the option `+hmqd`.
+
+What do these different configurations do and when should we use them?
+Let's start by going through what happens when one Erlang process sends a message to another.
+The sending process needs to do a couple of things:
+
+1. calculate [how large](https://github.com/erlang/otp/blob/OTP-18.0/erts/emulator/beam/erl_message.c#L1031) the message to be sent is
+2. [allocate enough space](https://github.com/erlang/otp/blob/OTP-18.0/erts/emulator/beam/erl_message.c#L1033) to fit the entire message
+3. [copy](https://github.com/erlang/otp/blob/OTP-18.0/erts/emulator/beam/erl_message.c#L1040) the message payload
+4. allocate a message container with some meta data
+5. [insert](https://github.com/erlang/otp/blob/OTP-18.0/erts/emulator/beam/erl_message.c#L502) the message container in the receiver process' [message queue](https://github.com/erlang/otp/blob/OTP-18.0/erts/emulator/beam/erl_process.h#L1042)
+
+The process flag `message_queue_data`, of the receiver process, controls the message allocating strategy of the sender process in step 2 and also how the message data is treated by the garbage collector.
+
+The procedure above is different from how it worked prior to 19.0. Before 19.0 there was no configuration option, the behaviour was always very similar to how the `on_heap` option is in 19.0.
+
+### Message allocating strategies
+
+If set to `on_heap`, the sending process will first attempt to allocate the space for the message directly on the young heap block of the receiving process.
+This is not always possible as it requires taking the *main lock* of the receiving process. The main lock is also held when the process is executing. The possibility for a lock conflict is thus likely in an intensely collaborating system.
+If the sending process cannot acquire the main lock, a heap fragment is instead created for the message and the message payload is copied onto that.
+With the `off_heap` option the sender process always creates heap fragments for messages sent to that process.
+
+There are a bunch of different tradeoffs that come into play when trying to figure out which of the strategies you want to use.
+
+Using `off_heap` may seem like a nice way to get a more scalable system as you get very little contention on the main locks, however, allocating a heap fragment is more expensive than allocating on the heap of the receiving process. So if it is very unlikely that contention will occur, it is more efficient to try to allocate the message directly on the receiving process' heap.
+
+Using `on_heap` will force all messages to be part of on the young heap which will increase the amount of data that the garbage collector has to move. So if a garbage collection is triggered while processing a large amount of messages, they will be copied to the young heap. This in turn will lead to that the messages will quickly be promoted to the old heap and thus increase its size. This may be good or bad depending on exactly what the process does. A large old heap means that the young heap will also be larger, which in turn means that less garbage collections will be triggered while processing the message queue. This will temporarly increase the throughput of the process at the cost of more memory usage. However, if after all the messages have been consumed the process enters a state where a lot less messages are being received. Then it may be a long time before the next fullsweep garbage collection happens and the messages that are on the old heap will be there until that happens. So while `on_heap` is potentially faster than the other modes, it uses more memory for a longer time. This mode is the legacy mode which is almost how the message queue was handled before Erlang/OTP 19.0.
+
+Which one of these strategies is best depends a lot on what the process is doing and how it interacts with other processes. So, as always, profile the application and see how it behaves with the different options.
+
+ <a name="cheney">[1]</a>: C. J. Cheney. A nonrecursive list compacting algorithm. Commun. ACM, 13(11):677–678, Nov. 1970.
+
+ <a name="ungar">[2]</a>: D. Ungar. Generation scavenging: A non-disruptive high performance storage reclamation algorithm. SIGSOFT Softw. Eng. Notes, 9(3):157–167, Apr. 1984.
diff --git a/erts/emulator/internal_doc/beam_makeops.md b/erts/emulator/internal_doc/beam_makeops.md
new file mode 100644
index 0000000000..1da8d2ab05
--- /dev/null
+++ b/erts/emulator/internal_doc/beam_makeops.md
@@ -0,0 +1,1846 @@
+The beam\_makeops script
+=======================
+
+This document describes the **beam\_makeops** script.
+
+Introduction
+------------
+
+The **beam\_makeops** Perl script is used at build-time by both the
+compiler and runtime system. Given a number of input files (all with
+the extension `.tab`), it will generate source files used by the
+Erlang compiler and by the runtime system to load and execute BEAM
+instructions.
+
+Essentially those `.tab` files define:
+
+* External generic BEAM instructions. They are the instructions that
+are known to both the compiler and the runtime system. Generic
+instructions are stable between releases. New generic instructions
+with high numbers than previous instructions can be added in major
+releases. The OTP 20 release has 159 external generic instructions.
+
+* Internal generic instructions. They are known only to the runtime
+system and can be changed at any time without compatibility issues.
+They are created by transformation rules (described next).
+
+* Rules for transforming one or more generic instructions to other
+generic instructions. The transformation rules allow combining,
+splitting, and removal of instructions, as well as shuffling operands.
+Because of the transformation rules, the runtime can have many
+internal generic instructions that are only known to runtime system.
+
+* Specific BEAM instructions. The specific instructions are the
+instructions that are actually executed by the runtime system. They
+can be changed at any time without causing compatibility issues.
+The loader translates generic instructions to specific instructions.
+In general, for each generic instruction, there exists a family of
+specific instructions. The OTP 20 release has 389 specific
+instructions.
+
+* The implementation of specific instructions.
+
+Generic instructions have typed operands. Here are a few examples of
+operands for `move/2`:
+
+ {move,{atom,id},{x,5}}.
+ {move,{x,3},{x,0}}.
+ {move,{x,2},{y,1}}.
+
+When those instructions are loaded, the loader rewrites them
+to specific instructions:
+
+ move_cx id 5
+ move_xx 3 0
+ move_xy 2 1
+
+Corresponding to each generic instruction, there is a family of
+specific instructions. The types that an instance of a specific
+instruction can handle are encoded in the instruction names. For
+example, `move_xy` takes an X register number as the first operand and
+a Y register number as the second operand. `move_cx` takes a tagged
+Erlang term as the first operand and an X register number as the
+second operand.
+
+An example: the move instruction
+--------------------------------
+
+Using the `move` instruction as an example, we will give a quick
+tour to show the main features of **beam\_makeops**.
+
+In the `compiler` application, in the file `genop.tab`, there is the
+following line:
+
+ 64: move/2
+
+This is a definition of an external generic BEAM instruction. Most
+importantly it specifices that the opcode is 64. It also defines that
+it has two operands. The BEAM assembler will use the opcode when
+creating `.beam` files. The compiler does not really need the arity,
+but it will use it as an internal sanity check when assembling the
+BEAM code.
+
+Let's have a look at `ops.tab` in `erts/emulator/beam`, where the
+specific `move` instructions are defined. Here are a few of them:
+
+ move x x
+ move x y
+ move c x
+
+Each specific instructions is defined by following the name of the
+instruction with the types for each operand. An operand type is a
+single letter. For example, `x` means an X register, `y`
+means a Y register, and `c` is a "constant" (a tagged term such as
+an integer, an atom, or a literal).
+
+Now let's look at the implementation of the `move` instruction. There
+are multiple files containing implementations of instructions in the
+`erts/emulator/beam` directory. The `move` instruction is defined in
+`instrs.tab`. It looks like this:
+
+ move(Src, Dst) {
+ $Dst = $Src;
+ }
+
+The implementation for an instruction largely follows the C syntax,
+except that the variables in the function head don't have any types.
+The `$` before an identifier denotes a macro expansion. Thus,
+`$Src` will expand to the code to pick up the source operand for
+the instruction and `$Dst` to the code for the destination register.
+
+We will look at the code for each specific instruction in turn. To
+make the code easier to understand, let's first look at the memory
+layout for the instruction `{move,{atom,id},{x,5}}`:
+
+ +--------------------+--------------------+
+ I -> | 40 | &&lb_move_cx |
+ +--------------------+--------------------+
+ | Tagged atom 'id' |
+ +--------------------+--------------------+
+
+This example and all other examples in the document assumes a 64-bit
+archictecture, and furthermore that pointers to C code fit in 32 bits.
+
+`I` in the BEAM virtual machine is the instruction pointer. When BEAM
+executes an instruction, `I` points to the first word of the
+instruction.
+
+`&&lb_move_cx` is the address to C code that implements `move_cx`. It
+is stored in the lower 32 bits of the word. In the upper 32 bits is
+the byte offset to the X register; the register number 5 has been
+multiplied by the word size size 8.
+
+In the next word the tagged atom `id` is stored.
+
+With that background, we can look at the generated code for `move_cx`
+in `beam_hot.h`:
+
+ OpCase(move_cx):
+ {
+ BeamInstr next_pf = BeamCodeAddr(I[2]);
+ xb(BeamExtraData(I[0])) = I[1];
+ I += 2;
+ ASSERT(VALID_INSTR(next_pf));
+ GotoPF(next_pf);
+ }
+
+We will go through each line in turn.
+
+* `OpCase(move_cx):` defines a label for the instruction. The
+`OpCase()` macro is defined in `beam_emu.c`. It will expand this line
+to `lb_move_cx:`.
+
+* `BeamInstr next_pf = BeamCodeAddr(I[2]);` fetches the pointer to
+code for the next instruction to be executed. The `BeamCodeAddr()`
+macro extracts the pointer from the lower 32 bits of the instruction
+word.
+
+* `xb(BeamExtraData(I[0])) = I[1];` is the expansion of `$Dst = $Src`.
+`BeamExtraData()` is a macro that will extract the upper 32 bits from
+the instruction word. In this example, it will return 40 which is the
+byte offset for X register 5. The `xb()` macro will cast a byte
+pointer to an `Eterm` pointer and dereference it. The `I[1]` on
+the right side of the `=` fetches an Erlang term (the atom `id` in
+this case).
+
+* `I += 2` advances the instruction pointer to the next
+instruction.
+
+* In a debug-compiled emulator, `ASSERT(VALID_INSTR(next_pf));` makes
+sure that `next_pf` is a valid instruction (that is, that it points
+within the `process_main()` function in `beam_emu.c`).
+
+* `GotoPF(next_pf);` transfers control to the next instruction.
+
+Now let's look at the implementation of `move_xx`:
+
+ OpCase(move_xx):
+ {
+ Eterm tmp_packed1 = BeamExtraData(I[0]);
+ BeamInstr next_pf = BeamCodeAddr(I[1]);
+ xb((tmp_packed1>>BEAM_TIGHT_SHIFT)) = xb(tmp_packed1&BEAM_TIGHT_MASK);
+ I += 1;
+ ASSERT(VALID_INSTR(next_pf));
+ GotoPF(next_pf);
+ }
+
+We will go through the lines that are new or have changed compared to
+`move_cx`.
+
+* `Eterm tmp_packed1 = BeamExtraData(I[0]);` picks up both X register
+numbers packed into the upper 32 bits of the instruction word.
+
+* `BeamInstr next_pf = BeamCodeAddr(I[1]);` pre-fetches the address of
+the next instruction. Note that because both X registers operands fits
+into the instruction word, the next instruction is in the very next
+word.
+
+* `xb((tmp_packed1>>BEAM_TIGHT_SHIFT)) = xb(tmp_packed1&BEAM_TIGHT_MASK);`
+copies the source to the destination. (For a 64-bit architecture,
+`BEAM_TIGHT_SHIFT` is 16 and `BEAM_TIGHT_MASK` is `0xFFFF`.)
+
+* `I += 1;` advances the instruction pointer to the next instruction.
+
+`move_xy` is almost identical to `move_xx`. The only difference is
+the use of the `yb()` macro instead of `xb()` to reference the
+destination register:
+
+ OpCase(move_xy):
+ {
+ Eterm tmp_packed1 = BeamExtraData(I[0]);
+ BeamInstr next_pf = BeamCodeAddr(I[1]);
+ yb((tmp_packed1>>BEAM_TIGHT_SHIFT)) = xb(tmp_packed1&BEAM_TIGHT_MASK);
+ I += 1;
+ ASSERT(VALID_INSTR(next_pf));
+ GotoPF(next_pf);
+ }
+
+### Transformation rules ###
+
+Next let's look at how we can do some optimizations using transformation
+rules. For simple instructions such as `move/2`, the instruction dispatch
+overhead can be substantial. A simple optimization is to combine common
+instructions sequences to a single instruction. One such common sequence
+is multiple `move` instructions moving X registers to Y registers.
+
+Using the following rule we can combine two `move` instructions
+to a `move2` instruction:
+
+ move X1=x Y1=y | move X2=x Y2=y => move2 X1 Y1 X2 Y2
+
+The left side of the arrow (`=>`) is a pattern. If the pattern
+matches, the matching instructions will be replaced by the
+instructions on the right side. Variables in a pattern must start
+with an uppercase letter just as in Erlang. A pattern variable may be
+followed `=` and one or more type letters to constrain the match to
+one of those types. The variables that are bound on the left side can
+be used on the right side.
+
+We will also need to define a specific instruction and an implementation:
+
+ # In ops.tab
+ move2 x y x y
+
+ // In instrs.tab
+ move2(S1, D1, S2, D2) {
+ Eterm V1, V2;
+ V1 = $S1;
+ V2 = $S2;
+ $D1 = V1;
+ $D2 = V2;
+ }
+
+When the loader has found a match and replaced the matched instructions,
+it will match the new instructions against the transformation rules.
+Because of that, we can define the rule for a `move3/6` instruction
+as follows:
+
+ move2 X1=x Y1=y X2=x Y2=y | move X3=x Y3=y => \
+ move3 X1 Y1 X2 Y2 X3 Y3
+
+(A `\` before a newline can be used to break a long line for readability.)
+
+It would also be possible to define it like this:
+
+ move X1=x Y1=y | move X2=x Y2=y | move X3=x Y3=y => \
+ move3 X1 Y1 X2 Y2 X3 Y3
+
+but in that case it must be defined before the rule for `move2/4`
+because the first matching rule will be applied.
+
+One must be careful not to create infinite loops. For example, if we
+for some reason would want to reverse the operand order for the `move`
+instruction, we must not do like this:
+
+ move Src Dst => move Dst Src
+
+The loader would swap the operands forever. To avoid the loop, we must
+rename the instruction. For example:
+
+ move Src Dst => assign Dst Src
+
+This concludes the quick tour of the features of **beam\_makeops**.
+
+Short overview of instruction loading
+-------------------------------------
+
+To give some background to the rest of this document, here follows a
+quick overview of how instructions are loaded.
+
+* The loader reads and decodes one instruction at a time from the BEAM
+code and creates a generic instruction. Many transformation rules
+must look at multiple instructions, so the loader will
+keep multiple generic instructions in a linked list.
+
+* The loader tries to apply transformation rules against the
+generic instructions in the linked list. If a rule matches, the
+matched instructions will be removed and replaced with new
+generic instructions constructed from the right side of the
+transformation.
+
+* If a transformation rule matched, the loader applies the
+transformation rules again.
+
+* If no transformation rule match, the loader will begin rewriting
+the first of generic instructions to a specific instruction.
+
+* First the loader will search for a specific operation where the
+types for all operands match the type for the generic instruction.
+The first matching instruction will be selected. **beam\_makeops**
+has ordered the specific instructions so that instructions with more
+specific operands comes before instructions with less specific
+operands. For example, `move_nx` is more specific than `move_cx`. If
+the first operand is `[]` (NIL), `move_nx` will be selected.
+
+* Given the opcode for the selected specific instruction, the loader
+looks up the pointer to the C code for the instruction and stores
+in the code area for the module being loaded.
+
+* The loader translates each operand to a machine word and stores it
+in the code area. The operand type for the selected specific
+instruction guides the translation. For example, if the type is `e`,
+the value of the operand is an index into an arry of external
+functions and will be translated to a pointer to the export entry for
+the function to call. If the type is `x`, the number of the X
+register will be multiplied by the word size to produce a byte offset.
+
+* The loader runs the packing engine to pack multiple operands into a
+single word. The packing engine is controlled by a small program,
+which is a string where each character is an instruction. For
+example, the code to pack the operands for `move_xy` is `"22#"` (on a
+64-bit machine). That program will pack the byte offsets for both
+registers into the same word as the pointer to C code.
+
+Running beam_makeops
+--------------------
+
+**beam\_makeops** is found in `$ERL_TOP/erts/emulator/utils`. Options
+start with a hyphen (`-`). The options are followed by the name of
+the input files. By convention, all input files have the extension
+`.tab`, but is not enforced by **beam\_makeops**.
+
+### The -outdir option ###
+
+The option `-outdir Directory` specifies the output directory for
+the generated files. Default is the current working directory.
+
+### Running beam_makeops for the compiler ###
+
+Give the option `-compiler` to produce output files for the compiler.
+The following files will be written to the output directory:
+
+* `beam_opcodes.erl` - Used primarily by `beam_asm` and `beam_diasm`.
+
+* `beam_opcode.hrl` - Used by `beam_asm`. It contains tag definitions
+used for encoding instruction operands.
+
+The input file should only contain the definition of BEAM_FORMAT_NUMBER
+and external generic instructions. (Everything else would be ignored.)
+
+### Running beam_makeops for the emulator ###
+
+Give the option `-emulator` to produce output files for the emulator.
+The following output files will be generated in the output directory.
+
+* `beam_hot.h`, `beam_warm.h`, `beam_cold.`h - Implementation of
+instructions. Included inside the `process_main()` function in
+`beam_emu.c`.
+
+* `beam_opcodes.c` - Defines static data used by the loader
+(`beam_load.c`). Data about generic instructions, specific
+instructions (including how to pack their operands), and
+transformation rules are all part of this file.
+
+* `beam_opcodes.h` - Miscellanous preprocessor definitions, mainly
+used by `beam_load.c` but also by `beam_{hot,warm,cold}.h`.
+
+* `beam_pred_funcs.h` - Included by `beam_load.c`. Contains defines
+needed to call guard constraints in transformation rules.
+
+* `beam_tr_funcs.h` - Included by `beam_load.c`. Contains defines
+needed to call a C function to the right of a transformation rule.
+
+The following options can be given:
+
+* `wordsize 32|64` - Defines the word size. Default is 32.
+
+* `code-model Model` - The code model as given to `-mcmodel` option
+for GCC. Default is `unknown`. If the code model is `small` (and
+the word size is 64 bits), **beam\_makeops** will pack operands
+into the upper 32 bits of the instruction word.
+
+* `DSymbol=0|1` - Defines the value for a symbol. The symbol can be
+used in `%if` and `%unless` directives.
+
+Syntax of .tab files
+--------------------
+
+### Comments ###
+
+Any line starting with `#` is a comment and is ignored.
+
+A line with `//` is also a comment. It is recommended to only
+use this style of comments in files that define implementations of
+instructions.
+
+A long line can be broken into shorter lines by a placing a`\` before
+the newline.
+
+### Variable definitions ###
+
+A variable definition binds a variable to a Perl variable. It is only
+meaningful to add a new definition if **beam\_makeops** is updated
+at the same time to use the variable. A variable definition looks this:
+
+*name*=*value*[;]
+
+where *name* is the name of a Perl variable in **beam\_makeops**,
+and *value* is the value to be given to the variable. The line
+can optionally end with a `;` (to avoid messing up the
+C indentation mode in Emacs).
+
+Here follows a description of the variables that are defined.
+
+#### BEAM\_FORMAT\_NUMBER ####
+
+`genop.tab` has the following definition:
+
+ BEAM_FORMAT_NUMBER=0
+
+It defines the version of the instruction set (which will be
+included in the code header in the BEAM code). Theoretically,
+the version could be bumped, and all instructions changed.
+In practice, we would have two support two instruction sets
+in the runtime system for at least two releases, so it will
+probably never happen in practice.
+
+#### GC\_REGEXP ####
+
+In `macros.tab`, there is a definition of `GC_REGEXP`.
+It will be described in [a later section](#the-gc_regexp-definition).
+
+### Directives ###
+
+There are directives to classify specific instructions depending
+on how frequently used they are:
+
+* `%hot` - Implementation will be placed in `beam_hot.h`. Frequently
+executed instructions.
+
+* `%warm` - Implementation will be placed in `beam_warm.h`. Binary
+syntax instructions.
+
+* `%cold` - Implementation will be placed in `beam_cold.h`. Trace
+instructions and infrequently used instructions.
+
+Default is `%hot`. The directives will be applied to declarations
+of the specific instruction that follow. Here is an example:
+
+ %cold
+ is_number f? xy
+ %hot
+
+#### Conditional compilation directives ####
+
+The `%if` directive includes a range of lines if a condition is
+true. For example:
+
+ %if ARCH_64
+ i_bs_get_integer_32 x f? x
+ %endif
+
+The specific instruction `i_bs_get_integer_32` will only be defined
+on a 64-bit machine.
+
+The condition can be inverted by using `%unless` instead of `%if`:
+
+ %unless NO_FPE_SIGNALS
+ fcheckerror p => i_fcheckerror
+ i_fcheckerror
+ fclearerror
+ %endif
+
+It is also possible to add an `%else` clause:
+
+ %if ARCH_64
+ BS_SAFE_MUL(A, B, Fail, Dst) {
+ Uint64 res = ($A) * ($B);
+ if (res / $B != $A) {
+ $Fail;
+ }
+ $Dst = res;
+ }
+ %else
+ BS_SAFE_MUL(A, B, Fail, Dst) {
+ Uint64 res = (Uint64)($A) * (Uint64)($B);
+ if ((res >> (8*sizeof(Uint))) != 0) {
+ $Fail;
+ }
+ $Dst = res;
+ }
+ %endif
+
+#### Symbols that are defined in directives ####
+
+The following symbols are always defined.
+
+* `ARCH_64` - is 1 for a 64-bit machine, and 0 otherwise.
+* `ARCH_32` - is 1 for 32-bit machine, and 1 otherwise.
+
+The `Makefile` for building the emulator currently defines the
+following symbols by using the `-D` option on the command line for
+**beam\_makeops**.
+
+* `NO_FPE_SIGNALS` - 1 if FPE signals are not enable in runtime system,
+0 otherwise.
+* `USE_VM_PROBES` - 1 if the runtime system is compiled to use VM probes (support for dtrace or systemtap), 0 otherwise.
+
+### Defining external generic instructions ###
+
+External generic BEAM instructions are known to both the compiler and
+the runtime system. They remain stable between releases. A new major
+release may add more external generic instructions, but must not change
+the semantics for a previously defined instruction.
+
+The syntax for an external generic instruction is as follows:
+
+*opcode*: [-]*name*/*arity*
+
+*opcode* is an integer greater than or equal to 1.
+
+*name* is an identifier starting with a lowercase letter. *arity* is
+an integer denoting the number of operands.
+
+*name* can optionally be preceded by `-` to indicate that it has been
+obsoleted. The compiler is not allowed to generate BEAM files that
+use obsolete instructions and the loader will refuse to load BEAM
+files that use obsolete instructions.
+
+It only makes sense to define external generic instructions in the
+file `genop.tab` in `lib/compiler/src`, because the compiler must
+know about them in order to use them.
+
+New instructions must be added at the end of the file, with higher
+numbers than the previous instructions.
+
+### Defining internal generic instructions ###
+
+Internal generic instructions are known only to the runtime
+system and can be changed at any time without compatibility issues.
+
+There are two ways to define internal generic instructions:
+
+* Implicitly when a specific instruction is defined. This is by far
+the most common way. Whenever a specific instruction is created,
+**beam\_makeops** automatically creates an internal generic instruction
+if it does not previously exist.
+
+* Explicitly. This is necessary only when a generic instruction does
+not have any corresponding specific instruction.
+
+The syntax for an internal generic instruction is as follows:
+
+*name*/*arity*
+
+*name* is an identifier starting with a lowercase letter. *arity* is
+an integer denoting the number of operands.
+
+### About generic instructions in general ###
+
+Each generic instruction has an opcode. The opcode is an integer,
+greater than or equal to 1. For an external generic instruction, it
+must be explicitly given `genop.tab`, while internal generic
+instructions are automatically numbered by **beam\_makeops**.
+
+The identity of a generic instruction is its name combined with its
+arity. That means that it is allowed to define two distinct generic
+instructions having the same name but with different arities. For
+example:
+
+ move_window/5
+ move_window/6
+
+Each operand of a generic instruction is tagged with its type. A generic
+instruction can have one of the following types:
+
+* `x` - X register.
+
+* `y` - Y register.
+
+* `l` - Floating point register number.
+
+* `i` - Tagged literal integer.
+
+* `a` - Tagged literal atom.
+
+* `n` - NIL (`[]`, the empty list).
+
+* `q` - Literal that don't fit in a word, that is an object stored on
+the heap such as a list or tuple. Any heap object type is supported,
+even types that don't have real literals such as external references.
+
+* `f` - Non-zero failure label.
+
+* `p` - Zero failure label.
+
+* `u` - Untagged integer that fits in a machine word. It is used for many
+different purposes, such as the number of live registers in `test_heap/2`,
+as a reference to the export for `call_ext/2`, and as the flags operand for
+binary syntax instructions. When the generic instruction is translated to a
+specific instruction, the type for the operand in the specific operation will
+tell the loader how to treat the operand.
+
+* `o` - Overflow. If the value for an `u` operand does not fit in a machine
+word, the type of the operand will be changed to `o` (with no associated
+value). Currently only used internally in the loader in the guard constraint
+function `binary_too_big()`.
+
+* `v` - Arity value. Only used internally in the loader.
+
+
+### Defining specific instructions ###
+
+The specific instructions are known only to the runtime system and
+are the instructions that are actually executed. They can be changed
+at any time without causing compatibility issues.
+
+A specific instruction can have at most 6 operands.
+
+A specific instruction is defined by first giving its name followed by
+the types for each operand. For example:
+
+ move x y
+
+Internally, for example in the generated code and in the output from
+the BEAM disassembler, the instruction `move x y` will be called `move_xy`.
+
+The name for a specific instruction is an identifier starting with a
+lowercase letter. A type is an lowercase or uppercase letter.
+
+All specific instructions with a given name must have the same number
+of operands. That is, the following is **not** allowed:
+
+ move x x
+ move x y x y
+
+Here follows the type letters that more or less directly corresponds
+to the types for generic instructions.
+
+* `x` - X register. Will be loaded as a byte offset to the X register
+relative to the base of X register array. (Can be packed with other
+operands.)
+
+* `y` - Y register. Will be loaded as a byte offset to the Y register
+relative to the stack frame. (Can be packed with other operands.)
+
+* `r` - X register 0. An implicit operand that will not be stored in
+the loaded code.
+
+* `l` - Floating point register number. (Can be packed with other
+operands.)
+
+* `i` - Tagged literal integer (a SMALL that will fit in one word).
+
+* `a` - Tagged atom.
+
+* `n` - NIL or the empty list. (Will not be stored in the loaded code.)
+
+* `q` - Tagged CONS or BOXED pointer. That is, a term such as a list
+or tuple. Any heap object type is supported, even types that don't
+have real literals such as external references.
+
+* `f` - Failure label (non-zero). The target for a branch
+or call instruction.
+
+* `p` - The 0 failure label, meaning that an exception should be raised
+if the instruction fails. (Will not be stored in the loaded code.)
+
+* `c` - Any literal term; that is, immediate literals such as SMALL,
+and CONS or BOXED pointers to literals. (Can be used where the
+operand in the generic instruction has one of the types `i`, `a`, `n`,
+or `q`.)
+
+The types that follow do a type test of the operand at runtime; thus,
+they are generally more expensive in terms of runtime than the types
+described earlier. However, those operand types are needed to avoid a
+combinatorial explosion in the number of specific instructions and
+overall code size of `process_main()`.
+
+* `s` - Tagged source: X register, Y register, or a literal term. The
+tag will be tested at runtime to retrieve the value from an X
+register, a Y register, or simply use the value as a tagged Erlang
+term. (Implementation note: An X register is tagged as a pid, and a Y
+register as a port. Therefore the literal term must not contain a
+port or pid.)
+
+* `S` - Tagged source register (X or Y). The tag will be tested at
+runtime to retrieve the value from an X register or a Y register. Slighly
+cheaper than `s`.
+
+* `d` - Tagged destination register (X or Y). The tag will be tested
+at runtime to set up a pointer to the destination register. If the
+instrution performs a garbarge collection, it must use the
+`$REFRESH_GEN_DEST()` macro to refresh the pointer before storing to
+it (there are more details about that in a later section).
+
+* `j` - A failure label (combination of `f` and `p`). If the branch target 0,
+an exception will be raised if instruction fails, otherwise control will be
+transfered to the target address.
+
+The types that follows are all applied to an operand that has the `u`
+type.
+
+* `t` - An untagged integer that will fit in 12 bits (0-4096). It can be
+packed with other operands in a word. Most often used as the number
+of live registers in instructions such as `test_heap`.
+
+* `I` - An untagged integer that will fit in 32 bits. It can be
+packed with other operands in a word on a 64-bit system.
+
+* `W` - Untagged integer or pointer. Not possible to pack with other
+operands.
+
+* `e` - Pointer to an export entry. Use by call instructions that call
+other modules, such as `call_ext`.
+
+* `L` - A label. Only used by the `label/1` instruction.
+
+* `b` - Pointer to BIF. Used by instructions that BIFs, such as
+`call_bif`.
+
+* `A` - A tagged arityvalue. Used in instructions that test the arity
+of a tuple.
+
+* `P` - A byte offset into a tuple.
+
+* `Q` - A byte offset into the stack. Used for updating the frame
+pointer register. Can be packed with other operands.
+
+When the loader translates a generic instruction a specific
+instruction, it will choose the most specific instruction that will
+fit the types. Consider the following two instructions:
+
+ move c x
+ move n x
+
+The `c` operand can encode any literal value, including NIL. The
+`n` operand only works for NIL. If we have the generic instruction
+`{move,nil,{x,1}}`, the loader will translate it to `move_nx 1`
+because `move n x` is more specific. `move_nx` could be slightly
+faster or smaller (depending on the architecture), because the `[]`
+is not stored explicitly as an operand.
+
+#### Syntactic sugar for specific instructions ####
+
+It is possible to specify more than one type letter for each operand.
+Here is an example:
+
+ move cxy xy
+
+This is syntactic sugar for:
+
+ move c x
+ move c y
+ move x x
+ move x y
+ move y x
+ move y y
+
+Note the difference between `move c xy` and `move c d`. Note that `move c xy`
+is equivalent to the following two definitions:
+
+ move c x
+ move c y
+
+On the other hand, `move c d` is a single instruction. At runtime,
+the `d` operand will be tested to see whether it refers to an X
+register or a Y register, and a pointer to the register will be set
+up.
+
+#### The '?' type modifier ####
+
+The character `?` can be added to the end of an operand to indicate
+that the operand will not be used every time the instruction is executed.
+For example:
+
+ allocate_heap t I t?
+ is_eq_exact f? x xy
+
+In `allocate_heap`, the last operand is the number of live registers.
+It will only be used if there is not enough heap space and a garbage
+collection must be performed.
+
+In `is_eq_exact`, the failure address (the first operand) will only be
+used if the two register operands are not equal.
+
+Knowing that an operand is not always used can improve how packing
+is done for some instructions.
+
+For the `allocate_heap` instruction, without the `?` the packing would
+be done like this:
+
+ +--------------------+--------------------+
+ I -> | Stack needed | &&lb_allocate_heap +
+ +--------------------+--------------------+
+ | Heap needed | Live registers +
+ +--------------------+--------------------+
+
+"Stack needed" and "Heap needed" are always used, but they are in
+different words. Thus, at runtime the `allocate_heap` instruction
+must read both words from memory even though it will not always use
+"Live registers".
+
+With the `?`, the operands will be packed like this:
+
+ +--------------------+--------------------+
+ I -> | Live registers | &&lb_allocate_heap +
+ +--------------------+--------------------+
+ | Heap needed | Stack needed +
+ +--------------------+--------------------+
+
+Now "Stack needed" and "Heap needed" are in the same word.
+
+### Defining transformation rules ###
+
+Transformation rules are used to rewrite generic instructions to other
+generic instructions. The transformations rules are applied
+repeatedly until no rule match. At that point, the first instruction
+in the resulting instruction sequence will be converted to a specific
+instruction and added to the code for the module being loaded. Then
+the transformation rules for the remaining instructions are run in the
+same way.
+
+A rule is recognized by its right-pointer arrow: `=>`. To the left of
+the arrow is one or more instruction patterns, separated by `|`. To
+the right of the arrow is zero or more instructions, separated by `|`.
+If the instructions from the BEAM code matches the instruction
+patterns on the left side, they will be replaced with instructions on
+the right side (or removed if there are no instructions on the right).
+
+#### Defining instruction patterns ####
+
+We will start looking at the patterns on the left side of the arrow.
+
+A pattern for an instruction consists of its name, followed by a pattern
+for each of its operands. The operand patterns are separated by spaces.
+
+The simplest possible pattern is a variable. Just like in Erlang,
+a variable must begin with an uppercase letter. If the same variable is
+used in multiple operands, the pattern will only match if the operands
+are equal. For example:
+
+ move Same Same =>
+
+This pattern will match if the operands for `move` are the same. If
+the pattern match, the instruction will be removed. (That used to be an
+actual rule a long time ago when the compiler would occasionally produce
+instructions such as `{move,{x,2},{x,2}}`.)
+
+Variables that have been bound on the left side can be used on the
+right side. For example, this rule will rewrite all `move` instructions
+to `assign` instructions with the operands swapped:
+
+ move Src Dst => assign Dst Src
+
+If we only want to match operands of a certain type, we can
+use a type constraint. A type constraint consists of one or more
+lowercase letters, each specifying a type. For example:
+
+ is_integer Fail an => jump Fail
+
+The second operand pattern, `an`, will match if the second operand is
+either an atom or NIL (the empty list). In case of a match, the
+`is_integer/2` instruction will be replaced with a `jump/1`
+instruction.
+
+An operand pattern can bind a variable and constrain the type at the
+same time by following the variable with a `=` and the constraint.
+For example:
+
+ is_eq_exact Fail=f R=xy C=q => i_is_eq_exact_literal Fail R C
+
+Here the `is_eq_exact` instruction is replaced with a specialized instruction
+that only compares literals, but only if the first operand is a register and
+the second operand is a literal.
+
+#### Further constraining patterns ####
+
+In addition to specifying a type letter, the actual value for the type can
+be specified. For example:
+
+ move C=c x==1 => move_x1 C
+
+Here the second operand of `move` is constrained to be X register 1.
+
+When specifying an atom constraint, the atom is written as it would be
+in the C source code. That is, it needs an `am_` prefix, and it must
+be listed in `atom.names`. For example:
+
+ is_boolean Fail=f a==am_true =>
+ is_boolean Fail=f a==am_false =>
+
+There are several constraints available for testing whether a call is to a BIF
+or a function.
+
+The constraint `u$is_bif` will test whether the given operand refers to a BIF.
+For example:
+
+ call_ext u Bif=u$is_bif => call_bif Bif
+ call_ext u Func => i_call_ext Func
+
+The `call_ext` instruction can be used to call functions written in
+Erlang as well as BIFs (or more properly called SNIFs). The
+`u$is_bif` constraint will match if the operand refers to a BIF (that
+is, if it is listed in the file `bif.tab`). Note that `u$is_bif`
+should only be applied to operands that are known to contain an index
+to the import table chunk in the BEAM file (such operands have the
+type `b` or `e` in the corresponding specific instruction). If
+applied to other `u` operands, it will at best return a nonsense
+result.
+
+The `u$is_not_bif` constraint matches if the operand does not refer to
+a BIF (not listed in `bif.tab`). For example:
+
+ 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
+
+The `u$bif:Module:Name/Arity` constraint tests whether the given
+operand refers to a specific BIF. Note that `Module:Name/Arity`
+**must** be an existing BIF defined in `bif.tab`, or there will
+be a compilation error. It is useful when a call to a specific BIF
+should be replaced with an instruction as in this example:
+
+ gc_bif2 Fail Live u$bif:erlang:splus/2 S1 S2 Dst => \
+ gen_plus Fail Live S1 S2 Dst
+
+Here the call to the GC BIF `'+'/2` will be replaced with the instruction
+`gen_plus/5`. Note that the same name as used in the C source code must be
+used for the BIF, which in this case is `splus`. It is defined like this
+in `bit.tab`:
+
+ ubif erlang:'+'/2 splus_2
+
+The `u$func:Module:Name/Arity` will test whether the given operand is a
+a specific function. Here is an example:
+
+ bif1 Fail u$func:erlang:is_constant/1 Src Dst => too_old_compiler
+
+`is_constant/1` used to be a BIF a long time ago. The transformation
+replaces the call with the `too_old_compiler` instruction which will produce
+a nicer error message than the default error would be for a missing guard BIF.
+
+#### Type constraints allowed in patterns ####
+
+Here are all type letters that are allowed on the left side of a transformation
+rule.
+
+* `u` - An untagged integer that fits in a machine word.
+
+* `x` - X register.
+
+* `y` - Y register.
+
+* `l` - Floating point register number.
+
+* `i` - Tagged literal integer.
+
+* `a` - Tagged literal atom.
+
+* `n` - NIL (`[]`, the empty list).
+
+* `q` - Literals that don't fit in a word, such as list or tuples.
+
+* `f` - Non-zero failure label.
+
+* `p` - The zero failure label.
+
+* `j` - Any label. Equivalent to `fp`.
+
+* `c` - Any literal term. Equivalent to `ainq`.
+
+* `s` - X register, Y register, or any literal term. Equivalent to `xyc`.
+
+* `d` - X or Y register. Equivalent to `xy`. (In a pattern `d` will
+match both source and destination registers. As an operand in a specific
+instruction, it must only be used for a destination register.)
+
+* `o` - Overflow. An untagged integer that does not fit in a machine word.
+
+#### Guard constraints ####
+
+If the constraints described so far is not enough, additional
+constraints can be written in C in `beam_load.c` and be called as a
+guard function on the left side of the transformation. If the guard
+function returns a non-zero value, the matching of the rule will
+continue, otherwise the match will fail. For example:
+
+ ensure_map Lit=q | literal_is_map(Lit) =>
+
+The guard test `literal_is_map/1` tests whether the given literal is a map.
+If the literal is a map, the instruction is unnecessary and can be removed.
+
+It is outside the scope for this document to describe in detail how such
+guard functions are written, but for the curious here is the implementation
+of `literal_is_map()`:
+
+ static int
+ literal_is_map(LoaderState* stp, GenOpArg Lit)
+ {
+ Eterm term;
+
+ ASSERT(Lit.type == TAG_q);
+ term = stp->literals[Lit.val].term;
+ return is_map(term);
+ }
+
+#### Handling instruction with variable number of operands ####
+
+Some instructions, such as `select_val/3`, essentially has a variable
+number of operands. Such instructions have a `{list,[...]}` operand
+as their last operand in the BEAM assembly code. For example:
+
+ {select_val,{x,0},
+ {f,1},
+ {list,[{atom,b},{f,4},{atom,a},{f,5}]}}.
+
+The loader will convert a `{list,[...]}` operand to an `u` operand whose
+value is the number of elements in the list, followed by each element in
+the list. The instruction above would be translated to the following
+generic instruction:
+
+ {select_val,{x,0},{f,1},{u,4},{atom,b},{f,4},{atom,a},{f,5}}
+
+To match a variable number of arguments we need to use the special
+operand type `*` like this:
+
+ select_val Src=aiq Fail=f Size=u List=* => \
+ i_const_select_val Src Fail Size List
+
+This transformation renames a `select_val/3` instruction
+with a constant source operand to `i_const_select_val/3`.
+
+#### Constructing new instructions on the right side ####
+
+The most common operand on the right side is a variable that was bound while
+matching the left side. For example:
+
+ trim N Remaining => i_trim N
+
+An operand can also be a type letter to construct an operand of that type.
+Each type has a default value. For example, the type `x` has the default
+value 1023, which is the highest X register. That makes `x` on the right
+side a convenient shortcut for a temporary X register. For example:
+
+ is_number Fail Literal=q => move Literal x | is_number Fail x
+
+If the second operand for `is_number/2` is a literal, it will be moved to
+X register 1023. Then `is_number/2` will test whether the value stored in
+X register 1023 is a number.
+
+This kind of transformation is useful when it is rare that an operand can
+be anything else but a register. In the case of `is_number/2`, the second
+operand is always a register unless the compiler optimizations have been
+disabled.
+
+If the default value is not suitable, the type letter can be followed
+by `=` and a value. Most types take an integer value. The value for
+an atom is written the same way as in the C source code. For example,
+the atom `false` is written as `am_false`. The atom must be listed in
+`atom.names`.
+
+Here is an example showing how values can be specified:
+
+ bs_put_utf32 Fail=j Flags=u Src=s => \
+ i_bs_validate_unicode Fail Src | \
+ bs_put_integer Fail i=32 u=1 Flags Src
+
+#### Type letters on the right side ####
+
+Here follows all types that are allowed to be used in operands for
+instructions being constructed on the right side of a transformation
+rule.
+
+* `u` - Construct an untagged integer. The default value is 0.
+
+* `x` - X register. The default value is 1023. That makes `x` convenient to
+use as a temporary X register.
+
+* `y` - Y register. The default value is 0.
+
+* `l` - Foating 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`).
+
+* `n` - NIL (`[]`, the empty list).
+
+#### Function call on the right side ####
+
+Transformations that are not possible to describe with the rule
+language as described here can be written as a C function in
+`beam_load.c` and called from the right side of a transformation. The
+left side of the transformation will perform the match and bind
+operands to variables. The variables can then be passed to a
+generator function on the right side. For example:
+
+ bif2 Fail=j u$bif:erlang:element/2 Index=s Tuple=xy Dst=d => \
+ gen_element(Jump, Index, Tuple, Dst)
+
+This transformation rule matches a call to the BIF `element/2`.
+The operands will be captured and the function `gen_element()` will
+be called.
+
+`gen_element()` will produce one of two instructions depending
+on `Index`. If `Index` is an integer in the range from 1 up to
+the maximum tuple size, the instruction `i_fast_element/2` will
+be produced, otherwise the instruction `i_element/4` will be
+produced. The corresponding specific instructions are:
+
+ i_fast_element xy j? I d
+ i_element xy j? s d
+
+The `i_fast_element/2` instruction is faster because the tuple is
+already an untagged integer. It also knows that the index is at least
+1, so it does not have to test for that. The `i_element/4`
+instruction will have to fetch the index from a register, test that it
+is an integer, and untag the integer.
+
+It is outside the scope of this document to describe in detail how
+generator functions are written, but for the curious, here is the
+implementation of `gen_element()`:
+
+ static GenOp*
+ gen_element(LoaderState* stp, GenOpArg Fail,
+ GenOpArg Index, GenOpArg Tuple, GenOpArg Dst)
+ {
+ GenOp* op;
+
+ NEW_GENOP(stp, op);
+ op->arity = 4;
+ op->next = NULL;
+
+ if (Index.type == TAG_i && Index.val > 0 &&
+ Index.val <= ERTS_MAX_TUPLE_SIZE &&
+ (Tuple.type == TAG_x || Tuple.type == TAG_y)) {
+ op->op = genop_i_fast_element_4;
+ op->a[0] = Tuple;
+ op->a[1] = Fail;
+ op->a[2].type = TAG_u;
+ op->a[2].val = Index.val;
+ op->a[3] = Dst;
+ } else {
+ op->op = genop_i_element_4;
+ op->a[0] = Tuple;
+ op->a[1] = Fail;
+ op->a[2] = Index;
+ op->a[3] = Dst;
+ }
+
+ return op;
+ }
+}
+
+### Defining the implementation ###
+
+The actual implementation of instructions are also defined in `.tab`
+files processed by **beam\_makeops**. For practical reasons,
+instruction definitions are stored in several files, at the time of
+writing in the following files:
+
+ bif_instrs.tab
+ arith_instrs.tab
+ bs_instrs.tab
+ float_instrs.tab
+ instrs.tab
+ map_instrs.tab
+ msg_instrs.tab
+ select_instrs.tab
+ trace_instrs.tab
+
+There is also a file that only contains macro definitions:
+
+ macros.tab
+
+The syntax of each file is similar to C code. In fact, most of
+the contents *is* C code, interspersed with macro invocations.
+
+To allow Emacs to auto-indent the code, each file starts with the
+following line:
+
+ // -*- c -*-
+
+To avoid messing up the indentation, all comments are written
+as C++ style comments (`//`) instead of `#`. Note that a comment
+must start at the beginning of a line.
+
+The meat of an instruction definition file are macro definitions.
+We have seen this macro definition before:
+
+ move(Src, Dst) {
+ $Dst = $Src;
+ }
+
+A macro definitions must start at the beginning of the line (no spaces
+allowed), the opening curly bracket must be on the same line, and the
+finishing curly bracket must be at the beginning of a line. It is
+recommended that the macro body is properly indented.
+
+As a convention, the macro arguments in the head all start with an
+uppercase letter. In the body, the macro arguments can be expanded
+by preceding them with `$`.
+
+A macro definition whose name and arity matches a family of
+specific instructions is assumed to be the implementation of that
+instruction.
+
+A macro can also be invoked from within another macro. For example,
+`move_deallocate_return/2` avoids repeating code by invoking
+`$deallocate_return()` as a macro:
+
+ move_deallocate_return(Src, Deallocate) {
+ x(0) = $Src;
+ $deallocate_return($Deallocate);
+ }
+
+Here is the definition of `deallocate_return/1`:
+
+ 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;
+ }
+
+The expanded code for `move_deallocate_return` will look this:
+
+ OpCase(move_deallocate_return_cQ):
+ {
+ x(0) = I[1];
+ do {
+ int words_to_pop = Qb(BeamExtraData(I[0]));
+ SET_I((BeamInstr *) cp_val(*E));
+ E = ADD_BYTE_OFFSET(E, words_to_pop);
+ CHECK_TERM(x(0));
+ DispatchReturn;
+ } while (0);
+ }
+
+When expanding macros, **beam\_makeops** wraps the expansion in a
+`do`/`while` wrapper unless **beam\_makeops** can clearly see that no
+wrapper is needed. In this case, the wrapper is needed.
+
+Note that arguments for macros cannot be complex expressions, because
+the arguments are split on `,`. For example, the following would
+not work because **beam\_makeops** would split the expression into
+two arguments:
+
+ $deallocate_return(get_deallocation(y, $Deallocate));
+
+#### Code generation directives ####
+
+Within macro definitions, `//` comments are in general not treated
+specially. They will be copied to the file with the generated code
+along with the rest of code in the body.
+
+However, there is an exception. Within a macro definition, a line that
+starts with whitespace followed by `//|` is treated specially. The
+rest of the line is assumed to contain directives to control code
+generation.
+
+Currently, two code generation directives are recognized:
+
+* `-no_prefetch`
+* `-no_next`
+
+##### The -no_prefetch directive #####
+
+To see what `-no_prefetch` does, let's first look at the default code
+generation. Here is the code generated for `move_cx`:
+
+ OpCase(move_cx):
+ {
+ BeamInstr next_pf = BeamCodeAddr(I[2]);
+ xb(BeamExtraData(I[0])) = I[1];
+ I += 2;
+ ASSERT(VALID_INSTR(next_pf));
+ GotoPF(next_pf);
+ }
+
+Note that the very first thing done is to fetch the address to the
+next instruction. The reason is that it usually improves performance.
+
+Just as a demonstration, we can add a `-no_prefetch` directive to
+the `move/2` instruction:
+
+ move(Src, Dst) {
+ //| -no_prefetch
+ $Dst = $Src;
+ }
+
+We can see that the prefetch is no longer done:
+
+ OpCase(move_cx):
+ {
+ xb(BeamExtraData(I[0])) = I[1];
+ I += 2;
+ ASSERT(VALID_INSTR(*I));
+ Goto(*I);
+ }
+
+When would we want to turn off the prefetch in practice?
+
+In instructions that will not always execute the next instruction.
+For example:
+
+ is_atom(Fail, Src) {
+ if (is_not_atom($Src)) {
+ $FAIL($Fail);
+ }
+ }
+
+ // From macros.tab
+ FAIL(Fail) {
+ //| -no_prefetch
+ $SET_I_REL($Fail);
+ Goto(*I);
+ }
+
+`is_atom/2` may either execute the next instruction (if the second
+operand is an atom) or branch to the failure label.
+
+The generated code looks like this:
+
+ OpCase(is_atom_fx):
+ {
+ if (is_not_atom(xb(I[1]))) {
+ ASSERT(VALID_INSTR(*(I + (fb(BeamExtraData(I[0]))) + 0)));
+ I += fb(BeamExtraData(I[0])) + 0;;
+ Goto(*I);;
+ }
+ I += 2;
+ ASSERT(VALID_INSTR(*I));
+ Goto(*I);
+ }
+
+##### The -no_next directive #####
+
+Next we will look at when the `-no_next` directive can be used. Here
+is the `jump/1` instruction:
+
+ jump(Fail) {
+ $JUMP($Fail);
+ }
+
+ // From macros.tab
+ JUMP(Fail) {
+ //| -no_next
+ $SET_I_REL($Fail);
+ Goto(*I);
+ }
+
+The generated code looks like this:
+
+ OpCase(jump_f):
+ {
+ ASSERT(VALID_INSTR(*(I + (fb(BeamExtraData(I[0]))) + 0)));
+ I += fb(BeamExtraData(I[0])) + 0;;
+ Goto(*I);;
+ }
+
+If we remove the `-no_next` directive, the code would look like this:
+
+ OpCase(jump_f):
+ {
+ BeamInstr next_pf = BeamCodeAddr(I[1]);
+ ASSERT(VALID_INSTR(*(I + (fb(BeamExtraData(I[0]))) + 0)));
+ I += fb(BeamExtraData(I[0])) + 0;;
+ Goto(*I);;
+ I += 1;
+ ASSERT(VALID_INSTR(next_pf));
+ GotoPF(next_pf);
+ }
+
+In the end, the C compiler will probably optimize this code to the
+same native code as the first version, but the first version is certainly
+much easier to read for human readers.
+
+#### Macros in the macros.tab file ####
+
+The file `macros.tab` contains many useful macros. When implementing
+new instructions it is good practice to look through `macros.tab` to
+see if any of existing macros can be used rather than re-inventing
+the wheel.
+
+We will describe a few of the most useful macros here.
+
+##### The GC_REGEXP definition #####
+
+The following line defines a regular expression that will recognize
+a call to a function that does a garbage collection:
+
+ GC_REGEXP=erts_garbage_collect|erts_gc|GcBifFunction;
+
+The purpose is that **beam\_makeops** can verify that an instruction
+that does a garbage collection and has an `d` operand uses the
+`$REFRESH_GEN_DEST()` macro.
+
+If you need to define a new function that does garbage collection,
+you should give it the prefix `erts_gc_`. If that is not possible
+you should update the regular expression so that it will match your
+new function.
+
+##### FAIL(Fail) #####
+
+Branch to `$Fail`. Will suppress prefetch (`-no_prefetch`). Typical use:
+
+ is_nonempty_list(Fail, Src) {
+ if (is_not_list($Src)) {
+ $FAIL($Fail);
+ }
+ }
+
+##### JUMP(Fail) #####
+
+Branch to `$Fail`. Suppresses generation of dispatch of the next
+instruction (`-no_next`). Typical use:
+
+ jump(Fail) {
+ $JUMP($Fail);
+ }
+
+##### GC_TEST(NeedStack, NeedHeap, Live) #####
+
+`$GC_TEST(NeedStack, NeedHeap, Live)` tests that given amount of
+stack space and heap space is available. If not it will do a
+garbage collection. Typical use:
+
+ test_heap(Nh, Live) {
+ $GC_TEST(0, $Nh, $Live);
+ }
+
+##### AH(NeedStack, NeedHeap, Live) #####
+
+`AH(NeedStack, NeedHeap, Live)` allocates a stack frame and
+optionally additional heap space.
+
+#### Pre-defined macros and variables ####
+
+**beam\_makeops** defines several built-in macros and pre-bound variables.
+
+##### The NEXT_INSTRUCTION pre-bound variable #####
+
+The NEXT_INSTRUCTION is a pre-bound variable that is available in
+all instructions. It expands to the address of the next instruction.
+
+Here is an example:
+
+ i_call(CallDest) {
+ SET_CP(c_p, $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
+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();;
+ }
+
+We can see that that `$NEXT_INSTRUCTION` has been expanded to `I+1`.
+That makes sense since the size of the `i_call_f/1` instruction is
+one word.
+
+##### The IP_ADJUSTMENT pre-bound variable #####
+
+`$IP_ADJUSTMENT` is usually 0. In a few combined instructions
+(described below) it can be non-zero. It is used like this
+in `macros.tab`:
+
+ SET_I_REL(Offset) {
+ ASSERT(VALID_INSTR(*(I + ($Offset) + $IP_ADJUSTMENT)));
+ I += $Offset + $IP_ADJUSTMENT;
+ }
+
+Avoid using `IP_ADJUSTMENT` directly. Use `SET_I_REL()` or
+one of the macros that invoke such as `FAIL()` or `JUMP()`
+defined in `macros.tab`.
+
+#### Pre-defined macro functions ####
+
+##### The IF() macro #####
+
+`$IF(Expr, IfTrue, IfFalse)` evaluates `Expr`, which must be a valid
+Perl expression (which for simple numeric expressions have the same
+syntax as C). If `Expr` evaluates to 0, the entire `IF()` expression will be
+replaced with `IfFalse`, otherwise it will be replaced with `IfTrue`.
+
+See the description of `OPERAND_POSITION()` for an example.
+
+##### The OPERAND\_POSITION() macro #####
+
+`$OPERAND_POSITION(Expr)` returns the position for `Expr`, if
+`Expr` is an operand that is not packed. The first operand is
+at position 1.
+
+Returns 0 otherwise.
+
+This macro could be used like this in order to share code:
+
+ FAIL(Fail) {
+ //| -no_prefetch
+ $IF($OPERAND_POSITION($Fail) == 1 && $IP_ADJUSTMENT == 0,
+ goto common_jump,
+ $DO_JUMP($Fail));
+ }
+
+ DO_JUMP(Fail) {
+ $SET_I_REL($Fail);
+ Goto(*I));
+ }
+
+ // In beam_emu.c:
+ common_jump:
+ I += I[1];
+ Goto(*I));
+
+
+#### The $REFRESH\_GEN\_DEST() macro ####
+
+When a specific instruction has a `d` operand, early during execution
+of the instruction, a pointer will be initialized to point to the X or
+Y register in question.
+
+If there is a garbage collection before the result is stored,
+the stack will move and if the `d` operand refered to a Y
+register, the pointer will no longer be valid. (Y registers are
+stored on the stack.)
+
+In those circumstances, `$REFRESH_GEN_DEST()` must be invoked
+to set up the pointer again. **beam\_makeops** will notice
+if there is a call to a function that does a garbage collection and
+`$REFRESH_GEN_DEST()` is not called.
+
+Here is a complete example. The `new_map` instruction is defined
+like this:
+
+ new_map d t I
+
+It is implemented like this:
+
+ new_map(Dst, Live, N) {
+ Eterm res;
+
+ HEAVY_SWAPOUT;
+ res = erts_gc_new_map(c_p, reg, $Live, $N, $NEXT_INSTRUCTION);
+ HEAVY_SWAPIN;
+ $REFRESH_GEN_DEST();
+ $Dst = res;
+ $NEXT($NEXT_INSTRUCTION+$N);
+ }
+
+If we have forgotten the `$REFRESH_GEN_DEST()` there would be a message
+similar to this:
+
+ pointer to destination register is invalid after GC -- use $REFRESH_GEN_DEST()
+ ... from the body of new_map at beam/map_instrs.tab(30)
+
+#### Combined instructions ####
+
+**Problem**: For frequently executed instructions we want to use
+"fast" operands types such as `x` and `y`, as opposed to `s` or `S`.
+To avoid an explosion in code size, we want to share most of the
+implementation between the instructions. Here are the specific
+instructions for `i_increment/5`:
+
+ i_increment r W t d
+ i_increment x W t d
+ i_increment y W t d
+
+The `i_increment` instruction is implemented like this:
+
+ i_increment(Source, IncrementVal, Live, Dst) {
+ Eterm increment_reg_source = $Source;
+ Eterm increment_val = $IncrementVal;
+ Uint live;
+ Eterm result;
+
+ if (ERTS_LIKELY(is_small(increment_reg_val))) {
+ Sint i = signed_val(increment_reg_val) + increment_val;
+ if (ERTS_LIKELY(IS_SSMALL(i))) {
+ $Dst = make_small(i);
+ $NEXT0();
+ }
+ }
+ live = $Live;
+ HEAVY_SWAPOUT;
+ reg[live] = increment_reg_val;
+ reg[live+1] = make_small(increment_val);
+ result = erts_gc_mixed_plus(c_p, reg, live);
+ HEAVY_SWAPIN;
+ ERTS_HOLE_CHECK(c_p);
+ if (ERTS_LIKELY(is_value(result))) {
+ $REFRESH_GEN_DEST();
+ $Dst = result;
+ $NEXT0();
+ }
+ ASSERT(c_p->freason != BADMATCH || is_value(c_p->fvalue));
+ goto find_func_info;
+ }
+
+There will be three almost identical copies of the code. Given the
+size of the code, that could be too high cost to pay.
+
+To avoid the three copies of the code, we could use only one specific
+instruction:
+
+ i_increment S W t d
+
+(The same implementation as above will work.)
+
+That reduces the code size, but is slower because `S` means that
+there will be extra code to test whether the operand refers to an X
+register or a Y register.
+
+**Solution**: We can use "combined instructions". Combined
+instructions are combined from instruction fragments. The
+bulk of the code can be shared.
+
+Here we will show how `i_increment` can be implemented as a combined
+instruction. We will show each individual fragment first, and then
+show how to connect them together. First we will need a variable that
+we can store the value fetched from the register in:
+
+ increment.head() {
+ Eterm increment_reg_val;
+ }
+
+The name `increment` is the name of the group that the fragment
+belongs to. Note that it does not need to have the same
+name as the instruction. The group name is followed by `.` and
+the name of the fragment. The name `head` is pre-defined.
+The code in it will be placed at the beginning of a block, so
+that all fragments in the group can access it.
+
+Next we define the fragment that will pick up the value from the
+register from the first operand:
+
+ increment.fetch(Src) {
+ increment_reg_val = $Src;
+ }
+
+We call this fragment `fetch`. This fragment will be duplicated three
+times, one for each value of the first operand (`r`, `x`, and `y`).
+
+Next we define the main part of the code that do the actual incrementing.
+
+ increment.execute(IncrementVal, Live, Dst) {
+ Eterm increment_val = $IncrementVal;
+ Uint live;
+ Eterm result;
+
+ if (ERTS_LIKELY(is_small(increment_reg_val))) {
+ Sint i = signed_val(increment_reg_val) + increment_val;
+ if (ERTS_LIKELY(IS_SSMALL(i))) {
+ $Dst = make_small(i);
+ $NEXT0();
+ }
+ }
+ live = $Live;
+ HEAVY_SWAPOUT;
+ reg[live] = increment_reg_val;
+ reg[live+1] = make_small(increment_val);
+ result = erts_gc_mixed_plus(c_p, reg, live);
+ HEAVY_SWAPIN;
+ ERTS_HOLE_CHECK(c_p);
+ if (ERTS_LIKELY(is_value(result))) {
+ $REFRESH_GEN_DEST();
+ $Dst = result;
+ $NEXT0();
+ }
+ ASSERT(c_p->freason != BADMATCH || is_value(c_p->fvalue));
+ goto find_func_info;
+ }
+
+We call this fragment `execute`. It will handle the three remaining
+operands (`W t d`). There will only be one copy of this fragment.
+
+Now that we have defined the fragments, we need to inform
+**beam\_makeops** how they should be connected:
+
+ i_increment := increment.fetch.execute;
+
+To the left of the `:=` is the name of the specific instruction that
+should be implemented by the fragments, in this case `i_increment`.
+To the right of `:=` is the name of the group with the fragments,
+followed by a `.`. Then the name of the fragments in the group are
+listed in the order they should be executed. Note that the `head`
+fragment is not listed.
+
+The line ends in `;` (to avoid messing up the indentation in Emacs).
+
+(Note that in practice the `:=` line is usually placed before the
+fragments.)
+
+The generated code looks like this:
+
+ {
+ Eterm increment_reg_val;
+ OpCase(i_increment_rWtd):
+ {
+ increment_reg_val = r(0);
+ }
+ goto increment__execute;
+
+ OpCase(i_increment_xWtd):
+ {
+ increment_reg_val = xb(BeamExtraData(I[0]));
+ }
+ goto increment__execute;
+
+ OpCase(i_increment_yWtd):
+ {
+ increment_reg_val = yb(BeamExtraData(I[0]));
+ }
+ goto increment__execute;
+
+ increment__execute:
+ {
+ // Here follows the code from increment.execute()
+ .
+ .
+ .
+ }
+
+##### Some notes about combined instructions #####
+
+The operands that are different must be at
+the beginning of the instruction. All operands in the last
+fragment must have the same operands in all variants of
+the specific instruction.
+
+As an example, the following specific instructions cannot be
+implemented as a combined instruction:
+
+ i_times j? t x x d
+ i_times j? t x y d
+ i_times j? t s s d
+
+We would have to change the order of the operands so that the
+two operands that are different are placed first:
+
+ i_times x x j? t d
+ i_times x y j? t d
+ i_times s s j? t d
+
+We can then define:
+
+ i_times := times.fetch.execute;
+
+ times.head {
+ Eterm op1, op2;
+ }
+
+ times.fetch(Src1, Src2) {
+ op1 = $Src1;
+ op2 = $Src2;
+ }
+
+ times.execute(Fail, Live, Dst) {
+ // Multiply op1 and op2.
+ .
+ .
+ .
+ }
+
+Several instructions can share a group. As an example, the following
+instructions have different names, but in the end they all create a
+binary. The last two operands are common for all of them:
+
+ i_bs_init_fail xy j? t? x
+ i_bs_init_fail_heap s I j? t? x
+ i_bs_init W t? x
+ i_bs_init_heap W I t? x
+
+The instructions are defined like this (formatted with extra
+spaces for clarity):
+
+ i_bs_init_fail_heap := bs_init . fail_heap . verify . execute;
+ i_bs_init_fail := bs_init . fail . verify . execute;
+ i_bs_init := bs_init . . plain . execute;
+ i_bs_init_heap := bs_init . heap . execute;
+
+Note that the first two instruction have three fragments, while the
+other two only have two fragments. Here are the fragments:
+
+ bs_init_bits.head() {
+ Eterm num_bits_term;
+ Uint num_bits;
+ Uint alloc;
+ }
+
+ bs_init_bits.plain(NumBits) {
+ num_bits = $NumBits;
+ alloc = 0;
+ }
+
+ bs_init_bits.heap(NumBits, Alloc) {
+ num_bits = $NumBits;
+ alloc = $Alloc;
+ }
+
+ bs_init_bits.fail(NumBitsTerm) {
+ num_bits_term = $NumBitsTerm;
+ alloc = 0;
+ }
+
+ bs_init_bits.fail_heap(NumBitsTerm, Alloc) {
+ num_bits_term = $NumBitsTerm;
+ alloc = $Alloc;
+ }
+
+ bs_init_bits.verify(Fail) {
+ // Verify the num_bits_term, fail using $FAIL
+ // if there is a problem.
+ .
+ .
+ .
+ }
+
+ bs_init_bits.execute(Live, Dst) {
+ // Long complicated code to a create a binary.
+ .
+ .
+ .
+ }
+
+The full definitions of those instructions can be found in `bs_instrs.tab`.
+The generated code can be found in `beam_warm.h`.
diff --git a/erts/emulator/internal_doc/figures/.gitignore b/erts/emulator/internal_doc/figures/.gitignore
new file mode 100644
index 0000000000..c2813ac866
--- /dev/null
+++ b/erts/emulator/internal_doc/figures/.gitignore
@@ -0,0 +1 @@
+*.eps \ No newline at end of file
diff --git a/erts/emulator/internal_doc/figures/Makefile b/erts/emulator/internal_doc/figures/Makefile
new file mode 100644
index 0000000000..111cb393fb
--- /dev/null
+++ b/erts/emulator/internal_doc/figures/Makefile
@@ -0,0 +1,20 @@
+# In order to update the figures you have to have both dia
+# and imagemagick installed.
+
+DIAGRAMS=$(wildcard *.dia)
+EPS_DIAGRAMS=$(patsubst %.dia,%.eps,$(DIAGRAMS))
+PNG_DIAGRAMS=$(patsubst %.dia,%.png,$(DIAGRAMS))
+
+diagrams: $(EPS_DIAGRAMS)
+
+png: $(PNG_DIAGRAMS)
+
+update_png: png
+ git add $(PNG_DIAGRAMS)
+ git commit -m "Update internal docs figures"
+
+%.eps: %.dia
+ dia --export=$@ $<
+
+%.png: %.eps
+ convert $< -resize 65% $@
diff --git a/erts/emulator/internal_doc/figures/gc-heap-scan1.dia b/erts/emulator/internal_doc/figures/gc-heap-scan1.dia
new file mode 100644
index 0000000000..b8a32dc092
--- /dev/null
+++ b/erts/emulator/internal_doc/figures/gc-heap-scan1.dia
Binary files differ
diff --git a/erts/emulator/internal_doc/figures/gc-heap-scan1.png b/erts/emulator/internal_doc/figures/gc-heap-scan1.png
new file mode 100644
index 0000000000..9724fc8698
--- /dev/null
+++ b/erts/emulator/internal_doc/figures/gc-heap-scan1.png
Binary files differ
diff --git a/erts/emulator/internal_doc/figures/gc-heap-stop.dia b/erts/emulator/internal_doc/figures/gc-heap-stop.dia
new file mode 100644
index 0000000000..af219958c6
--- /dev/null
+++ b/erts/emulator/internal_doc/figures/gc-heap-stop.dia
Binary files differ
diff --git a/erts/emulator/internal_doc/figures/gc-heap-stop.png b/erts/emulator/internal_doc/figures/gc-heap-stop.png
new file mode 100644
index 0000000000..ec79790d36
--- /dev/null
+++ b/erts/emulator/internal_doc/figures/gc-heap-stop.png
Binary files differ
diff --git a/erts/emulator/internal_doc/figures/gc-rootset-scan.dia b/erts/emulator/internal_doc/figures/gc-rootset-scan.dia
new file mode 100644
index 0000000000..d6147740e5
--- /dev/null
+++ b/erts/emulator/internal_doc/figures/gc-rootset-scan.dia
Binary files differ
diff --git a/erts/emulator/internal_doc/figures/gc-rootset-scan.png b/erts/emulator/internal_doc/figures/gc-rootset-scan.png
new file mode 100644
index 0000000000..06509f83c3
--- /dev/null
+++ b/erts/emulator/internal_doc/figures/gc-rootset-scan.png
Binary files differ
diff --git a/erts/emulator/internal_doc/figures/gc-start.dia b/erts/emulator/internal_doc/figures/gc-start.dia
new file mode 100644
index 0000000000..41832d29b0
--- /dev/null
+++ b/erts/emulator/internal_doc/figures/gc-start.dia
Binary files differ
diff --git a/erts/emulator/internal_doc/figures/gc-start.png b/erts/emulator/internal_doc/figures/gc-start.png
new file mode 100644
index 0000000000..6c29e9adb0
--- /dev/null
+++ b/erts/emulator/internal_doc/figures/gc-start.png
Binary files differ
diff --git a/erts/emulator/internal_doc/figures/gc-watermark-2.dia b/erts/emulator/internal_doc/figures/gc-watermark-2.dia
new file mode 100644
index 0000000000..2ea3be3fb2
--- /dev/null
+++ b/erts/emulator/internal_doc/figures/gc-watermark-2.dia
Binary files differ
diff --git a/erts/emulator/internal_doc/figures/gc-watermark-2.png b/erts/emulator/internal_doc/figures/gc-watermark-2.png
new file mode 100644
index 0000000000..bca110282d
--- /dev/null
+++ b/erts/emulator/internal_doc/figures/gc-watermark-2.png
Binary files differ
diff --git a/erts/emulator/internal_doc/figures/gc-watermark.dia b/erts/emulator/internal_doc/figures/gc-watermark.dia
new file mode 100644
index 0000000000..a5de7565dc
--- /dev/null
+++ b/erts/emulator/internal_doc/figures/gc-watermark.dia
Binary files differ
diff --git a/erts/emulator/internal_doc/figures/gc-watermark.png b/erts/emulator/internal_doc/figures/gc-watermark.png
new file mode 100644
index 0000000000..b835c7694e
--- /dev/null
+++ b/erts/emulator/internal_doc/figures/gc-watermark.png
Binary files differ
diff --git a/erts/emulator/nifs/common/prim_buffer_nif.c b/erts/emulator/nifs/common/prim_buffer_nif.c
new file mode 100644
index 0000000000..a8ef5fc355
--- /dev/null
+++ b/erts/emulator/nifs/common/prim_buffer_nif.c
@@ -0,0 +1,512 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson 2017. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * %CopyrightEnd%
+ */
+
+#define STATIC_ERLANG_NIF 1
+
+#include "erl_nif.h"
+#include "config.h"
+#include "sys.h"
+
+#ifdef VALGRIND
+# include <valgrind/memcheck.h>
+#endif
+
+#define ACCUMULATOR_SIZE (2 << 10)
+
+#define FIND_NIF_RESCHEDULE_SIZE (1 << 20)
+
+/* NIF interface declarations */
+static int load(ErlNifEnv *env, void** priv_data, ERL_NIF_TERM load_info);
+static int upgrade(ErlNifEnv *env, void** priv_data, void** old_priv_data, ERL_NIF_TERM load_info);
+static void unload(ErlNifEnv *env, void* priv_data);
+
+static ErlNifResourceType *rtype_buffer;
+
+static ERL_NIF_TERM am_ok;
+static ERL_NIF_TERM am_error;
+
+static ERL_NIF_TERM am_lock_order_violation;
+
+static ERL_NIF_TERM am_acquired;
+static ERL_NIF_TERM am_busy;
+
+static ERL_NIF_TERM am_continue;
+
+static ERL_NIF_TERM am_out_of_memory;
+static ERL_NIF_TERM am_not_found;
+
+typedef struct {
+#ifdef DEBUG
+ erts_atomic32_t concurrent_users;
+#endif
+
+ ErlNifBinary accumulator;
+ size_t accumulated_bytes;
+ int accumulator_present;
+
+ ErlNifIOQueue *queue;
+
+ erts_atomic32_t external_lock;
+} buffer_data_t;
+
+static ERL_NIF_TERM new_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]);
+
+static ERL_NIF_TERM peek_head_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]);
+static ERL_NIF_TERM skip_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]);
+static ERL_NIF_TERM size_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]);
+
+static ERL_NIF_TERM write_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]);
+static ERL_NIF_TERM copying_read_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]);
+
+static ERL_NIF_TERM find_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]);
+
+static ERL_NIF_TERM trylock_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]);
+static ERL_NIF_TERM unlock_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]);
+
+static ErlNifFunc nif_funcs[] = {
+ {"new", 0, new_nif},
+ {"size", 1, size_nif},
+ {"peek_head", 1, peek_head_nif},
+ {"copying_read", 2, copying_read_nif},
+ {"write", 2, write_nif},
+ {"skip", 2, skip_nif},
+ {"find_byte_index", 2, find_nif},
+ {"try_lock", 1, trylock_nif},
+ {"unlock", 1, unlock_nif},
+};
+
+ERL_NIF_INIT(prim_buffer, nif_funcs, load, NULL, upgrade, unload)
+
+static void gc_buffer(ErlNifEnv *env, void* data);
+
+static int load(ErlNifEnv *env, void** priv_data, ERL_NIF_TERM load_info)
+{
+ am_ok = enif_make_atom(env, "ok");
+ am_error = enif_make_atom(env, "error");
+
+ am_lock_order_violation = enif_make_atom(env, "lock_order_violation");
+ am_acquired = enif_make_atom(env, "acquired");
+ am_busy = enif_make_atom(env, "busy");
+
+ am_continue = enif_make_atom(env, "continue");
+
+ am_out_of_memory = enif_make_atom(env, "out_of_memory");
+ am_not_found = enif_make_atom(env, "not_found");
+
+ rtype_buffer = enif_open_resource_type(env, NULL, "gc_buffer", gc_buffer,
+ ERL_NIF_RT_CREATE, NULL);
+
+ *priv_data = NULL;
+
+ return 0;
+}
+
+static void unload(ErlNifEnv *env, void* priv_data)
+{
+
+}
+
+static int upgrade(ErlNifEnv *env, void** priv_data, void** old_priv_data, ERL_NIF_TERM load_info)
+{
+ if(*old_priv_data != NULL) {
+ return -1; /* Don't know how to do that */
+ }
+
+ if(*priv_data != NULL) {
+ return -1; /* Don't know how to do that */
+ }
+
+ if(load(env, priv_data, load_info)) {
+ return -1;
+ }
+
+ return 0;
+}
+
+static void gc_buffer(ErlNifEnv *env, void* data) {
+ buffer_data_t *buffer = (buffer_data_t*)data;
+
+ if(buffer->accumulator_present) {
+ enif_release_binary(&buffer->accumulator);
+ }
+
+ enif_ioq_destroy(buffer->queue);
+}
+
+static int get_buffer_data(ErlNifEnv *env, ERL_NIF_TERM opaque, buffer_data_t **buffer) {
+ return enif_get_resource(env, opaque, rtype_buffer, (void **)buffer);
+}
+
+/* Copies a number of bytes from the head of the iovec, skipping "vec_skip"
+ * vector elements followed by "byte_skip" bytes on the target vector. */
+static void copy_from_iovec(SysIOVec *iovec, int vec_len, int vec_skip,
+ size_t byte_skip, size_t size, char *data) {
+
+ size_t bytes_copied, skip_offset;
+ int vec_index;
+
+ skip_offset = byte_skip;
+ vec_index = vec_skip;
+ bytes_copied = 0;
+
+ while(bytes_copied < size) {
+ size_t block_size, copy_size;
+ char *block_start;
+
+ ASSERT(vec_index < vec_len);
+
+ block_start = (char*)iovec[vec_index].iov_base;
+ block_size = iovec[vec_index].iov_len;
+
+ copy_size = MIN(size - bytes_copied, block_size - skip_offset);
+ sys_memcpy(&data[bytes_copied], &block_start[skip_offset], copy_size);
+
+ bytes_copied += copy_size;
+ skip_offset = 0;
+
+ vec_index++;
+ }
+}
+
+/* Convenience function for copy_from_iovec over queues. */
+static void copy_from_queue(ErlNifIOQueue *queue, int queue_skip,
+ size_t byte_skip, size_t size, char *data) {
+
+ SysIOVec *queued_data;
+ int queue_length;
+
+ queued_data = enif_ioq_peek(queue, &queue_length);
+ ASSERT(queue_skip < queue_length);
+
+ copy_from_iovec(queued_data, queue_length, queue_skip, byte_skip, size, data);
+}
+
+static int enqueue_write_accumulator(buffer_data_t *buffer) {
+ ASSERT(!buffer->accumulator_present ^ (buffer->accumulated_bytes > 0));
+
+ if(buffer->accumulator_present && buffer->accumulated_bytes > 0) {
+ if(!enif_realloc_binary(&buffer->accumulator, buffer->accumulated_bytes)) {
+ return 0;
+ } else if(!enif_ioq_enq_binary(buffer->queue, &buffer->accumulator, 0)) {
+ return 0;
+ }
+
+ /* The queue owns the accumulator now. */
+ buffer->accumulator_present = 0;
+ buffer->accumulated_bytes = 0;
+ }
+
+ return 1;
+}
+
+static int combine_small_writes(buffer_data_t *buffer, ErlNifIOVec *iovec) {
+ ASSERT(!buffer->accumulator_present ^ (buffer->accumulated_bytes > 0));
+
+ if(buffer->accumulated_bytes + iovec->size >= ACCUMULATOR_SIZE) {
+ if(iovec->size >= (ACCUMULATOR_SIZE / 2)) {
+ return 0;
+ }
+
+ if(!enqueue_write_accumulator(buffer)) {
+ return 0;
+ }
+ }
+
+ if(!buffer->accumulator_present) {
+ if(!enif_alloc_binary(ACCUMULATOR_SIZE, &buffer->accumulator)) {
+ return 0;
+ }
+
+ buffer->accumulator_present = 1;
+ }
+
+ copy_from_iovec(iovec->iov, iovec->iovcnt, 0, 0, iovec->size,
+ (char*)&buffer->accumulator.data[buffer->accumulated_bytes]);
+ buffer->accumulated_bytes += iovec->size;
+
+ return 1;
+}
+
+/* *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** */
+
+static ERL_NIF_TERM new_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
+ buffer_data_t *buffer;
+ ERL_NIF_TERM result;
+
+ buffer = (buffer_data_t*)enif_alloc_resource(rtype_buffer, sizeof(buffer_data_t));
+ buffer->queue = enif_ioq_create(ERL_NIF_IOQ_NORMAL);
+
+ if(buffer->queue != NULL) {
+#ifdef DEBUG
+ erts_atomic32_init_nob(&buffer->concurrent_users, 0);
+#endif
+ erts_atomic32_init_nob(&buffer->external_lock, 0);
+
+ buffer->accumulator_present = 0;
+ buffer->accumulated_bytes = 0;
+
+ result = enif_make_resource(env, buffer);
+ } else {
+ result = enif_raise_exception(env, am_out_of_memory);
+ }
+
+ enif_release_resource(buffer);
+
+ return result;
+}
+
+static ERL_NIF_TERM size_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
+ buffer_data_t *buffer;
+
+ size_t total_size;
+
+ if(argc != 1 || !get_buffer_data(env, argv[0], &buffer)) {
+ return enif_make_badarg(env);
+ }
+
+ ASSERT(erts_atomic32_inc_read_acqb(&buffer->concurrent_users) == 1);
+
+ total_size = enif_ioq_size(buffer->queue);
+
+ if(buffer->accumulator_present) {
+ total_size += buffer->accumulated_bytes;
+ } else {
+ ASSERT(buffer->accumulated_bytes == 0);
+ }
+
+ ASSERT(erts_atomic32_dec_read_relb(&buffer->concurrent_users) == 0);
+
+ return enif_make_uint64(env, total_size);
+}
+
+static ERL_NIF_TERM copying_read_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
+ buffer_data_t *buffer;
+
+ ERL_NIF_TERM result;
+ unsigned char *data;
+ Uint64 block_size;
+
+ if(argc != 2 || !get_buffer_data(env, argv[0], &buffer)
+ || !enif_get_uint64(env, argv[1], &block_size)) {
+ return enif_make_badarg(env);
+ }
+
+ ASSERT(erts_atomic32_inc_read_acqb(&buffer->concurrent_users) == 1);
+
+ if(!enqueue_write_accumulator(buffer)) {
+ return enif_raise_exception(env, am_out_of_memory);
+ }
+
+ if(enif_ioq_size(buffer->queue) < block_size) {
+ return enif_make_badarg(env);
+ }
+
+ data = enif_make_new_binary(env, block_size, &result);
+
+ if(block_size > 0) {
+ copy_from_queue(buffer->queue, 0, 0, block_size, (char*)data);
+ enif_ioq_deq(buffer->queue, block_size, NULL);
+ }
+
+ ASSERT(erts_atomic32_dec_read_relb(&buffer->concurrent_users) == 0);
+
+ return result;
+}
+
+static ERL_NIF_TERM write_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
+ buffer_data_t *buffer;
+
+ ErlNifIOVec vec, *iovec = &vec;
+ ERL_NIF_TERM tail;
+
+ if(argc != 2 || !get_buffer_data(env, argv[0], &buffer)
+ || !enif_inspect_iovec(env, 64, argv[1], &tail, &iovec)) {
+ return enif_make_badarg(env);
+ }
+
+ ASSERT(erts_atomic32_inc_read_acqb(&buffer->concurrent_users) == 1);
+
+ if(!combine_small_writes(buffer, iovec)) {
+ if(!enqueue_write_accumulator(buffer) || !enif_ioq_enqv(buffer->queue, iovec, 0)) {
+ return enif_raise_exception(env, am_out_of_memory);
+ }
+ }
+
+ ASSERT(erts_atomic32_dec_read_relb(&buffer->concurrent_users) == 0);
+
+ if(!enif_is_empty_list(env, tail)) {
+ const ERL_NIF_TERM new_argv[2] = {argv[0], tail};
+
+ return enif_schedule_nif(env, "write", 0, &write_nif, argc, new_argv);
+ }
+
+ return am_ok;
+}
+
+static ERL_NIF_TERM peek_head_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
+ buffer_data_t *buffer;
+
+ ERL_NIF_TERM result;
+
+ if(argc != 1 || !get_buffer_data(env, argv[0], &buffer)) {
+ return enif_make_badarg(env);
+ }
+
+ ASSERT(erts_atomic32_inc_read_acqb(&buffer->concurrent_users) == 1);
+
+ if(!enqueue_write_accumulator(buffer)) {
+ return enif_raise_exception(env, am_out_of_memory);
+ }
+
+ if(!enif_ioq_peek_head(env, buffer->queue, NULL, &result)) {
+ return enif_make_badarg(env);
+ }
+
+ ASSERT(erts_atomic32_dec_read_relb(&buffer->concurrent_users) == 0);
+
+ return result;
+}
+
+static ERL_NIF_TERM skip_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
+ buffer_data_t *buffer;
+
+ Uint64 block_size;
+
+ if(argc != 2 || !get_buffer_data(env, argv[0], &buffer)
+ || !enif_get_uint64(env, argv[1], &block_size)) {
+ return enif_make_badarg(env);
+ }
+
+ ASSERT(erts_atomic32_inc_read_acqb(&buffer->concurrent_users) == 1);
+
+ if(!enqueue_write_accumulator(buffer)) {
+ return enif_raise_exception(env, am_out_of_memory);
+ } else if(enif_ioq_size(buffer->queue) < block_size) {
+ return enif_make_badarg(env);
+ }
+
+ enif_ioq_deq(buffer->queue, block_size, NULL);
+
+ ASSERT(erts_atomic32_dec_read_relb(&buffer->concurrent_users) == 0);
+
+ return am_ok;
+}
+
+static ERL_NIF_TERM find_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
+ buffer_data_t *buffer;
+
+ int queue_length, queue_index;
+ SysIOVec *queued_data;
+ size_t queue_size;
+
+ size_t search_offset;
+ int needle;
+
+ if(argc != 2 || !get_buffer_data(env, argv[0], &buffer)
+ || !enif_get_int(env, argv[1], &needle)) {
+ return enif_make_badarg(env);
+ }
+
+ ASSERT(erts_atomic32_inc_read_acqb(&buffer->concurrent_users) == 1);
+
+ if(!enqueue_write_accumulator(buffer)) {
+ return enif_raise_exception(env, am_out_of_memory);
+ } else if(needle < 0 || needle > 255) {
+ return enif_make_badarg(env);
+ }
+
+ queued_data = enif_ioq_peek(buffer->queue, &queue_length);
+ queue_size = enif_ioq_size(buffer->queue);
+ queue_index = 0;
+
+ search_offset = 0;
+
+ if(queue_size > (FIND_NIF_RESCHEDULE_SIZE / 100)) {
+ if(enif_thread_type() == ERL_NIF_THR_NORMAL_SCHEDULER) {
+ int timeslice_percent;
+
+ if(queue_size >= FIND_NIF_RESCHEDULE_SIZE) {
+ ASSERT(erts_atomic32_dec_read_relb(&buffer->concurrent_users) == 0);
+
+ return enif_schedule_nif(env, "find",
+ ERL_NIF_DIRTY_JOB_CPU_BOUND, &find_nif, argc, argv);
+ }
+
+ timeslice_percent = (queue_size * 100) / FIND_NIF_RESCHEDULE_SIZE;
+ enif_consume_timeslice(env, timeslice_percent);
+ }
+ }
+
+ while(queue_index < queue_length) {
+ char *needle_address;
+ char *block_start;
+ size_t block_size;
+
+ block_start = queued_data[queue_index].iov_base;
+ block_size = queued_data[queue_index].iov_len;
+
+ needle_address = memchr(block_start, needle, block_size);
+
+ if(needle_address != NULL) {
+ size_t result = search_offset + (needle_address - block_start);
+
+ ASSERT(erts_atomic32_dec_read_relb(&buffer->concurrent_users) == 0);
+
+ return enif_make_tuple2(env, am_ok, enif_make_uint64(env, result));
+ }
+
+ search_offset += block_size;
+ queue_index++;
+ }
+
+ ASSERT(erts_atomic32_dec_read_relb(&buffer->concurrent_users) == 0);
+
+ return am_not_found;
+}
+
+/* */
+
+static ERL_NIF_TERM trylock_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
+ buffer_data_t *buffer;
+
+ if(argc != 1 || !get_buffer_data(env, argv[0], &buffer)) {
+ return enif_make_badarg(env);
+ }
+
+ if(erts_atomic32_cmpxchg_acqb(&buffer->external_lock, 1, 0) == 0) {
+ return am_acquired;
+ }
+
+ return am_busy;
+}
+
+static ERL_NIF_TERM unlock_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
+ buffer_data_t *buffer;
+
+ if(argc != 1 || !get_buffer_data(env, argv[0], &buffer)) {
+ return enif_make_badarg(env);
+ }
+
+ if(erts_atomic32_cmpxchg_relb(&buffer->external_lock, 0, 1) == 0) {
+ return enif_raise_exception(env, am_lock_order_violation);
+ }
+
+ return am_ok;
+}
diff --git a/erts/emulator/nifs/common/prim_file_nif.c b/erts/emulator/nifs/common/prim_file_nif.c
new file mode 100644
index 0000000000..bbd9becb47
--- /dev/null
+++ b/erts/emulator/nifs/common/prim_file_nif.c
@@ -0,0 +1,1237 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson 2017. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * %CopyrightEnd%
+ */
+
+#define STATIC_ERLANG_NIF 1
+
+#include "erl_nif.h"
+#include "config.h"
+#include "sys.h"
+
+#ifdef VALGRIND
+# include <valgrind/memcheck.h>
+#endif
+
+#include "erl_driver.h"
+#include "prim_file_nif.h"
+
+/* NIF interface declarations */
+static int load(ErlNifEnv *env, void** priv_data, ERL_NIF_TERM load_info);
+static int upgrade(ErlNifEnv *env, void** priv_data, void** old_priv_data, ERL_NIF_TERM load_info);
+static void unload(ErlNifEnv *env, void* priv_data);
+
+static ErlNifResourceType *efile_resource_type;
+
+static ERL_NIF_TERM am_ok;
+static ERL_NIF_TERM am_error;
+static ERL_NIF_TERM am_continue;
+
+static ERL_NIF_TERM am_file_info;
+
+/* File modes */
+static ERL_NIF_TERM am_read;
+static ERL_NIF_TERM am_write;
+static ERL_NIF_TERM am_exclusive;
+static ERL_NIF_TERM am_append;
+static ERL_NIF_TERM am_sync;
+static ERL_NIF_TERM am_skip_type_check;
+
+/* enum efile_access_t; read and write are defined above.*/
+static ERL_NIF_TERM am_read_write;
+static ERL_NIF_TERM am_none;
+
+/* enum efile_advise_t */
+static ERL_NIF_TERM am_normal;
+static ERL_NIF_TERM am_random;
+static ERL_NIF_TERM am_sequential;
+static ERL_NIF_TERM am_will_need;
+static ERL_NIF_TERM am_dont_need;
+static ERL_NIF_TERM am_no_reuse;
+
+/* enum efile_filetype_t */
+static ERL_NIF_TERM am_device;
+static ERL_NIF_TERM am_directory;
+static ERL_NIF_TERM am_regular;
+static ERL_NIF_TERM am_symlink;
+static ERL_NIF_TERM am_other;
+
+/* enum efile_seek_t, 'eof' marker. */
+static ERL_NIF_TERM am_bof;
+static ERL_NIF_TERM am_cur;
+static ERL_NIF_TERM am_eof;
+
+static ERL_NIF_TERM read_info_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]);
+static ERL_NIF_TERM set_permissions_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]);
+static ERL_NIF_TERM set_owner_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]);
+static ERL_NIF_TERM set_time_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]);
+
+static ERL_NIF_TERM read_link_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]);
+static ERL_NIF_TERM list_dir_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]);
+
+static ERL_NIF_TERM make_hard_link_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]);
+static ERL_NIF_TERM make_soft_link_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]);
+static ERL_NIF_TERM rename_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]);
+static ERL_NIF_TERM make_dir_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]);
+static ERL_NIF_TERM del_file_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]);
+static ERL_NIF_TERM del_dir_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]);
+static ERL_NIF_TERM get_device_cwd_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]);
+static ERL_NIF_TERM get_cwd_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]);
+static ERL_NIF_TERM set_cwd_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]);
+
+static ERL_NIF_TERM read_file_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]);
+
+static ERL_NIF_TERM get_handle_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]);
+static ERL_NIF_TERM altname_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]);
+
+static ERL_NIF_TERM open_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]);
+
+/* All file handle operations are passed through a wrapper that handles state
+ * transitions, marking it as busy during the course of the operation, and
+ * closing on completion if the owner died in the middle of an operation.
+ *
+ * This is pretty ugly but required as there's no way to tell when it's safe to
+ * asynchronously close a file; the event could have fired just before landing
+ * in a system call which will fail with EBADF at best or alias a newly opened
+ * fd at worst.
+ *
+ * The old driver got away with enqueueing the close operation on the same
+ * async queue as all of its other operations, but since dirty schedulers use a
+ * single global queue there's no natural way to schedule an asynchronous close
+ * "behind" other operations.
+ *
+ * The states may transition as follows:
+ *
+ * IDLE ->
+ * BUSY (file_handle_wrapper) |
+ * CLOSED (owner_death_callback)
+ *
+ * BUSY ->
+ * IDLE (file_handle_wrapper)
+ * CLOSED (close_nif_impl)
+ * CLOSE_PENDING (owner_death_callback)
+ *
+ * CLOSE_PENDING ->
+ * CLOSED (file_handle_wrapper)
+ */
+
+typedef ERL_NIF_TERM (*file_op_impl_t)(efile_data_t *d, ErlNifEnv *env,
+ int argc, const ERL_NIF_TERM argv[]);
+static ERL_NIF_TERM file_handle_wrapper(file_op_impl_t operation, ErlNifEnv *env,
+ int argc, const ERL_NIF_TERM argv[]);
+
+#define WRAP_FILE_HANDLE_EXPORT(name) \
+ static ERL_NIF_TERM name ## _impl (efile_data_t *d, ErlNifEnv *env, \
+ int argc, const ERL_NIF_TERM argv[]);\
+ static ERL_NIF_TERM name(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) { \
+ return file_handle_wrapper( name ## _impl , env, argc, argv); \
+ }
+
+WRAP_FILE_HANDLE_EXPORT(close_nif)
+WRAP_FILE_HANDLE_EXPORT(read_nif)
+WRAP_FILE_HANDLE_EXPORT(write_nif)
+WRAP_FILE_HANDLE_EXPORT(pread_nif)
+WRAP_FILE_HANDLE_EXPORT(pwrite_nif)
+WRAP_FILE_HANDLE_EXPORT(seek_nif)
+WRAP_FILE_HANDLE_EXPORT(sync_nif)
+WRAP_FILE_HANDLE_EXPORT(truncate_nif)
+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)
+
+static ErlNifFunc nif_funcs[] = {
+ /* File handle ops */
+ {"open_nif", 2, open_nif, ERL_NIF_DIRTY_JOB_IO_BOUND},
+ {"close_nif", 1, close_nif, ERL_NIF_DIRTY_JOB_IO_BOUND},
+ {"read_nif", 2, read_nif, ERL_NIF_DIRTY_JOB_IO_BOUND},
+ {"write_nif", 2, write_nif, ERL_NIF_DIRTY_JOB_IO_BOUND},
+ {"pread_nif", 3, pread_nif, ERL_NIF_DIRTY_JOB_IO_BOUND},
+ {"pwrite_nif", 3, pwrite_nif, ERL_NIF_DIRTY_JOB_IO_BOUND},
+ {"seek_nif", 3, seek_nif, ERL_NIF_DIRTY_JOB_IO_BOUND},
+ {"sync_nif", 2, sync_nif, ERL_NIF_DIRTY_JOB_IO_BOUND},
+ {"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},
+
+ /* Filesystem ops */
+ {"make_hard_link_nif", 2, make_hard_link_nif, ERL_NIF_DIRTY_JOB_IO_BOUND},
+ {"make_soft_link_nif", 2, make_soft_link_nif, ERL_NIF_DIRTY_JOB_IO_BOUND},
+ {"rename_nif", 2, rename_nif, ERL_NIF_DIRTY_JOB_IO_BOUND},
+ {"read_info_nif", 2, read_info_nif, ERL_NIF_DIRTY_JOB_IO_BOUND},
+ {"set_permissions_nif", 2, set_permissions_nif, ERL_NIF_DIRTY_JOB_IO_BOUND},
+ {"set_owner_nif", 3, set_owner_nif, ERL_NIF_DIRTY_JOB_IO_BOUND},
+ {"set_time_nif", 4, set_time_nif, ERL_NIF_DIRTY_JOB_IO_BOUND},
+ {"read_link_nif", 1, read_link_nif, ERL_NIF_DIRTY_JOB_IO_BOUND},
+ {"list_dir_nif", 1, list_dir_nif, ERL_NIF_DIRTY_JOB_IO_BOUND},
+ {"make_dir_nif", 1, make_dir_nif, ERL_NIF_DIRTY_JOB_IO_BOUND},
+ {"del_file_nif", 1, del_file_nif, ERL_NIF_DIRTY_JOB_IO_BOUND},
+ {"del_dir_nif", 1, del_dir_nif, ERL_NIF_DIRTY_JOB_IO_BOUND},
+ {"get_device_cwd_nif", 1, get_device_cwd_nif, ERL_NIF_DIRTY_JOB_IO_BOUND},
+ {"set_cwd_nif", 1, set_cwd_nif, ERL_NIF_DIRTY_JOB_IO_BOUND},
+ {"get_cwd_nif", 0, get_cwd_nif, ERL_NIF_DIRTY_JOB_IO_BOUND},
+
+ /* These operations are equivalent to chained calls of other operations,
+ * but have been moved down to avoid excessive rescheduling. */
+ {"ipread_s32bu_p32bu_nif", 3, ipread_s32bu_p32bu_nif, ERL_NIF_DIRTY_JOB_IO_BOUND},
+ {"read_file_nif", 1, read_file_nif, ERL_NIF_DIRTY_JOB_IO_BOUND},
+
+ /* Internal ops. */
+ {"get_handle_nif", 1, get_handle_nif},
+ {"altname_nif", 1, altname_nif, ERL_NIF_DIRTY_JOB_IO_BOUND},
+};
+
+ERL_NIF_INIT(prim_file, nif_funcs, load, NULL, upgrade, unload)
+
+static void owner_death_callback(ErlNifEnv* env, void* obj, ErlNifPid* pid, ErlNifMonitor* mon);
+static void gc_callback(ErlNifEnv *env, void* data);
+
+static int load(ErlNifEnv *env, void** priv_data, ERL_NIF_TERM load_info)
+{
+ ErlNifResourceTypeInit callbacks;
+
+ am_ok = enif_make_atom(env, "ok");
+ am_error = enif_make_atom(env, "error");
+ am_continue = enif_make_atom(env, "continue");
+
+ am_read = enif_make_atom(env, "read");
+ am_write = enif_make_atom(env, "write");
+ am_exclusive = enif_make_atom(env, "exclusive");
+ 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_read_write = enif_make_atom(env, "read_write");
+ am_none = enif_make_atom(env, "none");
+
+ am_normal = enif_make_atom(env, "normal");
+ am_random = enif_make_atom(env, "random");
+ am_sequential = enif_make_atom(env, "sequential");
+ am_will_need = enif_make_atom(env, "will_need");
+ am_dont_need = enif_make_atom(env, "dont_need");
+ am_no_reuse = enif_make_atom(env, "no_reuse");
+
+ am_device = enif_make_atom(env, "device");
+ am_directory = enif_make_atom(env, "directory");
+ am_regular = enif_make_atom(env, "regular");
+ am_symlink = enif_make_atom(env, "symlink");
+ am_other = enif_make_atom(env, "other");
+
+ am_file_info = enif_make_atom(env, "file_info");
+
+ am_bof = enif_make_atom(env, "bof");
+ am_cur = enif_make_atom(env, "cur");
+ am_eof = enif_make_atom(env, "eof");
+
+ callbacks.down = owner_death_callback;
+ callbacks.dtor = gc_callback;
+ callbacks.stop = NULL;
+
+ efile_resource_type = enif_open_resource_type_x(env, "efile", &callbacks,
+ ERL_NIF_RT_CREATE, NULL);
+
+ *priv_data = NULL;
+
+ return 0;
+}
+
+static void unload(ErlNifEnv *env, void* priv_data)
+{
+
+}
+
+static int upgrade(ErlNifEnv *env, void** priv_data, void** old_priv_data, ERL_NIF_TERM load_info)
+{
+ if(*old_priv_data != NULL) {
+ return -1; /* Don't know how to do that */
+ }
+ if(*priv_data != NULL) {
+ return -1; /* Don't know how to do that */
+ }
+ if(load(env, priv_data, load_info)) {
+ return -1;
+ }
+ return 0;
+}
+
+static ERL_NIF_TERM posix_error_to_tuple(ErlNifEnv *env, posix_errno_t posix_errno) {
+ ERL_NIF_TERM error = enif_make_atom(env, erl_errno_id(posix_errno));
+ return enif_make_tuple2(env, am_error, error);
+}
+
+static int get_file_data(ErlNifEnv *env, ERL_NIF_TERM opaque, efile_data_t **d) {
+ return enif_get_resource(env, opaque, efile_resource_type, (void **)d);
+}
+
+static ERL_NIF_TERM file_handle_wrapper(file_op_impl_t operation, ErlNifEnv *env,
+ int argc, const ERL_NIF_TERM argv[]) {
+
+ efile_data_t *d;
+
+ enum efile_state_t previous_state;
+ ERL_NIF_TERM result;
+
+ if(argc < 1 || !get_file_data(env, argv[0], &d)) {
+ return enif_make_badarg(env);
+ }
+
+ previous_state = erts_atomic32_cmpxchg_acqb(&d->state,
+ EFILE_STATE_BUSY, EFILE_STATE_IDLE);
+
+ if(previous_state == EFILE_STATE_IDLE) {
+ result = operation(d, env, argc - 1, &argv[1]);
+
+ previous_state = erts_atomic32_cmpxchg_relb(&d->state,
+ EFILE_STATE_IDLE, EFILE_STATE_BUSY);
+
+ ASSERT(previous_state != EFILE_STATE_IDLE);
+
+ if(previous_state == EFILE_STATE_CLOSE_PENDING) {
+ /* This is the only point where a change from CLOSE_PENDING is
+ * possible, and we're running synchronously, so we can't race with
+ * anything else here. */
+ erts_atomic32_set_acqb(&d->state, EFILE_STATE_CLOSED);
+ efile_close(d);
+ }
+ } else {
+ /* CLOSE_PENDING should be impossible at this point since it requires
+ * a transition from BUSY; the only valid state here is CLOSED. */
+ ASSERT(previous_state == EFILE_STATE_CLOSED);
+
+ result = posix_error_to_tuple(env, EINVAL);
+ }
+
+ return result;
+}
+
+static void owner_death_callback(ErlNifEnv* env, void* obj, ErlNifPid* pid, ErlNifMonitor* mon) {
+ efile_data_t *d = (efile_data_t*)obj;
+
+ (void)env;
+ (void)pid;
+ (void)mon;
+
+ for(;;) {
+ enum efile_state_t previous_state;
+
+ previous_state = erts_atomic32_cmpxchg_acqb(&d->state,
+ EFILE_STATE_CLOSED, EFILE_STATE_IDLE);
+
+ switch(previous_state) {
+ case EFILE_STATE_IDLE:
+ efile_close(d);
+ return;
+ case EFILE_STATE_CLOSE_PENDING:
+ case EFILE_STATE_CLOSED:
+ /* We're either already closed or managed to mark ourselves for
+ * closure in the previous iteration. */
+ return;
+ case EFILE_STATE_BUSY:
+ /* Schedule ourselves to be closed once the current operation
+ * finishes, retrying the [IDLE -> CLOSED] transition in case we
+ * narrowly passed the [BUSY -> IDLE] one. */
+ erts_atomic32_cmpxchg_nob(&d->state,
+ EFILE_STATE_CLOSE_PENDING, EFILE_STATE_BUSY);
+ break;
+ }
+ }
+}
+
+static void gc_callback(ErlNifEnv *env, void* data) {
+ efile_data_t *d = (efile_data_t*)data;
+
+ enum efile_state_t previous_state;
+
+ (void)env;
+
+ previous_state = erts_atomic32_cmpxchg_acqb(&d->state,
+ EFILE_STATE_CLOSED, EFILE_STATE_IDLE);
+
+ ASSERT(previous_state != EFILE_STATE_CLOSE_PENDING &&
+ previous_state != EFILE_STATE_BUSY);
+
+ if(previous_state == EFILE_STATE_IDLE) {
+ efile_close(d);
+ }
+}
+
+static ERL_NIF_TERM efile_filetype_to_atom(enum efile_filetype_t type) {
+ switch(type) {
+ case EFILE_FILETYPE_DEVICE: return am_device;
+ case EFILE_FILETYPE_DIRECTORY: return am_directory;
+ case EFILE_FILETYPE_REGULAR: return am_regular;
+ case EFILE_FILETYPE_SYMLINK: return am_symlink;
+ case EFILE_FILETYPE_OTHER: return am_other;
+ }
+
+ return am_other;
+}
+
+static ERL_NIF_TERM efile_access_to_atom(enum efile_access_t type) {
+ if(type & EFILE_ACCESS_READ && !(type & EFILE_ACCESS_WRITE)) {
+ return am_read;
+ } else if(type & EFILE_ACCESS_WRITE && !(type & EFILE_ACCESS_READ)) {
+ return am_write;
+ } else if(type & EFILE_ACCESS_READ_WRITE) {
+ return am_read_write;
+ }
+
+ return am_none;
+}
+
+static enum efile_modes_t efile_translate_modelist(ErlNifEnv *env, ERL_NIF_TERM list) {
+ enum efile_modes_t modes;
+ ERL_NIF_TERM head, tail;
+
+ modes = 0;
+
+ while(enif_get_list_cell(env, list, &head, &tail)) {
+ if(enif_is_identical(head, am_read)) {
+ modes |= EFILE_MODE_READ;
+ } else if(enif_is_identical(head, am_write)) {
+ modes |= EFILE_MODE_WRITE;
+ } else if(enif_is_identical(head, am_exclusive)) {
+ modes |= EFILE_MODE_EXCLUSIVE;
+ } else if(enif_is_identical(head, am_append)) {
+ modes |= EFILE_MODE_APPEND;
+ } else if(enif_is_identical(head, am_sync)) {
+ modes |= EFILE_MODE_SYNC;
+ } else if(enif_is_identical(head, am_skip_type_check)) {
+ modes |= EFILE_MODE_SKIP_TYPE_CHECK;
+ } else {
+ /* Modes like 'raw', 'ram', 'delayed_writes' etc are handled
+ * further up the chain. */
+ }
+
+ list = tail;
+ }
+
+ if(modes & (EFILE_MODE_APPEND | EFILE_MODE_EXCLUSIVE)) {
+ /* 'append' and 'exclusive' are documented as "open for writing." */
+ modes |= EFILE_MODE_WRITE;
+ } else if(!(modes & EFILE_MODE_READ_WRITE)) {
+ /* Defaulting to read if !(W|R) is undocumented, but specifically
+ * tested against in file_SUITE. */
+ modes |= EFILE_MODE_READ;
+ }
+
+ return modes;
+}
+
+static ERL_NIF_TERM open_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
+ posix_errno_t posix_errno;
+ efile_data_t *d;
+
+ ErlNifPid controlling_process;
+ enum efile_modes_t modes;
+ ERL_NIF_TERM result;
+ efile_path_t path;
+
+ if(argc != 2 || !enif_is_list(env, argv[1])) {
+ return enif_make_badarg(env);
+ }
+
+ modes = efile_translate_modelist(env, argv[1]);
+
+ if((posix_errno = efile_marshal_path(env, argv[0], &path))) {
+ return posix_error_to_tuple(env, posix_errno);
+ } else if((posix_errno = efile_open(&path, modes, efile_resource_type, &d))) {
+ return posix_error_to_tuple(env, posix_errno);
+ }
+
+ result = enif_make_resource(env, d);
+ enif_release_resource(d);
+
+ enif_self(env, &controlling_process);
+
+ if(enif_monitor_process(env, d, &controlling_process, &d->monitor)) {
+ return posix_error_to_tuple(env, EINVAL);
+ }
+
+ return enif_make_tuple2(env, am_ok, result);
+}
+
+static ERL_NIF_TERM close_nif_impl(efile_data_t *d, ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
+ enum efile_state_t previous_state;
+
+ if(argc != 0) {
+ return enif_make_badarg(env);
+ }
+
+ previous_state = erts_atomic32_cmpxchg_acqb(&d->state,
+ EFILE_STATE_CLOSED, EFILE_STATE_BUSY);
+
+ ASSERT(previous_state == EFILE_STATE_CLOSE_PENDING ||
+ previous_state == EFILE_STATE_BUSY);
+
+ if(previous_state == EFILE_STATE_BUSY) {
+ enif_demonitor_process(env, d, &d->monitor);
+
+ if(!efile_close(d)) {
+ return posix_error_to_tuple(env, d->posix_errno);
+ }
+ }
+
+ return am_ok;
+}
+
+static ERL_NIF_TERM read_nif_impl(efile_data_t *d, ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
+ Sint64 bytes_read, block_size;
+ SysIOVec read_vec[1];
+ ErlNifBinary result;
+
+ if(argc != 1 || !enif_is_number(env, argv[0])) {
+ return enif_make_badarg(env);
+ }
+
+ if(!enif_get_int64(env, argv[0], &block_size) || block_size < 0) {
+ return posix_error_to_tuple(env, EINVAL);
+ }
+
+ if(!enif_alloc_binary(block_size, &result)) {
+ return posix_error_to_tuple(env, ENOMEM);
+ }
+
+ read_vec[0].iov_base = result.data;
+ read_vec[0].iov_len = result.size;
+
+ bytes_read = efile_readv(d, read_vec, 1);
+ ASSERT(bytes_read <= block_size);
+
+ if(bytes_read < 0) {
+ return posix_error_to_tuple(env, d->posix_errno);
+ } else if(bytes_read == 0) {
+ enif_release_binary(&result);
+ return am_eof;
+ }
+
+ if(bytes_read < block_size && !enif_realloc_binary(&result, bytes_read)) {
+ ERTS_INTERNAL_ERROR("Failed to shrink read result.");
+ }
+
+ return enif_make_tuple2(env, am_ok, enif_make_binary(env, &result));
+}
+
+static ERL_NIF_TERM write_nif_impl(efile_data_t *d, ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
+ ErlNifIOVec vec, *input = &vec;
+ Sint64 bytes_written;
+ ERL_NIF_TERM tail;
+
+ if(argc != 1 || !enif_inspect_iovec(env, 64, argv[0], &tail, &input)) {
+ return enif_make_badarg(env);
+ }
+
+ bytes_written = efile_writev(d, input->iov, input->iovcnt);
+
+ if(bytes_written < 0) {
+ return posix_error_to_tuple(env, d->posix_errno);
+ }
+
+ if(!enif_is_empty_list(env, tail)) {
+ ASSERT(bytes_written > 0);
+ return enif_make_tuple2(env, am_continue, tail);
+ }
+
+ return am_ok;
+}
+
+static ERL_NIF_TERM pread_nif_impl(efile_data_t *d, ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
+ Sint64 bytes_read, block_size, offset;
+ SysIOVec read_vec[1];
+ ErlNifBinary result;
+
+ if(argc != 2 || !enif_is_number(env, argv[0])
+ || !enif_is_number(env, argv[1])) {
+ return enif_make_badarg(env);
+ }
+
+ if(!enif_get_int64(env, argv[0], &offset) ||
+ !enif_get_int64(env, argv[1], &block_size) ||
+ (offset < 0 || block_size < 0)) {
+ return posix_error_to_tuple(env, EINVAL);
+ }
+
+ if(!enif_alloc_binary(block_size, &result)) {
+ return posix_error_to_tuple(env, ENOMEM);
+ }
+
+ read_vec[0].iov_base = result.data;
+ read_vec[0].iov_len = result.size;
+
+ bytes_read = efile_preadv(d, offset, read_vec, 1);
+
+ if(bytes_read < 0) {
+ return posix_error_to_tuple(env, d->posix_errno);
+ } else if(bytes_read == 0) {
+ enif_release_binary(&result);
+ return am_eof;
+ }
+
+ if(bytes_read < block_size && !enif_realloc_binary(&result, bytes_read)) {
+ ERTS_INTERNAL_ERROR("Failed to shrink pread result.");
+ }
+
+ return enif_make_tuple2(env, am_ok, enif_make_binary(env, &result));
+}
+
+static ERL_NIF_TERM pwrite_nif_impl(efile_data_t *d, ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
+ ErlNifIOVec vec, *input = &vec;
+ Sint64 bytes_written, offset;
+ ERL_NIF_TERM tail;
+
+ if(argc != 2 || !enif_is_number(env, argv[0])
+ || !enif_inspect_iovec(env, 64, argv[1], &tail, &input)) {
+ return enif_make_badarg(env);
+ }
+
+ if(!enif_get_int64(env, argv[0], &offset) || offset < 0) {
+ return posix_error_to_tuple(env, EINVAL);
+ }
+
+ bytes_written = efile_pwritev(d, offset, input->iov, input->iovcnt);
+
+ if(bytes_written < 0) {
+ return posix_error_to_tuple(env, d->posix_errno);
+ }
+
+ if(!enif_is_empty_list(env, tail)) {
+ ASSERT(bytes_written > 0);
+ return enif_make_tuple3(env, am_continue,
+ enif_make_int64(env, bytes_written), tail);
+ }
+
+ return am_ok;
+}
+
+static ERL_NIF_TERM seek_nif_impl(efile_data_t *d, ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
+ Sint64 new_position, offset;
+ enum efile_seek_t seek;
+
+ if(argc != 2 || !enif_get_int64(env, argv[1], &offset)) {
+ return enif_make_badarg(env);
+ }
+
+ if(enif_is_identical(argv[0], am_bof)) {
+ seek = EFILE_SEEK_BOF;
+ } else if(enif_is_identical(argv[0], am_cur)) {
+ seek = EFILE_SEEK_CUR;
+ } else if(enif_is_identical(argv[0], am_eof)) {
+ seek = EFILE_SEEK_EOF;
+ } else {
+ return enif_make_badarg(env);
+ }
+
+ if(!efile_seek(d, seek, offset, &new_position)) {
+ return posix_error_to_tuple(env, d->posix_errno);
+ }
+
+ return enif_make_tuple2(env, am_ok, enif_make_uint64(env, new_position));
+}
+
+static ERL_NIF_TERM sync_nif_impl(efile_data_t *d, ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
+ int data_only;
+
+ if(argc != 1 || !enif_get_int(env, argv[0], &data_only)) {
+ return enif_make_badarg(env);
+ }
+
+ if(!efile_sync(d, data_only)) {
+ return posix_error_to_tuple(env, d->posix_errno);
+ }
+
+ return am_ok;
+}
+
+static ERL_NIF_TERM truncate_nif_impl(efile_data_t *d, ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
+ if(argc != 0) {
+ return enif_make_badarg(env);
+ }
+
+ if(!efile_truncate(d)) {
+ return posix_error_to_tuple(env, d->posix_errno);
+ }
+
+ return am_ok;
+}
+
+static ERL_NIF_TERM allocate_nif_impl(efile_data_t *d, ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
+ Sint64 offset, length;
+
+ if(argc != 2 || !enif_is_number(env, argv[0])
+ || !enif_is_number(env, argv[1])) {
+ return enif_make_badarg(env);
+ }
+
+ if(!enif_get_int64(env, argv[0], &offset) ||
+ !enif_get_int64(env, argv[1], &length) ||
+ (offset < 0 || length < 0)) {
+ return posix_error_to_tuple(env, EINVAL);
+ }
+
+ if(!efile_allocate(d, offset, length)) {
+ return posix_error_to_tuple(env, d->posix_errno);
+ }
+
+ return am_ok;
+}
+
+static ERL_NIF_TERM advise_nif_impl(efile_data_t *d, ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
+ enum efile_advise_t advise;
+ Sint64 offset, length;
+
+ if(argc != 3 || !enif_is_number(env, argv[0])
+ || !enif_is_number(env, argv[1])) {
+ return enif_make_badarg(env);
+ }
+
+ if(!enif_get_int64(env, argv[0], &offset) ||
+ !enif_get_int64(env, argv[1], &length) ||
+ (offset < 0 || length < 0)) {
+ return posix_error_to_tuple(env, EINVAL);
+ }
+
+ if(enif_is_identical(argv[2], am_normal)) {
+ advise = EFILE_ADVISE_NORMAL;
+ } else if(enif_is_identical(argv[2], am_random)) {
+ advise = EFILE_ADVISE_RANDOM;
+ } else if(enif_is_identical(argv[2], am_sequential)) {
+ advise = EFILE_ADVISE_SEQUENTIAL;
+ } else if(enif_is_identical(argv[2], am_will_need)) {
+ advise = EFILE_ADVISE_WILL_NEED;
+ } else if(enif_is_identical(argv[2], am_dont_need)) {
+ advise = EFILE_ADVISE_DONT_NEED;
+ } else if(enif_is_identical(argv[2], am_no_reuse)) {
+ advise = EFILE_ADVISE_NO_REUSE;
+ } else {
+ /* The tests check for EINVAL instead of badarg. Sigh. */
+ return posix_error_to_tuple(env, EINVAL);
+ }
+
+ if(!efile_advise(d, offset, length, advise)) {
+ return posix_error_to_tuple(env, d->posix_errno);
+ }
+
+ return am_ok;
+}
+
+/* This undocumented function reads a pointer and then reads the data block
+ * described by said pointer. It was reverse-engineered from the old
+ * implementation so while all tests pass it may not be entirely correct. Our
+ * current understanding is as follows:
+ *
+ * Pointer layout:
+ *
+ * <<Size:1/integer-unit:32, Offset:1/integer-unit:32>>
+ *
+ * Where Offset is the -absolute- address to the data block.
+ *
+ * *) If we fail to read the pointer block in its entirety, we return eof.
+ * *) If the provided max_payload_size is larger than Size, we return eof.
+ * *) If we fail to read any data whatsoever at Offset, we return
+ * {ok, {Size, Offset, eof}}
+ * *) Otherwise, we return {ok, {Size, Offset, Data}}. Note that the size
+ * of Data may be smaller than Size if we encounter EOF before we could
+ * read the entire block.
+ *
+ * On errors we'll return {error, posix()} regardless of whether they
+ * happened before or after reading the pointer block. */
+static ERL_NIF_TERM ipread_s32bu_p32bu_nif_impl(efile_data_t *d, ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
+ Sint64 payload_offset, payload_size;
+
+ SysIOVec read_vec[1];
+ Sint64 bytes_read;
+
+ ErlNifBinary payload;
+
+ if(argc != 2 || !enif_is_number(env, argv[0])
+ || !enif_is_number(env, argv[1])) {
+ return enif_make_badarg(env);
+ }
+
+ {
+ Sint64 max_payload_size, pointer_offset;
+ unsigned char pointer_block[8];
+
+ if(!enif_get_int64(env, argv[0], &pointer_offset) ||
+ !enif_get_int64(env, argv[1], &max_payload_size) ||
+ (pointer_offset < 0 || max_payload_size >= 1u << 31)) {
+ return posix_error_to_tuple(env, EINVAL);
+ }
+
+ read_vec[0].iov_base = pointer_block;
+ read_vec[0].iov_len = sizeof(pointer_block);
+
+ bytes_read = efile_preadv(d, pointer_offset, read_vec, 1);
+
+ if(bytes_read < 0) {
+ return posix_error_to_tuple(env, d->posix_errno);
+ } else if(bytes_read < sizeof(pointer_block)) {
+ return am_eof;
+ }
+
+ payload_size = (Uint32)get_int32(&pointer_block[0]);
+ payload_offset = (Uint32)get_int32(&pointer_block[4]);
+
+ if(payload_size > max_payload_size) {
+ return am_eof;
+ }
+ }
+
+ if(!enif_alloc_binary(payload_size, &payload)) {
+ return posix_error_to_tuple(env, ENOMEM);
+ }
+
+ read_vec[0].iov_base = payload.data;
+ read_vec[0].iov_len = payload.size;
+
+ bytes_read = efile_preadv(d, payload_offset, read_vec, 1);
+
+ if(bytes_read < 0) {
+ return posix_error_to_tuple(env, d->posix_errno);
+ } else if(bytes_read == 0) {
+ enif_release_binary(&payload);
+
+ return enif_make_tuple2(env, am_ok,
+ enif_make_tuple3(env,
+ enif_make_uint(env, payload_size),
+ enif_make_uint(env, payload_offset),
+ am_eof));
+ }
+
+ if(bytes_read < payload.size && !enif_realloc_binary(&payload, bytes_read)) {
+ ERTS_INTERNAL_ERROR("Failed to shrink ipread payload.");
+ }
+
+ return enif_make_tuple2(env, am_ok,
+ enif_make_tuple3(env,
+ enif_make_uint(env, payload_size),
+ enif_make_uint(env, payload_offset),
+ enif_make_binary(env, &payload)));
+}
+
+static ERL_NIF_TERM get_handle_nif_impl(efile_data_t *d, ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
+ if(argc != 0) {
+ return enif_make_badarg(env);
+ }
+
+ return efile_get_handle(env, d);
+}
+
+static ERL_NIF_TERM read_info_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
+ posix_errno_t posix_errno;
+
+ efile_fileinfo_t info = {0};
+ efile_path_t path;
+ int follow_links;
+
+ if(argc != 2 || !enif_get_int(env, argv[1], &follow_links)) {
+ return enif_make_badarg(env);
+ }
+
+ if((posix_errno = efile_marshal_path(env, argv[0], &path))) {
+ return posix_error_to_tuple(env, posix_errno);
+ } else if((posix_errno = efile_read_info(&path, follow_links, &info))) {
+ 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)
+ );
+}
+
+static ERL_NIF_TERM set_permissions_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
+ posix_errno_t posix_errno;
+
+ efile_path_t path;
+ Uint32 permissions;
+
+ if(argc != 2 || !enif_get_uint(env, argv[1], &permissions)) {
+ return enif_make_badarg(env);
+ }
+
+ if((posix_errno = efile_marshal_path(env, argv[0], &path))) {
+ return posix_error_to_tuple(env, posix_errno);
+ } else if((posix_errno = efile_set_permissions(&path, permissions))) {
+ return posix_error_to_tuple(env, posix_errno);
+ }
+
+ return am_ok;
+}
+
+static ERL_NIF_TERM set_owner_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
+ posix_errno_t posix_errno;
+
+ efile_path_t path;
+ Sint32 uid, gid;
+
+ if(argc != 3 || !enif_get_int(env, argv[1], &uid)
+ || !enif_get_int(env, argv[2], &gid)) {
+ return enif_make_badarg(env);
+ }
+
+ if((posix_errno = efile_marshal_path(env, argv[0], &path))) {
+ return posix_error_to_tuple(env, posix_errno);
+ } else if((posix_errno = efile_set_owner(&path, uid, gid))) {
+ return posix_error_to_tuple(env, posix_errno);
+ }
+
+ return am_ok;
+}
+
+static ERL_NIF_TERM set_time_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
+ posix_errno_t posix_errno;
+
+ Sint64 accessed, modified, created;
+ efile_path_t path;
+
+ if(argc != 4 || !enif_get_int64(env, argv[1], &accessed)
+ || !enif_get_int64(env, argv[2], &modified)
+ || !enif_get_int64(env, argv[3], &created)) {
+ return enif_make_badarg(env);
+ }
+
+ if((posix_errno = efile_marshal_path(env, argv[0], &path))) {
+ return posix_error_to_tuple(env, posix_errno);
+ } else if((posix_errno = efile_set_time(&path, accessed, modified, created))) {
+ return posix_error_to_tuple(env, posix_errno);
+ }
+
+ return am_ok;
+}
+
+static ERL_NIF_TERM read_link_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
+ posix_errno_t posix_errno;
+
+ efile_path_t path;
+ ERL_NIF_TERM result;
+
+ if(argc != 1) {
+ return enif_make_badarg(env);
+ }
+
+ if((posix_errno = efile_marshal_path(env, argv[0], &path))) {
+ return posix_error_to_tuple(env, posix_errno);
+ } else if((posix_errno = efile_read_link(env, &path, &result))) {
+ return posix_error_to_tuple(env, posix_errno);
+ }
+
+ return enif_make_tuple2(env, am_ok, result);
+}
+
+static ERL_NIF_TERM list_dir_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
+ posix_errno_t posix_errno;
+
+ efile_path_t path;
+ ERL_NIF_TERM result;
+
+ if(argc != 1) {
+ return enif_make_badarg(env);
+ }
+
+ if((posix_errno = efile_marshal_path(env, argv[0], &path))) {
+ return posix_error_to_tuple(env, posix_errno);
+ } else if((posix_errno = efile_list_dir(env, &path, &result))) {
+ return posix_error_to_tuple(env, posix_errno);
+ }
+
+ return enif_make_tuple2(env, am_ok, result);
+}
+
+static ERL_NIF_TERM rename_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
+ posix_errno_t posix_errno;
+
+ efile_path_t existing_path, new_path;
+
+ if(argc != 2) {
+ return enif_make_badarg(env);
+ }
+
+ if((posix_errno = efile_marshal_path(env, argv[0], &existing_path))) {
+ return posix_error_to_tuple(env, posix_errno);
+ } else if((posix_errno = efile_marshal_path(env, argv[1], &new_path))) {
+ return posix_error_to_tuple(env, posix_errno);
+ } else if((posix_errno = efile_rename(&existing_path, &new_path))) {
+ return posix_error_to_tuple(env, posix_errno);
+ }
+
+ return am_ok;
+}
+
+static ERL_NIF_TERM make_hard_link_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
+ posix_errno_t posix_errno;
+
+ efile_path_t existing_path, new_path;
+
+ if(argc != 2) {
+ return enif_make_badarg(env);
+ }
+
+ if((posix_errno = efile_marshal_path(env, argv[0], &existing_path))) {
+ return posix_error_to_tuple(env, posix_errno);
+ } else if((posix_errno = efile_marshal_path(env, argv[1], &new_path))) {
+ return posix_error_to_tuple(env, posix_errno);
+ } else if((posix_errno = efile_make_hard_link(&existing_path, &new_path))) {
+ return posix_error_to_tuple(env, posix_errno);
+ }
+
+ return am_ok;
+}
+
+static ERL_NIF_TERM make_soft_link_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
+ posix_errno_t posix_errno;
+
+ efile_path_t existing_path, new_path;
+
+ if(argc != 2) {
+ return enif_make_badarg(env);
+ }
+
+ if((posix_errno = efile_marshal_path(env, argv[0], &existing_path))) {
+ return posix_error_to_tuple(env, posix_errno);
+ } else if((posix_errno = efile_marshal_path(env, argv[1], &new_path))) {
+ return posix_error_to_tuple(env, posix_errno);
+ } else if((posix_errno = efile_make_soft_link(&existing_path, &new_path))) {
+ return posix_error_to_tuple(env, posix_errno);
+ }
+
+ return am_ok;
+}
+
+static ERL_NIF_TERM make_dir_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
+ posix_errno_t posix_errno;
+
+ efile_path_t path;
+
+ if(argc != 1) {
+ return enif_make_badarg(env);
+ }
+
+ if((posix_errno = efile_marshal_path(env, argv[0], &path))) {
+ return posix_error_to_tuple(env, posix_errno);
+ } else if((posix_errno = efile_make_dir(&path))) {
+ return posix_error_to_tuple(env, posix_errno);
+ }
+
+ return am_ok;
+}
+
+static ERL_NIF_TERM del_file_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
+ posix_errno_t posix_errno;
+
+ efile_path_t path;
+
+ if(argc != 1) {
+ return enif_make_badarg(env);
+ }
+
+ if((posix_errno = efile_marshal_path(env, argv[0], &path))) {
+ return posix_error_to_tuple(env, posix_errno);
+ } else if((posix_errno = efile_del_file(&path))) {
+ return posix_error_to_tuple(env, posix_errno);
+ }
+
+ return am_ok;
+}
+
+static ERL_NIF_TERM del_dir_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
+ posix_errno_t posix_errno;
+
+ efile_path_t path;
+
+ if(argc != 1) {
+ return enif_make_badarg(env);
+ }
+
+ if((posix_errno = efile_marshal_path(env, argv[0], &path))) {
+ return posix_error_to_tuple(env, posix_errno);
+ } else if((posix_errno = efile_del_dir(&path))) {
+ return posix_error_to_tuple(env, posix_errno);
+ }
+
+ return am_ok;
+}
+
+static ERL_NIF_TERM get_device_cwd_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
+ posix_errno_t posix_errno;
+
+ ERL_NIF_TERM result;
+ int device_index;
+
+ if(argc != 1 || !enif_get_int(env, argv[0], &device_index)) {
+ return enif_make_badarg(env);
+ }
+
+ if((posix_errno = efile_get_device_cwd(env, device_index, &result))) {
+ return posix_error_to_tuple(env, posix_errno);
+ }
+
+ return enif_make_tuple2(env, am_ok, result);
+}
+
+static ERL_NIF_TERM get_cwd_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
+ posix_errno_t posix_errno;
+ ERL_NIF_TERM result;
+
+ if(argc != 0) {
+ return enif_make_badarg(env);
+ }
+
+ if((posix_errno = efile_get_cwd(env, &result))) {
+ return posix_error_to_tuple(env, posix_errno);
+ }
+
+ return enif_make_tuple2(env, am_ok, result);
+}
+
+static ERL_NIF_TERM set_cwd_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
+ posix_errno_t posix_errno;
+
+ efile_path_t path;
+
+ if(argc != 1) {
+ return enif_make_badarg(env);
+ }
+
+ if((posix_errno = efile_marshal_path(env, argv[0], &path))) {
+ return posix_error_to_tuple(env, posix_errno);
+ } else if((posix_errno = efile_set_cwd(&path))) {
+ return posix_error_to_tuple(env, posix_errno);
+ }
+
+ return am_ok;
+}
+
+/** @brief Reads an entire file into \c result, stopping after \c size bytes or
+ * EOF. It will read until EOF if size is 0. */
+static posix_errno_t read_file(efile_data_t *d, size_t size, ErlNifBinary *result) {
+ size_t initial_buffer_size;
+ ssize_t bytes_read;
+
+ if(size == 0) {
+ initial_buffer_size = 16 << 10;
+ } else {
+ initial_buffer_size = size;
+ }
+
+ if(!enif_alloc_binary(initial_buffer_size, result)) {
+ return ENOMEM;
+ }
+
+ bytes_read = 0;
+
+ for(;;) {
+ ssize_t block_bytes_read;
+ SysIOVec read_vec[1];
+
+ read_vec[0].iov_base = result->data + bytes_read;
+ read_vec[0].iov_len = result->size - bytes_read;
+
+ block_bytes_read = efile_readv(d, read_vec, 1);
+
+ if(block_bytes_read < 0) {
+ enif_release_binary(result);
+ return d->posix_errno;
+ }
+
+ bytes_read += block_bytes_read;
+
+ if(block_bytes_read < (result->size - bytes_read)) {
+ /* EOF */
+ break;
+ } else if(bytes_read == size) {
+ break;
+ }
+
+ if(!enif_realloc_binary(result, bytes_read * 2)) {
+ enif_release_binary(result);
+ return ENOMEM;
+ }
+ }
+
+ /* The file may have shrunk since we queried its size, so we have to do
+ * this even when the size is known. */
+ if(bytes_read < result->size && !enif_realloc_binary(result, bytes_read)) {
+ ERTS_INTERNAL_ERROR("Failed to shrink read_file result.");
+ }
+
+ return 0;
+}
+
+static ERL_NIF_TERM read_file_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
+ posix_errno_t posix_errno;
+
+ efile_fileinfo_t info = {0};
+ efile_path_t path;
+ efile_data_t *d;
+
+ ErlNifBinary result;
+
+ if(argc != 1) {
+ return enif_make_badarg(env);
+ }
+
+ if((posix_errno = efile_marshal_path(env, argv[0], &path))) {
+ return posix_error_to_tuple(env, posix_errno);
+ } else if((posix_errno = efile_read_info(&path, 1, &info))) {
+ return posix_error_to_tuple(env, posix_errno);
+ } else if((posix_errno = efile_open(&path, EFILE_MODE_READ, efile_resource_type, &d))) {
+ return posix_error_to_tuple(env, posix_errno);
+ }
+
+ posix_errno = read_file(d, info.size, &result);
+ enif_release_resource(d);
+
+ if(posix_errno) {
+ return posix_error_to_tuple(env, posix_errno);
+ }
+
+ return enif_make_tuple2(env, am_ok, enif_make_binary(env, &result));
+}
+
+static ERL_NIF_TERM altname_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
+ posix_errno_t posix_errno;
+
+ efile_path_t path;
+ ERL_NIF_TERM result;
+
+ if(argc != 1) {
+ return enif_make_badarg(env);
+ }
+
+ if((posix_errno = efile_marshal_path(env, argv[0], &path))) {
+ return posix_error_to_tuple(env, posix_errno);
+ } else if((posix_errno = efile_altname(env, &path, &result))) {
+ return posix_error_to_tuple(env, posix_errno);
+ }
+
+ return enif_make_tuple2(env, am_ok, result);
+}
diff --git a/erts/emulator/nifs/common/prim_file_nif.h b/erts/emulator/nifs/common/prim_file_nif.h
new file mode 100644
index 0000000000..4194cdc7d9
--- /dev/null
+++ b/erts/emulator/nifs/common/prim_file_nif.h
@@ -0,0 +1,240 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson 2017. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * %CopyrightEnd%
+ */
+
+typedef int posix_errno_t;
+
+enum efile_modes_t {
+ EFILE_MODE_READ = (1 << 0),
+ EFILE_MODE_WRITE = (1 << 1), /* Implies truncating file when used alone. */
+ EFILE_MODE_APPEND = (1 << 2),
+ EFILE_MODE_EXCLUSIVE = (1 << 3),
+ EFILE_MODE_SYNC = (1 << 4),
+
+ 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_READ_WRITE = EFILE_MODE_READ | EFILE_MODE_WRITE
+};
+
+enum efile_access_t {
+ EFILE_ACCESS_NONE = 0,
+ EFILE_ACCESS_READ = 1,
+ EFILE_ACCESS_WRITE = 2,
+ EFILE_ACCESS_READ_WRITE = EFILE_ACCESS_READ | EFILE_ACCESS_WRITE
+};
+
+enum efile_seek_t {
+ EFILE_SEEK_BOF,
+ EFILE_SEEK_CUR,
+ EFILE_SEEK_EOF
+};
+
+enum efile_filetype_t {
+ EFILE_FILETYPE_DEVICE,
+ EFILE_FILETYPE_DIRECTORY,
+ EFILE_FILETYPE_REGULAR,
+ EFILE_FILETYPE_SYMLINK,
+ EFILE_FILETYPE_OTHER
+};
+
+enum efile_advise_t {
+ EFILE_ADVISE_NORMAL,
+ EFILE_ADVISE_RANDOM,
+ EFILE_ADVISE_SEQUENTIAL,
+ EFILE_ADVISE_WILL_NEED,
+ EFILE_ADVISE_DONT_NEED,
+ EFILE_ADVISE_NO_REUSE
+};
+
+enum efile_state_t {
+ EFILE_STATE_IDLE = 0,
+ EFILE_STATE_BUSY = 1,
+ EFILE_STATE_CLOSE_PENDING = 2,
+ EFILE_STATE_CLOSED = 3
+};
+
+typedef struct {
+ Sint64 size; /* Size of file */
+ Uint32 type; /* Type of file -- one of EFILE_FILETYPE_*. */
+ Uint32 access; /* Access to file -- one of EFILE_ACCESS_*. */
+ Uint32 mode; /* Access permissions -- bit field. */
+ Uint32 links; /* Number of links to file. */
+ Uint32 major_device; /* Major device or file system. */
+ Uint32 minor_device; /* Minor device (for devices). */
+ Uint32 inode; /* Inode number. */
+ Uint32 uid; /* User id of owner. */
+ Uint32 gid; /* Group id of owner. */
+ Sint64 a_time; /* Last time the file was accessed. */
+ Sint64 m_time; /* Last time the file was modified. */
+ Sint64 c_time; /* Windows: creation time, Unix: last inode
+ * change. */
+} efile_fileinfo_t;
+
+/* The smallest value that can be converted freely between universal, local,
+ * and POSIX time, as required by read_file_info/2. Corresponds to
+ * {{1902,1,1},{0,0,0}} */
+#define EFILE_MIN_FILETIME -2145916800
+
+/* Initializes an efile_data_t; must be used in efile_open on success. */
+#define EFILE_INIT_RESOURCE(__d, __modes) do { \
+ erts_atomic32_init_acqb(&(__d)->state, EFILE_STATE_IDLE); \
+ (__d)->posix_errno = 0; \
+ (__d)->modes = __modes; \
+ } while(0)
+
+typedef struct {
+ erts_atomic32_t state;
+
+ posix_errno_t posix_errno;
+ enum efile_modes_t modes;
+
+ ErlNifMonitor monitor;
+} efile_data_t;
+
+typedef ErlNifBinary efile_path_t;
+
+/* @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.
+ *
+ * This may change the structure of the path and its results should never be
+ * passed on to the user. Refer to the OS-specific implementation for details.
+ *
+ * @param path The term to translate; it must have been encoded with
+ * 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.
+ *
+ * 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.
+ *
+ * @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.
+ *
+ * @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. */
+Sint64 efile_preadv(efile_data_t *d, Sint64 offset, SysIOVec *iov, int iovlen);
+
+/* @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);
+
+int efile_sync(efile_data_t *d, int data_only);
+
+int efile_advise(efile_data_t *d, Sint64 offset, Sint64 length, enum efile_advise_t advise);
+int efile_allocate(efile_data_t *d, Sint64 offset, Sint64 length);
+int efile_truncate(efile_data_t *d);
+
+posix_errno_t efile_open(const efile_path_t *path, enum efile_modes_t modes,
+ ErlNifResourceType *nif_type, efile_data_t **d);
+
+/** @brief Closes a file. The file must have entered the CLOSED state prior to
+ * calling this to prevent double close. */
+int efile_close(efile_data_t *d);
+
+/* **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** */
+
+posix_errno_t efile_read_info(const efile_path_t *path, int follow_link, efile_fileinfo_t *result);
+
+/** @brief Sets the file times to the given values. Refer to efile_fileinfo_t
+ * for a description of each. */
+posix_errno_t efile_set_time(const efile_path_t *path, Sint64 a_time, Sint64 m_time, Sint64 c_time);
+
+/** @brief On Unix, this sets the file permissions according to the docs for
+ * file:write_file_info/2. On Windows it uses the "owner write permission" flag
+ * to toggle whether the file is read-only or not. */
+posix_errno_t efile_set_permissions(const efile_path_t *path, Uint32 permissions);
+
+/** @brief On Unix, this will set the owner/group to the given values. It will
+ * do nothing on other platforms. */
+posix_errno_t efile_set_owner(const efile_path_t *path, Sint32 owner, Sint32 group);
+
+/** @brief Resolves the final path of the given link. */
+posix_errno_t efile_read_link(ErlNifEnv *env, const efile_path_t *path, ERL_NIF_TERM *result);
+
+/** @brief Lists the contents of the given directory.
+ * @param result [out] A list of all the directory/file names contained in the
+ * given directory. */
+posix_errno_t efile_list_dir(ErlNifEnv *env, const efile_path_t *path, ERL_NIF_TERM *result);
+
+/** @brief Changes the name of an existing file or directory, from old_path
+ * to new_path.
+ *
+ * If old_path and new_path refer to the same file or directory, it does
+ * nothing and returns success. Otherwise if new_path already exists, it will
+ * be deleted and replaced by src subject to the following conditions:
+ *
+ * If old_path is a directory, new_path may be an empty directory.
+ * If old_path is a file, new_path may be a file.
+ *
+ * Neither of these are guaranteed to be atomic. In any other situation where
+ * new_path already exists, the rename will fail.
+ *
+ * Some possible error codes:
+ *
+ * - EACCES: Either paths or one of their parent directories can't be read
+ * and/or written.
+ * - EEXIST: new_path is a non-empty directory.
+ * - EINVAL: old_path is a root directory or new_path is a subdirectory
+ * of new_path.
+ * - EISDIR: new_path is a directory, but old_path is not.
+ * - ENOTDIR: old_path is a directory, but new_path is not.
+ * - ENOENT: old_path doesn't exist, or either path is "".
+ * - EXDEV: The paths are on different filesystems.
+ *
+ * The implementation of rename may allow cross-filesystem renames,
+ * but the caller should be prepared to emulate it with copy and
+ * delete if errno is EXDEV. */
+posix_errno_t efile_rename(const efile_path_t *old_path, const efile_path_t *new_path);
+
+posix_errno_t efile_make_hard_link(const efile_path_t *existing_path, const efile_path_t *new_path);
+posix_errno_t efile_make_soft_link(const efile_path_t *existing_path, const efile_path_t *new_path);
+posix_errno_t efile_make_dir(const efile_path_t *path);
+
+posix_errno_t efile_del_file(const efile_path_t *path);
+posix_errno_t efile_del_dir(const efile_path_t *path);
+
+posix_errno_t efile_get_cwd(ErlNifEnv *env, ERL_NIF_TERM *result);
+posix_errno_t efile_set_cwd(const efile_path_t *path);
+
+/** @brief A Windows-specific function for returning the working directory of a
+ * given device.
+ *
+ * @param device_index The drive index; 1 for A, 2 for B, etc.
+ * @param result [out] The working directory of the given device
+ */
+posix_errno_t efile_get_device_cwd(ErlNifEnv *env, int device_index, ERL_NIF_TERM *result);
+
+/** @brief A Windows-specific function for returning the 8.3-name of a given
+ * file or directory. */
+posix_errno_t efile_altname(ErlNifEnv *env, const efile_path_t *path, ERL_NIF_TERM *result);
diff --git a/erts/emulator/nifs/common/zlib_nif.c b/erts/emulator/nifs/common/zlib_nif.c
index a1a65e1946..b709ed5a6f 100644
--- a/erts/emulator/nifs/common/zlib_nif.c
+++ b/erts/emulator/nifs/common/zlib_nif.c
@@ -69,11 +69,27 @@ typedef enum {
ST_DEFLATE = 1,
ST_INFLATE = 2,
ST_CLOSED = 3
-} zlib_state;
+} zlib_state_t;
+
+/* Controls what to do when the user attempts to decompress more data after
+ * Z_STREAM_END has been returned:
+ *
+ * - 'cut' wipes all further input and returns empty results until reset by
+ * the user. This is the default behavior, matching that of the old driver.
+ * - 'reset' resets the state without discarding any input, making it possible
+ * to decompress blindly concatenated streams.
+ * - 'error' crashes with a data error. */
+typedef enum {
+ EOS_BEHAVIOR_ERROR = 0,
+ EOS_BEHAVIOR_RESET = 1,
+ EOS_BEHAVIOR_CUT = 2
+} zlib_eos_behavior_t;
typedef struct {
z_stream s;
- zlib_state state;
+ zlib_state_t state;
+
+ zlib_eos_behavior_t eos_behavior;
/* These refer to the plaintext CRC, and are only needed for zlib:crc32/1
* which is deprecated. */
@@ -90,6 +106,7 @@ typedef struct {
int inflateChunk_buffer_size;
ErlNifPid controlling_process;
+ ErlNifMutex *controller_lock;
ErlNifIOQueue *input_queue;
@@ -101,8 +118,9 @@ typedef struct {
static ERL_NIF_TERM zlib_open(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]);
static ERL_NIF_TERM zlib_close(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]);
+static ERL_NIF_TERM zlib_set_controller(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]);
+
static ERL_NIF_TERM zlib_deflateInit(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]);
-static ERL_NIF_TERM zlib_deflateInit2(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]);
static ERL_NIF_TERM zlib_deflateSetDictionary(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]);
static ERL_NIF_TERM zlib_deflateReset(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]);
static ERL_NIF_TERM zlib_deflateEnd(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]);
@@ -110,7 +128,6 @@ static ERL_NIF_TERM zlib_deflateParams(ErlNifEnv *env, int argc, const ERL_NIF_T
static ERL_NIF_TERM zlib_deflate(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]);
static ERL_NIF_TERM zlib_inflateInit(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]);
-static ERL_NIF_TERM zlib_inflateInit2(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]);
static ERL_NIF_TERM zlib_inflateSetDictionary(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]);
static ERL_NIF_TERM zlib_inflateGetDictionary(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]);
static ERL_NIF_TERM zlib_inflateReset(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]);
@@ -129,9 +146,13 @@ static ERL_NIF_TERM zlib_setBufSize(ErlNifEnv *env, int argc, const ERL_NIF_TERM
static ERL_NIF_TERM zlib_enqueue_input(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]);
static ErlNifFunc nif_funcs[] = {
+ {"close_nif", 1, zlib_close},
+ {"open_nif", 0, zlib_open},
+
+ {"set_controller_nif", 2, zlib_set_controller},
+
/* deflate */
- {"deflateInit_nif", 2, zlib_deflateInit},
- {"deflateInit_nif", 6, zlib_deflateInit2},
+ {"deflateInit_nif", 6, zlib_deflateInit},
{"deflateSetDictionary_nif", 2, zlib_deflateSetDictionary},
{"deflateReset_nif", 1, zlib_deflateReset},
{"deflateEnd_nif", 1, zlib_deflateEnd},
@@ -139,8 +160,7 @@ static ErlNifFunc nif_funcs[] = {
{"deflate_nif", 4, zlib_deflate},
/* inflate */
- {"inflateInit_nif", 1, zlib_inflateInit},
- {"inflateInit_nif", 2, zlib_inflateInit2},
+ {"inflateInit_nif", 3, zlib_inflateInit},
{"inflateSetDictionary_nif", 2, zlib_inflateSetDictionary},
{"inflateGetDictionary_nif", 1, zlib_inflateGetDictionary},
{"inflateReset_nif", 1, zlib_inflateReset},
@@ -150,10 +170,6 @@ static ErlNifFunc nif_funcs[] = {
/* running checksum */
{"crc32_nif", 1, zlib_crc32},
- /* open & close */
- {"close_nif", 1, zlib_close},
- {"open_nif", 0, zlib_open},
-
/* The stash keeps a single term alive across calls, and is used in
* exception_on_need_dict/1 to retain the old error behavior, and for
* saving data flushed through deflateParams/3. */
@@ -269,9 +285,7 @@ static ERL_NIF_TERM zlib_return(ErlNifEnv *env, int code) {
return reason;
}
-static void gc_zlib(ErlNifEnv *env, void* data) {
- zlib_data_t *d = (zlib_data_t*)data;
-
+static void zlib_internal_close(zlib_data_t *d) {
if(d->state == ST_DEFLATE) {
deflateEnd(&d->s);
} else if(d->state == ST_INFLATE) {
@@ -279,8 +293,6 @@ static void gc_zlib(ErlNifEnv *env, void* data) {
}
if(d->state != ST_CLOSED) {
- enif_ioq_destroy(d->input_queue);
-
if(d->stash_env != NULL) {
enif_free_env(d->stash_env);
}
@@ -289,17 +301,36 @@ static void gc_zlib(ErlNifEnv *env, void* data) {
}
}
+static void gc_zlib(ErlNifEnv *env, void* data) {
+ zlib_data_t *d = (zlib_data_t*)data;
+
+ enif_mutex_destroy(d->controller_lock);
+ enif_ioq_destroy(d->input_queue);
+
+ zlib_internal_close(d);
+
+ (void)env;
+}
+
static int get_zlib_data(ErlNifEnv *env, ERL_NIF_TERM opaque, zlib_data_t **d) {
return enif_get_resource(env, opaque, rtype_zlib, (void **)d);
}
static int zlib_process_check(ErlNifEnv *env, zlib_data_t *d) {
+ int is_controlling_process;
ErlNifPid current_process;
enif_self(env, &current_process);
- return enif_is_identical(enif_make_pid(env, &current_process),
+ enif_mutex_lock(d->controller_lock);
+
+ is_controlling_process = enif_is_identical(
+ enif_make_pid(env, &current_process),
enif_make_pid(env, &d->controlling_process));
+
+ enif_mutex_unlock(d->controller_lock);
+
+ return is_controlling_process;
}
static void zlib_reset_input(zlib_data_t *d) {
@@ -504,14 +535,18 @@ static ERL_NIF_TERM zlib_open(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[
d->input_queue = enif_ioq_create(ERL_NIF_IOQ_NORMAL);
+ d->controller_lock = enif_mutex_create("zlib_controller_lock");
+
d->s.zalloc = zlib_alloc;
d->s.zfree = zlib_free;
d->s.opaque = d;
d->s.data_type = Z_BINARY;
- d->state = ST_NONE;
+ d->eos_behavior = EOS_BEHAVIOR_CUT;
d->eos_seen = 0;
+ d->state = ST_NONE;
+
d->want_output_crc = 0;
d->want_input_crc = 0;
d->is_raw_stream = 0;
@@ -542,50 +577,35 @@ static ERL_NIF_TERM zlib_close(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv
return enif_raise_exception(env, am_not_initialized);
}
- gc_zlib(env, d);
+ zlib_internal_close(d);
return am_ok;
}
-/* deflate */
-
-static ERL_NIF_TERM zlib_deflateInit(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
+static ERL_NIF_TERM zlib_set_controller(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
zlib_data_t *d;
- int level, res;
- if(argc != 2 || !get_zlib_data(env, argv[0], &d) ||
- !enif_get_int(env, argv[1], &level)) {
+ ErlNifPid new_owner;
+
+ if(argc != 2 || !get_zlib_data(env, argv[0], &d)
+ || !enif_get_local_pid(env, argv[1], &new_owner)) {
return enif_make_badarg(env);
} else if(!zlib_process_check(env, d)) {
return enif_raise_exception(env, am_not_on_controlling_process);
- } else if(d->state != ST_NONE) {
- return enif_raise_exception(env, am_already_initialized);
}
- res = deflateInit(&d->s, level);
+ enif_mutex_lock(d->controller_lock);
- if(res == Z_OK) {
- d->state = ST_DEFLATE;
- d->eos_seen = 0;
+ d->controlling_process = new_owner;
- /* FIXME: crc32/1 is documented as returning "the current calculated
- * checksum," but failed to mention that the old implementation only
- * calculated it when WindowBits < 0 (See zlib_deflateInit2).
- *
- * We could fix this behavior by setting d->want_input_crc to 1 here,
- * but we've decided to retain this quirk since the performance hit is
- * quite significant. */
- d->want_output_crc = 0;
- d->want_input_crc = 0;
-
- d->output_crc = crc32(0L, Z_NULL, 0);
- d->input_crc = crc32(0L, Z_NULL, 0);
- }
+ enif_mutex_unlock(d->controller_lock);
- return zlib_return(env, res);
+ return am_ok;
}
-static ERL_NIF_TERM zlib_deflateInit2(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
+/* deflate */
+
+static ERL_NIF_TERM zlib_deflateInit(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
zlib_data_t *d;
int level, method, windowBits, memLevel, strategy, res;
@@ -697,7 +717,9 @@ static ERL_NIF_TERM zlib_deflateEnd(ErlNifEnv *env, int argc, const ERL_NIF_TERM
static ERL_NIF_TERM zlib_deflateParams(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
zlib_data_t *d;
+
int res, level, strategy;
+ Bytef dummy_buffer;
if(argc != 3 || !get_zlib_data(env, argv[0], &d)
|| !enif_get_int(env, argv[1], &level)
@@ -709,12 +731,27 @@ static ERL_NIF_TERM zlib_deflateParams(ErlNifEnv *env, int argc, const ERL_NIF_T
return enif_raise_exception(env, am_not_initialized);
}
- /* deflateParams will flush everything currently in the stream, corrupting
- * the heap unless it's empty. We therefore pretend to have a full output
- * buffer, forcing a Z_BUF_ERROR if there's anything left to be flushed. */
- d->s.avail_out = 0;
+ /* This is a bit of a hack; deflateParams flushes with Z_BLOCK which won't
+ * stop at a byte boundary, so we can't split this operation up, and we
+ * can't allocate a buffer large enough to fit it in one go since we have
+ * to support zlib versions that lack deflatePending.
+ *
+ * We therefore flush everything prior to this call to ensure that we are
+ * stopped on a byte boundary and have no pending data. We then hand it a
+ * dummy buffer to detect when this assumption doesn't hold (Hopefully
+ * never), and to smooth over an issue with zlib 1.2.11 which always
+ * returns Z_BUF_ERROR when d->s.avail_out is 0, regardless of whether
+ * there's any pending data or not. */
+
+ d->s.next_out = &dummy_buffer;
+ d->s.avail_out = 1;
+
res = deflateParams(&d->s, level, strategy);
+ if(d->s.avail_out == 0) {
+ return zlib_return(env, Z_STREAM_ERROR);
+ }
+
return zlib_return(env, res);
}
@@ -741,39 +778,12 @@ static ERL_NIF_TERM zlib_deflate(ErlNifEnv *env, int argc, const ERL_NIF_TERM ar
static ERL_NIF_TERM zlib_inflateInit(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
zlib_data_t *d;
- int res;
-
- if(argc != 1 || !get_zlib_data(env, argv[0], &d)) {
- return enif_make_badarg(env);
- } else if(!zlib_process_check(env, d)) {
- return enif_raise_exception(env, am_not_on_controlling_process);
- } else if(d->state != ST_NONE) {
- return enif_raise_exception(env, am_already_initialized);
- }
- res = inflateInit(&d->s);
+ int windowBits, eosBehavior, res;
- if(res == Z_OK) {
- d->state = ST_INFLATE;
- d->eos_seen = 0;
-
- d->want_output_crc = 0;
- d->want_input_crc = 0;
- d->is_raw_stream = 0;
-
- d->output_crc = crc32(0L, Z_NULL, 0);
- d->input_crc = crc32(0L, Z_NULL, 0);
- }
-
- return zlib_return(env, res);
-}
-
-static ERL_NIF_TERM zlib_inflateInit2(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
- zlib_data_t *d;
- int windowBits, res;
-
- if(argc != 2 || !get_zlib_data(env, argv[0], &d)
- || !enif_get_int(env, argv[1], &windowBits)) {
+ if(argc != 3 || !get_zlib_data(env, argv[0], &d)
+ || !enif_get_int(env, argv[1], &windowBits)
+ || !enif_get_int(env, argv[2], &eosBehavior)) {
return enif_make_badarg(env);
} else if(!zlib_process_check(env, d)) {
return enif_raise_exception(env, am_not_on_controlling_process);
@@ -785,6 +795,8 @@ static ERL_NIF_TERM zlib_inflateInit2(ErlNifEnv *env, int argc, const ERL_NIF_TE
if(res == Z_OK) {
d->state = ST_INFLATE;
+
+ d->eos_behavior = eosBehavior;
d->eos_seen = 0;
d->is_raw_stream = (windowBits < 0);
@@ -934,6 +946,27 @@ static ERL_NIF_TERM zlib_inflate(ErlNifEnv *env, int argc, const ERL_NIF_TERM ar
return enif_raise_exception(env, am_not_initialized);
}
+ if(d->eos_seen && enif_ioq_size(d->input_queue) > 0) {
+ int res;
+
+ switch(d->eos_behavior) {
+ case EOS_BEHAVIOR_ERROR:
+ return zlib_return(env, Z_DATA_ERROR);
+ case EOS_BEHAVIOR_RESET:
+ res = inflateReset(&d->s);
+
+ if(res != Z_OK) {
+ return zlib_return(env, res);
+ }
+
+ d->eos_seen = 0;
+
+ break;
+ case EOS_BEHAVIOR_CUT:
+ zlib_reset_input(d);
+ }
+ }
+
return zlib_codec(&inflate, env, d, input_chunk_size, output_chunk_size, flush);
}
diff --git a/erts/emulator/nifs/unix/unix_prim_file.c b/erts/emulator/nifs/unix/unix_prim_file.c
new file mode 100644
index 0000000000..1637f9cb71
--- /dev/null
+++ b/erts/emulator/nifs/unix/unix_prim_file.c
@@ -0,0 +1,944 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson 2017. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * %CopyrightEnd%
+ */
+
+#include "erl_nif.h"
+#include "config.h"
+#include "sys.h"
+
+#ifdef VALGRIND
+# include <valgrind/memcheck.h>
+#endif
+
+#include "prim_file_nif.h"
+
+#if defined(__APPLE__) && defined(__MACH__) && !defined(__DARWIN__)
+#define __DARWIN__ 1
+#endif
+
+#if defined(__DARWIN__) || defined(HAVE_LINUX_FALLOC_H) || defined(HAVE_POSIX_FALLOCATE)
+#include <fcntl.h>
+#endif
+
+#ifdef HAVE_LINUX_FALLOC_H
+#include <linux/falloc.h>
+#endif
+
+#include <utime.h>
+
+/* Macros for testing file types. */
+#ifdef NO_UMASK
+#define FILE_MODE 0644
+#define DIR_MODE 0755
+#else
+#define FILE_MODE 0666
+#define DIR_MODE 0777
+#endif
+
+/* Old platforms might not have IOV_MAX defined. */
+#if !defined(IOV_MAX) && defined(UIO_MAXIOV)
+#define IOV_MAX UIO_MAXIOV
+#elif !defined(IOV_MAX)
+#define IOV_MAX 16
+#endif
+
+typedef struct {
+ efile_data_t common;
+ int fd;
+} efile_unix_t;
+
+static int has_invalid_null_termination(const ErlNifBinary *path) {
+ const char *null_pos, *end_pos;
+
+ null_pos = memchr(path->data, '\0', path->size);
+ end_pos = (const char*)&path->data[path->size] - 1;
+
+ if(null_pos == NULL) {
+ return 1;
+ }
+
+ /* prim_file:internal_name2native sometimes feeds us data that is "doubly"
+ * NUL-terminated, so we'll accept any number of trailing NULs so long as
+ * they aren't interrupted by anything else. */
+ while(null_pos < end_pos && (*null_pos) == '\0') {
+ null_pos++;
+ }
+
+ return null_pos != end_pos;
+}
+
+posix_errno_t efile_marshal_path(ErlNifEnv *env, ERL_NIF_TERM path, efile_path_t *result) {
+ if(!enif_inspect_binary(env, path, result)) {
+ return EINVAL;
+ }
+
+ if(has_invalid_null_termination(result)) {
+ return EINVAL;
+ }
+
+ return 0;
+}
+
+ERL_NIF_TERM efile_get_handle(ErlNifEnv *env, efile_data_t *d) {
+ efile_unix_t *u = (efile_unix_t*)d;
+
+ ERL_NIF_TERM result;
+ unsigned char *bits;
+
+ bits = enif_make_new_binary(env, sizeof(u->fd), &result);
+ memcpy(bits, &u->fd, sizeof(u->fd));
+
+ return result;
+}
+
+static int open_file_type_check(const efile_path_t *path, int fd) {
+ struct stat file_info;
+ int error;
+
+#ifndef HAVE_FSTAT
+ error = stat((const char*)path->data, &file_info);
+ (void)fd;
+#else
+ error = fstat(fd, &file_info);
+ (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;
+}
+
+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;
+
+ flags = 0;
+
+ if(modes & EFILE_MODE_READ && !(modes & EFILE_MODE_WRITE)) {
+ flags |= O_RDONLY;
+ } else if(modes & EFILE_MODE_WRITE && !(modes & EFILE_MODE_READ)) {
+ if(!(modes & EFILE_MODE_NO_TRUNCATE)) {
+ flags |= O_TRUNC;
+ }
+
+ flags |= O_WRONLY | O_CREAT;
+ } else if(modes & EFILE_MODE_READ_WRITE) {
+ flags |= O_RDWR | O_CREAT;
+ } else {
+ return EINVAL;
+ }
+
+ if(modes & EFILE_MODE_APPEND) {
+ flags &= ~O_TRUNC;
+ flags |= O_APPEND;
+ }
+
+ if(modes & EFILE_MODE_EXCLUSIVE) {
+ flags |= O_EXCL;
+ }
+
+ if(modes & EFILE_MODE_SYNC) {
+#ifndef O_SYNC
+ return ENOTSUP;
+#else
+ flags |= O_SYNC;
+#endif
+ }
+
+ do {
+ fd = open((const char*)path->data, flags, FILE_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)) {
+ close(fd);
+
+ /* This is blatantly incorrect, but we're documented as returning
+ * this for everything that isn't a file. */
+ return EISDIR;
+ }
+
+ u = (efile_unix_t*)enif_alloc_resource(nif_type, sizeof(efile_unix_t));
+ u->fd = fd;
+
+ EFILE_INIT_RESOURCE(&u->common, modes);
+ (*d) = &u->common;
+
+ return 0;
+ }
+
+ (*d) = NULL;
+ return errno;
+}
+
+int efile_close(efile_data_t *d) {
+ efile_unix_t *u = (efile_unix_t*)d;
+ int fd;
+
+ ASSERT(erts_atomic32_read_nob(&d->state) == EFILE_STATE_CLOSED);
+ ASSERT(u->fd != -1);
+
+ fd = u->fd;
+ u->fd = -1;
+
+ /* close(2) either always closes (*BSD, Linux) or leaves the fd in an
+ * undefined state (POSIX 2008, Solaris), so we must not retry on EINTR. */
+
+ if(close(fd) < 0) {
+ u->common.posix_errno = errno;
+ return 0;
+ }
+
+ return 1;
+}
+
+static void shift_iov(SysIOVec **iov, int *iovlen, ssize_t shift) {
+ SysIOVec *head_vec = (*iov);
+
+ ASSERT(shift >= 0);
+
+ while(shift > 0) {
+ ASSERT(head_vec < &(*iov)[*iovlen]);
+
+ if(shift < head_vec->iov_len) {
+ head_vec->iov_base = (char*)head_vec->iov_base + shift;
+ head_vec->iov_len -= shift;
+ break;
+ } else {
+ shift -= head_vec->iov_len;
+ head_vec++;
+ }
+ }
+
+ (*iovlen) -= head_vec - (*iov);
+ (*iov) = head_vec;
+}
+
+Sint64 efile_readv(efile_data_t *d, SysIOVec *iov, int iovlen) {
+ efile_unix_t *u = (efile_unix_t*)d;
+
+ Sint64 bytes_read;
+ ssize_t result;
+
+ bytes_read = 0;
+
+ do {
+ int use_fallback = 0;
+
+ if(iovlen < 1) {
+ result = 0;
+ break;
+ }
+
+ /* writev(2) implies readv(2) */
+#ifdef HAVE_WRITEV
+ result = readv(u->fd, iov, MIN(IOV_MAX, iovlen));
+
+ /* Fall back to using read(2) if readv(2) reports that the combined
+ * size of iov is greater than SSIZE_T_MAX. */
+ use_fallback = (result < 0 && errno == EINVAL);
+#else
+ use_fallback = 1;
+#endif
+
+ if(use_fallback) {
+ result = read(u->fd, iov->iov_base, iov->iov_len);
+ }
+
+ if(result > 0) {
+ shift_iov(&iov, &iovlen, result);
+ bytes_read += result;
+ }
+ } while(result > 0 || (result < 0 && errno == EINTR));
+
+ u->common.posix_errno = errno;
+
+ if(result == 0 && bytes_read > 0) {
+ return bytes_read;
+ }
+
+ return result;
+}
+
+Sint64 efile_writev(efile_data_t *d, SysIOVec *iov, int iovlen) {
+ efile_unix_t *u = (efile_unix_t*)d;
+
+ Sint64 bytes_written;
+ ssize_t result;
+
+ bytes_written = 0;
+
+ do {
+ int use_fallback = 0;
+
+ if(iovlen < 1) {
+ result = 0;
+ break;
+ }
+
+#ifdef HAVE_WRITEV
+ result = writev(u->fd, iov, MIN(IOV_MAX, iovlen));
+
+ /* Fall back to using write(2) if writev(2) reports that the combined
+ * size of iov is greater than SSIZE_T_MAX. */
+ use_fallback = (result < 0 && errno == EINVAL);
+#else
+ use_fallback = 1;
+#endif
+
+ if(use_fallback) {
+ result = write(u->fd, iov->iov_base, iov->iov_len);
+ }
+
+ if(result > 0) {
+ shift_iov(&iov, &iovlen, result);
+ bytes_written += result;
+ }
+ } while(result > 0 || (result < 0 && errno == EINTR));
+
+ u->common.posix_errno = errno;
+
+ if(result == 0 && bytes_written > 0) {
+ return bytes_written;
+ }
+
+ return result;
+}
+
+Sint64 efile_preadv(efile_data_t *d, Sint64 offset, SysIOVec *iov, int iovlen) {
+ efile_unix_t *u = (efile_unix_t*)d;
+
+ Uint64 bytes_read;
+ Sint64 result;
+
+#if !defined(HAVE_PREADV) && !defined(HAVE_PREAD)
+ /* This function is documented as leaving the file position undefined, but
+ * the old driver always reset it so there's probably code in the wild that
+ * relies on this behavior. */
+ off_t original_position = lseek(u->fd, 0, SEEK_CUR);
+
+ if(original_position < 0 || lseek(u->fd, offset, SEEK_SET) < 0) {
+ u->common.posix_errno = errno;
+ return -1;
+ }
+#endif
+
+ bytes_read = 0;
+
+ do {
+ if(iovlen < 1) {
+ result = 0;
+ break;
+ }
+
+#if defined(HAVE_PREADV)
+ result = preadv(u->fd, iov, MIN(IOV_MAX, iovlen), offset);
+#elif defined(HAVE_PREAD)
+ result = pread(u->fd, iov->iov_base, iov->iov_len, offset);
+#else
+ result = read(u->fd, iov->iov_base, iov->iov_len);
+#endif
+
+ if(result > 0) {
+ shift_iov(&iov, &iovlen, result);
+ bytes_read += result;
+ offset += result;
+ }
+ } while(result > 0 || (result < 0 && errno == EINTR));
+
+ u->common.posix_errno = errno;
+
+#if !defined(HAVE_PREADV) && !defined(HAVE_PREAD)
+ if(result >= 0) {
+ if(lseek(u->fd, original_position, SEEK_SET) < 0) {
+ u->common.posix_errno = errno;
+ return -1;
+ }
+ }
+#endif
+
+ if(result == 0 && bytes_read > 0) {
+ return bytes_read;
+ }
+
+ return result;
+}
+
+Sint64 efile_pwritev(efile_data_t *d, Sint64 offset, SysIOVec *iov, int iovlen) {
+ efile_unix_t *u = (efile_unix_t*)d;
+
+ Sint64 bytes_written;
+ ssize_t result;
+
+#if !defined(HAVE_PWRITEV) && !defined(HAVE_PWRITE)
+ off_t original_position = lseek(u->fd, 0, SEEK_CUR);
+
+ if(original_position < 0 || lseek(u->fd, offset, SEEK_SET) < 0) {
+ u->common.posix_errno = errno;
+ return -1;
+ }
+#endif
+
+ bytes_written = 0;
+
+ do {
+ if(iovlen < 1) {
+ result = 0;
+ break;
+ }
+
+#if defined(HAVE_PWRITEV)
+ result = pwritev(u->fd, iov, MIN(IOV_MAX, iovlen), offset);
+#elif defined(HAVE_PWRITE)
+ result = pwrite(u->fd, iov->iov_base, iov->iov_len, offset);
+#else
+ result = write(u->fd, iov->iov_base, iov->iov_len);
+#endif
+
+ if(result > 0) {
+ shift_iov(&iov, &iovlen, result);
+ bytes_written += result;
+ offset += result;
+ }
+ } while(result > 0 || (result < 0 && errno == EINTR));
+
+ u->common.posix_errno = errno;
+
+#if !defined(HAVE_PWRITEV) && !defined(HAVE_PWRITE)
+ if(result >= 0) {
+ if(lseek(u->fd, original_position, SEEK_SET) < 0) {
+ u->common.posix_errno = errno;
+ return -1;
+ }
+ }
+#endif
+
+ if(result == 0 && bytes_written > 0) {
+ return bytes_written;
+ }
+
+ return result;
+}
+
+int efile_seek(efile_data_t *d, enum efile_seek_t seek, Sint64 offset, Sint64 *new_position) {
+ efile_unix_t *u = (efile_unix_t*)d;
+ off_t result;
+ int whence;
+
+ switch(seek) {
+ case EFILE_SEEK_BOF: whence = SEEK_SET; break;
+ case EFILE_SEEK_CUR: whence = SEEK_CUR; break;
+ case EFILE_SEEK_EOF: whence = SEEK_END; break;
+ default: ERTS_INTERNAL_ERROR("Invalid seek parameter");
+ }
+
+ result = lseek(u->fd, offset, whence);
+
+ /*
+ * The man page for lseek (on SunOs 5) says:
+ *
+ * "if fildes is a remote file descriptor and offset is negative, lseek()
+ * returns the file pointer even if it is negative."
+ */
+ if(result < 0 && errno == 0) {
+ errno = EINVAL;
+ }
+
+ if(result < 0) {
+ u->common.posix_errno = errno;
+ return 0;
+ }
+
+ (*new_position) = result;
+
+ return 1;
+}
+
+int efile_sync(efile_data_t *d, int data_only) {
+ efile_unix_t *u = (efile_unix_t*)d;
+
+#if defined(HAVE_FDATASYNC) && !defined(__DARWIN__)
+ if(data_only) {
+ if(fdatasync(u->fd) < 0) {
+ u->common.posix_errno = errno;
+ return 0;
+ }
+
+ return 1;
+ }
+#endif
+
+#if defined(__DARWIN__) && defined(F_FULLFSYNC)
+ if(fcntl(u->fd, F_FULLFSYNC) < 0) {
+#else
+ if(fsync(u->fd) < 0) {
+#endif
+ u->common.posix_errno = errno;
+ return 0;
+ }
+
+ return 1;
+}
+
+int efile_advise(efile_data_t *d, Sint64 offset, Sint64 length, enum efile_advise_t advise) {
+ efile_unix_t *u = (efile_unix_t*)d;
+#ifdef HAVE_POSIX_FADVISE
+ int p_advise;
+
+ switch(advise) {
+ case EFILE_ADVISE_NORMAL: p_advise = POSIX_FADV_NORMAL; break;
+ case EFILE_ADVISE_RANDOM: p_advise = POSIX_FADV_RANDOM; break;
+ case EFILE_ADVISE_SEQUENTIAL: p_advise = POSIX_FADV_SEQUENTIAL; break;
+ case EFILE_ADVISE_WILL_NEED: p_advise = POSIX_FADV_WILLNEED; break;
+ case EFILE_ADVISE_DONT_NEED: p_advise = POSIX_FADV_DONTNEED; break;
+ case EFILE_ADVISE_NO_REUSE: p_advise = POSIX_FADV_NOREUSE; break;
+ default:
+ u->common.posix_errno = EINVAL;
+ return 0;
+ }
+
+ if(posix_fadvise(u->fd, offset, length, p_advise) < 0) {
+ u->common.posix_errno = errno;
+ return 0;
+ }
+
+ return 1;
+#else
+ /* We'll pretend to support this syscall as it's only a recommendation even
+ * on systems that do support it. */
+ return 1;
+#endif
+}
+
+int efile_allocate(efile_data_t *d, Sint64 offset, Sint64 length) {
+ efile_unix_t *u = (efile_unix_t*)d;
+ int ret = -1;
+
+ /* We prefer OS-specific methods, but fall back to posix_fallocate on
+ * failure. It's unclear whether this has any practical benefit on
+ * modern systems, but the old driver did it. */
+
+#if defined(HAVE_FALLOCATE)
+ /* Linux-specific */
+ do {
+ ret = fallocate(u->fd, FALLOC_FL_KEEP_SIZE, offset, length);
+ } while(ret < 0 && errno == EINTR);
+#elif defined(F_PREALLOCATE)
+ /* Mac-specific */
+ fstore_t fs = {};
+
+ fs.fst_flags = F_ALLOCATECONTIG;
+ fs.fst_posmode = F_VOLPOSMODE;
+ fs.fst_offset = offset;
+ fs.fst_length = length;
+
+ ret = fcntl(u->fd, F_PREALLOCATE, &fs);
+ if(ret < 0) {
+ fs.fst_flags = F_ALLOCATEALL;
+ ret = fcntl(u->fd, F_PREALLOCATE, &fs);
+ }
+#elif !defined(HAVE_POSIX_FALLOCATE)
+ u->common.posix_errno = ENOTSUP;
+ return 0;
+#endif
+
+#ifdef HAVE_POSIX_FALLOCATE
+ if(ret < 0) {
+ do {
+ ret = posix_fallocate(u->fd, offset, length);
+
+ /* On Linux and Solaris for example, posix_fallocate() returns a
+ * positive error number on error and it does not set errno. On
+ * FreeBSD however (9.0 at least), it returns -1 on error and it
+ * sets errno. */
+ if (ret > 0) {
+ errno = ret;
+ ret = -1;
+ }
+ } while(ret < 0 && errno == EINTR);
+ }
+#endif
+
+ if(ret < 0) {
+ u->common.posix_errno = errno;
+ return 0;
+ }
+
+ return 1;
+}
+
+int efile_truncate(efile_data_t *d) {
+ efile_unix_t *u = (efile_unix_t*)d;
+ off_t offset;
+
+ offset = lseek(u->fd, 0, SEEK_CUR);
+
+ if(offset < 0) {
+ u->common.posix_errno = errno;
+ return 0;
+ }
+
+ if(ftruncate(u->fd, offset) < 0) {
+ u->common.posix_errno = errno;
+ return 0;
+ }
+
+ return 1;
+}
+
+posix_errno_t efile_read_info(const efile_path_t *path, int follow_links, efile_fileinfo_t *result) {
+ struct stat data;
+
+ if(follow_links) {
+ if(stat((const char*)path->data, &data) < 0) {
+ return errno;
+ }
+ } else {
+ if(lstat((const char*)path->data, &data) < 0) {
+ return errno;
+ }
+ }
+
+ 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;
+
+#ifndef NO_ACCESS
+ result->access = EFILE_ACCESS_NONE;
+
+ if(access((const char*)path->data, R_OK) == 0) {
+ result->access |= EFILE_ACCESS_READ;
+ }
+ if(access((const char*)path->data, W_OK) == 0) {
+ result->access |= EFILE_ACCESS_WRITE;
+ }
+#else
+ /* Just look at read/write access for owner. */
+ result->access = ((data.st_mode >> 6) & 07) >> 1;
+#endif
+
+ return 0;
+}
+
+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;
+
+ if(chmod((const char*)path->data, new_modes) < 0) {
+ new_modes &= ~(S_ISUID | S_ISGID);
+
+ if (chmod((const char*)path->data, new_modes) < 0) {
+ return errno;
+ }
+ }
+
+ return 0;
+}
+
+posix_errno_t efile_set_owner(const efile_path_t *path, Sint32 owner, Sint32 group) {
+ if(chown((const char*)path->data, owner, group) < 0) {
+ return errno;
+ }
+
+ return 0;
+}
+
+posix_errno_t efile_set_time(const efile_path_t *path, Sint64 a_time, Sint64 m_time, Sint64 c_time) {
+ struct utimbuf tval;
+
+ tval.actime = (time_t)a_time;
+ tval.modtime = (time_t)m_time;
+
+ (void)c_time;
+
+ if(utime((const char*)path->data, &tval) < 0) {
+ return errno;
+ }
+
+ return 0;
+}
+
+posix_errno_t efile_read_link(ErlNifEnv *env, const efile_path_t *path, ERL_NIF_TERM *result) {
+ ErlNifBinary result_bin;
+
+ if(!enif_alloc_binary(256, &result_bin)) {
+ return ENOMEM;
+ }
+
+ for(;;) {
+ ssize_t bytes_copied;
+
+ bytes_copied = readlink((const char*)path->data, (char*)result_bin.data,
+ result_bin.size);
+
+ if(bytes_copied <= 0) {
+ posix_errno_t saved_errno = errno;
+ enif_release_binary(&result_bin);
+ return saved_errno;
+ } else if(bytes_copied < result_bin.size) {
+ if(!enif_realloc_binary(&result_bin, bytes_copied)) {
+ enif_release_binary(&result_bin);
+ return ENOMEM;
+ }
+
+ (*result) = enif_make_binary(env, &result_bin);
+
+ return 0;
+ }
+
+ /* The result didn't fit into the buffer, so we'll try again with a
+ * larger one. */
+
+ if(!enif_realloc_binary(&result_bin, result_bin.size * 2)) {
+ enif_release_binary(&result_bin);
+ return ENOMEM;
+ }
+ }
+}
+
+static int is_ignored_name(int name_length, const char *name) {
+ if(name_length == 1 && name[0] == '.') {
+ return 1;
+ } else if(name_length == 2 && memcmp(name, "..", 2) == 0) {
+ return 1;
+ }
+
+ return 0;
+}
+
+posix_errno_t efile_list_dir(ErlNifEnv *env, const efile_path_t *path, ERL_NIF_TERM *result) {
+ ERL_NIF_TERM list_head;
+
+ struct dirent *dir_entry;
+ DIR *dir_stream;
+
+ dir_stream = opendir((const char*)path->data);
+ if(dir_stream == NULL) {
+ posix_errno_t saved_errno = errno;
+ *result = enif_make_list(env, 0);
+ return saved_errno;
+ }
+
+ list_head = enif_make_list(env, 0);
+ dir_entry = readdir(dir_stream);
+
+ while(dir_entry != NULL) {
+ int name_length = strlen(dir_entry->d_name);
+
+ if(!is_ignored_name(name_length, dir_entry->d_name)) {
+ unsigned char *name_bytes;
+ ERL_NIF_TERM name_term;
+
+ name_bytes = enif_make_new_binary(env, name_length, &name_term);
+ sys_memcpy(name_bytes, dir_entry->d_name, name_length);
+
+ list_head = enif_make_list_cell(env, name_term, list_head);
+ }
+
+ dir_entry = readdir(dir_stream);
+ }
+
+ (*result) = list_head;
+ closedir(dir_stream);
+
+ return 0;
+}
+
+posix_errno_t efile_rename(const efile_path_t *old_path, const efile_path_t *new_path) {
+ if(rename((const char*)old_path->data, (const char*)new_path->data) < 0) {
+ if(errno == ENOTEMPTY) {
+ return EEXIST;
+ }
+
+ if(strcmp((const char*)old_path->data, "/") == 0) {
+ /* Alpha reports renaming / as EBUSY and Linux reports it as EACCES
+ * instead of EINVAL.*/
+ return EINVAL;
+ }
+
+ return errno;
+ }
+
+ return 0;
+}
+
+posix_errno_t efile_make_hard_link(const efile_path_t *existing_path, const efile_path_t *new_path) {
+ if(link((const char*)existing_path->data, (const char*)new_path->data) < 0) {
+ return errno;
+ }
+
+ return 0;
+}
+
+posix_errno_t efile_make_soft_link(const efile_path_t *existing_path, const efile_path_t *new_path) {
+ if(symlink((const char*)existing_path->data, (const char*)new_path->data) < 0) {
+ return errno;
+ }
+
+ return 0;
+}
+
+posix_errno_t efile_make_dir(const efile_path_t *path) {
+#ifdef NO_MKDIR_MODE
+ if(mkdir((const char*)path->data) < 0) {
+#else
+ if(mkdir((const char*)path->data, DIR_MODE) < 0) {
+#endif
+ return errno;
+ }
+
+ return 0;
+}
+
+posix_errno_t efile_del_file(const efile_path_t *path) {
+ if(unlink((const char*)path->data) < 0) {
+ /* Linux sets the wrong error code. */
+ if(errno == EISDIR) {
+ return EPERM;
+ }
+
+ return errno;
+ }
+
+ return 0;
+}
+
+posix_errno_t efile_del_dir(const efile_path_t *path) {
+ if(rmdir((const char*)path->data) < 0) {
+ posix_errno_t saved_errno = errno;
+
+ if(saved_errno == ENOTEMPTY) {
+ saved_errno = EEXIST;
+ }
+
+ /* The error code might be wrong if we're trying to delete the current
+ * directory. */
+ if(saved_errno == EEXIST) {
+ struct stat path_stat, cwd_stat;
+ int has_stat;
+
+ has_stat = (stat((const char*)path->data, &path_stat) == 0);
+ has_stat &= (stat(".", &cwd_stat) == 0);
+
+ if(has_stat && path_stat.st_ino == cwd_stat.st_ino) {
+ if(path_stat.st_dev == cwd_stat.st_dev) {
+ return EINVAL;
+ }
+ }
+ }
+
+ return saved_errno;
+ }
+
+ return 0;
+}
+
+posix_errno_t efile_set_cwd(const efile_path_t *path) {
+ if(chdir((const char*)path->data) < 0) {
+ return errno;
+ }
+
+ return 0;
+}
+
+posix_errno_t efile_get_device_cwd(ErlNifEnv *env, int device_index, ERL_NIF_TERM *result) {
+ (void)device_index;
+ (void)result;
+ (void)env;
+
+ return ENOTSUP;
+}
+
+posix_errno_t efile_get_cwd(ErlNifEnv *env, ERL_NIF_TERM *result) {
+ ErlNifBinary result_bin;
+ size_t bytes_copied;
+
+ if(!enif_alloc_binary(256, &result_bin)) {
+ return ENOMEM;
+ }
+
+ while(getcwd((char*)result_bin.data, result_bin.size) == NULL) {
+ posix_errno_t saved_errno = errno;
+
+ if(saved_errno != ERANGE) {
+ enif_release_binary(&result_bin);
+ return saved_errno;
+ } else {
+ if(!enif_realloc_binary(&result_bin, result_bin.size * 2)) {
+ enif_release_binary(&result_bin);
+ return ENOMEM;
+ }
+ }
+ }
+
+ /* getcwd(2) guarantees null-termination. */
+ bytes_copied = strlen((const char*)result_bin.data);
+
+ if(!enif_realloc_binary(&result_bin, bytes_copied)) {
+ enif_release_binary(&result_bin);
+ return ENOMEM;
+ }
+
+ (*result) = enif_make_binary(env, &result_bin);
+
+ return 0;
+}
+
+posix_errno_t efile_altname(ErlNifEnv *env, const efile_path_t *path, ERL_NIF_TERM *result) {
+ (void)path;
+ (void)result;
+
+ return ENOTSUP;
+}
diff --git a/erts/emulator/nifs/win32/win_prim_file.c b/erts/emulator/nifs/win32/win_prim_file.c
new file mode 100644
index 0000000000..8058350b25
--- /dev/null
+++ b/erts/emulator/nifs/win32/win_prim_file.c
@@ -0,0 +1,1428 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson 2017. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * %CopyrightEnd%
+ */
+
+#include "erl_nif.h"
+#include "config.h"
+#include "sys.h"
+
+#include "prim_file_nif.h"
+
+#include <windows.h>
+#include <strsafe.h>
+#include <wchar.h>
+
+#define IS_SLASH(a) ((a) == L'\\' || (a) == L'/')
+
+#define FILE_SHARE_FLAGS (FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE)
+
+#define LP_PREFIX L"\\\\?\\"
+#define LP_PREFIX_SIZE (sizeof(LP_PREFIX) - sizeof(WCHAR))
+#define LP_PREFIX_LENGTH (LP_PREFIX_SIZE / sizeof(WCHAR))
+
+#define PATH_LENGTH(path) (path->size / sizeof(WCHAR) - 1)
+
+#define ASSERT_PATH_FORMAT(path) \
+ do { \
+ ASSERT(PATH_LENGTH(path) >= 4 && \
+ !memcmp(path->data, LP_PREFIX, LP_PREFIX_SIZE)); \
+ ASSERT(PATH_LENGTH(path) == wcslen((WCHAR*)path->data)); \
+ } while(0)
+
+#define TICKS_PER_SECOND (10000000ULL)
+#define EPOCH_DIFFERENCE (11644473600LL)
+
+#define FILETIME_TO_EPOCH(epoch, ft) \
+ do { \
+ ULARGE_INTEGER ull; \
+ ull.LowPart = (ft).dwLowDateTime; \
+ ull.HighPart = (ft).dwHighDateTime; \
+ (epoch) = ((ull.QuadPart / TICKS_PER_SECOND) - EPOCH_DIFFERENCE); \
+ } while(0)
+
+#define EPOCH_TO_FILETIME(ft, epoch) \
+ do { \
+ ULARGE_INTEGER ull; \
+ ull.QuadPart = (((epoch) + EPOCH_DIFFERENCE) * TICKS_PER_SECOND); \
+ (ft).dwLowDateTime = ull.LowPart; \
+ (ft).dwHighDateTime = ull.HighPart; \
+ } while(0)
+
+typedef struct {
+ efile_data_t common;
+ HANDLE handle;
+} efile_win_t;
+
+static int windows_to_posix_errno(DWORD last_error);
+
+static int has_invalid_null_termination(const ErlNifBinary *path) {
+ const WCHAR *null_pos, *end_pos;
+
+ null_pos = wmemchr((const WCHAR*)path->data, L'\0', path->size);
+ end_pos = (const WCHAR*)&path->data[path->size] - 1;
+
+ if(null_pos == NULL) {
+ return 1;
+ }
+
+ /* prim_file:internal_name2native sometimes feeds us data that is "doubly"
+ * NUL-terminated, so we'll accept any number of trailing NULs so long as
+ * they aren't interrupted by anything else. */
+ while(null_pos < end_pos && (*null_pos) == L'\0') {
+ null_pos++;
+ }
+
+ return null_pos != end_pos;
+}
+
+static posix_errno_t get_full_path(ErlNifEnv *env, WCHAR *input, efile_path_t *result) {
+ DWORD maximum_length, actual_length;
+ int add_long_prefix;
+
+ maximum_length = GetFullPathNameW(input, 0, NULL, NULL);
+ add_long_prefix = 0;
+
+ if(maximum_length == 0) {
+ /* POSIX doesn't have the concept of a "path error" in the same way
+ * Windows does, so we'll return ENOENT since that's what most POSIX
+ * APIs would return if they were fed such garbage. */
+ return ENOENT;
+ }
+
+ maximum_length += LP_PREFIX_LENGTH;
+
+ if(!enif_alloc_binary(maximum_length * sizeof(WCHAR), result)) {
+ return ENOMEM;
+ }
+
+ actual_length = GetFullPathNameW(input, maximum_length, (WCHAR*)result->data, NULL);
+
+ if(actual_length < maximum_length) {
+ int has_long_path_prefix;
+ WCHAR *path_start;
+
+ /* Make sure we have a long-path prefix; GetFullPathNameW only adds one
+ * if the path is relative. */
+ has_long_path_prefix = actual_length >= LP_PREFIX_LENGTH &&
+ !sys_memcmp(result->data, LP_PREFIX, LP_PREFIX_SIZE);
+
+ if(!has_long_path_prefix) {
+ sys_memmove(result->data + LP_PREFIX_SIZE, result->data,
+ (actual_length + 1) * sizeof(WCHAR));
+ sys_memcpy(result->data, LP_PREFIX, LP_PREFIX_SIZE);
+ actual_length += LP_PREFIX_LENGTH;
+ }
+
+ path_start = (WCHAR*)result->data;
+
+ /* We're removing trailing slashes since quite a few APIs refuse to
+ * work with them, and none require them. We only check the last
+ * character since GetFullPathNameW folds slashes together. */
+ if(IS_SLASH(path_start[actual_length - 1])) {
+ if(path_start[actual_length - 2] != L':') {
+ path_start[actual_length - 1] = L'\0';
+ actual_length--;
+ }
+ }
+
+ if(!enif_realloc_binary(result, (actual_length + 1) * sizeof(WCHAR))) {
+ enif_release_binary(result);
+ return ENOMEM;
+ }
+
+ enif_make_binary(env, result);
+ return 0;
+ }
+
+ /* We may end up here if the current directory changes to something longer
+ * between/during GetFullPathName. There's nothing sensible we can do about
+ * this. */
+
+ enif_release_binary(result);
+
+ return EINVAL;
+}
+
+posix_errno_t efile_marshal_path(ErlNifEnv *env, ERL_NIF_TERM path, efile_path_t *result) {
+ ErlNifBinary raw_path;
+
+ if(!enif_inspect_binary(env, path, &raw_path)) {
+ return EINVAL;
+ } else if(raw_path.size % sizeof(WCHAR)) {
+ return EINVAL;
+ }
+
+ if(has_invalid_null_termination(&raw_path)) {
+ return EINVAL;
+ }
+
+ return get_full_path(env, (WCHAR*)raw_path.data, result);
+}
+
+ERL_NIF_TERM efile_get_handle(ErlNifEnv *env, efile_data_t *d) {
+ efile_win_t *w = (efile_win_t*)d;
+
+ ERL_NIF_TERM result;
+ unsigned char *bits;
+
+ bits = enif_make_new_binary(env, sizeof(w->handle), &result);
+ memcpy(bits, &w->handle, sizeof(w->handle));
+
+ return result;
+}
+
+/** @brief Converts a native path to the preferred form in "erlang space,"
+ * without path-prefixes, forward-slashes, or NUL terminators. */
+static int normalize_path_result(ErlNifBinary *path) {
+ WCHAR *path_iterator, *path_start, *path_end;
+ int length;
+
+ path_start = (WCHAR*)path->data;
+ length = wcslen(path_start);
+
+ ASSERT(length < path->size / sizeof(WCHAR));
+
+ /* Get rid of the long-path prefix, if present. */
+ if(length >= LP_PREFIX_LENGTH) {
+ if(!sys_memcmp(path_start, LP_PREFIX, LP_PREFIX_SIZE)) {
+ length -= LP_PREFIX_LENGTH;
+
+ sys_memmove(path_start, &path_start[LP_PREFIX_LENGTH],
+ length * sizeof(WCHAR));
+ }
+ }
+
+ path_end = &path_start[length];
+ path_iterator = path_start;
+
+ /* Convert drive letters to lowercase, if present. */
+ if(length >= 2 && path_start[1] == L':') {
+ WCHAR drive_letter = path_start[0];
+
+ if(drive_letter >= L'A' && drive_letter <= L'Z') {
+ path_start[0] = drive_letter - L'A' + L'a';
+ }
+ }
+
+ while(path_iterator < path_end) {
+ if(*path_iterator == L'\\') {
+ *path_iterator = L'/';
+ }
+
+ path_iterator++;
+ }
+
+ /* Truncate the result to its actual length; we don't want to include the
+ * NUL terminator. */
+ return enif_realloc_binary(path, length * sizeof(WCHAR));
+}
+
+/* @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);
+
+ if(attributes == INVALID_FILE_ATTRIBUTES) {
+ return 0;
+ }
+
+ return !!((attributes & mask) == mask);
+}
+
+static int is_ignored_name(int name_length, const WCHAR *name) {
+ if(name_length == 1 && name[0] == L'.') {
+ return 1;
+ } else if(name_length == 2 && !sys_memcmp(name, L"..", 2 * sizeof(WCHAR))) {
+ return 1;
+ }
+
+ return 0;
+}
+
+static int get_drive_number(const efile_path_t *path) {
+ const WCHAR *path_start;
+ int length;
+
+ ASSERT_PATH_FORMAT(path);
+
+ path_start = (WCHAR*)path->data + LP_PREFIX_LENGTH;
+ length = PATH_LENGTH(path) - LP_PREFIX_LENGTH;
+
+ if(length >= 2 && path_start[1] == L':') {
+ WCHAR drive_letter = path_start[0];
+
+ if(drive_letter >= L'A' && drive_letter <= L'Z') {
+ return drive_letter - L'A' + 1;
+ } else if(drive_letter >= L'a' && drive_letter <= L'z') {
+ return drive_letter - L'a' + 1;
+ }
+ }
+
+ return -1;
+}
+
+/* @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;
+ int result = 0;
+
+ mount_a = enif_alloc(path_a->size);
+ mount_b = enif_alloc(path_b->size);
+
+ if(mount_a != NULL && mount_b != NULL) {
+ int length_a, length_b;
+
+ length_a = PATH_LENGTH(path_a);
+ length_b = PATH_LENGTH(path_b);
+
+ if(GetVolumePathNameW((WCHAR*)path_a->data, mount_a, length_a)) {
+ ASSERT(wcslen(mount_a) <= length_a);
+
+ if(GetVolumePathNameW((WCHAR*)path_b->data, mount_b, length_b)) {
+ ASSERT(wcslen(mount_b) <= length_b);
+
+ result = !_wcsicmp(mount_a, mount_b);
+ }
+ }
+ }
+
+ if(mount_b != NULL) {
+ enif_free(mount_b);
+ }
+
+ if(mount_a != NULL) {
+ enif_free(mount_a);
+ }
+
+ return result;
+}
+
+/* Mirrors the PathIsRootW function of the shell API, but doesn't choke on
+ * paths longer than MAX_PATH. */
+static int is_path_root(const efile_path_t *path) {
+ const WCHAR *path_start, *path_end;
+ int length;
+
+ ASSERT_PATH_FORMAT(path);
+
+ path_start = (WCHAR*)path->data + LP_PREFIX_LENGTH;
+ length = PATH_LENGTH(path) - LP_PREFIX_LENGTH;
+
+ path_end = &path_start[length];
+
+ if(length == 1) {
+ /* A single \ refers to the root of the current working directory. */
+ return IS_SLASH(path_start[0]);
+ } else if(length == 3 && iswalpha(path_start[0]) && path_start[1] == L':') {
+ /* Drive letter. */
+ return IS_SLASH(path_start[2]);
+ } else if(length >= 4) {
+ /* Check whether we're a UNC root, eg. \\server, \\server\share */
+ const WCHAR *path_iterator;
+
+ if(!IS_SLASH(path_start[0]) || !IS_SLASH(path_start[1])) {
+ return 0;
+ }
+
+ path_iterator = path_start + 2;
+
+ /* Slide to the slash between the server and share names, if present. */
+ while(path_iterator < path_end && !IS_SLASH(*path_iterator)) {
+ path_iterator++;
+ }
+
+ /* Slide past the end of the string, stopping at the first slash we
+ * encounter. */
+ do {
+ path_iterator++;
+ } while(path_iterator < path_end && !IS_SLASH(*path_iterator));
+
+ /* If we're past the end of the string and it didnt't end with a slash,
+ * then we're a root path. */
+ return path_iterator >= path_end && !IS_SLASH(path_start[length - 1]);
+ }
+
+ return 0;
+}
+
+posix_errno_t efile_open(const efile_path_t *path, enum efile_modes_t modes,
+ ErlNifResourceType *nif_type, efile_data_t **d) {
+
+ DWORD attributes, access_flags, open_mode;
+ HANDLE handle;
+
+ ASSERT_PATH_FORMAT(path);
+
+ access_flags = 0;
+ open_mode = 0;
+
+ 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)) {
+ access_flags = GENERIC_WRITE;
+ open_mode = CREATE_ALWAYS;
+ } else if(modes & EFILE_MODE_READ_WRITE) {
+ access_flags = GENERIC_READ | GENERIC_WRITE;
+ open_mode = OPEN_ALWAYS;
+ } else {
+ return EINVAL;
+ }
+
+ if(modes & EFILE_MODE_APPEND) {
+ access_flags |= FILE_APPEND_DATA;
+ open_mode = OPEN_ALWAYS;
+ }
+
+ if(modes & EFILE_MODE_EXCLUSIVE) {
+ open_mode = CREATE_NEW;
+ }
+
+ if(modes & EFILE_MODE_SYNC) {
+ attributes = FILE_FLAG_WRITE_THROUGH;
+ } else {
+ attributes = FILE_ATTRIBUTE_NORMAL;
+ }
+
+ handle = CreateFileW((WCHAR*)path->data, access_flags,
+ FILE_SHARE_FLAGS, NULL, open_mode, attributes, NULL);
+
+ if(handle != INVALID_HANDLE_VALUE) {
+ efile_win_t *w;
+
+ w = (efile_win_t*)enif_alloc_resource(nif_type, sizeof(efile_win_t));
+ w->handle = handle;
+
+ EFILE_INIT_RESOURCE(&w->common, modes);
+ (*d) = &w->common;
+
+ return 0;
+ } else {
+ DWORD last_error = GetLastError();
+
+ /* Rewrite all failures on directories to EISDIR to match the old
+ * driver. */
+ if(has_file_attributes(path, FILE_ATTRIBUTE_DIRECTORY)) {
+ return EISDIR;
+ }
+
+ return windows_to_posix_errno(last_error);
+ }
+}
+
+int efile_close(efile_data_t *d) {
+ efile_win_t *w = (efile_win_t*)d;
+ HANDLE handle;
+
+ ASSERT(erts_atomic32_read_nob(&d->state) == EFILE_STATE_CLOSED);
+ ASSERT(w->handle != INVALID_HANDLE_VALUE);
+
+ handle = w->handle;
+ w->handle = INVALID_HANDLE_VALUE;
+
+ if(!CloseHandle(handle)) {
+ w->common.posix_errno = windows_to_posix_errno(GetLastError());
+ return 0;
+ }
+
+ return 1;
+}
+
+static void shift_overlapped(OVERLAPPED *overlapped, DWORD shift) {
+ LARGE_INTEGER offset;
+
+ ASSERT(shift >= 0);
+
+ offset.HighPart = overlapped->OffsetHigh;
+ offset.LowPart = overlapped->Offset;
+
+ /* ~(Uint64)0 is a magic value ("append to end of file") which needs to be
+ * preserved. Other positions resulting in overflow would have errored out
+ * just prior to this point. */
+ if(offset.QuadPart != ERTS_UINT64_MAX) {
+ offset.QuadPart += shift;
+ }
+
+ /* All unused fields must be zeroed for the next call. */
+ sys_memset(overlapped, 0, sizeof(*overlapped));
+ overlapped->OffsetHigh = offset.HighPart;
+ overlapped->Offset = offset.LowPart;
+}
+
+static void shift_iov(SysIOVec **iov, int *iovlen, DWORD shift) {
+ SysIOVec *head_vec = (*iov);
+
+ ASSERT(shift >= 0);
+
+ while(shift > 0) {
+ ASSERT(head_vec < &(*iov)[*iovlen]);
+
+ if(shift < head_vec->iov_len) {
+ head_vec->iov_base = (char*)head_vec->iov_base + shift;
+ head_vec->iov_len -= shift;
+ break;
+ } else {
+ shift -= head_vec->iov_len;
+ head_vec++;
+ }
+ }
+
+ (*iovlen) -= head_vec - (*iov);
+ (*iov) = head_vec;
+}
+
+typedef BOOL (WINAPI *io_op_t)(HANDLE, LPVOID, DWORD, LPDWORD, LPOVERLAPPED);
+
+static Sint64 internal_sync_io(efile_win_t *w, io_op_t operation,
+ SysIOVec *iov, int iovlen, OVERLAPPED *overlapped) {
+
+ Sint64 bytes_processed = 0;
+
+ for(;;) {
+ DWORD block_bytes_processed, last_error;
+ BOOL succeeded;
+
+ if(iovlen < 1) {
+ return bytes_processed;
+ }
+
+ succeeded = operation(w->handle, iov->iov_base, iov->iov_len,
+ &block_bytes_processed, overlapped);
+ last_error = GetLastError();
+
+ if(!succeeded && (last_error != ERROR_HANDLE_EOF)) {
+ w->common.posix_errno = windows_to_posix_errno(last_error);
+ return -1;
+ } else if(block_bytes_processed == 0) {
+ /* EOF */
+ return bytes_processed;
+ }
+
+ if(overlapped != NULL) {
+ shift_overlapped(overlapped, block_bytes_processed);
+ }
+
+ shift_iov(&iov, &iovlen, block_bytes_processed);
+
+ bytes_processed += block_bytes_processed;
+ }
+}
+
+Sint64 efile_readv(efile_data_t *d, SysIOVec *iov, int iovlen) {
+ efile_win_t *w = (efile_win_t*)d;
+
+ return internal_sync_io(w, ReadFile, iov, iovlen, NULL);
+}
+
+Sint64 efile_writev(efile_data_t *d, SysIOVec *iov, int iovlen) {
+ efile_win_t *w = (efile_win_t*)d;
+
+ OVERLAPPED __overlapped, *overlapped;
+ Uint64 bytes_written;
+
+ if(w->common.modes & EFILE_MODE_APPEND) {
+ overlapped = &__overlapped;
+
+ sys_memset(overlapped, 0, sizeof(*overlapped));
+ overlapped->OffsetHigh = 0xFFFFFFFF;
+ overlapped->Offset = 0xFFFFFFFF;
+ } else {
+ overlapped = NULL;
+ }
+
+ return internal_sync_io(w, WriteFile, iov, iovlen, overlapped);
+}
+
+Sint64 efile_preadv(efile_data_t *d, Sint64 offset, SysIOVec *iov, int iovlen) {
+ efile_win_t *w = (efile_win_t*)d;
+
+ OVERLAPPED overlapped;
+
+ sys_memset(&overlapped, 0, sizeof(overlapped));
+ overlapped.OffsetHigh = (offset >> 32) & 0xFFFFFFFF;
+ overlapped.Offset = offset & 0xFFFFFFFF;
+
+ return internal_sync_io(w, ReadFile, iov, iovlen, &overlapped);
+}
+
+Sint64 efile_pwritev(efile_data_t *d, Sint64 offset, SysIOVec *iov, int iovlen) {
+ efile_win_t *w = (efile_win_t*)d;
+
+ OVERLAPPED overlapped;
+
+ sys_memset(&overlapped, 0, sizeof(overlapped));
+ overlapped.OffsetHigh = (offset >> 32) & 0xFFFFFFFF;
+ overlapped.Offset = offset & 0xFFFFFFFF;
+
+ return internal_sync_io(w, WriteFile, iov, iovlen, &overlapped);
+}
+
+int efile_seek(efile_data_t *d, enum efile_seek_t seek, Sint64 offset, Sint64 *new_position) {
+ efile_win_t *w = (efile_win_t*)d;
+
+ LARGE_INTEGER large_offset, large_new_position;
+ DWORD whence;
+
+ switch(seek) {
+ case EFILE_SEEK_BOF: whence = FILE_BEGIN; break;
+ case EFILE_SEEK_CUR: whence = FILE_CURRENT; break;
+ case EFILE_SEEK_EOF: whence = FILE_END; break;
+ default: ERTS_INTERNAL_ERROR("Invalid seek parameter");
+ }
+
+ large_offset.QuadPart = offset;
+
+ if(!SetFilePointerEx(w->handle, large_offset, &large_new_position, whence)) {
+ w->common.posix_errno = windows_to_posix_errno(GetLastError());
+ return 0;
+ }
+
+ (*new_position) = large_new_position.QuadPart;
+
+ return 1;
+}
+
+int efile_sync(efile_data_t *d, int data_only) {
+ efile_win_t *w = (efile_win_t*)d;
+
+ /* Windows doesn't support data-only syncing. */
+ (void)data_only;
+
+ if(!FlushFileBuffers(w->handle)) {
+ w->common.posix_errno = windows_to_posix_errno(GetLastError());
+ return 0;
+ }
+
+ return 1;
+}
+
+int efile_advise(efile_data_t *d, Sint64 offset, Sint64 length, enum efile_advise_t advise) {
+ /* Windows doesn't support this, but we'll pretend it does since the call
+ * is only a recommendation even on systems that do support it. */
+
+ (void)d;
+ (void)offset;
+ (void)length;
+ (void)advise;
+
+ return 1;
+}
+
+int efile_allocate(efile_data_t *d, Sint64 offset, Sint64 length) {
+ efile_win_t *w = (efile_win_t*)d;
+
+ (void)d;
+ (void)offset;
+ (void)length;
+
+ w->common.posix_errno = ENOTSUP;
+
+ return 0;
+}
+
+int efile_truncate(efile_data_t *d) {
+ efile_win_t *w = (efile_win_t*)d;
+
+ if(!SetEndOfFile(w->handle)) {
+ w->common.posix_errno = windows_to_posix_errno(GetLastError());
+ return 0;
+ }
+
+ return 1;
+}
+
+static int is_executable_file(const efile_path_t *path) {
+ /* We're using the file extension in order to be quirks-compliant with the
+ * old driver, which never bothered to check the actual permissions. We
+ * could easily do so now (cf. GetNamedSecurityInfo) but the execute
+ * permission is only relevant for files that are started with the default
+ * loader, and batch files run just fine with read permission alone. */
+
+ int length = PATH_LENGTH(path);
+
+ if(length >= 4) {
+ const WCHAR *last_four = &((WCHAR*)path->data)[length - 4];
+
+ if (!_wcsicmp(last_four, L".exe") ||
+ !_wcsicmp(last_four, L".cmd") ||
+ !_wcsicmp(last_four, L".bat") ||
+ !_wcsicmp(last_four, L".com")) {
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+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;
+
+ sys_memset(&native_file_info, 0, sizeof(native_file_info));
+
+ attributes = GetFileAttributesW((WCHAR*)path->data);
+
+ if(attributes == INVALID_FILE_ATTRIBUTES) {
+ DWORD last_error = GetLastError();
+
+ /* Querying a network share root fails with ERROR_BAD_NETPATH, so we'll
+ * fake it as a directory just like local roots. */
+ if(!is_path_root(path) || last_error != ERROR_BAD_NETPATH) {
+ return windows_to_posix_errno(last_error);
+ }
+
+ attributes = 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. */
+ } else {
+ HANDLE handle;
+
+ if(follow_links && (attributes & FILE_ATTRIBUTE_REPARSE_POINT)) {
+ posix_errno_t posix_errno;
+ efile_path_t resolved_path;
+
+ posix_errno = internal_read_link(path, &resolved_path);
+
+ if(posix_errno != 0) {
+ return posix_errno;
+ }
+
+ 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);
+ }
+
+ 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;
+ }
+ }
+
+ if(attributes & FILE_ATTRIBUTE_REPARSE_POINT) {
+ 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;
+
+ return 0;
+}
+
+posix_errno_t efile_set_permissions(const efile_path_t *path, Uint32 permissions) {
+ DWORD attributes = GetFileAttributesW((WCHAR*)path->data);
+
+ if(attributes == INVALID_FILE_ATTRIBUTES) {
+ return windows_to_posix_errno(GetLastError());
+ }
+
+ if(permissions & _S_IWRITE) {
+ attributes &= ~FILE_ATTRIBUTE_READONLY;
+ } else {
+ attributes |= FILE_ATTRIBUTE_READONLY;
+ }
+
+ if(SetFileAttributesW((WCHAR*)path->data, attributes)) {
+ return 0;
+ }
+
+ return windows_to_posix_errno(GetLastError());
+}
+
+posix_errno_t efile_set_owner(const efile_path_t *path, Sint32 owner, Sint32 group) {
+ (void)path;
+ (void)owner;
+ (void)group;
+
+ return 0;
+}
+
+posix_errno_t efile_set_time(const efile_path_t *path, Sint64 a_time, Sint64 m_time, Sint64 c_time) {
+ FILETIME accessed, modified, created;
+ DWORD last_error, attributes;
+ HANDLE handle;
+
+ attributes = GetFileAttributesW((WCHAR*)path->data);
+
+ if(attributes == INVALID_FILE_ATTRIBUTES) {
+ return windows_to_posix_errno(GetLastError());
+ }
+
+ /* If the file is read-only, we have to make it temporarily writable while
+ * setting new metadata. */
+ if(attributes & FILE_ATTRIBUTE_READONLY) {
+ DWORD without_readonly = attributes & ~FILE_ATTRIBUTE_READONLY;
+
+ if(!SetFileAttributesW((WCHAR*)path->data, without_readonly)) {
+ return windows_to_posix_errno(GetLastError());
+ }
+ }
+
+ EPOCH_TO_FILETIME(modified, m_time);
+ EPOCH_TO_FILETIME(accessed, a_time);
+ EPOCH_TO_FILETIME(created, c_time);
+
+ handle = CreateFileW((WCHAR*)path->data, GENERIC_READ | GENERIC_WRITE,
+ FILE_SHARE_FLAGS, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
+ last_error = GetLastError();
+
+ if(handle != INVALID_HANDLE_VALUE) {
+ if(SetFileTime(handle, &created, &accessed, &modified)) {
+ last_error = ERROR_SUCCESS;
+ } else {
+ last_error = GetLastError();
+ }
+
+ CloseHandle(handle);
+ }
+
+ if(attributes & FILE_ATTRIBUTE_READONLY) {
+ SetFileAttributesW((WCHAR*)path->data, attributes);
+ }
+
+ return windows_to_posix_errno(last_error);
+}
+
+static posix_errno_t internal_read_link(const efile_path_t *path, 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;
+ }
+
+ actual_length = GetFinalPathNameByHandleW(link_handle,
+ (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);
+ }
+
+ /* GetFinalPathNameByHandle always prepends with "\\?\" and NUL-terminates,
+ * so we never have to touch-up the resulting path. */
+
+ ASSERT_PATH_FORMAT(result);
+
+ return 0;
+}
+
+posix_errno_t efile_read_link(ErlNifEnv *env, const efile_path_t *path, ERL_NIF_TERM *result) {
+ posix_errno_t posix_errno;
+ ErlNifBinary result_bin;
+ DWORD attributes;
+
+ ASSERT_PATH_FORMAT(path);
+
+ attributes = GetFileAttributesW((WCHAR*)path->data);
+
+ if(attributes == INVALID_FILE_ATTRIBUTES) {
+ return windows_to_posix_errno(GetLastError());
+ } else if(!(attributes & FILE_ATTRIBUTE_REPARSE_POINT)) {
+ return EINVAL;
+ }
+
+ posix_errno = internal_read_link(path, &result_bin);
+
+ if(posix_errno == 0) {
+ if(!normalize_path_result(&result_bin)) {
+ enif_release_binary(&result_bin);
+ return ENOMEM;
+ }
+
+ (*result) = enif_make_binary(env, &result_bin);
+ }
+
+ return posix_errno;
+}
+
+posix_errno_t efile_list_dir(ErlNifEnv *env, const efile_path_t *path, ERL_NIF_TERM *result) {
+ ERL_NIF_TERM list_head;
+ WIN32_FIND_DATAW data;
+ HANDLE search_handle;
+ WCHAR *search_path;
+ DWORD last_error;
+
+ ASSERT_PATH_FORMAT(path);
+
+ search_path = enif_alloc(path->size + 2 * sizeof(WCHAR));
+
+ if(search_path == NULL) {
+ return ENOMEM;
+ }
+
+ sys_memcpy(search_path, path->data, path->size);
+ search_path[PATH_LENGTH(path) + 0] = L'\\';
+ search_path[PATH_LENGTH(path) + 1] = L'*';
+ search_path[PATH_LENGTH(path) + 2] = L'\0';
+
+ search_handle = FindFirstFileW(search_path, &data);
+ last_error = GetLastError();
+
+ enif_free(search_path);
+
+ if(search_handle == INVALID_HANDLE_VALUE) {
+ return windows_to_posix_errno(last_error);
+ }
+
+ list_head = enif_make_list(env, 0);
+
+ do {
+ int name_length = wcslen(data.cFileName);
+
+ if(!is_ignored_name(name_length, data.cFileName)) {
+ unsigned char *name_bytes;
+ ERL_NIF_TERM name_term;
+ size_t name_size;
+
+ name_size = name_length * sizeof(WCHAR);
+
+ name_bytes = enif_make_new_binary(env, name_size, &name_term);
+ sys_memcpy(name_bytes, data.cFileName, name_size);
+
+ list_head = enif_make_list_cell(env, name_term, list_head);
+ }
+ } while(FindNextFileW(search_handle, &data));
+
+ FindClose(search_handle);
+ (*result) = list_head;
+
+ return 0;
+}
+
+posix_errno_t efile_rename(const efile_path_t *old_path, const efile_path_t *new_path) {
+ BOOL old_is_directory, new_is_directory;
+ DWORD move_flags, last_error;
+
+ ASSERT_PATH_FORMAT(old_path);
+ ASSERT_PATH_FORMAT(new_path);
+
+ move_flags = MOVEFILE_COPY_ALLOWED | MOVEFILE_WRITE_THROUGH;
+
+ if(MoveFileExW((WCHAR*)old_path->data, (WCHAR*)new_path->data, move_flags)) {
+ return 0;
+ }
+
+ last_error = GetLastError();
+
+ old_is_directory = has_file_attributes(old_path, FILE_ATTRIBUTE_DIRECTORY);
+ new_is_directory = has_file_attributes(new_path, FILE_ATTRIBUTE_DIRECTORY);
+
+ switch(last_error) {
+ case ERROR_SHARING_VIOLATION:
+ case ERROR_ACCESS_DENIED:
+ if(old_is_directory) {
+ BOOL moved_into_itself;
+
+ moved_into_itself = (old_path->size <= new_path->size) &&
+ !_wcsnicmp((WCHAR*)old_path->data, (WCHAR*)new_path->data,
+ PATH_LENGTH(old_path));
+
+ if(moved_into_itself) {
+ return EINVAL;
+ } else if(is_path_root(old_path)) {
+ return EINVAL;
+ }
+
+ /* Renaming a directory across volumes needs to be rewritten as
+ * EXDEV so that the caller can respond by simulating it with
+ * copy/delete operations.
+ *
+ * Files are handled through MOVEFILE_COPY_ALLOWED. */
+ if(!has_same_mount_point(old_path, new_path)) {
+ return EXDEV;
+ }
+ }
+ break;
+ case ERROR_PATH_NOT_FOUND:
+ case ERROR_FILE_NOT_FOUND:
+ return ENOENT;
+ case ERROR_ALREADY_EXISTS:
+ case ERROR_FILE_EXISTS:
+ if(old_is_directory && !new_is_directory) {
+ return ENOTDIR;
+ } else if(!old_is_directory && new_is_directory) {
+ return EISDIR;
+ } else if(old_is_directory && new_is_directory) {
+ /* This will fail if the destination isn't empty. */
+ if(RemoveDirectoryW((WCHAR*)new_path->data)) {
+ return efile_rename(old_path, new_path);
+ }
+
+ return EEXIST;
+ } else if(!old_is_directory && !new_is_directory) {
+ /* This is pretty iffy; the public documentation says that the
+ * operation may EACCES on some systems when either file is open,
+ * which gives us room to use MOVEFILE_REPLACE_EXISTING and be done
+ * with it, but the old implementation simulated Unix semantics and
+ * there's a lot of code that relies on that.
+ *
+ * The simulation renames the destination to a scratch name to get
+ * around the fact that it's impossible to open (and by extension
+ * rename) a file that's been deleted while open. It has a few
+ * drawbacks though;
+ *
+ * 1) It's not atomic as there's a small window where there's no
+ * file at all on the destination path.
+ * 2) It will confuse applications that subscribe to folder
+ * changes.
+ * 3) It will fail if we lack general permission to write in the
+ * same folder. */
+
+ WCHAR *swap_path = enif_alloc(new_path->size + sizeof(WCHAR) * 64);
+
+ if(swap_path == NULL) {
+ return ENOMEM;
+ } else {
+ static LONGLONG unique_counter = 0;
+ WCHAR *swap_path_end;
+
+ /* We swap in the same folder as the destination to be
+ * reasonably sure that it's on the same volume. Note that
+ * we're avoiding GetTempFileNameW as it will fail on long
+ * paths. */
+
+ sys_memcpy(swap_path, (WCHAR*)new_path->data, new_path->size);
+ swap_path_end = swap_path + PATH_LENGTH(new_path);
+
+ while(!IS_SLASH(*swap_path_end)) {
+ ASSERT(swap_path_end > swap_path);
+ swap_path_end--;
+ }
+
+ StringCchPrintfW(&swap_path_end[1], 64, L"erl-%lx-%llx.tmp",
+ GetCurrentProcessId(), unique_counter);
+ InterlockedIncrement64(&unique_counter);
+ }
+
+ if(MoveFileExW((WCHAR*)new_path->data, swap_path, MOVEFILE_REPLACE_EXISTING)) {
+ if(MoveFileExW((WCHAR*)old_path->data, (WCHAR*)new_path->data, move_flags)) {
+ last_error = ERROR_SUCCESS;
+ DeleteFileW(swap_path);
+ } else {
+ last_error = GetLastError();
+ MoveFileW(swap_path, (WCHAR*)new_path->data);
+ }
+ } else {
+ last_error = GetLastError();
+ DeleteFileW(swap_path);
+ }
+
+ enif_free(swap_path);
+
+ return windows_to_posix_errno(last_error);
+ }
+
+ return EEXIST;
+ }
+
+ return windows_to_posix_errno(last_error);
+}
+
+posix_errno_t efile_make_hard_link(const efile_path_t *existing_path, const efile_path_t *new_path) {
+ ASSERT_PATH_FORMAT(existing_path);
+ ASSERT_PATH_FORMAT(new_path);
+
+ if(!CreateHardLinkW((WCHAR*)new_path->data, (WCHAR*)existing_path->data, NULL)) {
+ return windows_to_posix_errno(GetLastError());
+ }
+
+ return 0;
+}
+
+posix_errno_t efile_make_soft_link(const efile_path_t *existing_path, const efile_path_t *new_path) {
+ DWORD link_flags;
+
+ ASSERT_PATH_FORMAT(existing_path);
+ ASSERT_PATH_FORMAT(new_path);
+
+ if(has_file_attributes(existing_path, FILE_ATTRIBUTE_DIRECTORY)) {
+ link_flags = SYMBOLIC_LINK_FLAG_DIRECTORY;
+ } else {
+ link_flags = 0;
+ }
+
+ if(!CreateSymbolicLinkW((WCHAR*)new_path->data, (WCHAR*)existing_path->data, link_flags)) {
+ return windows_to_posix_errno(GetLastError());
+ }
+
+ return 0;
+}
+
+posix_errno_t efile_make_dir(const efile_path_t *path) {
+ ASSERT_PATH_FORMAT(path);
+
+ if(!CreateDirectoryW((WCHAR*)path->data, NULL)) {
+ return windows_to_posix_errno(GetLastError());
+ }
+
+ return 0;
+}
+
+posix_errno_t efile_del_file(const efile_path_t *path) {
+ ASSERT_PATH_FORMAT(path);
+
+ if(!DeleteFileW((WCHAR*)path->data)) {
+ DWORD last_error = GetLastError();
+
+ switch(last_error) {
+ case ERROR_INVALID_NAME:
+ /* Attempted to delete a device or similar. */
+ return EACCES;
+ case ERROR_ACCESS_DENIED:
+ /* Windows NT reports removing a directory as EACCES instead of
+ * EPERM. */
+ if(has_file_attributes(path, FILE_ATTRIBUTE_DIRECTORY)) {
+ return EPERM;
+ }
+ break;
+ }
+
+ return windows_to_posix_errno(last_error);
+ }
+
+ return 0;
+}
+
+posix_errno_t efile_del_dir(const efile_path_t *path) {
+ ASSERT_PATH_FORMAT(path);
+
+ if(!RemoveDirectoryW((WCHAR*)path->data)) {
+ DWORD last_error = GetLastError();
+
+ if(last_error == ERROR_DIRECTORY) {
+ return ENOTDIR;
+ }
+
+ return windows_to_posix_errno(last_error);
+ }
+
+ return 0;
+}
+
+posix_errno_t efile_set_cwd(const efile_path_t *path) {
+ const WCHAR *path_start;
+
+ ASSERT_PATH_FORMAT(path);
+
+ /* We have to use _wchdir since that's the only function that updates the
+ * per-drive working directory, but it naively assumes that all paths
+ * starting with \\ are UNC paths, so we have to skip the \\?\-prefix. */
+ path_start = (WCHAR*)path->data + LP_PREFIX_LENGTH;
+
+ if(_wchdir(path_start)) {
+ return windows_to_posix_errno(GetLastError());
+ }
+
+ return 0;
+}
+
+static int is_valid_drive(int device_index) {
+ WCHAR drive_path[4] = {L'?', L':', L'\\', L'\0'};
+
+ if(device_index == 0) {
+ /* Default drive; always valid. */
+ return 1;
+ } else if(device_index > (L'Z' - L'A' + 1)) {
+ return 0;
+ }
+
+ drive_path[0] = device_index + L'A' - 1;
+
+ switch(GetDriveTypeW(drive_path)) {
+ case DRIVE_NO_ROOT_DIR:
+ case DRIVE_UNKNOWN:
+ return 0;
+ }
+
+ return 1;
+}
+
+posix_errno_t efile_get_device_cwd(ErlNifEnv *env, int device_index, ERL_NIF_TERM *result) {
+ ErlNifBinary result_bin;
+
+ /* _wgetdcwd might crash the entire emulator on debug builds since the CRT
+ * invalid parameter handler asserts if passed a non-existent drive (Or
+ * simply one that has been unmounted), so we check it ourselves to avoid
+ * that. */
+ if(!is_valid_drive(device_index)) {
+ return EACCES;
+ }
+
+ if(!enif_alloc_binary(MAX_PATH * sizeof(WCHAR), &result_bin)) {
+ return ENOMEM;
+ }
+
+ if(_wgetdcwd(device_index, (WCHAR*)result_bin.data, MAX_PATH) == NULL) {
+ enif_release_binary(&result_bin);
+ return EACCES;
+ }
+
+ if(!normalize_path_result(&result_bin)) {
+ enif_release_binary(&result_bin);
+ return ENOMEM;
+ }
+
+ (*result) = enif_make_binary(env, &result_bin);
+
+ return 0;
+}
+
+posix_errno_t efile_get_cwd(ErlNifEnv *env, ERL_NIF_TERM *result) {
+ return efile_get_device_cwd(env, 0, result);
+}
+
+posix_errno_t efile_altname(ErlNifEnv *env, const efile_path_t *path, ERL_NIF_TERM *result) {
+ ErlNifBinary result_bin;
+
+ ASSERT_PATH_FORMAT(path);
+
+ if(is_path_root(path)) {
+ /* Root paths can't be queried so we'll just return them as they are. */
+ if(!enif_alloc_binary(path->size, &result_bin)) {
+ return ENOMEM;
+ }
+
+ sys_memcpy(result_bin.data, path->data, path->size);
+ } else {
+ WIN32_FIND_DATAW data;
+ HANDLE handle;
+
+ WCHAR *name_buffer;
+ int name_length;
+
+ /* Reject path wildcards. */
+ if(wcspbrk(&((const WCHAR*)path->data)[4], L"?*")) {
+ return ENOENT;
+ }
+
+ handle = FindFirstFileW((const WCHAR*)path->data, &data);
+
+ if(handle == INVALID_HANDLE_VALUE) {
+ return windows_to_posix_errno(GetLastError());
+ }
+
+ FindClose(handle);
+
+ name_length = wcslen(data.cAlternateFileName);
+
+ if(name_length > 0) {
+ name_buffer = data.cAlternateFileName;
+ } else {
+ name_length = wcslen(data.cFileName);
+ name_buffer = data.cFileName;
+ }
+
+ /* Include NUL-terminator; it will be removed after normalization. */
+ name_length += 1;
+
+ if(!enif_alloc_binary(name_length * sizeof(WCHAR), &result_bin)) {
+ return ENOMEM;
+ }
+
+ sys_memcpy(result_bin.data, name_buffer, name_length * sizeof(WCHAR));
+ }
+
+ if(!normalize_path_result(&result_bin)) {
+ enif_release_binary(&result_bin);
+ return ENOMEM;
+ }
+
+ (*result) = enif_make_binary(env, &result_bin);
+
+ return 0;
+}
+
+static int windows_to_posix_errno(DWORD last_error) {
+ switch(last_error) {
+ case ERROR_SUCCESS:
+ return 0;
+ case ERROR_INVALID_FUNCTION:
+ case ERROR_INVALID_DATA:
+ case ERROR_INVALID_PARAMETER:
+ case ERROR_INVALID_TARGET_HANDLE:
+ case ERROR_INVALID_CATEGORY:
+ case ERROR_NEGATIVE_SEEK:
+ return EINVAL;
+ case ERROR_DIR_NOT_EMPTY:
+ return EEXIST;
+ case ERROR_BAD_FORMAT:
+ return ENOEXEC;
+ case ERROR_PATH_NOT_FOUND:
+ case ERROR_FILE_NOT_FOUND:
+ case ERROR_NO_MORE_FILES:
+ case ERROR_INVALID_NAME:
+ return ENOENT;
+ case ERROR_TOO_MANY_OPEN_FILES:
+ return EMFILE;
+ case ERROR_ACCESS_DENIED:
+ case ERROR_INVALID_ACCESS:
+ case ERROR_CURRENT_DIRECTORY:
+ case ERROR_SHARING_VIOLATION:
+ case ERROR_LOCK_VIOLATION:
+ case ERROR_INVALID_PASSWORD:
+ case ERROR_DRIVE_LOCKED:
+ return EACCES;
+ case ERROR_INVALID_HANDLE:
+ return EBADF;
+ case ERROR_NOT_ENOUGH_MEMORY:
+ case ERROR_OUTOFMEMORY:
+ case ERROR_OUT_OF_STRUCTURES:
+ return ENOMEM;
+ case ERROR_INVALID_DRIVE:
+ case ERROR_BAD_UNIT:
+ case ERROR_NOT_READY:
+ case ERROR_REM_NOT_LIST:
+ case ERROR_DUP_NAME:
+ case ERROR_BAD_NETPATH:
+ case ERROR_NETWORK_BUSY:
+ case ERROR_DEV_NOT_EXIST:
+ case ERROR_BAD_NET_NAME:
+ return ENXIO;
+ case ERROR_NOT_SAME_DEVICE:
+ return EXDEV;
+ case ERROR_WRITE_PROTECT:
+ return EROFS;
+ case ERROR_BAD_LENGTH:
+ case ERROR_BUFFER_OVERFLOW:
+ return E2BIG;
+ case ERROR_SEEK:
+ case ERROR_SECTOR_NOT_FOUND:
+ return ESPIPE;
+ case ERROR_NOT_DOS_DISK:
+ return ENODEV;
+ case ERROR_GEN_FAILURE:
+ return ENODEV;
+ case ERROR_SHARING_BUFFER_EXCEEDED:
+ case ERROR_NO_MORE_SEARCH_HANDLES:
+ return EMFILE;
+ case ERROR_HANDLE_EOF:
+ case ERROR_BROKEN_PIPE:
+ return EPIPE;
+ case ERROR_HANDLE_DISK_FULL:
+ case ERROR_DISK_FULL:
+ return ENOSPC;
+ case ERROR_NOT_SUPPORTED:
+ return ENOTSUP;
+ case ERROR_FILE_EXISTS:
+ case ERROR_ALREADY_EXISTS:
+ case ERROR_CANNOT_MAKE:
+ return EEXIST;
+ case ERROR_ALREADY_ASSIGNED:
+ return EBUSY;
+ case ERROR_NO_PROC_SLOTS:
+ return EAGAIN;
+ case ERROR_CANT_RESOLVE_FILENAME:
+ return EMLINK;
+ case ERROR_PRIVILEGE_NOT_HELD:
+ return EPERM;
+ case ERROR_ARENA_TRASHED:
+ case ERROR_INVALID_BLOCK:
+ case ERROR_BAD_ENVIRONMENT:
+ case ERROR_BAD_COMMAND:
+ case ERROR_CRC:
+ case ERROR_OUT_OF_PAPER:
+ case ERROR_READ_FAULT:
+ case ERROR_WRITE_FAULT:
+ case ERROR_WRONG_DISK:
+ case ERROR_NET_WRITE_FAULT:
+ return EIO;
+ default: /* not to do with files I expect. */
+ return EIO;
+ }
+}
diff --git a/erts/emulator/pcre/README.pcre_update.md b/erts/emulator/pcre/README.pcre_update.md
index 8caf575d31..599e3d0d12 100644
--- a/erts/emulator/pcre/README.pcre_update.md
+++ b/erts/emulator/pcre/README.pcre_update.md
@@ -2,7 +2,7 @@
## The basic changes to the PCRE library
-To work with the Erlang VM, PCRE has been changed in two important ways:
+To work with the Erlang VM, PCRE has been changed in three important ways:
1. The main execution machine in pcre\_exec has been modified so that
matching can be interrupted and restarted. This functionality utilizes
diff --git a/erts/emulator/sys/common/erl_check_io.c b/erts/emulator/sys/common/erl_check_io.c
index 8928529984..3e77dce1cd 100644
--- a/erts/emulator/sys/common/erl_check_io.c
+++ b/erts/emulator/sys/common/erl_check_io.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2006-2017. All Rights Reserved.
+ * Copyright Ericsson AB 2006-2018. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -885,7 +885,7 @@ enif_select(ErlNifEnv* env,
ErtsDrvSelectDataState *free_select = NULL;
ErtsNifSelectDataState *free_nif = NULL;
- ASSERT(!(resource->monitors && resource->monitors->is_dying));
+ ASSERT(!resource->monitors);
#ifdef ERTS_SYS_CONTINOUS_FD_NUMBERS
if (!grow_drv_ev_state(fd)) {
diff --git a/erts/emulator/sys/common/erl_mmap.c b/erts/emulator/sys/common/erl_mmap.c
index a9c6e72c5f..5dadd8a5a6 100644
--- a/erts/emulator/sys/common/erl_mmap.c
+++ b/erts/emulator/sys/common/erl_mmap.c
@@ -296,11 +296,10 @@ typedef struct {
}ErtsFreeSegMap;
struct ErtsMemMapper_ {
- int (*reserve_physical)(char *, UWord, int exec);
+ int (*reserve_physical)(char *, UWord);
void (*unreserve_physical)(char *, UWord);
int supercarrier;
int no_os_mmap;
- int executable; /* is client a native code allocator? */
/*
* Super unaligned area is located above super aligned
* area. That is, `sa.bot` is beginning of the super
@@ -358,10 +357,6 @@ char* erts_literals_start;
UWord erts_literals_size;
#endif
-#ifdef ERTS_HAVE_EXEC_MMAPPER
-ErtsMemMapper erts_exec_mmapper;
-#endif
-
#define ERTS_MMAP_SIZE_SC_SA_INC(SZ) \
do { \
@@ -1240,7 +1235,6 @@ Eterm build_free_seg_list(Process* p, ErtsFreeSegMap* map)
#if HAVE_MMAP
# define ERTS_MMAP_PROT (PROT_READ|PROT_WRITE)
-# define ERTS_MMAP_PROT_EXEC (PROT_READ|PROT_WRITE|PROT_EXEC)
# if defined(MAP_ANONYMOUS)
# define ERTS_MMAP_FLAGS (MAP_ANON|MAP_PRIVATE)
# define ERTS_MMAP_FD (-1)
@@ -1254,26 +1248,24 @@ Eterm build_free_seg_list(Process* p, ErtsFreeSegMap* map)
#endif
static ERTS_INLINE void *
-os_mmap(void *hint_ptr, UWord size, int try_superalign, int executable)
+os_mmap(void *hint_ptr, UWord size, int try_superalign)
{
#if HAVE_MMAP
- const int prot = executable ? ERTS_MMAP_PROT_EXEC : ERTS_MMAP_PROT;
void *res;
#ifdef MAP_ALIGN
if (try_superalign)
- res = mmap((void *) ERTS_SUPERALIGNED_SIZE, size, prot,
+ res = mmap((void *) ERTS_SUPERALIGNED_SIZE, size, ERTS_MMAP_PROT,
ERTS_MMAP_FLAGS|MAP_ALIGN, ERTS_MMAP_FD, 0);
else
#endif
- res = mmap((void *) hint_ptr, size, prot,
+ res = mmap((void *) hint_ptr, size, ERTS_MMAP_PROT,
ERTS_MMAP_FLAGS, ERTS_MMAP_FD, 0);
if (res == MAP_FAILED)
return NULL;
return res;
#elif HAVE_VIRTUALALLOC
- const DWORD prot = executable ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE;
return (void *) VirtualAlloc(NULL, (SIZE_T) size,
- MEM_COMMIT|MEM_RESERVE, prot);
+ MEM_COMMIT|MEM_RESERVE, PAGE_READWRITE);
#else
# error "missing mmap() or similar"
#endif
@@ -1330,7 +1322,6 @@ os_mremap(void *ptr, UWord old_size, UWord new_size, int try_superalign)
#if HAVE_MMAP
#define ERTS_MMAP_RESERVE_PROT (ERTS_MMAP_PROT)
-#define ERTS_MMAP_RESERVE_PROT_EXEC (ERTS_MMAP_PROT_EXEC)
#define ERTS_MMAP_RESERVE_FLAGS (ERTS_MMAP_FLAGS|MAP_FIXED)
#define ERTS_MMAP_UNRESERVE_PROT (PROT_NONE)
#if defined(__FreeBSD__)
@@ -1346,10 +1337,9 @@ os_mremap(void *ptr, UWord old_size, UWord new_size, int try_superalign)
#endif /* __FreeBSD__ */
static int
-os_reserve_physical(char *ptr, UWord size, int exec)
+os_reserve_physical(char *ptr, UWord size)
{
- const int prot = exec ? ERTS_MMAP_RESERVE_PROT_EXEC : ERTS_MMAP_RESERVE_PROT;
- void *res = mmap((void *) ptr, (size_t) size, prot,
+ void *res = mmap((void *) ptr, (size_t) size, ERTS_MMAP_RESERVE_PROT,
ERTS_MMAP_RESERVE_FLAGS, ERTS_MMAP_FD, 0);
if (res == (void *) MAP_FAILED)
return 0;
@@ -1366,37 +1356,11 @@ os_unreserve_physical(char *ptr, UWord size)
}
static void *
-os_mmap_virtual(char *ptr, UWord size, int exec)
+os_mmap_virtual(char *ptr, UWord size)
{
int flags = ERTS_MMAP_VIRTUAL_FLAGS;
void* res;
-#ifdef ERTS_ALC_A_EXEC
- if (exec) {
- ASSERT(!ptr);
- /* OTP-19.0: Nice hack below cut-and-pasted from hipe_amd64.c */
-
-# ifdef MAP_32BIT
- /* If we got MAP_32BIT (Linux), then use that to ask for low memory */
- flags |= MAP_32BIT;
-# else
- /* FreeBSD doesn't have MAP_32BIT, and it doesn't respect
- a plain map_hint (returns high mappings even though the
- hint refers to a free area), so we have to use both map_hint
- and MAP_FIXED to get addresses below the 2GB boundary.
- This is even worse than the Linux/ppc64 case.
- Similarly, Solaris 10 doesn't have MAP_32BIT,
- and it doesn't respect a plain map_hint. */
- ptr = (char*)(512*1024*1024); /* 0.5GB */
-
-# if defined(__FreeBSD__) || defined(__sun__)
- flags |= MAP_FIXED;
-# endif
-# endif /* !MAP_32BIT */
- }
-#else /* !ERTS_ALC_A_EXEC */
- ASSERT(!exec);
-#endif
res = mmap((void *) ptr, (size_t) size, ERTS_MMAP_VIRTUAL_PROT,
flags, ERTS_MMAP_FD, 0);
if (res == (void *) MAP_FAILED)
@@ -1411,7 +1375,7 @@ os_mmap_virtual(char *ptr, UWord size, int exec)
#endif /* ERTS_HAVE_OS_MMAP */
-static int reserve_noop(char *ptr, UWord size, int exec)
+static int reserve_noop(char *ptr, UWord size)
{
#ifdef ERTS_MMAP_DEBUG_FILL_AREAS
Uint32 *uip, *end = (Uint32 *) (ptr + size);
@@ -1454,7 +1418,7 @@ alloc_desc_insert_free_seg(ErtsMemMapper* mm,
#if ERTS_HAVE_OS_MMAP
if (!mm->no_os_mmap) {
- ptr = os_mmap(mm->desc.new_area_hint, ERTS_PAGEALIGNED_SIZE, 0, 0);
+ ptr = os_mmap(mm->desc.new_area_hint, ERTS_PAGEALIGNED_SIZE, 0);
if (ptr) {
mm->desc.new_area_hint = ptr+ERTS_PAGEALIGNED_SIZE;
ERTS_MMAP_SIZE_OS_INC(ERTS_PAGEALIGNED_SIZE);
@@ -1473,7 +1437,7 @@ alloc_desc_insert_free_seg(ErtsMemMapper* mm,
da_map = &mm->sua.map;
desc = lookup_free_seg(da_map, ERTS_PAGEALIGNED_SIZE);
if (desc) {
- if (mm->reserve_physical(desc->start, ERTS_PAGEALIGNED_SIZE, 0))
+ if (mm->reserve_physical(desc->start, ERTS_PAGEALIGNED_SIZE))
ERTS_MMAP_SIZE_SC_SUA_INC(ERTS_PAGEALIGNED_SIZE);
else
desc = NULL;
@@ -1483,7 +1447,7 @@ alloc_desc_insert_free_seg(ErtsMemMapper* mm,
da_map = &mm->sa.map;
desc = lookup_free_seg(da_map, ERTS_PAGEALIGNED_SIZE);
if (desc) {
- if (mm->reserve_physical(desc->start, ERTS_PAGEALIGNED_SIZE, 0))
+ if (mm->reserve_physical(desc->start, ERTS_PAGEALIGNED_SIZE))
ERTS_MMAP_SIZE_SC_SA_INC(ERTS_PAGEALIGNED_SIZE);
else
desc = NULL;
@@ -1544,7 +1508,7 @@ erts_mmap(ErtsMemMapper* mm, Uint32 flags, UWord *sizep)
if (desc) {
seg = desc->start;
end = seg+asize;
- if (!mm->reserve_physical(seg, asize, mm->executable))
+ if (!mm->reserve_physical(seg, asize))
goto supercarrier_reserve_failure;
if (desc->end == end) {
delete_free_seg(&mm->sua.map, desc);
@@ -1559,8 +1523,7 @@ erts_mmap(ErtsMemMapper* mm, Uint32 flags, UWord *sizep)
}
if (asize <= mm->sua.bot - mm->sa.top) {
- if (!mm->reserve_physical(mm->sua.bot - asize, asize,
- mm->executable))
+ if (!mm->reserve_physical(mm->sua.bot - asize, asize))
goto supercarrier_reserve_failure;
mm->sua.bot -= asize;
seg = mm->sua.bot;
@@ -1576,8 +1539,7 @@ erts_mmap(ErtsMemMapper* mm, Uint32 flags, UWord *sizep)
char *start = seg = desc->start;
seg = (char *) ERTS_SUPERALIGNED_CEILING(seg);
end = seg+asize;
- if (!mm->reserve_physical(start, (UWord) (end - start),
- mm->executable))
+ if (!mm->reserve_physical(start, (UWord) (end - start)))
goto supercarrier_reserve_failure;
ERTS_MMAP_SIZE_SC_SA_INC(asize);
if (desc->end == end) {
@@ -1609,8 +1571,7 @@ erts_mmap(ErtsMemMapper* mm, Uint32 flags, UWord *sizep)
if (asize + (seg - start) <= mm->sua.bot - start) {
end = seg + asize;
- if (!mm->reserve_physical(start, (UWord) (end - start),
- mm->executable))
+ if (!mm->reserve_physical(start, (UWord) (end - start)))
goto supercarrier_reserve_failure;
mm->sa.top = end;
ERTS_MMAP_SIZE_SC_SA_INC(asize);
@@ -1632,8 +1593,7 @@ erts_mmap(ErtsMemMapper* mm, Uint32 flags, UWord *sizep)
seg = (char *) ERTS_SUPERALIGNED_CEILING(org_start);
end = seg + asize;
- if (!mm->reserve_physical(seg, (UWord) (org_end - seg),
- mm->executable))
+ if (!mm->reserve_physical(seg, (UWord) (org_end - seg)))
goto supercarrier_reserve_failure;
ERTS_MMAP_SIZE_SC_SUA_INC(asize);
if (org_start != seg) {
@@ -1666,13 +1626,13 @@ erts_mmap(ErtsMemMapper* mm, Uint32 flags, UWord *sizep)
/* Map using OS primitives */
if (!(ERTS_MMAPFLG_SUPERCARRIER_ONLY & flags) && !mm->no_os_mmap) {
if (!(ERTS_MMAPFLG_SUPERALIGNED & flags)) {
- seg = os_mmap(NULL, asize, 0, mm->executable);
+ seg = os_mmap(NULL, asize, 0);
if (!seg)
goto failure;
}
else {
asize = ERTS_SUPERALIGNED_CEILING(*sizep);
- seg = os_mmap(NULL, asize, 1, mm->executable);
+ seg = os_mmap(NULL, asize, 1);
if (!seg)
goto failure;
@@ -1682,8 +1642,7 @@ erts_mmap(ErtsMemMapper* mm, Uint32 flags, UWord *sizep)
os_munmap(seg, asize);
- ptr = os_mmap(NULL, asize + ERTS_SUPERALIGNED_SIZE, 1,
- mm->executable);
+ ptr = os_mmap(NULL, asize + ERTS_SUPERALIGNED_SIZE, 1);
if (!ptr)
goto failure;
@@ -2018,8 +1977,7 @@ erts_mremap(ErtsMemMapper* mm,
if (next && new_end <= next->end) {
if (!mm->reserve_physical(((char *) ptr) + old_size,
- asize - old_size,
- mm->executable))
+ asize - old_size))
goto supercarrier_reserve_failure;
if (new_end < next->end)
resize_free_seg(&mm->sua.map, next, new_end, next->end);
@@ -2037,8 +1995,7 @@ erts_mremap(ErtsMemMapper* mm,
if (end == mm->sa.top) {
if (new_end <= mm->sua.bot) {
if (!mm->reserve_physical(((char *) ptr) + old_size,
- asize - old_size,
- mm->executable))
+ asize - old_size))
goto supercarrier_reserve_failure;
mm->sa.top = new_end;
new_ptr = ptr;
@@ -2050,8 +2007,7 @@ erts_mremap(ErtsMemMapper* mm,
adjacent_free_seg(&mm->sa.map, start, end, &prev, &next);
if (next && new_end <= next->end) {
if (!mm->reserve_physical(((char *) ptr) + old_size,
- asize - old_size,
- mm->executable))
+ asize - old_size))
goto supercarrier_reserve_failure;
if (new_end < next->end)
resize_free_seg(&mm->sa.map, next, new_end, next->end);
@@ -2171,7 +2127,7 @@ static void hard_dbg_mseg_init(void);
#endif
void
-erts_mmap_init(ErtsMemMapper* mm, ErtsMMapInit *init, int executable)
+erts_mmap_init(ErtsMemMapper* mm, ErtsMMapInit *init)
{
static int is_first_call = 1;
int virtual_map = 0;
@@ -2203,7 +2159,6 @@ erts_mmap_init(ErtsMemMapper* mm, ErtsMMapInit *init, int executable)
mm->supercarrier = 0;
mm->reserve_physical = reserve_noop;
mm->unreserve_physical = unreserve_noop;
- mm->executable = executable;
#if HAVE_MMAP && !defined(MAP_ANON)
mm->mmap_fd = open("/dev/zero", O_RDWR);
@@ -2225,7 +2180,7 @@ erts_mmap_init(ErtsMemMapper* mm, ErtsMMapInit *init, int executable)
ptr = (char *) ERTS_PAGEALIGNED_CEILING(init->virtual_range.start);
end = (char *) ERTS_PAGEALIGNED_FLOOR(init->virtual_range.end);
sz = end - ptr;
- start = os_mmap_virtual(ptr, sz, executable);
+ start = os_mmap_virtual(ptr, sz);
if (!start || start > ptr || start >= end)
erts_exit(1,
"erts_mmap: Failed to create virtual range for super carrier\n");
@@ -2250,7 +2205,7 @@ erts_mmap_init(ErtsMemMapper* mm, ErtsMMapInit *init, int executable)
sz = ERTS_PAGEALIGNED_CEILING(init->scs);
#ifdef ERTS_HAVE_OS_PHYSICAL_MEMORY_RESERVATION
if (!init->scrpm) {
- start = os_mmap_virtual(NULL, sz, executable);
+ start = os_mmap_virtual(NULL, sz);
mm->reserve_physical = os_reserve_physical;
mm->unreserve_physical = os_unreserve_physical;
virtual_map = 1;
@@ -2262,7 +2217,7 @@ erts_mmap_init(ErtsMemMapper* mm, ErtsMMapInit *init, int executable)
* The whole supercarrier will by physically
* reserved all the time.
*/
- start = os_mmap(NULL, sz, 1, executable);
+ start = os_mmap(NULL, sz, 1);
}
if (!start)
erts_exit(1,
@@ -2336,7 +2291,7 @@ erts_mmap_init(ErtsMemMapper* mm, ErtsMMapInit *init, int executable)
mm->sua.top -= ERTS_PAGEALIGNED_SIZE;
mm->size.supercarrier.used.total += ERTS_PAGEALIGNED_SIZE;
#ifdef ERTS_HAVE_OS_PHYSICAL_MEMORY_RESERVATION
- if (!virtual_map || os_reserve_physical(mm->sua.top, ERTS_PAGEALIGNED_SIZE, 0))
+ if (!virtual_map || os_reserve_physical(mm->sua.top, ERTS_PAGEALIGNED_SIZE))
#endif
add_free_desc_area(mm, mm->sua.top, end);
mm->desc.reserved += (end - mm->sua.top) / sizeof(ErtsFreeSegDesc);
@@ -2349,7 +2304,7 @@ erts_mmap_init(ErtsMemMapper* mm, ErtsMMapInit *init, int executable)
* will be used for free segment descritors.
*/
#ifdef ERTS_HAVE_OS_PHYSICAL_MEMORY_RESERVATION
- if (virtual_map && !os_reserve_physical(start, mm->sa.bot - start, 0))
+ if (virtual_map && !os_reserve_physical(start, mm->sa.bot - start))
erts_exit(1, "erts_mmap: Failed to reserve physical memory for descriptors\n");
#endif
mm->desc.unused_start = start;
diff --git a/erts/emulator/sys/common/erl_mmap.h b/erts/emulator/sys/common/erl_mmap.h
index 2a07d93c8c..c1f9668b18 100644
--- a/erts/emulator/sys/common/erl_mmap.h
+++ b/erts/emulator/sys/common/erl_mmap.h
@@ -93,11 +93,6 @@ typedef struct {
#define ERTS_MMAP_INIT_LITERAL_INITER \
{{NULL, NULL}, {NULL, NULL}, ERTS_LITERAL_VIRTUAL_AREA_SIZE, 1, (1 << 10), 0}
-#define ERTS_HIPE_EXEC_VIRTUAL_AREA_SIZE (UWORD_CONSTANT(512)*1024*1024)
-
-#define ERTS_MMAP_INIT_HIPE_EXEC_INITER \
- {{NULL, NULL}, {NULL, NULL}, ERTS_HIPE_EXEC_VIRTUAL_AREA_SIZE, 1, (1 << 10), 0}
-
#define ERTS_SUPERALIGNED_SIZE \
(1 << ERTS_MMAP_SUPERALIGNED_BITS)
@@ -140,7 +135,7 @@ void *erts_mmap(ErtsMemMapper*, Uint32 flags, UWord *sizep);
void erts_munmap(ErtsMemMapper*, Uint32 flags, void *ptr, UWord size);
void *erts_mremap(ErtsMemMapper*, Uint32 flags, void *ptr, UWord old_size, UWord *sizep);
int erts_mmap_in_supercarrier(ErtsMemMapper*, void *ptr);
-void erts_mmap_init(ErtsMemMapper*, ErtsMMapInit*, int executable);
+void erts_mmap_init(ErtsMemMapper*, ErtsMMapInit*);
struct erts_mmap_info_struct
{
UWord sizes[6];
@@ -165,15 +160,6 @@ extern ErtsMemMapper erts_dflt_mmapper;
extern ErtsMemMapper erts_literal_mmapper;
# endif
-# if defined(ERTS_ALC_A_EXEC) && defined(__x86_64__)
- /*
- * On x86_64, exec_alloc employs its own super carrier 'erts_exec_mmaper'
- * to ensure low memory for HiPE AMD64 small code model.
- */
-# define ERTS_HAVE_EXEC_MMAPPER
-extern ErtsMemMapper erts_exec_mmapper;
-# endif
-
# endif /* ERTS_HAVE_OS_PHYSICAL_MEMORY_RESERVATION */
#endif /* ERTS_WANT_MEM_MAPPERS */
diff --git a/erts/emulator/sys/common/erl_mseg.c b/erts/emulator/sys/common/erl_mseg.c
index bf6de9b13a..ced3d61525 100644
--- a/erts/emulator/sys/common/erl_mseg.c
+++ b/erts/emulator/sys/common/erl_mseg.c
@@ -1406,15 +1406,9 @@ erts_mseg_init(ErtsMsegInit_t *init)
erts_mtx_init(&init_atoms_mutex, "mseg_init_atoms", NIL,
ERTS_LOCK_FLAGS_PROPERTY_STATIC | ERTS_LOCK_FLAGS_CATEGORY_GENERIC);
-#ifdef ERTS_HAVE_EXEC_MMAPPER
- /* Initialize erts_exec_mapper *FIRST*, to increase probability
- * of getting low memory for HiPE AMD64's small code model.
- */
- erts_mmap_init(&erts_exec_mmapper, &init->exec_mmap, 1);
-#endif
- erts_mmap_init(&erts_dflt_mmapper, &init->dflt_mmap, 0);
+ erts_mmap_init(&erts_dflt_mmapper, &init->dflt_mmap);
#if defined(ARCH_64) && defined(ERTS_HAVE_OS_PHYSICAL_MEMORY_RESERVATION)
- erts_mmap_init(&erts_literal_mmapper, &init->literal_mmap, 0);
+ erts_mmap_init(&erts_literal_mmapper, &init->literal_mmap);
#endif
if (!IS_2POW(GET_PAGE_SIZE))
diff --git a/erts/emulator/sys/common/erl_mseg.h b/erts/emulator/sys/common/erl_mseg.h
index bba0dec499..af275c18be 100644
--- a/erts/emulator/sys/common/erl_mseg.h
+++ b/erts/emulator/sys/common/erl_mseg.h
@@ -61,7 +61,6 @@ typedef struct {
Uint nos;
ErtsMMapInit dflt_mmap;
ErtsMMapInit literal_mmap;
- ErtsMMapInit exec_mmap;
} ErtsMsegInit_t;
#define ERTS_MSEG_INIT_DEFAULT_INITIALIZER \
@@ -72,7 +71,6 @@ typedef struct {
1000, /* cci: Cache check interval */ \
ERTS_MMAP_INIT_DEFAULT_INITER, \
ERTS_MMAP_INIT_LITERAL_INITER, \
- ERTS_MMAP_INIT_HIPE_EXEC_INITER \
}
typedef struct {
diff --git a/erts/emulator/sys/common/erl_osenv.c b/erts/emulator/sys/common/erl_osenv.c
new file mode 100644
index 0000000000..9f54d1dff0
--- /dev/null
+++ b/erts/emulator/sys/common/erl_osenv.c
@@ -0,0 +1,396 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2017. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * %CopyrightEnd%
+ */
+
+#include "erl_osenv.h"
+
+#include "global.h"
+#include "erl_alloc.h"
+#include "erl_process.h"
+
+#define STACKBUF_SIZE (512)
+
+typedef struct __env_rbtnode_t {
+ struct __env_rbtnode_t *parent;
+ struct __env_rbtnode_t *left;
+ struct __env_rbtnode_t *right;
+
+ int is_red;
+
+ erts_osenv_data_t key;
+ erts_osenv_data_t value;
+} env_rbtnode_t;
+
+#define ERTS_RBT_PREFIX env
+#define ERTS_RBT_T env_rbtnode_t
+#define ERTS_RBT_KEY_T erts_osenv_data_t
+#define ERTS_RBT_FLAGS_T int
+#define ERTS_RBT_INIT_EMPTY_TNODE(T) \
+ do { \
+ (T)->parent = NULL; \
+ (T)->left = NULL; \
+ (T)->right = NULL; \
+ (T)->is_red = 0; \
+ } while(0)
+#define ERTS_RBT_IS_RED(T) ((T)->is_red)
+#define ERTS_RBT_SET_RED(T) ((T)->is_red = 1)
+#define ERTS_RBT_IS_BLACK(T) (!ERTS_RBT_IS_RED(T))
+#define ERTS_RBT_SET_BLACK(T) ((T)->is_red = 0)
+#define ERTS_RBT_GET_FLAGS(T) ((T)->is_red)
+#define ERTS_RBT_SET_FLAGS(T, F) ((T)->is_red = F)
+#define ERTS_RBT_GET_PARENT(T) ((T)->parent)
+#define ERTS_RBT_SET_PARENT(T, P) ((T)->parent = P)
+#define ERTS_RBT_GET_RIGHT(T) ((T)->right)
+#define ERTS_RBT_SET_RIGHT(T, R) ((T)->right = (R))
+#define ERTS_RBT_GET_LEFT(T) ((T)->left)
+#define ERTS_RBT_SET_LEFT(T, L) ((T)->left = (L))
+#define ERTS_RBT_GET_KEY(T) ((T)->key)
+#define ERTS_RBT_IS_LT(KX, KY) (compare_env_keys(KX, KY) < 0)
+#define ERTS_RBT_IS_EQ(KX, KY) (compare_env_keys(KX, KY) == 0)
+#define ERTS_RBT_WANT_FOREACH_DESTROY
+#define ERTS_RBT_WANT_FOREACH
+#define ERTS_RBT_WANT_REPLACE
+#define ERTS_RBT_WANT_DELETE
+#define ERTS_RBT_WANT_INSERT
+#define ERTS_RBT_WANT_LOOKUP
+
+static int compare_env_keys(const erts_osenv_data_t a, const erts_osenv_data_t b);
+
+#include "erl_rbtree.h"
+
+static int compare_env_keys(const erts_osenv_data_t a, const erts_osenv_data_t b) {
+ int relation = sys_memcmp(a.data, b.data, MIN(a.length, b.length));
+
+ if(relation != 0) {
+ return relation;
+ }
+
+ if(a.length < b.length) {
+ return -1;
+ } else if(a.length == b.length) {
+ return 0;
+ } else {
+ return 1;
+ }
+}
+
+static void *convert_value_to_native(Eterm term, char *stackbuf,
+ int stackbuf_size, Sint *length) {
+ int encoding;
+ void *result;
+
+ if(is_atom(term)) {
+ return NULL;
+ }
+
+ encoding = erts_get_native_filename_encoding();
+ *length = erts_native_filename_need(term, encoding);
+
+ if(*length < 0) {
+ return NULL;
+ } else if(*length >= stackbuf_size) {
+ result = erts_alloc(ERTS_ALC_T_TMP, *length);
+ } else {
+ result = stackbuf;
+ }
+
+ erts_native_filename_put(term, encoding, (byte*)result);
+
+ return result;
+}
+
+static void *convert_key_to_native(Eterm term, char *stackbuf,
+ int stackbuf_size, Sint *length) {
+ byte *name_iterator, *name_end;
+ void *result;
+ int encoding;
+
+ result = convert_value_to_native(term, stackbuf, stackbuf_size, length);
+
+ if(result == NULL || length == 0) {
+ return NULL;
+ }
+
+ encoding = erts_get_native_filename_encoding();
+
+ name_iterator = (byte*)result;
+ name_end = &name_iterator[*length];
+
+#ifdef __WIN32__
+ /* Windows stores per-drive working directories as variables starting with
+ * '=', so we skip the first character to tolerate that. */
+ name_iterator = erts_raw_env_next_char(name_iterator, encoding);
+#endif
+
+ while(name_iterator < name_end) {
+ if(erts_raw_env_char_is_7bit_ascii_char('=', name_iterator, encoding)) {
+ if(result != stackbuf) {
+ erts_free(ERTS_ALC_T_TMP, result);
+ }
+
+ return NULL;
+ }
+
+ name_iterator = erts_raw_env_next_char(name_iterator, encoding);
+ }
+
+ return result;
+}
+
+void erts_osenv_init(erts_osenv_t *env) {
+ env->variable_count = 0;
+ env->content_size = 0;
+ env->tree = NULL;
+}
+
+static void destroy_foreach(env_rbtnode_t *node, void *_state) {
+ erts_free(ERTS_ALC_T_ENVIRONMENT, node);
+ (void)_state;
+}
+
+void erts_osenv_clear(erts_osenv_t *env) {
+ env_rbt_foreach_destroy(&env->tree, &destroy_foreach, NULL);
+ erts_osenv_init(env);
+}
+
+struct __env_merge {
+ int overwrite_existing;
+ erts_osenv_t *env;
+};
+
+static void merge_foreach(env_rbtnode_t *node, void *_state) {
+ struct __env_merge *state = (struct __env_merge*)(_state);
+ env_rbtnode_t *existing_node;
+
+ existing_node = env_rbt_lookup(state->env->tree, node->key);
+
+ if(existing_node == NULL || state->overwrite_existing) {
+ erts_osenv_put_native(state->env, &node->key, &node->value);
+ }
+}
+
+void erts_osenv_merge(erts_osenv_t *env, const erts_osenv_t *with, int overwrite) {
+ struct __env_merge merge_state;
+
+ merge_state.overwrite_existing = overwrite;
+ merge_state.env = env;
+
+ env_rbt_foreach(with->tree, merge_foreach, &merge_state);
+}
+
+struct __env_foreach_term {
+ erts_osenv_foreach_term_cb_t user_callback;
+ struct process *process;
+ void *user_state;
+};
+
+static void foreach_term_wrapper(env_rbtnode_t *node, void *_state) {
+ struct __env_foreach_term *state = (struct __env_foreach_term*)_state;
+ Eterm key, value;
+
+ key = erts_convert_native_to_filename(state->process,
+ node->key.length, (byte*)node->key.data);
+ value = erts_convert_native_to_filename(state->process,
+ node->value.length, (byte*)node->value.data);
+
+ state->user_callback(state->process, state->user_state, key, value);
+}
+
+void erts_osenv_foreach_term(const erts_osenv_t *env, struct process *process,
+ void *state, erts_osenv_foreach_term_cb_t callback) {
+ struct __env_foreach_term wrapper_state;
+
+ wrapper_state.user_callback = callback;
+ wrapper_state.user_state = state;
+ wrapper_state.process = process;
+
+ env_rbt_foreach(env->tree, foreach_term_wrapper, &wrapper_state);
+}
+
+int erts_osenv_get_term(const erts_osenv_t *env, Process *process,
+ Eterm key_term, Eterm *out_term) {
+ char key_stackbuf[STACKBUF_SIZE];
+ erts_osenv_data_t key;
+ int result;
+
+ key.data = convert_key_to_native(key_term, key_stackbuf,
+ STACKBUF_SIZE, &key.length);
+ result = -1;
+
+ if(key.data != NULL) {
+ env_rbtnode_t *node;
+
+ node = env_rbt_lookup(env->tree, key);
+ result = 0;
+
+ if(node != NULL) {
+ (*out_term) = erts_convert_native_to_filename(process,
+ node->value.length, (byte*)node->value.data);
+ result = 1;
+ }
+
+ if(key.data != key_stackbuf) {
+ erts_free(ERTS_ALC_T_TMP, key.data);
+ }
+ }
+
+ return result;
+}
+
+int erts_osenv_put_term(erts_osenv_t *env, Eterm key_term, Eterm value_term) {
+ char key_stackbuf[STACKBUF_SIZE], value_stackbuf[STACKBUF_SIZE];
+ erts_osenv_data_t key, value;
+ int result;
+
+ key.data = convert_key_to_native(key_term, key_stackbuf,
+ STACKBUF_SIZE, &key.length);
+ value.data = convert_value_to_native(value_term, value_stackbuf,
+ STACKBUF_SIZE, &value.length);
+ result = -1;
+
+ if(value.data != NULL && key.data != NULL) {
+ result = erts_osenv_put_native(env, &key, &value);
+ }
+
+ if(value.data != NULL && value.data != value_stackbuf) {
+ erts_free(ERTS_ALC_T_TMP, value.data);
+ }
+
+ if(key.data != NULL && key.data != key_stackbuf) {
+ erts_free(ERTS_ALC_T_TMP, key.data);
+ }
+
+ return result;
+}
+
+int erts_osenv_unset_term(erts_osenv_t *env, Eterm key_term) {
+ char key_stackbuf[STACKBUF_SIZE];
+ erts_osenv_data_t key;
+ int result;
+
+ key.data = convert_key_to_native(key_term, key_stackbuf,
+ STACKBUF_SIZE, &key.length);
+ result = -1;
+
+ if(key.data != NULL) {
+ result = erts_osenv_unset_native(env, &key);
+
+ if(key.data != key_stackbuf) {
+ erts_free(ERTS_ALC_T_TMP, key.data);
+ }
+ }
+
+ return result;
+}
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+struct __env_foreach_native {
+ erts_osenv_foreach_native_cb_t user_callback;
+ void *user_state;
+};
+
+static void foreach_native_wrapper(env_rbtnode_t *node, void *_state) {
+ struct __env_foreach_native *state = (struct __env_foreach_native*)_state;
+
+ state->user_callback(state->user_state, &node->key, &node->value);
+}
+
+void erts_osenv_foreach_native(const erts_osenv_t *env, void *state,
+ erts_osenv_foreach_native_cb_t callback) {
+ struct __env_foreach_native wrapper_state;
+
+ wrapper_state.user_callback = callback;
+ wrapper_state.user_state = state;
+
+ env_rbt_foreach(env->tree, foreach_native_wrapper, &wrapper_state);
+}
+
+int erts_osenv_get_native(const erts_osenv_t *env,
+ const erts_osenv_data_t *key,
+ erts_osenv_data_t *value) {
+ env_rbtnode_t *node = env_rbt_lookup(env->tree, *key);
+
+ if(node != NULL) {
+ if(value != NULL) {
+ if(node->value.length > value->length) {
+ return -1;
+ }
+
+ sys_memcpy(value->data, node->value.data, node->value.length);
+ value->length = node->value.length;
+ }
+
+ return 1;
+ }
+
+ return 0;
+}
+
+int erts_osenv_put_native(erts_osenv_t *env, const erts_osenv_data_t *key,
+ const erts_osenv_data_t *value) {
+ env_rbtnode_t *old_node, *new_node;
+
+ new_node = erts_alloc(ERTS_ALC_T_ENVIRONMENT, sizeof(env_rbtnode_t) +
+ key->length + value->length);
+
+ new_node->key.data = (char*)(&new_node[1]);
+ new_node->key.length = key->length;
+ new_node->value.data = &((char*)new_node->key.data)[key->length];
+ new_node->value.length = value->length;
+
+ sys_memcpy(new_node->key.data, key->data, key->length);
+ sys_memcpy(new_node->value.data, value->data, value->length);
+
+ old_node = env_rbt_lookup(env->tree, *key);
+
+ if(old_node != NULL) {
+ env->content_size -= old_node->value.length;
+ env->content_size -= old_node->key.length;
+ env_rbt_replace(&env->tree, old_node, new_node);
+ } else {
+ env_rbt_insert(&env->tree, new_node);
+ env->variable_count++;
+ }
+
+ env->content_size += new_node->value.length;
+ env->content_size += new_node->key.length;
+
+ if(old_node != NULL) {
+ erts_free(ERTS_ALC_T_ENVIRONMENT, old_node);
+ }
+
+ return 1;
+}
+
+int erts_osenv_unset_native(erts_osenv_t *env, const erts_osenv_data_t *key) {
+ env_rbtnode_t *old_node = env_rbt_lookup(env->tree, *key);
+
+ if(old_node != NULL) {
+ env->content_size -= old_node->value.length;
+ env->content_size -= old_node->key.length;
+ env->variable_count -= 1;
+
+ env_rbt_delete(&env->tree, old_node);
+ erts_free(ERTS_ALC_T_ENVIRONMENT, old_node);
+ return 1;
+ }
+
+ return 0;
+}
diff --git a/erts/emulator/sys/common/erl_osenv.h b/erts/emulator/sys/common/erl_osenv.h
new file mode 100644
index 0000000000..4777f2148a
--- /dev/null
+++ b/erts/emulator/sys/common/erl_osenv.h
@@ -0,0 +1,121 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2017. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * %CopyrightEnd%
+ */
+
+/* This is a replacement for getenv(3) and friends, operating on instances so
+ * we can keep a common implementation for both the global and local (per-port)
+ * environments.
+ *
+ * The instances are not thread-safe on their own but unlike getenv(3) we're
+ * guaranteed to be the only user, so placing locks around all our accesses
+ * will suffice.
+ *
+ * Use erts_sys_rwlock_global_osenv to access the global environment. */
+
+#ifndef __ERL_OSENV_H__
+#define __ERL_OSENV_H__
+
+#ifdef HAVE_CONFIG_H
+# 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"
+
+struct __erts_osenv_data_t {
+ Sint length;
+ void *data;
+};
+
+void erts_osenv_init(erts_osenv_t *env);
+void erts_osenv_clear(erts_osenv_t *env);
+
+/* @brief Merges \c with into \c env
+ *
+ * @param overwrite Whether to overwrite existing entries or keep them as they
+ * are. */
+void erts_osenv_merge(erts_osenv_t *env, const erts_osenv_t *with, int overwrite);
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+/* @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]
+ *
+ * @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]
+ *
+ * @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
+ *
+ * @param value [in,out] The buffer to copy the value into, may be NULL if you
+ * only wish to query presence.
+ *
+ * @return 1 on success, 0 if the key couldn't be found, and -1 if if the value
+ * didn't fit into the buffer. */
+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]
+ *
+ * @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.
+ *
+ * @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);
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+typedef void (*erts_osenv_foreach_term_cb_t)(struct process *process,
+ void *state, Eterm key, Eterm value);
+
+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. */
+void erts_osenv_foreach_term(const erts_osenv_t *env, struct process *process,
+ void *state, erts_osenv_foreach_term_cb_t callback);
+
+/* @copydoc erts_osenv_foreach_term */
+void erts_osenv_foreach_native(const erts_osenv_t *env, void *state,
+ erts_osenv_foreach_native_cb_t callback);
+
+#endif
diff --git a/erts/emulator/sys/common/erl_poll.c b/erts/emulator/sys/common/erl_poll.c
index 30a595c17a..ced8a4a2a7 100644
--- a/erts/emulator/sys/common/erl_poll.c
+++ b/erts/emulator/sys/common/erl_poll.c
@@ -782,10 +782,14 @@ update_pollset(ErtsPollSet *ps, int fd, ErtsPollOp op, ErtsPollEvents events)
struct kevent evts[2];
struct timespec ts = {0, 0};
-#ifdef EV_DISPATCH
- /* If we have EV_DISPATCH we use it. The kevent descriptions for both
- read and write are added on OP_ADD and removed on OP_DEL. And then
- after than only EV_ENABLE|EV_DISPATCH are used.
+#if defined(EV_DISPATCH) && !defined(__OpenBSD__)
+ /* If we have EV_DISPATCH we use it, unless we are on OpenBSD as the
+ behavior of EV_EOF seems to be edge triggered there and we need it
+ to be level triggered.
+
+ The kevent descriptions for both read and write are added on OP_ADD
+ and removed on OP_DEL. And then after than only EV_ENABLE|EV_DISPATCH
+ are used.
It could be possible to not modify the pollset when disabling and/or
deleting events, but that may cause the poll threads to be awoken
@@ -2059,7 +2063,8 @@ uint32_t epoll_events(int kp_fd, int fd)
int ev_fd;
uint32_t events;
uint64_t data;
- if (fscanf(f,"tfd:%d events:%x data:%lx\n", &ev_fd, &events, &data) != 3) {
+ if (fscanf(f,"tfd:%d events:%x data:%llx\n", &ev_fd, &events,
+ (unsigned long long*)&data) != 3) {
fprintf(stderr,"failed to parse file %s on line %d, errno = %d\n", fname,
line,
errno);
@@ -2125,7 +2130,8 @@ ERTS_POLL_EXPORT(erts_poll_get_selected_events)(ErtsPollSet *ps,
int fd;
uint32_t events;
uint64_t data;
- if (fscanf(f,"tfd:%d events:%x data:%lx\n", &fd, &events, &data) != 3) {
+ if (fscanf(f,"tfd:%d events:%x data:%llx\n", &fd, &events,
+ (unsigned long long*)&data) != 3) {
fprintf(stderr,"failed to parse file %s on line %d, errno = %d\n",
fname, line, errno);
ASSERT(0);
diff --git a/erts/emulator/sys/common/erl_sys_common_misc.c b/erts/emulator/sys/common/erl_sys_common_misc.c
index 420138ff0a..41a6fcb7e1 100644
--- a/erts/emulator/sys/common/erl_sys_common_misc.c
+++ b/erts/emulator/sys/common/erl_sys_common_misc.c
@@ -142,7 +142,16 @@ sys_double_to_chars(double fp, char *buffer, size_t buffer_size)
return sys_double_to_chars_ext(fp, buffer, buffer_size, SYS_DEFAULT_FLOAT_DECIMALS);
}
-/* Convert float to string using fixed point notation.
+
+#if SIZEOF_LONG == 8
+# define round_int64 lround
+#elif SIZEOF_LONG_LONG == 8
+# define round_int64 llround
+#else
+# error "No 64-bit integer type?"
+#endif
+
+/* Convert float to string
* decimals must be >= 0
* if compact != 0, the trailing 0's will be truncated
*/
@@ -150,93 +159,39 @@ int
sys_double_to_chars_fast(double f, char *buffer, int buffer_size, int decimals,
int compact)
{
- /* Note that some C compilers don't support "static const" propagation
- * so we use a defines */
- #define SYS_DOUBLE_RND_CONST 0.55555555555555555
+ #define SYS_DOUBLE_RND_CONST 0.5
#define FRAC_SIZE 52
#define EXP_SIZE 11
- #define EXP_MASK ((1ll << EXP_SIZE) - 1)
- #define MAX_DECIMALS (sizeof(cs_sys_double_pow10) \
- / sizeof(cs_sys_double_pow10[0]))
- #define FRAC_MASK ((1ll << FRAC_SIZE) - 1)
- #define FRAC_MASK2 ((1ll << (FRAC_SIZE + 1)) - 1)
- #define MAX_FLOAT (1ll << (FRAC_SIZE+1))
-
- static const double cs_sys_double_pow10[] = {
- SYS_DOUBLE_RND_CONST / 1ll,
- SYS_DOUBLE_RND_CONST / 10ll,
- SYS_DOUBLE_RND_CONST / 100ll,
- SYS_DOUBLE_RND_CONST / 1000ll,
- SYS_DOUBLE_RND_CONST / 10000ll,
- SYS_DOUBLE_RND_CONST / 100000ll,
- SYS_DOUBLE_RND_CONST / 1000000ll,
- SYS_DOUBLE_RND_CONST / 10000000ll,
- SYS_DOUBLE_RND_CONST / 100000000ll,
- SYS_DOUBLE_RND_CONST / 1000000000ll,
- SYS_DOUBLE_RND_CONST / 10000000000ll,
- SYS_DOUBLE_RND_CONST / 100000000000ll,
- SYS_DOUBLE_RND_CONST / 1000000000000ll,
- SYS_DOUBLE_RND_CONST / 10000000000000ll,
- SYS_DOUBLE_RND_CONST / 100000000000000ll,
- SYS_DOUBLE_RND_CONST / 1000000000000000ll,
- SYS_DOUBLE_RND_CONST / 10000000000000000ll,
- SYS_DOUBLE_RND_CONST / 100000000000000000ll,
- SYS_DOUBLE_RND_CONST / 1000000000000000000ll
+ #define EXP_MASK (((Uint64)1 << EXP_SIZE) - 1)
+ #define MAX_DECIMALS (sizeof(pow10v) / sizeof(pow10v[0]))
+ #define FRAC_MASK (((Uint64)1 << FRAC_SIZE) - 1)
+ #define FRAC_MASK2 (((Uint64)1 << (FRAC_SIZE + 1)) - 1)
+ #define MAX_FLOAT ((Uint64)1 << (FRAC_SIZE+1))
+
+ static const double pow10v[] = {
+ 1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9,
+ 1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18
};
- long long mantissa, int_part = 0, frac_part = 0;
- short exp;
- int max;
+ double af;
+ Uint64 int_part, frac_part;
int neg;
- double fr;
- union { long long L; double F; } x;
char *p = buffer;
if (decimals < 0)
return -1;
- /* Round the number to given decimal places. The number of 5's in the
- * SYS_DOUBLE_RND_CONST constant is chosen such that adding any more 5's doesn't
- * change the double precision of the number, i.e.:
- * 1> term_to_binary(0.55555555555555555, [{minor_version, 1}]).
- * <<131,70,63,225,199,28,113,199,28,114>>
- * 2> term_to_binary(0.5555555555555555555, [{minor_version, 1}]).
- * <<131,70,63,225,199,28,113,199,28,114>>
- */
- if (f >= 0) {
- neg = 0;
- fr = decimals < MAX_DECIMALS ? (f + cs_sys_double_pow10[decimals]) : f;
- x.F = fr;
- } else {
+ if (f < 0) {
neg = 1;
- fr = decimals < MAX_DECIMALS ? (f - cs_sys_double_pow10[decimals]) : f;
- x.F = -fr;
+ af = -f;
}
-
- exp = (x.L >> FRAC_SIZE) & EXP_MASK;
- mantissa = x.L & FRAC_MASK;
-
- if (exp == EXP_MASK) {
- if (mantissa == 0) {
- if (neg)
- *p++ = '-';
- *p++ = 'i';
- *p++ = 'n';
- *p++ = 'f';
- } else {
- *p++ = 'n';
- *p++ = 'a';
- *p++ = 'n';
- }
- *p = '\0';
- return p - buffer;
+ else {
+ neg = 0;
+ af = f;
}
- exp -= EXP_MASK >> 1;
- mantissa |= (1ll << FRAC_SIZE);
-
/* Don't bother with optimizing too large numbers or too large precision */
- if (x.F > MAX_FLOAT || decimals >= MAX_DECIMALS) {
+ if (af > MAX_FLOAT || decimals >= MAX_DECIMALS) {
int len = erts_snprintf(buffer, buffer_size, "%.*f", decimals, f);
char* p = buffer + len;
if (len >= buffer_size)
@@ -246,62 +201,64 @@ sys_double_to_chars_fast(double f, char *buffer, int buffer_size, int decimals,
p = find_first_trailing_zero(p);
*p = '\0';
return p - buffer;
- } else if (exp >= FRAC_SIZE) {
- int_part = mantissa << (exp - FRAC_SIZE);
- } else if (exp >= 0) {
- int_part = mantissa >> (FRAC_SIZE - exp);
- frac_part = (mantissa << (exp + 1)) & FRAC_MASK2;
- } else /* if (exp < 0) */ {
- frac_part = (mantissa & FRAC_MASK2) >> -(exp + 1);
}
- if (!int_part) {
- if (neg)
- *p++ = '-';
- *p++ = '0';
- } else {
- int ret, i, n;
- while (int_part != 0) {
- long long j = int_part / 10;
- *p++ = (char)(int_part - ((j << 3) + (j << 1)) + '0');
- int_part = j;
- }
- if (neg)
- *p++ = '-';
- /* Reverse string */
- ret = p - buffer;
- for (i = 0, n = ret/2; i < n; i++) {
- int j = ret - i - 1;
- char c = buffer[i];
- buffer[i] = buffer[j];
- buffer[j] = c;
- }
- }
-
- if (decimals > 0) {
- int i;
- *p++ = '.';
+ if (decimals) {
+ double int_f = floor(af);
+ double frac_f = round((af - int_f) * pow10v[decimals]);
- max = buffer_size - (p - buffer) - 1 /* leave room for trailing '\0' */;
+ int_part = (Uint64)int_f;
+ frac_part = (Uint64)frac_f;
- if (decimals > max)
- return -1; /* the number is not large enough to fit in the buffer */
+ if (frac_f >= pow10v[decimals]) {
+ /* rounding overflow carry into int_part */
+ int_part++;
+ frac_part = 0;
+ }
- max = decimals;
+ do {
+ Uint64 n;
+ if (!frac_part) {
+ do {
+ *p++ = '0';
+ } while (--decimals);
+ break;
+ }
+ n = frac_part / 10;
+ *p++ = (char)((frac_part - n*10) + '0');
+ frac_part = n;
+ } while (--decimals);
- for (i = 0; i < max; i++) {
- /* frac_part *= 10; */
- frac_part = (frac_part << 3) + (frac_part << 1);
+ *p++ = '.';
+ }
+ else
+ int_part = (Uint64)round_int64(af);
- *p++ = (char)((frac_part >> (FRAC_SIZE + 1)) + '0');
- frac_part &= FRAC_MASK2;
+ if (!int_part) {
+ *p++ = '0';
+ } else {
+ do {
+ Uint64 n = int_part / 10;
+ *p++ = (char)((int_part - n*10) + '0');
+ int_part = n;
+ } while (int_part);
+ }
+ if (neg)
+ *p++ = '-';
+
+ {/* Reverse string */
+ int i = 0;
+ int j = p - buffer - 1;
+ for ( ; i < j; i++, j--) {
+ char tmp = buffer[i];
+ buffer[i] = buffer[j];
+ buffer[j] = tmp;
}
-
- /* Delete trailing zeroes */
- if (compact)
- p = find_first_trailing_zero(p);
}
+ /* Delete trailing zeroes */
+ if (compact)
+ p = find_first_trailing_zero(p);
*p = '\0';
return p - buffer;
}
diff --git a/erts/emulator/sys/unix/erl_child_setup.c b/erts/emulator/sys/unix/erl_child_setup.c
index 69fc6c2879..10601529a4 100644
--- a/erts/emulator/sys/unix/erl_child_setup.c
+++ b/erts/emulator/sys/unix/erl_child_setup.c
@@ -56,6 +56,8 @@
#include <stdio.h>
#include <stdarg.h>
#include <sys/wait.h>
+#include <sys/types.h>
+#include <sys/socket.h>
#define WANT_NONBLOCKING
@@ -437,6 +439,21 @@ main(int argc, char *argv[])
exit(1);
}
+ /* Ignore SIGTERM.
+ Some container environments send SIGTERM to all processes
+ when terminating. We don't want erl_child_setup to terminate
+ in these cases as that will prevent beam from properly
+ cleaning up.
+ */
+ sa.sa_handler = SIG_IGN;
+ sigemptyset(&sa.sa_mask);
+ sa.sa_flags = 0;
+
+ if (sigaction(SIGTERM, &sa, 0) == -1) {
+ perror(NULL);
+ exit(1);
+ }
+
forker_hash_init();
SET_CLOEXEC(uds_fd);
diff --git a/erts/emulator/sys/unix/erl_unix_sys.h b/erts/emulator/sys/unix/erl_unix_sys.h
index b6f5b319ee..e367d565a7 100644
--- a/erts/emulator/sys/unix/erl_unix_sys.h
+++ b/erts/emulator/sys/unix/erl_unix_sys.h
@@ -133,7 +133,7 @@
#define ERTS_SYS_CONTINOUS_FD_NUMBERS
-typedef void *GETENV_STATE;
+void erts_sys_env_init(void);
/*
** For the erl_timer_sup module.
diff --git a/erts/emulator/sys/unix/sys.c b/erts/emulator/sys/unix/sys.c
index 6315135151..189ca083d7 100644
--- a/erts/emulator/sys/unix/sys.c
+++ b/erts/emulator/sys/unix/sys.c
@@ -62,9 +62,6 @@
#include "erl_mseg.h"
-extern char **environ;
-erts_rwmtx_t environ_rwmtx;
-
#define MAX_VSIZE 16 /* Max number of entries allowed in an I/O
* vector sock_sendv().
*/
@@ -77,7 +74,7 @@ erts_rwmtx_t environ_rwmtx;
#include "erl_check_io.h"
#include "erl_cpu_topology.h"
-
+#include "erl_osenv.h"
extern int driver_interrupt(int, int);
extern void do_break(void);
@@ -454,10 +451,10 @@ prepare_crash_dump(int secs)
close(crashdump_companion_cube_fd);
envsz = sizeof(env);
- i = erts_sys_getenv__("ERL_CRASH_DUMP_NICE", env, &envsz);
+ i = erts_sys_explicit_8bit_getenv("ERL_CRASH_DUMP_NICE", env, &envsz);
if (i >= 0) {
int nice_val;
- nice_val = i != 0 ? 0 : atoi(env);
+ nice_val = i != 1 ? 0 : atoi(env);
if (nice_val > 39) {
nice_val = 39;
}
@@ -749,34 +746,6 @@ void os_version(int *pMajor, int *pMinor, int *pBuild) {
*pBuild = get_number(&release); /* Pointer to build number. */
}
-void init_getenv_state(GETENV_STATE *state)
-{
- erts_rwmtx_rlock(&environ_rwmtx);
- *state = NULL;
-}
-
-char *getenv_string(GETENV_STATE *state0)
-{
- char **state = (char **) *state0;
- char *cp;
-
- ERTS_LC_ASSERT(erts_lc_rwmtx_is_rlocked(&environ_rwmtx));
-
- if (state == NULL)
- state = environ;
-
- cp = *state++;
- *state0 = (GETENV_STATE) state;
-
- return cp;
-}
-
-void fini_getenv_state(GETENV_STATE *state)
-{
- *state = NULL;
- erts_rwmtx_runlock(&environ_rwmtx);
-}
-
void erts_do_break_handling(void)
{
struct termios temp_mode;
@@ -830,90 +799,6 @@ void sys_get_pid(char *buffer, size_t buffer_size){
erts_snprintf(buffer, buffer_size, "%lu",(unsigned long) p);
}
-int
-erts_sys_putenv_raw(char *key, char *value) {
- return erts_sys_putenv(key, value);
-}
-int
-erts_sys_putenv(char *key, char *value)
-{
- int res;
- char *env;
- Uint need = strlen(key) + strlen(value) + 2;
-
-#ifdef HAVE_COPYING_PUTENV
- env = erts_alloc(ERTS_ALC_T_TMP, need);
-#else
- env = erts_alloc(ERTS_ALC_T_PUTENV_STR, need);
- erts_atomic_add_nob(&sys_misc_mem_sz, need);
-#endif
- strcpy(env,key);
- strcat(env,"=");
- strcat(env,value);
- erts_rwmtx_rwlock(&environ_rwmtx);
- res = putenv(env);
- erts_rwmtx_rwunlock(&environ_rwmtx);
-#ifdef HAVE_COPYING_PUTENV
- erts_free(ERTS_ALC_T_TMP, env);
-#endif
- return res;
-}
-
-int
-erts_sys_getenv__(char *key, char *value, size_t *size)
-{
- int res;
- char *orig_value = getenv(key);
- if (!orig_value)
- res = -1;
- else {
- size_t len = sys_strlen(orig_value);
- if (len >= *size) {
- *size = len + 1;
- res = 1;
- }
- else {
- *size = len;
- sys_memcpy((void *) value, (void *) orig_value, len+1);
- res = 0;
- }
- }
- return res;
-}
-
-int
-erts_sys_getenv_raw(char *key, char *value, size_t *size) {
- return erts_sys_getenv(key, value, size);
-}
-
-/*
- * erts_sys_getenv
- * returns:
- * -1, if environment key is not set with a value
- * 0, if environment key is set and value fits into buffer size
- * 1, if environment key is set but does not fit into buffer size
- * size is set with the needed buffer size value
- */
-
-int
-erts_sys_getenv(char *key, char *value, size_t *size)
-{
- int res;
- erts_rwmtx_rlock(&environ_rwmtx);
- res = erts_sys_getenv__(key, value, size);
- erts_rwmtx_runlock(&environ_rwmtx);
- return res;
-}
-
-int
-erts_sys_unsetenv(char *key)
-{
- int res;
- erts_rwmtx_rwlock(&environ_rwmtx);
- res = unsetenv(key);
- erts_rwmtx_rwunlock(&environ_rwmtx);
- return res;
-}
void sys_init_io(void) { }
void erts_sys_alloc_init(void) { }
@@ -1260,14 +1145,9 @@ erts_sys_main_thread(void)
}
}
-
void
erl_sys_args(int* argc, char** argv)
{
-
- erts_rwmtx_init(&environ_rwmtx, "environ", NIL,
- ERTS_LOCK_FLAGS_PROPERTY_STATIC | ERTS_LOCK_FLAGS_CATEGORY_GENERIC);
-
ASSERT(argc && argv);
max_files = erts_check_io_max_files();
@@ -1275,4 +1155,5 @@ erl_sys_args(int* argc, char** argv)
init_smp_sig_notify();
init_smp_sig_suspend();
+ erts_sys_env_init();
}
diff --git a/erts/emulator/sys/unix/sys_drivers.c b/erts/emulator/sys/unix/sys_drivers.c
index 0228e1af54..117855acf0 100644
--- a/erts/emulator/sys/unix/sys_drivers.c
+++ b/erts/emulator/sys/unix/sys_drivers.c
@@ -50,14 +50,14 @@
#include <sys/ioctl.h>
#endif
+#include <sys/types.h>
+#include <sys/socket.h>
+
#define WANT_NONBLOCKING /* must define this to pull in defs from sys.h */
#include "sys.h"
#include "erl_threads.h"
-extern char **environ;
-extern erts_rwmtx_t environ_rwmtx;
-
extern erts_atomic_t sys_misc_mem_sz;
static Eterm forker_port;
@@ -180,7 +180,7 @@ erl_sys_late_init(void)
opts.read_write = 0;
opts.hide_window = 0;
opts.wd = NULL;
- opts.envir = NULL;
+ erts_osenv_init(&opts.envir);
opts.exit_status = 0;
opts.overlapped_io = 0;
opts.spawn_type = ERTS_SPAWN_ANY;
@@ -443,85 +443,55 @@ static void close_pipes(int ifd[2], int ofd[2])
close(ofd[1]);
}
-static char **build_unix_environment(char *block)
+struct __add_spawn_env_state {
+ struct iovec *iov;
+ int *iov_index;
+
+ Sint32 *payload_size;
+ char *env_block;
+};
+
+static void add_spawn_env_block_foreach(void *_state,
+ const erts_osenv_data_t *key,
+ const erts_osenv_data_t *value)
{
- int i;
- int j;
- int len;
- char *cp;
- char **cpp;
- char** old_env;
-
- ERTS_LC_ASSERT(erts_lc_rwmtx_is_rlocked(&environ_rwmtx));
-
- cp = block;
- len = 0;
- while (*cp != '\0') {
- cp += strlen(cp) + 1;
- len++;
- }
- old_env = environ;
- while (*old_env++ != NULL) {
- len++;
- }
-
- cpp = (char **) erts_alloc_fnf(ERTS_ALC_T_ENVIRONMENT,
- sizeof(char *) * (len+1));
- if (cpp == NULL) {
- return NULL;
- }
+ struct __add_spawn_env_state *state;
+ struct iovec *iov;
- cp = block;
- len = 0;
- while (*cp != '\0') {
- cpp[len] = cp;
- cp += strlen(cp) + 1;
- len++;
- }
-
- i = len;
- for (old_env = environ; *old_env; old_env++) {
- char* old = *old_env;
-
- for (j = 0; j < len; j++) {
- char *s, *t;
-
- /* check if cpp[j] equals old
- before the = sign,
- i.e.
- "TMPDIR=/tmp/" */
- s = cpp[j];
- t = old;
- while (*s == *t && *s != '=') {
- s++, t++;
- }
- if (*s == '=' && *t == '=') {
- break;
- }
- }
+ state = (struct __add_spawn_env_state*)(_state);
+ iov = &state->iov[*state->iov_index];
- if (j == len) { /* New version not found */
- cpp[len++] = old;
- }
- }
+ iov->iov_base = state->env_block;
- for (j = 0; j < i; ) {
- size_t last = strlen(cpp[j])-1;
- if (cpp[j][last] == '=' && strchr(cpp[j], '=') == cpp[j]+last) {
- cpp[j] = cpp[--len];
- if (len < i) {
- i--;
- } else {
- j++;
- }
- }
- else {
- j++;
- }
- }
+ sys_memcpy(state->env_block, key->data, key->length);
+ state->env_block += key->length;
+ *state->env_block++ = '=';
+ sys_memcpy(state->env_block, value->data, value->length);
+ state->env_block += value->length;
+ *state->env_block++ = '\0';
- cpp[len] = NULL;
- return cpp;
+ iov->iov_len = state->env_block - (char*)iov->iov_base;
+
+ (*state->payload_size) += iov->iov_len;
+ (*state->iov_index)++;
+}
+
+static void *add_spawn_env_block(const erts_osenv_t *env, struct iovec *iov,
+ int *iov_index, Sint32 *payload_size) {
+ struct __add_spawn_env_state add_state;
+ char *env_block;
+
+ env_block = erts_alloc(ERTS_ALC_T_TMP, env->content_size +
+ env->variable_count * sizeof("=\0"));
+
+ add_state.iov = iov;
+ add_state.iov_index = iov_index;
+ add_state.env_block = env_block;
+ add_state.payload_size = payload_size;
+
+ erts_osenv_foreach_native(env, &add_state, add_spawn_env_block_foreach);
+
+ return env_block;
}
static ErlDrvData spawn_start(ErlDrvPort port_num, char* name,
@@ -531,7 +501,6 @@ static ErlDrvData spawn_start(ErlDrvPort port_num, char* name,
#define CMD_LINE_PREFIX_STR_SZ (sizeof(CMD_LINE_PREFIX_STR) - 1)
int len;
- char **new_environ;
ErtsSysDriverData *dd;
char *cmd_line;
char wd_buff[MAXPATHLEN+1];
@@ -598,19 +567,7 @@ static ErlDrvData spawn_start(ErlDrvPort port_num, char* name,
memcpy((void *) (cmd_line + CMD_LINE_PREFIX_STR_SZ), (void *) name, len);
cmd_line[CMD_LINE_PREFIX_STR_SZ + len] = '\0';
len = CMD_LINE_PREFIX_STR_SZ + len + 1;
- }
-
- erts_rwmtx_rlock(&environ_rwmtx);
-
- if (opts->envir == NULL) {
- new_environ = environ;
- } else if ((new_environ = build_unix_environment(opts->envir)) == NULL) {
- erts_rwmtx_runlock(&environ_rwmtx);
- close_pipes(ifd, ofd);
- erts_free(ERTS_ALC_T_TMP, (void *) cmd_line);
- errno = ENOMEM;
- return ERL_DRV_ERROR_ERRNO;
- }
+}
if ((cwd = getcwd(wd_buff, MAXPATHLEN+1)) == NULL) {
/* on some OSs this call opens a fd in the
@@ -619,9 +576,6 @@ static ErlDrvData spawn_start(ErlDrvPort port_num, char* name,
int err = errno;
close_pipes(ifd, ofd);
erts_free(ERTS_ALC_T_TMP, (void *) cmd_line);
- if (new_environ != environ)
- erts_free(ERTS_ALC_T_ENVIRONMENT, (void *) new_environ);
- erts_rwmtx_runlock(&environ_rwmtx);
errno = err;
return ERL_DRV_ERROR_ERRNO;
}
@@ -629,6 +583,7 @@ static ErlDrvData spawn_start(ErlDrvPort port_num, char* name,
wd = opts->wd;
{
+ void *environment_block;
struct iovec *io_vector;
int iov_len = 5;
char nullbuff[] = "\0";
@@ -641,10 +596,8 @@ static ErlDrvData spawn_start(ErlDrvPort port_num, char* name,
if (wd) iov_len++;
- /* count number of elements in environment */
- while(new_environ[env_len] != NULL)
- env_len++;
- iov_len += 1 + env_len; /* num envs including size int */
+ /* num envs including size int */
+ iov_len += 1 + opts->envir.variable_count;
/* count number of element in argument list */
if (opts->spawn_type == ERTS_SPAWN_EXECUTABLE) {
@@ -661,10 +614,7 @@ static ErlDrvData spawn_start(ErlDrvPort port_num, char* name,
if (!io_vector) {
close_pipes(ifd, ofd);
- erts_rwmtx_runlock(&environ_rwmtx);
erts_free(ERTS_ALC_T_TMP, (void *) cmd_line);
- if (new_environ != environ)
- erts_free(ERTS_ALC_T_ENVIRONMENT, (void *) new_environ);
errno = ENOMEM;
return ERL_DRV_ERROR_ERRNO;
}
@@ -699,16 +649,13 @@ static ErlDrvData spawn_start(ErlDrvPort port_num, char* name,
io_vector[i++].iov_len = 1;
buffsz += io_vector[i-1].iov_len;
+ env_len = htonl(opts->envir.variable_count);
io_vector[i].iov_base = (void*)&env_len;
- env_len = htonl(env_len);
io_vector[i++].iov_len = sizeof(env_len);
buffsz += io_vector[i-1].iov_len;
- for (j = 0; new_environ[j] != NULL; j++) {
- io_vector[i].iov_base = new_environ[j];
- io_vector[i++].iov_len = strlen(new_environ[j]) + 1;
- buffsz += io_vector[i-1].iov_len;
- }
+ environment_block = add_spawn_env_block(&opts->envir, io_vector, &i,
+ &buffsz);
/* only append arguments if this was a spawn_executable */
if (opts->spawn_type == ERTS_SPAWN_EXECUTABLE) {
@@ -744,9 +691,6 @@ static ErlDrvData spawn_start(ErlDrvPort port_num, char* name,
int err = errno;
close_pipes(ifd, ofd);
erts_free(ERTS_ALC_T_TMP, io_vector);
- if (new_environ != environ)
- erts_free(ERTS_ALC_T_ENVIRONMENT, (void *) new_environ);
- erts_rwmtx_runlock(&environ_rwmtx);
erts_free(ERTS_ALC_T_TMP, (void *) cmd_line);
errno = err;
return ERL_DRV_ERROR_ERRNO;
@@ -767,16 +711,12 @@ static ErlDrvData spawn_start(ErlDrvPort port_num, char* name,
driver_select(port_num, ofd[1], ERL_DRV_WRITE|ERL_DRV_USE, 1);
}
+ erts_free(ERTS_ALC_T_TMP, environment_block);
erts_free(ERTS_ALC_T_TMP, io_vector);
}
erts_free(ERTS_ALC_T_TMP, (void *) cmd_line);
- if (new_environ != environ)
- erts_free(ERTS_ALC_T_ENVIRONMENT, (void *) new_environ);
-
- erts_rwmtx_runlock(&environ_rwmtx);
-
dd = create_driver_data(port_num, ifd[0], ofd[1], opts->packet_bytes,
DO_WRITE | DO_READ, opts->exit_status,
0, 0);
@@ -1652,15 +1592,13 @@ static ErlDrvData forker_start(ErlDrvPort port_num, char* name,
forker_port = erts_drvport2id(port_num);
- res = erts_sys_getenv_raw("BINDIR", bindir, &bindirsz);
- if (res != 0) {
- if (res < 0)
- erts_exit(1,
- "Environment variable BINDIR is not set\n");
- if (res > 0)
- erts_exit(1,
- "Value of environment variable BINDIR is too large\n");
+ res = erts_sys_explicit_8bit_getenv("BINDIR", bindir, &bindirsz);
+ if (res == 0) {
+ erts_exit(1, "Environment variable BINDIR is not set\n");
+ } else if(res < 0) {
+ erts_exit(1, "Value of environment variable BINDIR is too large\n");
}
+
if (bindir[0] != DIR_SEPARATOR_CHAR)
erts_exit(1,
"Environment variable BINDIR does not contain an"
diff --git a/erts/emulator/sys/unix/sys_env.c b/erts/emulator/sys/unix/sys_env.c
new file mode 100644
index 0000000000..4d8301f985
--- /dev/null
+++ b/erts/emulator/sys/unix/sys_env.c
@@ -0,0 +1,133 @@
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "sys.h"
+#include "erl_osenv.h"
+#include "erl_alloc.h"
+
+#include "erl_thr_progress.h"
+
+static erts_osenv_t sysenv_global_env;
+static erts_rwmtx_t sysenv_rwmtx;
+
+extern char **environ;
+
+static void import_initial_env(void);
+
+void erts_sys_env_init() {
+ erts_rwmtx_init(&sysenv_rwmtx, "environ", NIL,
+ ERTS_LOCK_FLAGS_PROPERTY_STATIC | ERTS_LOCK_FLAGS_CATEGORY_GENERIC);
+
+ erts_osenv_init(&sysenv_global_env);
+ import_initial_env();
+}
+
+const erts_osenv_t *erts_sys_rlock_global_osenv() {
+ erts_rwmtx_rlock(&sysenv_rwmtx);
+ return &sysenv_global_env;
+}
+
+erts_osenv_t *erts_sys_rwlock_global_osenv() {
+ erts_rwmtx_rwlock(&sysenv_rwmtx);
+ return &sysenv_global_env;
+}
+
+void erts_sys_rwunlock_global_osenv() {
+ erts_rwmtx_rwunlock(&sysenv_rwmtx);
+}
+
+void erts_sys_runlock_global_osenv() {
+ erts_rwmtx_runlock(&sysenv_rwmtx);
+}
+
+int erts_sys_explicit_8bit_putenv(char *key, char *value) {
+ erts_osenv_data_t env_key, env_value;
+ int result;
+
+ env_key.length = sys_strlen(key);
+ env_key.data = key;
+
+ env_value.length = sys_strlen(value);
+ env_value.data = value;
+
+ {
+ erts_osenv_t *env = erts_sys_rwlock_global_osenv();
+ result = erts_osenv_put_native(env, &env_key, &env_value);
+ erts_sys_rwunlock_global_osenv();
+ }
+
+ return result;
+}
+
+int erts_sys_explicit_8bit_getenv(char *key, char *value, size_t *size) {
+ erts_osenv_data_t env_key, env_value;
+ int result;
+
+ env_key.length = sys_strlen(key);
+ env_key.data = key;
+
+ /* Reserve space for NUL termination. */
+ env_value.length = *size - 1;
+ env_value.data = value;
+
+ {
+ const erts_osenv_t *env = erts_sys_rlock_global_osenv();
+ result = erts_osenv_get_native(env, &env_key, &env_value);
+ erts_sys_runlock_global_osenv();
+ }
+
+ if(result == 1) {
+ value[env_value.length] = '\0';
+ }
+
+ *size = env_value.length;
+
+ return result;
+}
+
+int erts_sys_explicit_host_getenv(char *key, char *value, size_t *size) {
+ char *orig_value;
+ size_t length;
+
+ orig_value = getenv(key);
+
+ if(orig_value == NULL) {
+ return 0;
+ }
+
+ length = sys_strlen(orig_value);
+
+ if (length >= *size) {
+ *size = length + 1;
+ return -1;
+ }
+
+ sys_memcpy((void*)value, (void*)orig_value, length + 1);
+ *size = length;
+
+ return 1;
+}
+
+static void import_initial_env(void) {
+ char **environ_iterator, *environ_variable;
+
+ environ_iterator = environ;
+
+ while ((environ_variable = *(environ_iterator++)) != NULL) {
+ char *separator_index = strchr(environ_variable, '=');
+
+ if (separator_index != NULL) {
+ erts_osenv_data_t env_key, env_value;
+
+ env_key.length = separator_index - environ_variable;
+ env_key.data = environ_variable;
+
+ env_value.length = sys_strlen(separator_index) - 1;
+ env_value.data = separator_index + 1;
+
+ erts_osenv_put_native(&sysenv_global_env, &env_key, &env_value);
+ }
+ }
+}
diff --git a/erts/emulator/sys/unix/sys_uds.c b/erts/emulator/sys/unix/sys_uds.c
index dd0a3b03ff..278c6b6ba1 100644
--- a/erts/emulator/sys/unix/sys_uds.c
+++ b/erts/emulator/sys/unix/sys_uds.c
@@ -18,6 +18,42 @@
* %CopyrightEnd%
*/
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#if defined(__sun__) && !defined(_XOPEN_SOURCE)
+#define _XOPEN_SOURCE 500
+#endif
+
+#include <limits.h>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#ifdef HAVE_SYS_SOCKETIO_H
+# include <sys/socketio.h>
+#endif
+#ifdef HAVE_SYS_SOCKIO_H
+# include <sys/sockio.h>
+#endif
+
+#ifdef HAVE_NET_ERRNO_H
+#include <net/errno.h>
+#endif
+
+#ifdef HAVE_DIRENT_H
+# include <dirent.h>
+#endif
+
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
#include "sys_uds.h"
int
diff --git a/erts/emulator/sys/unix/sys_uds.h b/erts/emulator/sys/unix/sys_uds.h
index a598102d5c..26c91d6a00 100644
--- a/erts/emulator/sys/unix/sys_uds.h
+++ b/erts/emulator/sys/unix/sys_uds.h
@@ -21,18 +21,6 @@
#ifndef _ERL_UNIX_UDS_H
#define _ERL_UNIX_UDS_H
-#ifdef HAVE_CONFIG_H
-# include "config.h"
-#endif
-
-#if defined(__sun__) && !defined(_XOPEN_SOURCE)
-#define _XOPEN_SOURCE 500
-#endif
-
-#include <limits.h>
-
-#include <sys/types.h>
-#include <sys/socket.h>
#include <sys/uio.h>
#if defined IOV_MAX
@@ -43,8 +31,6 @@
#define MAXIOV 16
#endif
-#include "sys.h"
-
int sys_uds_readv(int fd, struct iovec *iov, size_t iov_len,
int *fds, int fd_count, int flags);
int sys_uds_read(int fd, char *buff, size_t len,
diff --git a/erts/emulator/sys/win32/erl_win32_sys_ddll.c b/erts/emulator/sys/win32/erl_win32_sys_ddll.c
index 274133a346..fc2179328f 100644
--- a/erts/emulator/sys/win32/erl_win32_sys_ddll.c
+++ b/erts/emulator/sys/win32/erl_win32_sys_ddll.c
@@ -46,9 +46,17 @@ static TWinDynDriverCallbacks wddc;
static TWinDynNifCallbacks nif_callbacks;
void erl_sys_ddll_init(void) {
+ WCHAR cwd_buffer[MAX_PATH];
+
tls_index = TlsAlloc();
ERL_INIT_CALLBACK_STRUCTURE(wddc);
+ /* LOAD_WITH_ALTERED_SEARCH_PATH removes the startup directory from the
+ * search path, so we add it separately to be backwards compatible. */
+ if (GetCurrentDirectoryW(sizeof(cwd_buffer), cwd_buffer)) {
+ SetDllDirectoryW(cwd_buffer);
+ }
+
#define ERL_NIF_API_FUNC_DECL(RET,NAME,ARGS) nif_callbacks.NAME = NAME
#include "erl_nif_api_funcs.h"
#undef ERL_NIF_API_FUNC_DECL
@@ -81,7 +89,10 @@ int erts_sys_ddll_open(const char *full_name, void **handle, ErtsSysDdllError* e
ERTS_ALC_T_TMP, &used, EXT_LEN);
wcscpy(&wcp[used/2 - 1], FILE_EXT_WCHAR);
- if ((hinstance = LoadLibraryW(wcp)) == NULL) {
+ /* LOAD_WITH_ALTERED_SEARCH_PATH adds the specified DLL's directory to the
+ * dependency search path. This also removes the directory we started in,
+ * but we've explicitly added that in in erl_sys_ddll_init. */
+ if ((hinstance = LoadLibraryExW(wcp, NULL, LOAD_WITH_ALTERED_SEARCH_PATH)) == NULL) {
code = ERL_DE_DYNAMIC_ERROR_OFFSET - GetLastError();
if (err != NULL) {
err->str = erts_sys_ddll_error(code);
diff --git a/erts/emulator/sys/win32/sys.c b/erts/emulator/sys/win32/sys.c
index 0598a12351..a1c630d68a 100644
--- a/erts/emulator/sys/win32/sys.c
+++ b/erts/emulator/sys/win32/sys.c
@@ -77,6 +77,7 @@ static int create_pipe(LPHANDLE, LPHANDLE, BOOL, BOOL);
static int application_type(const wchar_t* originalName, wchar_t fullPath[MAX_PATH],
BOOL search_in_path, BOOL handle_quotes,
int *error_return);
+static void *build_env_block(const erts_osenv_t *env);
HANDLE erts_service_event;
@@ -1190,7 +1191,6 @@ spawn_start(ErlDrvPort port_num, char* utf8_name, SysDriverOpts* opts)
int ok;
int neededSelects = 0;
SECURITY_ATTRIBUTES sa = {sizeof(SECURITY_ATTRIBUTES), NULL, TRUE};
- char* envir = opts->envir;
int errno_return = -1;
wchar_t *name;
int len;
@@ -1265,29 +1265,33 @@ spawn_start(ErlDrvPort port_num, char* utf8_name, SysDriverOpts* opts)
name[i] = L'\0';
}
DEBUGF(("Spawning \"%S\"\n", name));
- envir = win_build_environment(envir); /* Always a unicode environment */
- ok = create_child_process(name,
- hChildStdin,
- hChildStdout,
- hChildStderr,
- &dp->port_pid,
- &pid,
- opts->hide_window,
- (LPVOID) envir,
- (wchar_t *) opts->wd,
- opts->spawn_type,
- (wchar_t **) opts->argv,
- &errno_return);
- CloseHandle(hChildStdin);
- CloseHandle(hChildStdout);
- if (close_child_stderr && hChildStderr != INVALID_HANDLE_VALUE &&
- hChildStderr != 0) {
- CloseHandle(hChildStderr);
- }
- erts_free(ERTS_ALC_T_TMP, name);
-
- if (envir != NULL) {
- erts_free(ERTS_ALC_T_ENVIRONMENT, envir);
+
+ {
+ void *environment_block = build_env_block(&opts->envir);
+
+ ok = create_child_process(name,
+ hChildStdin,
+ hChildStdout,
+ hChildStderr,
+ &dp->port_pid,
+ &pid,
+ opts->hide_window,
+ environment_block,
+ (wchar_t *) opts->wd,
+ opts->spawn_type,
+ (wchar_t **) opts->argv,
+ &errno_return);
+
+ CloseHandle(hChildStdin);
+ CloseHandle(hChildStdout);
+
+ if (close_child_stderr && hChildStderr != INVALID_HANDLE_VALUE &&
+ hChildStderr != 0) {
+ CloseHandle(hChildStderr);
+ }
+
+ erts_free(ERTS_ALC_T_TMP, environment_block);
+ erts_free(ERTS_ALC_T_TMP, name);
}
if (!ok) {
@@ -1338,6 +1342,41 @@ spawn_start(ErlDrvPort port_num, char* utf8_name, SysDriverOpts* opts)
return retval;
}
+struct __build_env_state {
+ WCHAR *next_variable;
+};
+
+static void build_env_foreach(void *_state, const erts_osenv_data_t *key,
+ const erts_osenv_data_t *value)
+{
+ struct __build_env_state *state = (struct __build_env_state*)(_state);
+
+ sys_memcpy(state->next_variable, key->data, key->length);
+ state->next_variable += (int)key->length / sizeof(WCHAR);
+ *state->next_variable++ = L'=';
+
+ sys_memcpy(state->next_variable, value->data, value->length);
+ state->next_variable += (int)value->length / sizeof(WCHAR);
+ *state->next_variable++ = L'\0';
+}
+
+/* Builds an environment block suitable for CreateProcessW. */
+static void *build_env_block(const erts_osenv_t *env) {
+ struct __build_env_state build_state;
+ WCHAR *env_block;
+
+ env_block = erts_alloc(ERTS_ALC_T_TMP, env->content_size +
+ (env->variable_count * sizeof(L"=\0") + sizeof(L'\0')));
+
+ build_state.next_variable = env_block;
+
+ erts_osenv_foreach_native(env, &build_state, build_env_foreach);
+
+ (*build_state.next_variable) = L'\0';
+
+ return env_block;
+}
+
static int
create_file_thread(AsyncIo* aio, int mode)
{
diff --git a/erts/emulator/sys/win32/sys_env.c b/erts/emulator/sys/win32/sys_env.c
index 5792816267..c78161b344 100644
--- a/erts/emulator/sys/win32/sys_env.c
+++ b/erts/emulator/sys/win32/sys_env.c
@@ -1,319 +1,212 @@
-/*
- * %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%
- */
-
-#ifdef HAVE_CONFIG_H
-# include "config.h"
-#endif
-
-#include "sys.h"
-#include "erl_sys_driver.h"
-#include "erl_alloc.h"
-
-static WCHAR *merge_environment(WCHAR *current, WCHAR *add);
-static WCHAR *arg_to_env(WCHAR **arg);
-static WCHAR **env_to_arg(WCHAR *env);
-static WCHAR **find_arg(WCHAR **arg, WCHAR *str);
-static int compare(const void *a, const void *b);
-
-static erts_rwmtx_t environ_rwmtx;
-
-void
-erts_sys_env_init(void)
-{
- erts_rwmtx_init(&environ_rwmtx, "environ", NIL,
- ERTS_LOCK_FLAGS_PROPERTY_STATIC | ERTS_LOCK_FLAGS_CATEGORY_GENERIC);
-}
-
-int
-erts_sys_putenv_raw(char *key, char *value)
-{
- int res;
- erts_rwmtx_rwlock(&environ_rwmtx);
- res = (SetEnvironmentVariable((LPCTSTR) key,
- (LPCTSTR) value) ? 0 : 1);
- erts_rwmtx_rwunlock(&environ_rwmtx);
- return res;
-}
-
-int
-erts_sys_putenv(char *key, char *value)
-{
- int res;
- WCHAR *wkey = (WCHAR *) key;
- WCHAR *wvalue = (WCHAR *) value;
- erts_rwmtx_rwlock(&environ_rwmtx);
- res = (SetEnvironmentVariableW(wkey,
- wvalue) ? 0 : 1);
- erts_rwmtx_rwunlock(&environ_rwmtx);
- return res;
-}
-
-int
-erts_sys_getenv(char *key, char *value, size_t *size)
-{
- size_t req_size = 0;
- int res = 0;
- DWORD new_size;
- WCHAR *wkey = (WCHAR *) key;
- WCHAR *wvalue = (WCHAR *) value;
- DWORD wsize = *size / (sizeof(WCHAR) / sizeof(char));
-
- SetLastError(0);
- erts_rwmtx_rlock(&environ_rwmtx);
- new_size = GetEnvironmentVariableW(wkey,
- wvalue,
- (DWORD) wsize);
- res = !new_size && GetLastError() == ERROR_ENVVAR_NOT_FOUND ? -1 : 0;
- erts_rwmtx_runlock(&environ_rwmtx);
- if (res < 0)
- return res;
- res = new_size > wsize ? 1 : 0;
- *size = new_size * (sizeof(WCHAR) / sizeof(char));
- return res;
-}
-int
-erts_sys_getenv__(char *key, char *value, size_t *size)
-{
- size_t req_size = 0;
- int res = 0;
- DWORD new_size;
-
- SetLastError(0);
- new_size = GetEnvironmentVariable((LPCTSTR) key,
- (LPTSTR) value,
- (DWORD) *size);
- res = !new_size && GetLastError() == ERROR_ENVVAR_NOT_FOUND ? -1 : 0;
- if (res < 0)
- return res;
- res = new_size > *size ? 1 : 0;
- *size = new_size;
- return res;
-}
-
-int
-erts_sys_getenv_raw(char *key, char *value, size_t *size)
-{
- int res;
- erts_rwmtx_rlock(&environ_rwmtx);
- res = erts_sys_getenv__(key, value, size);
- erts_rwmtx_runlock(&environ_rwmtx);
- return res;
-}
-
-void init_getenv_state(GETENV_STATE *state)
-{
- erts_rwmtx_rlock(&environ_rwmtx);
- state->environment_strings = GetEnvironmentStringsW();
- state->next_string = state->environment_strings;
-}
-
-char *getenv_string(GETENV_STATE *state)
-{
- ERTS_LC_ASSERT(erts_lc_rwmtx_is_rlocked(&environ_rwmtx));
- if (state->next_string[0] == L'\0') {
- return NULL;
- } else {
- WCHAR *res = state->next_string;
- state->next_string += wcslen(res) + 1;
- return (char *) res;
- }
-}
-
-void fini_getenv_state(GETENV_STATE *state)
-{
- FreeEnvironmentStringsW(state->environment_strings);
- state->environment_strings = state->next_string = NULL;
- erts_rwmtx_runlock(&environ_rwmtx);
-}
-
-int erts_sys_unsetenv(char *key)
-{
- int res = 0;
- WCHAR *wkey = (WCHAR *) key;
-
- SetLastError(0);
- erts_rwmtx_rlock(&environ_rwmtx);
- GetEnvironmentVariableW(wkey,
- NULL,
- 0);
- if (GetLastError() != ERROR_ENVVAR_NOT_FOUND) {
- res = (SetEnvironmentVariableW(wkey,
- NULL) ? 0 : 1);
- }
- erts_rwmtx_runlock(&environ_rwmtx);
- return res;
-}
-
-char*
-win_build_environment(char* new_env)
-{
- if (new_env == NULL) {
- return NULL;
- } else {
- WCHAR *tmp, *merged, *tmp_new;
-
- tmp_new = (WCHAR *) new_env;
-
- erts_rwmtx_rlock(&environ_rwmtx);
- tmp = GetEnvironmentStringsW();
- merged = merge_environment(tmp, tmp_new);
-
- FreeEnvironmentStringsW(tmp);
- erts_rwmtx_runlock(&environ_rwmtx);
- return (char *) merged;
- }
-}
-
-static WCHAR *
-merge_environment(WCHAR *old, WCHAR *add)
-{
- WCHAR **a_arg = env_to_arg(add);
- WCHAR **c_arg = env_to_arg(old);
- WCHAR *ret;
- int i, j;
-
- for(i = 0; c_arg[i] != NULL; ++i)
- ;
-
- for(j = 0; a_arg[j] != NULL; ++j)
- ;
-
- c_arg = erts_realloc(ERTS_ALC_T_TMP,
- c_arg, (i+j+1) * sizeof(WCHAR *));
-
- for(j = 0; a_arg[j] != NULL; ++j){
- WCHAR **tmp;
- WCHAR *current = a_arg[j];
- WCHAR *eq_p = wcschr(current,L'=');
- int unset = (eq_p!=NULL && eq_p[1]==L'\0');
-
- if ((tmp = find_arg(c_arg, current)) != NULL) {
- if (!unset) {
- *tmp = current;
- } else {
- *tmp = c_arg[--i];
- c_arg[i] = NULL;
- }
- } else if (!unset) {
- c_arg[i++] = current;
- c_arg[i] = NULL;
- }
- }
- ret = arg_to_env(c_arg);
- erts_free(ERTS_ALC_T_TMP, c_arg);
- erts_free(ERTS_ALC_T_TMP, a_arg);
- return ret;
-}
-
-static WCHAR**
-find_arg(WCHAR **arg, WCHAR *str)
-{
- WCHAR *tmp;
- int len;
-
- if ((tmp = wcschr(str, L'=')) != NULL) {
- tmp++;
- len = tmp - str;
- while (*arg != NULL){
- if (_wcsnicmp(*arg, str, len) == 0){
- return arg;
- }
- ++arg;
- }
- }
- return NULL;
-}
-
-static int
-compare(const void *a, const void *b)
-{
- WCHAR *s1 = *((WCHAR **) a);
- WCHAR *s2 = *((WCHAR **) b);
- WCHAR *e1 = wcschr(s1,L'=');
- WCHAR *e2 = wcschr(s2,L'=');
- int ret;
- int len;
-
- if(!e1)
- e1 = s1 + wcslen(s1);
- if(!e2)
- e2 = s2 + wcslen(s2);
-
- if((e1 - s1) > (e2 - s2))
- len = (e2 - s2);
- else
- len = (e1 - s1);
-
- ret = _wcsnicmp(s1,s2,len);
- if (ret == 0)
- return ((e1 - s1) - (e2 - s2));
- else
- return ret;
-}
-
-static WCHAR**
-env_to_arg(WCHAR *env)
-{
- WCHAR **ret;
- WCHAR *tmp;
- int i;
- int num_strings = 0;
-
- for(tmp = env; *tmp != '\0'; tmp += wcslen(tmp)+1) {
- ++num_strings;
- }
- ret = erts_alloc(ERTS_ALC_T_TMP, sizeof(WCHAR *) * (num_strings + 1));
- i = 0;
- for(tmp = env; *tmp != '\0'; tmp += wcslen(tmp)+1){
- ret[i++] = tmp;
- }
- ret[i] = NULL;
- return ret;
-}
-
-static WCHAR *
-arg_to_env(WCHAR **arg)
-{
- WCHAR *block;
- WCHAR *ptr;
- int i;
- int totlen = 1; /* extra '\0' */
-
- for(i = 0; arg[i] != NULL; ++i) {
- totlen += wcslen(arg[i])+1;
- }
-
- /* sort the environment vector */
- qsort(arg, i, sizeof(WCHAR *), &compare);
-
- if (totlen == 1){
- block = erts_alloc(ERTS_ALC_T_ENVIRONMENT, 2 * sizeof(WCHAR));
- block[0] = block[1] = '\0';
- } else {
- block = erts_alloc(ERTS_ALC_T_ENVIRONMENT, totlen * sizeof(WCHAR));
- ptr = block;
- for(i=0; arg[i] != NULL; ++i){
- wcscpy(ptr, arg[i]);
- ptr += wcslen(ptr)+1;
- }
- *ptr = '\0';
- }
- return block;
-}
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2002-2017. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * %CopyrightEnd%
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "sys.h"
+#include "erl_sys_driver.h"
+#include "erl_alloc.h"
+
+static erts_osenv_t sysenv_global_env;
+static erts_rwmtx_t sysenv_rwmtx;
+
+static void import_initial_env(void);
+
+void erts_sys_env_init() {
+ erts_rwmtx_init(&sysenv_rwmtx, "environ", NIL,
+ ERTS_LOCK_FLAGS_PROPERTY_STATIC | ERTS_LOCK_FLAGS_CATEGORY_GENERIC);
+
+ erts_osenv_init(&sysenv_global_env);
+ import_initial_env();
+}
+
+const erts_osenv_t *erts_sys_rlock_global_osenv() {
+ erts_rwmtx_rlock(&sysenv_rwmtx);
+ return &sysenv_global_env;
+}
+
+erts_osenv_t *erts_sys_rwlock_global_osenv() {
+ erts_rwmtx_rwlock(&sysenv_rwmtx);
+ return &sysenv_global_env;
+}
+
+void erts_sys_runlock_global_osenv() {
+ erts_rwmtx_runlock(&sysenv_rwmtx);
+}
+
+void erts_sys_rwunlock_global_osenv() {
+ erts_rwmtx_rwunlock(&sysenv_rwmtx);
+}
+
+int erts_sys_explicit_host_getenv(char *key, char *value, size_t *size) {
+ size_t new_size = GetEnvironmentVariableA(key, value, (DWORD)*size);
+
+ if(new_size == 0 && GetLastError() == ERROR_ENVVAR_NOT_FOUND) {
+ return 0;
+ } else if(new_size > *size) {
+ return -1;
+ }
+
+ *size = new_size;
+ return 1;
+}
+
+int erts_sys_explicit_8bit_putenv(char *key, char *value) {
+ WCHAR *wide_key, *wide_value;
+ int key_length, value_length;
+ int result;
+
+ /* Note that we do *NOT* honor the filename encoding flags (+fnu/+fnl)
+ * here; the previous implementation used SetEnvironmentVariableA and
+ * things may break if we step away from that. */
+
+ key_length = MultiByteToWideChar(CP_ACP, 0, key, -1, NULL, 0);
+ value_length = MultiByteToWideChar(CP_ACP, 0, value, -1, NULL, 0);
+
+ /* Report "not found" if either string isn't convertible. */
+ if(key_length == 0 || value_length == 0) {
+ return 0;
+ }
+
+ wide_key = erts_alloc(ERTS_ALC_T_TMP, key_length * sizeof(WCHAR));
+ wide_value = erts_alloc(ERTS_ALC_T_TMP, value_length * sizeof(WCHAR));
+
+ MultiByteToWideChar(CP_ACP, 0, key, -1, wide_key, key_length);
+ MultiByteToWideChar(CP_ACP, 0, value, -1, wide_value, value_length);
+
+ {
+ erts_osenv_data_t env_key, env_value;
+ erts_osenv_t *env;
+
+ env = erts_sys_rwlock_global_osenv();
+
+ /* -1 to exclude the NUL terminator. */
+ env_key.length = (key_length - 1) * sizeof(WCHAR);
+ env_key.data = wide_key;
+
+ env_value.length = (value_length - 1) * sizeof(WCHAR);
+ env_value.data = wide_value;
+
+ result = erts_osenv_put_native(env, &env_key, &env_value);
+ erts_sys_rwunlock_global_osenv();
+ }
+
+ erts_free(ERTS_ALC_T_TMP, wide_key);
+ erts_free(ERTS_ALC_T_TMP, wide_value);
+
+ return result;
+}
+
+int erts_sys_explicit_8bit_getenv(char *key, char *value, size_t *size) {
+ erts_osenv_data_t env_key, env_value;
+ int key_length, value_length, result;
+ WCHAR *wide_key, *wide_value;
+
+ key_length = MultiByteToWideChar(CP_ACP, 0, key, -1, NULL, 0);
+
+ /* Report "not found" if the string isn't convertible. */
+ if(key_length == 0) {
+ return 0;
+ }
+
+ wide_key = erts_alloc(ERTS_ALC_T_TMP, key_length * sizeof(WCHAR));
+ MultiByteToWideChar(CP_ACP, 0, key, -1, wide_key, key_length);
+
+ /* We assume that the worst possible size is twice the output buffer width,
+ * as we could theoretically be on a code page that requires surrogates. */
+ value_length = (*size) * 2;
+ wide_value = erts_alloc(ERTS_ALC_T_TMP, value_length * sizeof(WCHAR));
+
+ {
+ const erts_osenv_t *env = erts_sys_rlock_global_osenv();
+
+ /* -1 to exclude the NUL terminator. */
+ env_key.length = (key_length - 1) * sizeof(WCHAR);
+ env_key.data = wide_key;
+
+ env_value.length = value_length * sizeof(WCHAR);
+ env_value.data = wide_value;
+
+ result = erts_osenv_get_native(env, &env_key, &env_value);
+ erts_sys_runlock_global_osenv();
+ }
+
+ if(result == 1 && env_value.length > 0) {
+ /* This function doesn't NUL-terminate if the provided size is >= 0,
+ * so we pass (*size - 1) to reserve space for it and then do it
+ * manually. */
+ *size = WideCharToMultiByte(CP_ACP, 0, env_value.data,
+ env_value.length / sizeof(WCHAR), value, *size - 1, NULL, NULL);
+
+ if(*size == 0) {
+ if(GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
+ result = -1;
+ } else {
+ result = 0;
+ }
+ }
+ } else {
+ *size = 0;
+ }
+
+ if(*size > 0) {
+ value[*size] = '\0';
+ }
+
+ erts_free(ERTS_ALC_T_TMP, wide_key);
+ erts_free(ERTS_ALC_T_TMP, wide_value);
+
+ return result;
+}
+
+static void import_initial_env(void) {
+ WCHAR *environment_block, *current_variable;
+
+ environment_block = GetEnvironmentStringsW();
+ current_variable = environment_block;
+
+ while(wcslen(current_variable) > 0) {
+ WCHAR *separator_index = wcschr(current_variable, L'=');
+
+ /* We tolerate environment variables starting with '=' as the per-drive
+ * working directories are stored this way. */
+ if(separator_index == current_variable) {
+ separator_index = wcschr(separator_index + 1, L'=');
+ }
+
+ if(separator_index != NULL && separator_index != current_variable) {
+ erts_osenv_data_t env_key, env_value;
+
+ env_key.length = (separator_index - current_variable) * sizeof(WCHAR);
+ env_key.data = current_variable;
+
+ env_value.length = (wcslen(separator_index) - 1) * sizeof(WCHAR);
+ env_value.data = separator_index + 1;
+
+ erts_osenv_put_native(&sysenv_global_env, &env_key, &env_value);
+ }
+
+ current_variable += wcslen(current_variable) + 1;
+ }
+
+ FreeEnvironmentStringsW(environment_block);
+}
diff --git a/erts/emulator/test/Makefile b/erts/emulator/test/Makefile
index b17170c8b8..bf00de2204 100644
--- a/erts/emulator/test/Makefile
+++ b/erts/emulator/test/Makefile
@@ -57,6 +57,7 @@ MODULES= \
dirty_nif_SUITE \
distribution_SUITE \
driver_SUITE \
+ dump_SUITE \
efile_SUITE \
erts_debug_SUITE \
estone_SUITE \
diff --git a/erts/emulator/test/alloc_SUITE.erl b/erts/emulator/test/alloc_SUITE.erl
index f0871ead7d..88ff2a7a92 100644
--- a/erts/emulator/test/alloc_SUITE.erl
+++ b/erts/emulator/test/alloc_SUITE.erl
@@ -31,6 +31,7 @@
mseg_clear_cache/1,
erts_mmap/1,
cpool/1,
+ set_dyn_param/1,
migration/1]).
-include_lib("common_test/include/ct.hrl").
@@ -41,6 +42,7 @@ suite() ->
all() ->
[basic, coalesce, threads, realloc_copy, bucket_index,
+ set_dyn_param,
bucket_mask, rbtree, mseg_clear_cache, erts_mmap, cpool, migration].
init_per_testcase(Case, Config) when is_list(Config) ->
@@ -65,7 +67,11 @@ mseg_clear_cache(Cfg) -> drv_case(Cfg).
cpool(Cfg) -> drv_case(Cfg).
migration(Cfg) ->
- drv_case(Cfg, concurrent, "+MZe true").
+ %% Enable test_alloc.
+ %% Disable driver_alloc to avoid recursive alloc_util calls
+ %% through enif_mutex_create() in my_creating_mbc().
+ drv_case(Cfg, concurrent, "+MZe true +MRe false"),
+ drv_case(Cfg, concurrent, "+MZe true +MRe false +MZas ageffcbf").
erts_mmap(Config) when is_list(Config) ->
case {os:type(), mmsc_flags()} of
@@ -89,10 +95,11 @@ mmsc_flags() ->
mmsc_flags(Env) ->
case os:getenv(Env) of
false -> false;
- V -> case string:str(V, "+MMsc") of
- 0 -> false;
- P -> Env ++ "=" ++ string:substr(V, P)
- end
+ V ->
+ case string:find(V, "+MMsc") of
+ nomatch -> false;
+ SubStr -> Env ++ "=" ++ SubStr
+ end
end.
erts_mmap_do(Config, SCO, SCRPM, SCRFSD) ->
@@ -109,7 +116,7 @@ erts_mmap_do(Config, SCO, SCRPM, SCRFSD) ->
0 -> O1;
_ -> O1 ++ " +MMscrfsd"++integer_to_list(SCRFSD)
end,
- {ok, Node} = start_node(Config, Opts),
+ {ok, Node} = start_node(Config, Opts, []),
Self = self(),
Ref = make_ref(),
F = fun() ->
@@ -139,6 +146,82 @@ erts_mmap_do(Config, SCO, SCRPM, SCRFSD) ->
Result.
+%% Test erlang:system_flag(erts_alloc, ...)
+set_dyn_param(_Config) ->
+ {_, _, _, AlcList} = erlang:system_info(allocator),
+
+ {Enabled, Disabled, Others} =
+ lists:foldl(fun({sys_alloc,_}, {Es, Ds, Os}) ->
+ {Es, [sys_alloc | Ds], Os};
+
+ ({AT, Opts}, {Es, Ds, Os}) when is_list(Opts) ->
+ case lists:keyfind(e, 1, Opts) of
+ {e, true} ->
+ {[AT | Es], Ds, Os};
+ {e, false} ->
+ {Es, [AT | Ds], Os};
+ false ->
+ {Es, Ds, [AT | Os]}
+ end;
+
+ (_, Acc) -> Acc
+ end,
+ {[], [], []},
+ AlcList),
+
+ Param = sbct,
+ lists:foreach(fun(AT) -> set_dyn_param_enabled(AT, Param) end,
+ Enabled),
+
+ lists:foreach(fun(AT) ->
+ Tpl = {AT, Param, 12345},
+ io:format("~p\n", [Tpl]),
+ notsup = erlang:system_flag(erts_alloc, Tpl)
+ end,
+ Disabled),
+
+ lists:foreach(fun(AT) ->
+ Tpl = {AT, Param, 12345},
+ io:format("~p\n", [Tpl]),
+ {'EXIT',{badarg,_}} =
+ (catch erlang:system_flag(erts_alloc, Tpl))
+ end,
+ Others),
+ ok.
+
+set_dyn_param_enabled(AT, Param) ->
+ OldVal = get_alc_param(AT, Param),
+
+ Val1 = OldVal div 2,
+ Tuple = {AT, Param, Val1},
+ io:format("~p\n", [Tuple]),
+ ok = erlang:system_flag(erts_alloc, Tuple),
+ Val1 = get_alc_param(AT, Param),
+
+ ok = erlang:system_flag(erts_alloc, {AT, Param, OldVal}),
+ OldVal = get_alc_param(AT, Param),
+ ok.
+
+get_alc_param(AT, Param) ->
+ lists:foldl(fun({instance,_,Istats}, Acc) ->
+ {options,Opts} = lists:keyfind(options, 1, Istats),
+ {Param,Val} = lists:keyfind(Param, 1, Opts),
+ {as,Strategy} = lists:keyfind(as, 1, Opts),
+
+ case {param_for_strat(Param, Strategy), Acc} of
+ {false, _} -> Acc;
+ {true, undefined} -> Val;
+ {true, _} ->
+ Val = Acc
+ end
+ end,
+ undefined,
+ erlang:system_info({allocator, AT})).
+
+param_for_strat(sbct, gf) -> false;
+param_for_strat(_, _) -> true.
+
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% %%
%% Internal functions %%
@@ -150,7 +233,9 @@ drv_case(Config) ->
drv_case(Config, Mode, NodeOpts) when is_list(Config) ->
case os:type() of
{Family, _} when Family == unix; Family == win32 ->
- {ok, Node} = start_node(Config, NodeOpts),
+ %%Prog = {prog,"/my/own/otp/bin/cerl -debug"},
+ Prog = [],
+ {ok, Node} = start_node(Config, NodeOpts, Prog),
Self = self(),
Ref = make_ref(),
spawn_link(Node,
@@ -216,19 +301,35 @@ wait_for_memory_deallocations() ->
end.
print_stats(migration) ->
- {Btot,Ctot} = lists:foldl(fun({instance,Inr,Istats}, {Bacc,Cacc}) ->
- {mbcs,MBCS} = lists:keyfind(mbcs, 1, Istats),
- Btup = lists:keyfind(blocks, 1, MBCS),
- Ctup = lists:keyfind(carriers, 1, MBCS),
- io:format("{instance,~p,~p,~p}\n", [Inr, Btup, Ctup]),
- {tuple_add(Bacc,Btup),tuple_add(Cacc,Ctup)};
- (_, Acc) -> Acc
- end,
- {{blocks,0,0,0},{carriers,0,0,0}},
- erlang:system_info({allocator,test_alloc})),
-
+ IFun = fun({instance,Inr,Istats}, {Bacc,Cacc,Pacc}) ->
+ {mbcs,MBCS} = lists:keyfind(mbcs, 1, Istats),
+ Btup = lists:keyfind(blocks, 1, MBCS),
+ Ctup = lists:keyfind(carriers, 1, MBCS),
+
+ Ptup = case lists:keyfind(mbcs_pool, 1, Istats) of
+ {mbcs_pool,POOL} ->
+ {blocks, Bpool} = lists:keyfind(blocks, 1, POOL),
+ {carriers, Cpool} = lists:keyfind(carriers, 1, POOL),
+ {pool, Bpool, Cpool};
+ false ->
+ {pool, 0, 0}
+ end,
+ io:format("{instance,~p,~p,~p,~p}}\n",
+ [Inr, Btup, Ctup, Ptup]),
+ {tuple_add(Bacc,Btup),tuple_add(Cacc,Ctup),
+ tuple_add(Pacc,Ptup)};
+ (_, Acc) -> Acc
+ end,
+
+ {Btot,Ctot,Ptot} = lists:foldl(IFun,
+ {{blocks,0,0,0},{carriers,0,0,0},{pool,0,0}},
+ erlang:system_info({allocator,test_alloc})),
+
+ {pool, PBtot, PCtot} = Ptot,
io:format("Number of blocks : ~p\n", [Btot]),
- io:format("Number of carriers: ~p\n", [Ctot]);
+ io:format("Number of carriers: ~p\n", [Ctot]),
+ io:format("Number of pooled blocks : ~p\n", [PBtot]),
+ io:format("Number of pooled carriers: ~p\n", [PCtot]);
print_stats(_) -> ok.
tuple_add(T1, T2) ->
@@ -325,13 +426,13 @@ handle_result(_State, Result0) ->
continue
end.
-start_node(Config, Opts) when is_list(Config), is_list(Opts) ->
+start_node(Config, Opts, Prog) when is_list(Config), is_list(Opts) ->
case proplists:get_value(debug,Config) of
true -> {ok, node()};
- _ -> start_node_1(Config, Opts)
+ _ -> start_node_1(Config, Opts, Prog)
end.
-start_node_1(Config, Opts) ->
+start_node_1(Config, Opts, Prog) ->
Pa = filename:dirname(code:which(?MODULE)),
Name = list_to_atom(atom_to_list(?MODULE)
++ "-"
@@ -340,7 +441,11 @@ start_node_1(Config, Opts) ->
++ integer_to_list(erlang:system_time(second))
++ "-"
++ integer_to_list(erlang:unique_integer([positive]))),
- test_server:start_node(Name, slave, [{args, Opts++" -pa "++Pa}]).
+ ErlArg = case Prog of
+ [] -> [];
+ _ -> [{erl,[Prog]}]
+ end,
+ test_server:start_node(Name, slave, [{args, Opts++" -pa "++Pa} | ErlArg]).
stop_node(Node) when Node =:= node() -> ok;
stop_node(Node) ->
diff --git a/erts/emulator/test/alloc_SUITE_data/allocator_test.h b/erts/emulator/test/alloc_SUITE_data/allocator_test.h
index 97ee58cdad..5272f86c98 100644
--- a/erts/emulator/test/alloc_SUITE_data/allocator_test.h
+++ b/erts/emulator/test/alloc_SUITE_data/allocator_test.h
@@ -156,7 +156,8 @@ typedef void* erts_cond;
#define IS_SMP_ENABLED ((int) ALC_TEST0(0xf13))
#define ALLOC_TEST(S) ((void*) ALC_TEST1(0xf14, (S)))
#define FREE_TEST(P) ((void) ALC_TEST1(0xf15, (P)))
-#define SET_TEST_MBC_USER_HEADER(SZ,CMBC,DMBC) ((int)ALC_TEST3(0xf16, (SZ), (CMBC), (DMBC)))
-#define GET_TEST_MBC_SIZE() ((int) ALC_TEST0(0xf17))
+#define REALLOC_TEST(P,S) ((void*) ALC_TEST2(0xf16, (P), (S)))
+#define SET_TEST_MBC_USER_HEADER(SZ,CMBC,DMBC) ((int)ALC_TEST3(0xf17, (SZ), (CMBC), (DMBC)))
+#define GET_TEST_MBC_SIZE() ((int) ALC_TEST0(0xf18))
#endif
diff --git a/erts/emulator/test/alloc_SUITE_data/migration.c b/erts/emulator/test/alloc_SUITE_data/migration.c
index b9a4de03b3..1d974225fc 100644
--- a/erts/emulator/test/alloc_SUITE_data/migration.c
+++ b/erts/emulator/test/alloc_SUITE_data/migration.c
@@ -223,6 +223,42 @@ static int rand_int(MigrationState* state, int low, int high)
return low + (x % (high+1-low));
}
+enum Operation
+{
+ ALLOCATE_OP,
+ FREE_OP,
+ REALLOC_OP,
+ CLEANUP_OP
+};
+
+static enum Operation rand_op(MigrationState* state)
+{
+ int r = rand_int(state, 1, 100);
+ switch (state->phase) {
+ case GROWING:
+ FATAL_ASSERT(state->nblocks < state->max_nblocks);
+ if (r > 10 || state->nblocks == 0)
+ return ALLOCATE_OP;
+ else if (r > 5)
+ return FREE_OP;
+ else
+ return REALLOC_OP;
+
+ case SHRINKING:
+ FATAL_ASSERT(state->nblocks > 0);
+ if (r > 10 || state->nblocks == state->max_nblocks)
+ return FREE_OP;
+ else if (r > 5)
+ return ALLOCATE_OP;
+ else
+ return REALLOC_OP;
+
+ case CLEANUP:
+ return CLEANUP_OP;
+ default:
+ FATAL_ASSERT(!"Invalid op phase");
+ }
+}
static void do_cleanup(TestCaseState_t *tcs, MigrationState* state)
{
@@ -275,53 +311,75 @@ testcase_run(TestCaseState_t *tcs)
state->goal_nblocks = rand_int(state, 1, state->max_nblocks);
}
- switch (state->phase) {
- case GROWING: {
+ switch (rand_op(state)) {
+ case ALLOCATE_OP: {
MyBlock* p;
FATAL_ASSERT(!state->blockv[state->nblocks]);
- p = ALLOC_TEST(rand_int(state, state->block_size/2, state->block_size));
+ p = ALLOC_TEST(rand_int(state, state->block_size/2, state->block_size));
FATAL_ASSERT(p);
add_block(p, state);
- state->blockv[state->nblocks] = p;
- if (++state->nblocks >= state->goal_nblocks) {
- /*testcase_printf(tcs, "%d: Grown to %d blocks", tcs->thr_nr, state->nblocks);*/
- state->phase = SHRINKING;
- state->goal_nblocks = rand_int(state, 0, state->goal_nblocks-1);
- }
- else
- FATAL_ASSERT(!state->blockv[state->nblocks]);
+ state->blockv[state->nblocks++] = p;
break;
}
- case SHRINKING: {
+ case FREE_OP: {
int ix = rand_int(state, 0, state->nblocks-1);
FATAL_ASSERT(state->blockv[ix]);
remove_block(state->blockv[ix]);
FREE_TEST(state->blockv[ix]);
state->blockv[ix] = state->blockv[--state->nblocks];
state->blockv[state->nblocks] = NULL;
-
- if (state->nblocks <= state->goal_nblocks) {
- /*testcase_printf(tcs, "%d: Shrunk to %d blocks", tcs->thr_nr, state->nblocks);*/
- if (++state->round >= MAX_ROUNDS) {
- state->phase = CLEANUP;
- } else {
- state->phase = GROWING;
- state->goal_nblocks = rand_int(state, state->goal_nblocks+1, state->max_nblocks);
- }
- }
break;
}
+ case REALLOC_OP: {
+ int ix = rand_int(state, 0, state->nblocks-1);
+ MyBlock* p;
+ FATAL_ASSERT(state->blockv[ix]);
+ remove_block(state->blockv[ix]);
+ p = REALLOC_TEST(state->blockv[ix], rand_int(state, state->block_size/2, state->block_size));
+ FATAL_ASSERT(p);
+ add_block(p, state);
+ state->blockv[ix] = p;
+ break;
+ }
+ case CLEANUP_OP:
+ do_cleanup(tcs, state);
+ break;
+ default:
+ FATAL_ASSERT(!"Invalid operation");
+ }
+
+ switch (state->phase) {
+ case GROWING: {
+ if (state->nblocks >= state->goal_nblocks) {
+ /*testcase_printf(tcs, "%d: Grown to %d blocks", tcs->thr_nr, state->nblocks);*/
+ state->phase = SHRINKING;
+ state->goal_nblocks = rand_int(state, 0, state->goal_nblocks-1);
+ }
+ else
+ FATAL_ASSERT(!state->blockv[state->nblocks]);
+ break;
+ }
+ case SHRINKING: {
+ if (state->nblocks <= state->goal_nblocks) {
+ /*testcase_printf(tcs, "%d: Shrunk to %d blocks", tcs->thr_nr, state->nblocks);*/
+ if (++state->round >= MAX_ROUNDS) {
+ state->phase = CLEANUP;
+ } else {
+ state->phase = GROWING;
+ state->goal_nblocks = rand_int(state, state->goal_nblocks+1, state->max_nblocks);
+ }
+ }
+ break;
+ }
case CLEANUP:
- do_cleanup(tcs, state);
- break;
+ case DONE:
+ break;
default:
FATAL_ASSERT(!"Invalid phase");
}
- if (state->phase == DONE) {
- }
- else {
+ if (state->phase != DONE) {
testcase_continue(tcs);
}
}
diff --git a/erts/emulator/test/beam_SUITE.erl b/erts/emulator/test/beam_SUITE.erl
index 6a54fa87e0..bdf8f6c34e 100644
--- a/erts/emulator/test/beam_SUITE.erl
+++ b/erts/emulator/test/beam_SUITE.erl
@@ -113,20 +113,41 @@ packed_registers(Config) when is_list(Config) ->
VarName = list_to_atom("M"++integer_to_list(V)),
merl:var(VarName)
end || V <- Seq],
+ MoreNewVars = [begin
+ VarName = list_to_atom("MM"++integer_to_list(V)),
+ merl:var(VarName)
+ end || V <- Seq],
+ TupleEls = [?Q("id(_@Value@)") || {_,Value} <- S0],
S = [?Q("_@Var = id(_@Value@)") || {Var,Value} <- S0],
Code = ?Q(["-module('@Mod@').\n"
"-export([f/0]).\n"
"f() ->\n"
+ "Tuple = id({_@TupleEls}),\n"
+ "{_@MoreNewVars} = Tuple,\n"
"_@S,\n"
"_ = id(0),\n"
"L = [_@Vars],\n"
"_ = id(1),\n"
"[_@NewVars] = L,\n" %Test get_list/3.
"_ = id(2),\n"
- "id([_@Vars,_@NewVars]).\n"
+ "id([_@Vars,_@NewVars,_@MoreNewVars]).\n"
"id(I) -> I.\n"]),
merl:compile_and_load(Code),
- CombinedSeq = Seq ++ Seq,
+
+ %% Optionally print the generated code.
+ PrintCode = false, %Change to true to print code.
+
+ case PrintCode of
+ false ->
+ ok;
+ true ->
+ merl:print(Code),
+ erts_debug:df(Mod),
+ {ok,Dis} = file:read_file(atom_to_list(Mod)++".dis"),
+ io:put_chars(Dis)
+ end,
+
+ CombinedSeq = Seq ++ Seq ++ Seq,
CombinedSeq = Mod:f(),
%% Clean up.
diff --git a/erts/emulator/test/beam_literals_SUITE.erl b/erts/emulator/test/beam_literals_SUITE.erl
index 09761263e2..b447ca0210 100644
--- a/erts/emulator/test/beam_literals_SUITE.erl
+++ b/erts/emulator/test/beam_literals_SUITE.erl
@@ -248,35 +248,58 @@ literal_type_tests(Config) when is_list(Config) ->
ok.
make_test([{is_function=T,L}|Ts]) ->
- [test(T, L),test(T, 0, L)|make_test(Ts)];
+ [guard_test(T, L),guard_test(T, 0, L),body_test(T, L),body_test(T, 0, L)|make_test(Ts)];
make_test([{T,L}|Ts]) ->
- [test(T, L)|make_test(Ts)];
+ [guard_test(T, L),body_test(T, L)|make_test(Ts)];
make_test([]) -> [].
-test(T, L) ->
- S = lists:flatten(io_lib:format("begin io:format(\"~~p~n\", [{~p,~p}]), if ~w(~w) -> true; true -> false end end. ", [T, L, T, L])),
- {ok,Toks,_Line} = erl_scan:string(S),
- {ok,E} = erl_parse:parse_exprs(Toks),
- {value,Val,_Bs} = erl_eval:exprs(E, []),
+guard_test(_, L) when is_function(L) ->
+ %% Skip guard tests with exports - they are not literals
+ {atom,erl_anno:new(0),true};
+guard_test(T, L) ->
+ S = io_lib:format("begin io:format(\"~~p~n\", [{~p,~p}]), if ~w(~w) -> true; true -> false end end. ", [T, L, T, L]),
+ {Val,Expr} = eval_string(S),
+ Anno = erl_anno:new(0),
+ {match,Anno,{atom,Anno,Val},Expr}.
+
+guard_test(_, _, L) when is_function(L) ->
+ %% Skip guard tests with exports - they are not literals
+ {atom,erl_anno:new(0),true};
+guard_test(T, A, L) ->
+ S = io_lib:format("begin io:format(\"~~p~n\", [{~p,~p,~p}]), if ~w(~w, ~w) -> true; true -> false end end. ", [T,L,A,T,L,A]),
+ {Val,Expr} = eval_string(S),
+ Anno = erl_anno:new(0),
+ {match,Anno,{atom,Anno,Val},Expr}.
+
+body_test(T, L) ->
+ S = io_lib:format("begin io:format(\"~~p~n\", [{~p,~p}]), ~w(~w) end. ", [T,L,T,L]),
+ {Val,Expr} = eval_string(S),
Anno = erl_anno:new(0),
- {match,Anno,{atom,Anno,Val},hd(E)}.
+ {match,Anno,{atom,Anno,Val},Expr}.
-test(T, A, L) ->
- S = lists:flatten(io_lib:format("begin io:format(\"~~p~n\", [{~p,~p,~p}]), if ~w(~w, ~w) -> true; true -> false end end. ",
- [T,L,A,T,L,A])),
- {ok,Toks,_Line} = erl_scan:string(S),
+body_test(T, A, L) ->
+ S = io_lib:format("begin io:format(\"~~p~n\", [{~p,~p,~p}]), ~w(~w,~w) end. ", [T,L,A,T,L,A]),
+ {Val,Expr} = eval_string(S),
+ Anno = erl_anno:new(0),
+ {match,Anno,{atom,Anno,Val},Expr}.
+
+eval_string(S) ->
+ {ok,Toks,_Line} = erl_scan:string(lists:flatten(S)),
{ok,E} = erl_parse:parse_exprs(Toks),
{value,Val,_Bs} = erl_eval:exprs(E, []),
- Anno = erl_anno:new(0),
- {match,Anno,{atom,Anno,Val},hd(E)}.
-
+ {Val,hd(E)}.
+
literals() ->
[42,
3.14,
-3,
32982724987789283473473838474,
[],
- xxxx].
+ "abc",
+ <<"abc">>,
+ {},
+ xxxx,
+ fun erlang:erase/0].
type_tests() ->
[is_boolean,
diff --git a/erts/emulator/test/bif_SUITE.erl b/erts/emulator/test/bif_SUITE.erl
index 04b7f2de15..32bfcd5520 100644
--- a/erts/emulator/test/bif_SUITE.erl
+++ b/erts/emulator/test/bif_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2005-2018. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -33,7 +33,10 @@
atom_to_binary/1,min_max/1, erlang_halt/1,
erl_crash_dump_bytes/1,
is_builtin/1, error_stacktrace/1,
- error_stacktrace_during_call_trace/1]).
+ error_stacktrace_during_call_trace/1,
+ group_leader_prio/1, group_leader_prio_dirty/1,
+ is_process_alive/1,
+ process_info_blast/1]).
suite() ->
[{ct_hooks,[ts_install_cth]},
@@ -46,7 +49,9 @@ all() ->
display, display_string, list_to_utf8_atom,
atom_to_binary, binary_to_atom, binary_to_existing_atom,
erl_crash_dump_bytes, min_max, erlang_halt, is_builtin,
- error_stacktrace, error_stacktrace_during_call_trace].
+ error_stacktrace, error_stacktrace_during_call_trace,
+ group_leader_prio, group_leader_prio_dirty,
+ is_process_alive, process_info_blast].
%% Uses erlang:display to test that erts_printf does not do deep recursion
display(Config) when is_list(Config) ->
@@ -731,10 +736,12 @@ erlang_halt(Config) when is_list(Config) ->
[broken_halt, "Validate correct crash dump"]),
{ok,_} = wait_until_stable_size(CrashDump,-1),
{ok, Bin} = file:read_file(CrashDump),
- case {string:str(binary_to_list(Bin),"\n=end\n"),
- string:str(binary_to_list(Bin),"\r\n=end\r\n")} of
- {0,0} -> ct:fail("Could not find end marker in crash dump");
- _ -> ok
+ case {string:find(Bin, <<"\n=end\n">>),
+ string:find(Bin, <<"\r\n=end\r\n">>)} of
+ {nomatch,nomatch} ->
+ ct:fail("Could not find end marker in crash dump");
+ {_,_} ->
+ ok
end.
wait_until_stable_size(_File,-10) ->
@@ -779,14 +786,20 @@ is_builtin(_Config) ->
{F,A} <- M:module_info(exports)],
Exp = ordsets:from_list(Exp0),
- %% erlang:apply/3 is considered to be built-in, but is not
- %% implemented as other BIFs.
+ %% Built-ins implemented as special instructions.
+ Instructions = [{erlang,apply,2},{erlang,apply,3},{erlang,yield,0}],
- Builtins0 = [{erlang,apply,3}|erlang:system_info(snifs)],
+ Builtins0 = Instructions ++ erlang:system_info(snifs),
Builtins = ordsets:from_list(Builtins0),
- NotBuiltin = ordsets:subtract(Exp, Builtins),
- _ = [true = erlang:is_builtin(M, F, A) || {M,F,A} <- Builtins],
- _ = [false = erlang:is_builtin(M, F, A) || {M,F,A} <- NotBuiltin],
+
+ Fakes = [{M,F,42} || {M,F,_} <- Instructions],
+ All = ordsets:from_list(Fakes ++ Exp),
+ NotBuiltin = ordsets:subtract(All, Builtins),
+
+ _ = [{true,_} = {erlang:is_builtin(M, F, A),MFA} ||
+ {M,F,A}=MFA <- Builtins],
+ _ = [{false,_} = {erlang:is_builtin(M, F, A),MFA} ||
+ {M,F,A}=MFA <- NotBuiltin],
ok.
@@ -817,7 +830,6 @@ error_stacktrace_during_call_trace(Config) when is_list(Config) ->
end
end,
ok.
-
error_stacktrace_test() ->
Types = [apply_const_last, apply_const, apply_last,
@@ -955,9 +967,210 @@ do_error_1(call) ->
erlang:error(id(oops)).
+group_leader_prio(Config) when is_list(Config) ->
+ group_leader_prio_test(false).
+
+group_leader_prio_dirty(Config) when is_list(Config) ->
+ group_leader_prio_test(true).
+
+group_leader_prio_test(Dirty) ->
+ %%
+ %% Unfortunately back in the days node local group_leader/2 was not
+ %% implemented as sending an asynchronous signal to the process to change
+ %% group leader for. Instead it has always been synchronously changed, and
+ %% nothing in the documentation have hinted otherwise... Therefore I do not
+ %% dare the change this.
+ %%
+ %% In order to prevent priority inversion, the priority of the receiver of
+ %% the group leader signal is elevated while handling incoming signals if
+ %% the sender has a higher priority than the receiver. This test tests that
+ %% the priority elevation actually works...
+ %%
+ Tester = self(),
+ Init = erlang:whereis(init),
+ GL = erlang:group_leader(),
+ process_flag(priority, max),
+ {TestProcFun, NTestProcs}
+ = case Dirty of
+ false ->
+ %% These processes will handle all incoming signals
+ %% by them selves...
+ {fun () ->
+ Tester ! {alive, self()},
+ receive after infinity -> ok end
+ end,
+ 100};
+ true ->
+ %% These processes wont handle incoming signals by
+ %% them selves since they are stuck on dirty schedulers
+ %% when we try to change group leader. A dirty process
+ %% signal handler process (system process) will be notified
+ %% of the need to handle incoming signals for these processes,
+ %% and will instead handle the signal for these processes...
+ {fun () ->
+ %% The following sends the message '{alive, self()}'
+ %% to Tester once on a dirty io scheduler, then wait
+ %% there until the process terminates...
+ erts_debug:dirty_io(alive_waitexiting, Tester)
+ end,
+ erlang:system_info(dirty_io_schedulers)}
+ end,
+ TPs = lists:map(fun (_) ->
+ spawn_opt(TestProcFun,
+ [link, {priority, normal}])
+ end, lists:seq(1, NTestProcs)),
+ lists:foreach(fun (TP) -> receive {alive, TP} -> ok end end, TPs),
+ TLs = lists:map(fun (_) ->
+ spawn_opt(fun () -> tok_loop() end,
+ [link, {priority, high}])
+ end,
+ lists:seq(1, 2*erlang:system_info(schedulers))),
+ %% Wait to ensure distribution of high prio processes over schedulers...
+ receive after 1000 -> ok end,
+ %%
+ %% Test that we can get group-leader signals through to normal prio
+ %% processes from a max prio process even though all schedulers are filled
+ %% with executing high prio processes.
+ %%
+ lists:foreach(fun (_) ->
+ lists:foreach(fun (TP) ->
+ erlang:yield(),
+ %% whitebox -- Enqueue some signals on it
+ %% preventing us from hogging its main lock
+ %% and set group-leader directly....
+ erlang:demonitor(erlang:monitor(process, TP)),
+ true = erlang:group_leader(Init, TP),
+ {group_leader, Init} = process_info(TP, group_leader),
+ erlang:demonitor(erlang:monitor(process, TP)),
+ true = erlang:group_leader(GL, TP),
+ {group_leader, GL} = process_info(TP, group_leader)
+ end,
+ TPs)
+ end,
+ lists:seq(1,100)),
+ %%
+ %% Also test when it is exiting...
+ %%
+ lists:foreach(fun (TP) ->
+ erlang:yield(),
+ M = erlang:monitor(process, TP),
+ unlink(TP),
+ exit(TP, bang),
+ badarg = try
+ true = erlang:group_leader(Init, TP)
+ catch
+ error : What -> What
+ end,
+ receive
+ {'DOWN', M, process, TP, Reason} ->
+ bang = Reason
+ end
+ end,
+ TPs),
+ lists:foreach(fun (TL) ->
+ M = erlang:monitor(process, TL),
+ unlink(TL),
+ exit(TL, bang),
+ receive
+ {'DOWN', M, process, TL, Reason} ->
+ bang = Reason
+ end
+ end,
+ TLs),
+ ok.
+
+is_process_alive(Config) when is_list(Config) ->
+ process_flag(priority, max),
+ Ps = lists:map(fun (_) ->
+ spawn_opt(fun () -> tok_loop() end,
+ [{priority, high}, link])
+ end,
+ lists:seq(1, 2*erlang:system_info(schedulers))),
+ receive after 1000 -> ok end, %% Wait for load to spread
+ lists:foreach(fun (P) ->
+ %% Ensure that signal order is preserved
+ %% and that we are not starved due to
+ %% priority inversion
+ true = erlang:is_process_alive(P),
+ unlink(P),
+ true = erlang:is_process_alive(P),
+ exit(P, kill),
+ false = erlang:is_process_alive(P)
+ end,
+ Ps),
+ ok.
+process_info_blast(Config) when is_list(Config) ->
+ Tester = self(),
+ NoAttackers = 1000,
+ NoAL = lists:seq(1, NoAttackers),
+ Consume = make_ref(),
+ Victim = spawn_link(fun () ->
+ receive
+ Consume ->
+ ok
+ end,
+ consume_msgs()
+ end),
+ AFun = fun () ->
+ Victim ! hej,
+ Res = process_info(Victim, message_queue_len),
+ Tester ! {self(), Res}
+ end,
+ Attackers0 = lists:map(fun (_) ->
+ spawn_link(AFun)
+ end,
+ NoAL),
+ lists:foreach(fun (A) ->
+ receive
+ {A, Res} ->
+ case Res of
+ {message_queue_len, Len} when Len > 0, Len =< NoAttackers ->
+ Len;
+ Error ->
+ exit({unexpected, Error})
+ end
+ end
+ end,
+ Attackers0),
+ Attackers1 = lists:map(fun (_) ->
+ spawn_link(AFun)
+ end,
+ NoAL),
+ Victim ! Consume,
+ lists:foreach(fun (A) ->
+ receive
+ {A, Res} ->
+ case Res of
+ {message_queue_len, Len} when Len >= 0, Len =< 2*NoAttackers+1 ->
+ ok;
+ undefined ->
+ ok;
+ Error ->
+ exit({unexpected, Error})
+ end
+ end
+ end,
+ Attackers1),
+ KillFun = fun (P) ->
+ unlink(P),
+ exit(P, kill),
+ false = erlang:is_process_alive(P)
+ end,
+ lists:foreach(fun (A) -> KillFun(A) end, Attackers0),
+ lists:foreach(fun (A) -> KillFun(A) end, Attackers1),
+ KillFun(Victim),
+ ok.
-%% Helpers
+consume_msgs() ->
+ receive
+ _ ->
+ consume_msgs()
+ after 0 ->
+ ok
+ end.
+
+%% helpers
id(I) -> I.
@@ -997,3 +1210,11 @@ hostname([$@ | Hostname]) ->
list_to_atom(Hostname);
hostname([_C | Cs]) ->
hostname(Cs).
+
+tok_loop() ->
+ tok_loop(hej).
+
+tok_loop(hej) ->
+ tok_loop(hopp);
+tok_loop(hopp) ->
+ tok_loop(hej).
diff --git a/erts/emulator/test/binary_SUITE.erl b/erts/emulator/test/binary_SUITE.erl
index 61536bacd7..a3c3daac15 100644
--- a/erts/emulator/test/binary_SUITE.erl
+++ b/erts/emulator/test/binary_SUITE.erl
@@ -48,6 +48,7 @@
bad_list_to_binary/1, bad_binary_to_list/1,
t_split_binary/1, bad_split/1,
terms/1, terms_float/1, float_middle_endian/1,
+ b2t_used_big/1,
external_size/1, t_iolist_size/1,
t_hash/1,
bad_size/1,
@@ -57,7 +58,9 @@
otp_5484/1,otp_5933/1,
ordering/1,unaligned_order/1,gc_test/1,
bit_sized_binary_sizes/1,
- otp_6817/1,deep/1,obsolete_funs/1,robustness/1,otp_8117/1,
+ otp_6817/1,deep/1,
+ term2bin_tuple_fallbacks/1,
+ robustness/1,otp_8117/1,
otp_8180/1, trapping/1, large/1,
error_after_yield/1, cmp_old_impl/1]).
@@ -72,12 +75,14 @@ all() ->
t_split_binary, bad_split,
bad_list_to_binary, bad_binary_to_list, terms,
terms_float, float_middle_endian, external_size, t_iolist_size,
+ b2t_used_big,
bad_binary_to_term_2, safe_binary_to_term2,
bad_binary_to_term, bad_terms, t_hash, bad_size,
bad_term_to_binary, more_bad_terms, otp_5484, otp_5933,
ordering, unaligned_order, gc_test,
bit_sized_binary_sizes, otp_6817, otp_8117, deep,
- obsolete_funs, robustness, otp_8180, trapping, large,
+ term2bin_tuple_fallbacks,
+ robustness, otp_8180, trapping, large,
error_after_yield, cmp_old_impl].
groups() ->
@@ -257,6 +262,7 @@ test_deep_bitstr(List) ->
{Bin,bitstring_to_list(Bin)}.
bad_list_to_binary(Config) when is_list(Config) ->
+ test_bad_bin(<<1:1>>),
test_bad_bin(atom),
test_bad_bin(42),
test_bad_bin([1|2]),
@@ -425,40 +431,77 @@ bad_term_to_binary(Config) when is_list(Config) ->
terms(Config) when is_list(Config) ->
TestFun = fun(Term) ->
- try
- S = io_lib:format("~p", [Term]),
- io:put_chars(S)
- catch
- error:badarg ->
- io:put_chars("bit sized binary")
- end,
+ S = io_lib:format("~p", [Term]),
+ io:put_chars(S),
Bin = term_to_binary(Term),
case erlang:external_size(Bin) of
Sz when is_integer(Sz), size(Bin) =< Sz ->
ok
end,
- Bin1 = term_to_binary(Term, [{minor_version, 1}]),
- case erlang:external_size(Bin1, [{minor_version, 1}]) of
- Sz1 when is_integer(Sz1), size(Bin1) =< Sz1 ->
- ok
- end,
+ Bin1 = term_to_binary(Term, [{minor_version, 1}]),
+ case erlang:external_size(Bin1, [{minor_version, 1}]) of
+ Sz1 when is_integer(Sz1), size(Bin1) =< Sz1 ->
+ ok
+ end,
Term = binary_to_term_stress(Bin),
Term = binary_to_term_stress(Bin, [safe]),
- Unaligned = make_unaligned_sub_binary(Bin),
- Term = binary_to_term_stress(Unaligned),
- Term = binary_to_term_stress(Unaligned, []),
- Term = binary_to_term_stress(Bin, [safe]),
+ Bin_sz = byte_size(Bin),
+ {Term,Bin_sz} = binary_to_term_stress(Bin, [used]),
+
+ BinE = <<Bin/binary, 1, 2, 3>>,
+ {Term,Bin_sz} = binary_to_term_stress(BinE, [used]),
+
+ BinU = make_unaligned_sub_binary(Bin),
+ Term = binary_to_term_stress(BinU),
+ Term = binary_to_term_stress(BinU, []),
+ Term = binary_to_term_stress(BinU, [safe]),
+ {Term,Bin_sz} = binary_to_term_stress(BinU, [used]),
+
+ BinUE = make_unaligned_sub_binary(BinE),
+ {Term,Bin_sz} = binary_to_term_stress(BinUE, [used]),
+
BinC = erlang:term_to_binary(Term, [compressed]),
+ BinC_sz = byte_size(BinC),
+ true = BinC_sz =< size(Bin),
Term = binary_to_term_stress(BinC),
- true = size(BinC) =< size(Bin),
+ {Term, BinC_sz} = binary_to_term_stress(BinC, [used]),
+
Bin = term_to_binary(Term, [{compressed,0}]),
terms_compression_levels(Term, size(Bin), 1),
- UnalignedC = make_unaligned_sub_binary(BinC),
- Term = binary_to_term_stress(UnalignedC)
+
+ BinUC = make_unaligned_sub_binary(BinC),
+ Term = binary_to_term_stress(BinUC),
+ {Term,BinC_sz} = binary_to_term_stress(BinUC, [used]),
+
+ BinCE = <<BinC/binary, 1, 2, 3>>,
+ {Term,BinC_sz} = binary_to_term_stress(BinCE, [used]),
+
+ BinUCE = make_unaligned_sub_binary(BinCE),
+ Term = binary_to_term_stress(BinUCE),
+ {Term,BinC_sz} = binary_to_term_stress(BinUCE, [used])
end,
test_terms(TestFun),
ok.
+%% Test binary_to_term(_, [used]) returning a big Used integer.
+b2t_used_big(_Config) ->
+ case erlang:system_info(wordsize) of
+ 8 ->
+ {skipped, "This is not a 32-bit machine"};
+ 4 ->
+ %% Use a long utf8 atom for large external format but compact on heap.
+ BigAtom = binary_to_atom(<< <<16#F0908D88:32>> || _ <- lists:seq(1,255) >>,
+ utf8),
+ Atoms = (1 bsl 17) + (1 bsl 9),
+ BigAtomList = lists:duplicate(Atoms, BigAtom),
+ BigBin = term_to_binary(BigAtomList),
+ {BigAtomList, Used} = binary_to_term(BigBin, [used]),
+ 2 = erts_debug:size(Used),
+ Used = byte_size(BigBin),
+ Used = 1 + 1 + 4 + Atoms*(1+2+4*255) + 1,
+ ok
+ end.
+
terms_compression_levels(Term, UncompressedSz, Level) when Level < 10 ->
BinC = erlang:term_to_binary(Term, [{compressed,Level}]),
Term = binary_to_term_stress(BinC),
@@ -1160,7 +1203,7 @@ very_big_num(0, Result) ->
Result.
make_port() ->
- open_port({spawn, efile}, [eof]).
+ hd(erlang:ports()).
make_pid() ->
spawn_link(?MODULE, sleeper, []).
@@ -1261,40 +1304,28 @@ deep_roundtrip(T) ->
B = term_to_binary(T),
T = binary_to_term(B).
-obsolete_funs(Config) when is_list(Config) ->
+term2bin_tuple_fallbacks(Config) when is_list(Config) ->
erts_debug:set_internal_state(available_internal_state, true),
- X = id({1,2,3}),
- Y = id([a,b,c,d]),
- Z = id({x,y,z}),
- obsolete_fun(fun() -> ok end),
- obsolete_fun(fun() -> X end),
- obsolete_fun(fun(A) -> {A,X} end),
- obsolete_fun(fun() -> {X,Y} end),
- obsolete_fun(fun() -> {X,Y,Z} end),
-
- obsolete_fun(fun ?MODULE:all/1),
+ term2bin_tf(fun ?MODULE:all/1),
+ term2bin_tf(<<1:1>>),
+ term2bin_tf(<<90,80:7>>),
erts_debug:set_internal_state(available_internal_state, false),
ok.
-obsolete_fun(Fun) ->
- Tuple = case erlang:fun_info(Fun, type) of
- {type,external} ->
- {module,M} = erlang:fun_info(Fun, module),
- {name,F} = erlang:fun_info(Fun, name),
- {M,F};
- {type,local} ->
- {module,M} = erlang:fun_info(Fun, module),
- {index,I} = erlang:fun_info(Fun, index),
- {uniq,U} = erlang:fun_info(Fun, uniq),
- {env,E} = erlang:fun_info(Fun, env),
- {'fun',M,I,U,list_to_tuple(E)}
- end,
- Tuple = no_fun_roundtrip(Fun).
-
-no_fun_roundtrip(Term) ->
- binary_to_term_stress(erts_debug:get_internal_state({term_to_binary_no_funs,Term})).
+term2bin_tf(Term) ->
+ Tuple = case Term of
+ Fun when is_function(Fun) ->
+ {type, external} = erlang:fun_info(Fun, type),
+ {module,M} = erlang:fun_info(Fun, module),
+ {name,F} = erlang:fun_info(Fun, name),
+ {M,F};
+ BS when bit_size(BS) rem 8 =/= 0 ->
+ Bits = bit_size(BS) rem 8,
+ {<<BS/bitstring, 0:(8-Bits)>>, Bits}
+ end,
+ Tuple = binary_to_term_stress(erts_debug:get_internal_state({term_to_binary_tuple_fallbacks,Term})).
%% Test non-standard encodings never generated by term_to_binary/1
%% but recognized by binary_to_term/1.
@@ -1442,13 +1473,13 @@ error_after_yield(Type, M, F, AN, AFun, TrapFunc) ->
apply(M, F, A),
exit({unexpected_success, {M, F, A}})
catch
- error:Type ->
+ error:Type:Stk ->
erlang:trace(self(),false,[running,{tracer,Tracer}]),
%% We threw the exception from the native
%% function we trapped to, but we want
%% the BIF that originally was called
%% to appear in the stack trace.
- [{M, F, A, _} | _] = erlang:get_stacktrace()
+ [{M, F, A, _} | _] = Stk
end
end),
receive
diff --git a/erts/emulator/test/call_trace_SUITE.erl b/erts/emulator/test/call_trace_SUITE.erl
index 1251d644ae..d19f7f81ad 100644
--- a/erts/emulator/test/call_trace_SUITE.erl
+++ b/erts/emulator/test/call_trace_SUITE.erl
@@ -1116,8 +1116,8 @@ get_deep_4_loc(Arg) ->
deep_4(Arg),
ct:fail(should_not_return_to_here)
catch
- _:_ ->
- [{?MODULE,deep_4,1,Loc0}|_] = erlang:get_stacktrace(),
+ _:_:Stk ->
+ [{?MODULE,deep_4,1,Loc0}|_] = Stk,
Loc0
end.
diff --git a/erts/emulator/test/code_SUITE.erl b/erts/emulator/test/code_SUITE.erl
index 77321aa50f..661a2ee6c9 100644
--- a/erts/emulator/test/code_SUITE.erl
+++ b/erts/emulator/test/code_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-2017. All Rights Reserved.
+%% Copyright Ericsson AB 1999-2018. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -25,8 +25,10 @@
multi_proc_purge/1, t_check_old_code/1,
external_fun/1,get_chunk/1,module_md5/1,
constant_pools/1,constant_refc_binaries/1,
+ fake_literals/1,
false_dependency/1,coverage/1,fun_confusion/1,
- t_copy_literals/1, t_copy_literals_frags/1]).
+ t_copy_literals/1, t_copy_literals_frags/1,
+ erl_544/1]).
-define(line_trace, 1).
-include_lib("common_test/include/ct.hrl").
@@ -38,8 +40,10 @@ all() ->
call_purged_fun_code_reload, call_purged_fun_code_there,
multi_proc_purge, t_check_old_code, external_fun, get_chunk,
module_md5,
- constant_pools, constant_refc_binaries, false_dependency,
- coverage, fun_confusion, t_copy_literals, t_copy_literals_frags].
+ constant_pools, constant_refc_binaries, fake_literals,
+ false_dependency,
+ coverage, fun_confusion, t_copy_literals, t_copy_literals_frags,
+ erl_544].
init_per_suite(Config) ->
erts_debug:set_internal_state(available_internal_state, true),
@@ -554,6 +558,62 @@ wait_for_memory_deallocations() ->
wait_for_memory_deallocations()
end.
+fake_literals(_Config) ->
+ Mod = fake__literals__module,
+ try
+ do_fake_literals(Mod)
+ after
+ _ = code:purge(Mod),
+ _ = code:delete(Mod),
+ _ = code:purge(Mod),
+ _ = code:delete(Mod)
+ end,
+ ok.
+
+do_fake_literals(Mod) ->
+ Tid = ets:new(test, []),
+ ExtTerms = get_external_terms(),
+ Term0 = {self(),make_ref(),Tid,fun() -> ok end,ExtTerms},
+ Terms = [begin
+ make_literal_module(Mod, Term0),
+ Mod:term()
+ end || _ <- lists:seq(1, 10)],
+ verify_lit_terms(Terms, Term0),
+ true = ets:delete(Tid),
+ ok.
+
+make_literal_module(Mod, Term) ->
+ Exp = [{term,0}],
+ Attr = [],
+ Fs = [{function,term,0,2,
+ [{label,1},
+ {line,[]},
+ {func_info,{atom,Mod},{atom,term},0},
+ {label,2},
+ {move,{literal,Term},{x,0}},
+ return]}],
+ Asm = {Mod,Exp,Attr,Fs,2},
+ {ok,Mod,Beam} = compile:forms(Asm, [from_asm,binary,report]),
+ code:load_binary(Mod, atom_to_list(Mod), Beam).
+
+verify_lit_terms([H|T], Term) ->
+ case H =:= Term of
+ true ->
+ verify_lit_terms(T, Term);
+ false ->
+ error({bad_term,H})
+ end;
+verify_lit_terms([], _) ->
+ ok.
+
+get_external_terms() ->
+ {ok,Node} = test_server:start_node(?FUNCTION_NAME, slave, []),
+ Ref = rpc:call(Node, erlang, make_ref, []),
+ Ports = rpc:call(Node, erlang, ports, []),
+ Pid = rpc:call(Node, erlang, self, []),
+ _ = test_server:stop_node(Node),
+ {Ref,hd(Ports),Pid}.
+
%% OTP-7559: c_p->cp could contain garbage and create a false dependency
%% to a module in a process. (Thanks to Richard Carlsson.)
false_dependency(Config) when is_list(Config) ->
@@ -860,6 +920,53 @@ reloader(Mod,Code,Time) ->
reloader(Mod,Code,Time)
end.
+erl_544(Config) when is_list(Config) ->
+ case file:native_name_encoding() of
+ utf8 ->
+ {ok, CWD} = file:get_cwd(),
+ try
+ Mod = erl_544,
+ FileName = atom_to_list(Mod) ++ ".erl",
+ Priv = proplists:get_value(priv_dir, Config),
+ Data = proplists:get_value(data_dir, Config),
+ {ok, FileContent} = file:read_file(filename:join(Data,
+ FileName)),
+ Dir = filename:join(Priv, [16#2620,16#2620,16#2620]),
+ File = filename:join(Dir, FileName),
+ io:format("~ts~n", [File]),
+ ok = file:make_dir(Dir),
+ ok = file:set_cwd(Dir),
+ ok = file:write_file(File, [FileContent]),
+ {ok, Mod} = compile:file(File),
+ Res1 = (catch Mod:err()),
+ io:format("~p~n", [Res1]),
+ {'EXIT', {err, [{Mod, err, 0, Info1}|_]}} = Res1,
+ File = proplists:get_value(file, Info1),
+ Me = self(),
+ Go = make_ref(),
+ Tester = spawn_link(fun () ->
+ Mod:wait(Me, Go),
+ Mod:err()
+ end),
+ receive Go -> ok end,
+ Res2 = process_info(Tester, current_stacktrace),
+ io:format("~p~n", [Res2]),
+ {current_stacktrace, Stack} = Res2,
+ [{Mod, wait, 2, Info2}|_] = Stack,
+ File = proplists:get_value(file, Info2),
+ StackFun = fun(_, _, _) -> false end,
+ FormatFun = fun (Term, _) -> io_lib:format("~tp", [Term]) end,
+ Formated =
+ lib:format_stacktrace(1, Stack, StackFun, FormatFun),
+ true = is_list(Formated),
+ ok
+ after
+ ok = file:set_cwd(CWD)
+ end,
+ ok;
+ _Enc ->
+ {skipped, "Only run when native file name encoding is utf8"}
+ end.
%% Utilities.
diff --git a/erts/emulator/test/code_SUITE_data/erl_544.erl b/erts/emulator/test/code_SUITE_data/erl_544.erl
new file mode 100644
index 0000000000..c93f3ef5bc
--- /dev/null
+++ b/erts/emulator/test/code_SUITE_data/erl_544.erl
@@ -0,0 +1,35 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2018. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(erl_544).
+
+-export([err/0, wait/2]).
+
+err() ->
+ erlang:error(err).
+
+wait(Pid, Msg) ->
+ erlang:yield(),
+ Pid ! Msg,
+ receive
+ after infinity ->
+ ok
+ end,
+ err().
diff --git a/erts/emulator/test/ddll_SUITE.erl b/erts/emulator/test/ddll_SUITE.erl
index 031b05790d..4998fc08be 100644
--- a/erts/emulator/test/ddll_SUITE.erl
+++ b/erts/emulator/test/ddll_SUITE.erl
@@ -775,7 +775,7 @@ errors(Config) when is_list(Config) ->
{error, bad_driver_name} = erl_ddll:load_driver(Path, wrongname_drv),
%% We assume that there is a statically linked driver named "ddll":
- {error, linked_in_driver} = erl_ddll:unload_driver(efile),
+ {error, linked_in_driver} = erl_ddll:unload_driver(ram_file_drv),
{error, not_loaded} = erl_ddll:unload_driver("__pucko_driver__"),
case os:type() of
diff --git a/erts/emulator/test/decode_packet_SUITE.erl b/erts/emulator/test/decode_packet_SUITE.erl
index 54ee4d5567..0ccdbd7ee8 100644
--- a/erts/emulator/test/decode_packet_SUITE.erl
+++ b/erts/emulator/test/decode_packet_SUITE.erl
@@ -239,7 +239,7 @@ packet_size(Config) when is_list(Config) ->
%% Test OTP-9389, long HTTP header lines.
Opts = [{packet_size, 128}],
Pkt = list_to_binary(["GET / HTTP/1.1\r\nHost: localhost\r\nLink: /",
- string:chars($Y, 64), "\r\n\r\n"]),
+ lists:duplicate(64, $Y), "\r\n\r\n"]),
<<Pkt1:50/binary, Pkt2/binary>> = Pkt,
{ok, {http_request,'GET',{abs_path,"/"},{1,1}}, Rest1} =
erlang:decode_packet(http, Pkt1, Opts),
@@ -250,7 +250,7 @@ packet_size(Config) when is_list(Config) ->
erlang:decode_packet(httph, list_to_binary([Rest2, Pkt2]), Opts),
Pkt3 = list_to_binary(["GET / HTTP/1.1\r\nHost: localhost\r\nLink: /",
- string:chars($Y, 129), "\r\n\r\n"]),
+ lists:duplicate(129, $Y), "\r\n\r\n"]),
{ok, {http_request,'GET',{abs_path,"/"},{1,1}}, Rest3} =
erlang:decode_packet(http, Pkt3, Opts),
{ok, {http_header,_,'Host',_,"localhost"}, Rest4} =
@@ -509,9 +509,9 @@ decode_line(Bin,MaxLen) ->
end.
find_in_binary(Byte, Bin) ->
- case string:chr(binary_to_list(Bin),Byte) of
- 0 -> notfound;
- P -> P
+ case string:find(Bin, [Byte]) of
+ nomatch -> notfound;
+ Suffix -> byte_size(Bin) - byte_size(Suffix) + 1
end.
ssl(Config) when is_list(Config) ->
@@ -562,7 +562,7 @@ decode_pkt(Type,Bin,Opts) ->
otp_9389(Config) when is_list(Config) ->
Opts = [{packet_size, 16384}, {line_length, 3000}],
Pkt = list_to_binary(["GET / HTTP/1.1\r\nHost: localhost\r\nLink: /",
- string:chars($X, 8192),
+ lists:duplicate(8192, $X),
"\r\nContent-Length: 0\r\n\r\n"]),
<<Pkt1:5000/binary, Pkt2/binary>> = Pkt,
{ok, {http_request,'GET',{abs_path,"/"},{1,1}}, Rest1} =
diff --git a/erts/emulator/test/dirty_bif_SUITE.erl b/erts/emulator/test/dirty_bif_SUITE.erl
index 981ec4d48d..46eb0cba58 100644
--- a/erts/emulator/test/dirty_bif_SUITE.erl
+++ b/erts/emulator/test/dirty_bif_SUITE.erl
@@ -108,90 +108,80 @@ dirty_bif_exception(Config) when is_list(Config) ->
erts_debug:dirty_cpu(error, Error),
ct:fail(expected_exception)
catch
- error:ErrorType ->
- [{erts_debug,dirty_cpu,[error, Error],_}|_]
- = erlang:get_stacktrace(),
+ error:ErrorType:Stk1 ->
+ [{erts_debug,dirty_cpu,[error, Error],_}|_] = Stk1,
ok
end,
try
apply(erts_debug,dirty_cpu,[error, Error]),
ct:fail(expected_exception)
catch
- error:ErrorType ->
- [{erts_debug,dirty_cpu,[error, Error],_}|_]
- = erlang:get_stacktrace(),
+ error:ErrorType:Stk2 ->
+ [{erts_debug,dirty_cpu,[error, Error],_}|_] = Stk2,
ok
end,
try
erts_debug:dirty_io(error, Error),
ct:fail(expected_exception)
catch
- error:ErrorType ->
- [{erts_debug,dirty_io,[error, Error],_}|_]
- = erlang:get_stacktrace(),
+ error:ErrorType:Stk3 ->
+ [{erts_debug,dirty_io,[error, Error],_}|_] = Stk3,
ok
end,
try
apply(erts_debug,dirty_io,[error, Error]),
ct:fail(expected_exception)
catch
- error:ErrorType ->
- [{erts_debug,dirty_io,[error, Error],_}|_]
- = erlang:get_stacktrace(),
+ error:ErrorType:Stk4 ->
+ [{erts_debug,dirty_io,[error, Error],_}|_] = Stk4,
ok
end,
try
erts_debug:dirty(normal, error, Error),
ct:fail(expected_exception)
catch
- error:ErrorType ->
- [{erts_debug,dirty,[normal, error, Error],_}|_]
- = erlang:get_stacktrace(),
+ error:ErrorType:Stk5 ->
+ [{erts_debug,dirty,[normal, error, Error],_}|_] = Stk5,
ok
end,
try
apply(erts_debug,dirty,[normal, error, Error]),
ct:fail(expected_exception)
catch
- error:ErrorType ->
- [{erts_debug,dirty,[normal, error, Error],_}|_]
- = erlang:get_stacktrace(),
+ error:ErrorType:Stk6 ->
+ [{erts_debug,dirty,[normal, error, Error],_}|_] = Stk6,
ok
end,
try
erts_debug:dirty(dirty_cpu, error, Error),
ct:fail(expected_exception)
catch
- error:ErrorType ->
- [{erts_debug,dirty,[dirty_cpu, error, Error],_}|_]
- = erlang:get_stacktrace(),
+ error:ErrorType:Stk7 ->
+ [{erts_debug,dirty,[dirty_cpu, error, Error],_}|_] = Stk7,
ok
end,
try
apply(erts_debug,dirty,[dirty_cpu, error, Error]),
ct:fail(expected_exception)
catch
- error:ErrorType ->
- [{erts_debug,dirty,[dirty_cpu, error, Error],_}|_]
- = erlang:get_stacktrace(),
+ error:ErrorType:Stk8 ->
+ [{erts_debug,dirty,[dirty_cpu, error, Error],_}|_] = Stk8,
ok
end,
try
erts_debug:dirty(dirty_io, error, Error),
ct:fail(expected_exception)
catch
- error:ErrorType ->
- [{erts_debug,dirty,[dirty_io, error, Error],_}|_]
- = erlang:get_stacktrace(),
+ error:ErrorType:Stk9 ->
+ [{erts_debug,dirty,[dirty_io, error, Error],_}|_] = Stk9,
ok
end,
try
apply(erts_debug,dirty,[dirty_io, error, Error]),
ct:fail(expected_exception)
catch
- error:ErrorType ->
- [{erts_debug,dirty,[dirty_io, error, Error],_}|_]
- = erlang:get_stacktrace(),
+ error:ErrorType:Stk10 ->
+ [{erts_debug,dirty,[dirty_io, error, Error],_}|_] = Stk10,
ok
end
end,
@@ -204,25 +194,22 @@ dirty_bif_multischedule_exception(Config) when is_list(Config) ->
try
erts_debug:dirty_cpu(reschedule,1001)
catch
- error:badarg ->
- [{erts_debug,dirty_cpu,[reschedule, 1001],_}|_]
- = erlang:get_stacktrace(),
+ error:badarg:Stk1 ->
+ [{erts_debug,dirty_cpu,[reschedule, 1001],_}|_] = Stk1,
ok
end,
try
erts_debug:dirty_io(reschedule,1001)
catch
- error:badarg ->
- [{erts_debug,dirty_io,[reschedule, 1001],_}|_]
- = erlang:get_stacktrace(),
+ error:badarg:Stk2 ->
+ [{erts_debug,dirty_io,[reschedule, 1001],_}|_] = Stk2,
ok
end,
try
erts_debug:dirty(normal,reschedule,1001)
catch
- error:badarg ->
- [{erts_debug,dirty,[normal,reschedule,1001],_}|_]
- = erlang:get_stacktrace(),
+ error:badarg:Stk3 ->
+ [{erts_debug,dirty,[normal,reschedule,1001],_}|_] = Stk3,
ok
end.
@@ -230,6 +217,11 @@ dirty_scheduler_exit(Config) when is_list(Config) ->
{ok, Node} = start_node(Config, "+SDio 1"),
[ok] = mcall(Node,
[fun() ->
+ %% Perform a dry run to ensure that all required code
+ %% is loaded. Otherwise the test will fail since code
+ %% loading is done through dirty IO and it won't make
+ %% any progress during this test.
+ _DryRun = test_dirty_scheduler_exit(),
Start = erlang:monotonic_time(millisecond),
ok = test_dirty_scheduler_exit(),
End = erlang:monotonic_time(millisecond),
@@ -246,23 +238,22 @@ test_dse(0,Pids) ->
timer:sleep(100),
kill_dse(Pids,[]);
test_dse(N,Pids) ->
- Pid = spawn_link(fun () -> erts_debug:dirty_io(wait, 5000) end),
+ Pid = spawn_link(fun () -> erts_debug:dirty_io(wait, 1000) end),
test_dse(N-1,[Pid|Pids]).
kill_dse([],Killed) ->
- wait_dse(Killed);
+ wait_dse(Killed, ok);
kill_dse([Pid|Pids],AlreadyKilled) ->
exit(Pid,kill),
kill_dse(Pids,[Pid|AlreadyKilled]).
-wait_dse([]) ->
- ok;
-wait_dse([Pid|Pids]) ->
+wait_dse([], Result) ->
+ Result;
+wait_dse([Pid|Pids], Result) ->
receive
- {'EXIT',Pid,Reason} ->
- killed = Reason
- end,
- wait_dse(Pids).
+ {'EXIT', Pid, killed} -> wait_dse(Pids, Result);
+ {'EXIT', Pid, _Other} -> wait_dse(Pids, failed)
+ end.
dirty_call_while_terminated(Config) when is_list(Config) ->
Me = self(),
diff --git a/erts/emulator/test/dirty_nif_SUITE.erl b/erts/emulator/test/dirty_nif_SUITE.erl
index 13806fd5c4..93d0ac392c 100644
--- a/erts/emulator/test/dirty_nif_SUITE.erl
+++ b/erts/emulator/test/dirty_nif_SUITE.erl
@@ -109,9 +109,8 @@ dirty_nif_exception(Config) when is_list(Config) ->
call_dirty_nif_exception(1),
ct:fail(expected_badarg)
catch
- error:badarg ->
- [{?MODULE,call_dirty_nif_exception,[1],_}|_] =
- erlang:get_stacktrace(),
+ error:badarg:Stk1 ->
+ [{?MODULE,call_dirty_nif_exception,[1],_}|_] = Stk1,
ok
end,
try
@@ -121,9 +120,8 @@ dirty_nif_exception(Config) when is_list(Config) ->
call_dirty_nif_exception(0),
ct:fail(expected_badarg)
catch
- error:badarg ->
- [{?MODULE,call_dirty_nif_exception,[0],_}|_] =
- erlang:get_stacktrace(),
+ error:badarg:Stk2 ->
+ [{?MODULE,call_dirty_nif_exception,[0],_}|_] = Stk2,
ok
end,
%% this checks that a dirty NIF can raise various terms as
@@ -138,8 +136,8 @@ nif_raise_exceptions(NifFunc) ->
erlang:apply(?MODULE,NifFunc,[Term]),
ct:fail({expected,Term})
catch
- error:Term ->
- [{?MODULE,NifFunc,[Term],_}|_] = erlang:get_stacktrace(),
+ error:Term:Stk ->
+ [{?MODULE,NifFunc,[Term],_}|_] = Stk,
ok
end
end, ok, ExcTerms).
@@ -151,6 +149,11 @@ dirty_scheduler_exit(Config) when is_list(Config) ->
[ok] = mcall(Node,
[fun() ->
ok = erlang:load_nif(NifLib, []),
+ %% Perform a dry run to ensure that all required code
+ %% is loaded. Otherwise the test will fail since code
+ %% loading is done through dirty IO and it won't make
+ %% any progress during this test.
+ _DryRun = test_dirty_scheduler_exit(),
Start = erlang:monotonic_time(millisecond),
ok = test_dirty_scheduler_exit(),
End = erlang:monotonic_time(millisecond),
@@ -171,19 +174,18 @@ test_dse(N,Pids) ->
test_dse(N-1,[Pid|Pids]).
kill_dse([],Killed) ->
- wait_dse(Killed);
+ wait_dse(Killed, ok);
kill_dse([Pid|Pids],AlreadyKilled) ->
exit(Pid,kill),
kill_dse(Pids,[Pid|AlreadyKilled]).
-wait_dse([]) ->
- ok;
-wait_dse([Pid|Pids]) ->
+wait_dse([], Result) ->
+ Result;
+wait_dse([Pid|Pids], Result) ->
receive
- {'EXIT',Pid,Reason} ->
- killed = Reason
- end,
- wait_dse(Pids).
+ {'EXIT', Pid, killed} -> wait_dse(Pids, Result);
+ {'EXIT', Pid, _Other} -> wait_dse(Pids, failed)
+ end.
dirty_call_while_terminated(Config) when is_list(Config) ->
Me = self(),
@@ -287,9 +289,9 @@ access_dirty_heap(Dirty, RGL, N, R) ->
%% dirty NIF where the main lock is needed for that access do not get
%% blocked. Each test passes its pid to dirty_sleeper, which sends a
%% 'ready' message when it's running on a dirty scheduler and just before
-%% it starts a 6 second sleep. When it receives the message, it verifies
+%% it starts a 2 second sleep. When it receives the message, it verifies
%% that access to the dirty process is as it expects. After the dirty
-%% process finishes its 6 second sleep but before it returns from the dirty
+%% process finishes its 2 second sleep but before it returns from the dirty
%% scheduler, it sends a 'done' message. If the tester already received
%% that message, the test fails because it means attempting to access the
%% dirty process waited for that process to return to a regular scheduler,
@@ -353,7 +355,7 @@ dirty_process_trace(Config) when is_list(Config) ->
error(missing_trace_return_message)
end
after
- 6500 ->
+ 2500 ->
error(missing_done_message)
end,
ok
@@ -380,7 +382,7 @@ code_purge(Config) when is_list(Config) ->
Start = erlang:monotonic_time(),
{Pid1, Mon1} = spawn_monitor(fun () ->
dirty_code_test:func(fun () ->
- %% Sleep for 6 seconds
+ %% Sleep for 2 seconds
%% in dirty nif...
dirty_sleeper()
end)
@@ -388,7 +390,7 @@ code_purge(Config) when is_list(Config) ->
{module, dirty_code_test} = erlang:load_module(dirty_code_test, Bin),
{Pid2, Mon2} = spawn_monitor(fun () ->
dirty_code_test:func(fun () ->
- %% Sleep for 6 seconds
+ %% Sleep for 2 seconds
%% in dirty nif...
dirty_sleeper()
end)
@@ -490,7 +492,7 @@ test_dirty_process_access(Start, Test, Finish) ->
ok
end
after
- 3000 ->
+ 1000 ->
error(timeout)
end,
ok = Finish(NifPid).
diff --git a/erts/emulator/test/dirty_nif_SUITE_data/dirty_nif_SUITE.c b/erts/emulator/test/dirty_nif_SUITE_data/dirty_nif_SUITE.c
index 0321b9898f..a94a2c0b02 100644
--- a/erts/emulator/test/dirty_nif_SUITE_data/dirty_nif_SUITE.c
+++ b/erts/emulator/test/dirty_nif_SUITE_data/dirty_nif_SUITE.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2009-2017. All Rights Reserved.
+ * Copyright Ericsson AB 2009-2018. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -112,15 +112,12 @@ static ERL_NIF_TERM send_from_dirty_nif(ErlNifEnv* env, int argc, const ERL_NIF_
{
ERL_NIF_TERM result;
ErlNifPid pid;
- ErlNifEnv* menv;
int res;
if (!enif_get_local_pid(env, argv[0], &pid))
return enif_make_badarg(env);
result = enif_make_tuple2(env, enif_make_atom(env, "ok"), enif_make_pid(env, &pid));
- menv = enif_alloc_env();
- res = enif_send(env, &pid, menv, result);
- enif_free_env(menv);
+ res = enif_send(env, &pid, NULL, result);
if (!res)
return enif_make_badarg(env);
else
@@ -131,15 +128,12 @@ static ERL_NIF_TERM send_wait_from_dirty_nif(ErlNifEnv* env, int argc, const ERL
{
ERL_NIF_TERM result;
ErlNifPid pid;
- ErlNifEnv* menv;
int res;
if (!enif_get_local_pid(env, argv[0], &pid))
return enif_make_badarg(env);
result = enif_make_tuple2(env, enif_make_atom(env, "ok"), enif_make_pid(env, &pid));
- menv = enif_alloc_env();
- res = enif_send(env, &pid, menv, result);
- enif_free_env(menv);
+ res = enif_send(env, &pid, NULL, result);
#ifdef __WIN32__
Sleep(2000);
@@ -211,22 +205,17 @@ dirty_sleeper(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
/* If we get a pid argument, it indicates a process involved in the
test wants a message from us. Prior to the sleep we send a 'ready'
message, and then after the sleep, send a 'done' message. */
- if (argc == 1 && enif_get_local_pid(env, argv[0], &pid)) {
- msg_env = enif_alloc_env();
- enif_send(env, &pid, msg_env, enif_make_atom(msg_env, "ready"));
- }
+ if (argc == 1 && enif_get_local_pid(env, argv[0], &pid))
+ enif_send(env, &pid, NULL, enif_make_atom(env, "ready"));
#ifdef __WIN32__
- Sleep(6000);
+ Sleep(2000);
#else
- sleep(6);
+ sleep(2);
#endif
- if (argc == 1) {
- assert(msg_env != NULL);
- enif_send(env, &pid, msg_env, enif_make_atom(msg_env, "done"));
- enif_free_env(msg_env);
- }
+ if (argc == 1)
+ enif_send(env, &pid, NULL, enif_make_atom(env, "done"));
return enif_make_atom(env, "ok");
}
@@ -247,8 +236,8 @@ static ERL_NIF_TERM dirty_call_while_terminated_nif(ErlNifEnv* env, int argc, co
self_term = enif_make_pid(env, &self);
- result = enif_make_tuple2(env, enif_make_atom(env, "dirty_alive"), self_term);
menv = enif_alloc_env();
+ result = enif_make_tuple2(menv, enif_make_atom(menv, "dirty_alive"), self_term);
res = enif_send(env, &to, menv, result);
enif_free_env(menv);
if (!res)
@@ -259,9 +248,7 @@ static ERL_NIF_TERM dirty_call_while_terminated_nif(ErlNifEnv* env, int argc, co
;
result = enif_make_tuple2(env, enif_make_atom(env, "dirty_dead"), self_term);
- menv = enif_alloc_env();
- res = enif_send(env, &to, menv, result);
- enif_free_env(menv);
+ res = enif_send(env, &to, NULL, result);
#ifdef __WIN32__
Sleep(1000);
diff --git a/erts/emulator/test/distribution_SUITE.erl b/erts/emulator/test/distribution_SUITE.erl
index 28be4bfe37..e40d346e10 100644
--- a/erts/emulator/test/distribution_SUITE.erl
+++ b/erts/emulator/test/distribution_SUITE.erl
@@ -35,13 +35,18 @@
-include_lib("common_test/include/ct.hrl").
+%-define(Line, erlang:display({line,?LINE}),).
+-define(Line,).
+
-export([all/0, suite/0, groups/0,
ping/1, bulk_send_small/1,
+ group_leader/1,
+ optimistic_dflags/1,
bulk_send_big/1, bulk_send_bigbig/1,
local_send_small/1, local_send_big/1,
local_send_legal/1, link_to_busy/1, exit_to_busy/1,
lost_exit/1, link_to_dead/1, link_to_dead_new_node/1,
- applied_monitor_node/1, ref_port_roundtrip/1, nil_roundtrip/1,
+ ref_port_roundtrip/1, nil_roundtrip/1,
trap_bif_1/1, trap_bif_2/1, trap_bif_3/1,
stop_dist/1,
dist_auto_connect_never/1, dist_auto_connect_once/1,
@@ -56,10 +61,13 @@
bad_dist_ext_process_info/1,
bad_dist_ext_control/1,
bad_dist_ext_connection_id/1,
+ bad_dist_ext_size/1,
start_epmd_false/1, epmd_module/1]).
%% Internal exports.
-export([sender/3, receiver2/2, dummy_waiter/0, dead_process/0,
+ group_leader_1/1,
+ optimistic_dflags_echo/0, optimistic_dflags_sender/1,
roundtrip/1, bounce/1, do_dist_auto_connect/1, inet_rpc_server/1,
dist_parallel_sender/3, dist_parallel_receiver/0,
dist_evil_parallel_receiver/0]).
@@ -73,8 +81,10 @@ suite() ->
all() ->
[ping, {group, bulk_send}, {group, local_send},
+ group_leader,
+ optimistic_dflags,
link_to_busy, exit_to_busy, lost_exit, link_to_dead,
- link_to_dead_new_node, applied_monitor_node,
+ link_to_dead_new_node,
ref_port_roundtrip, nil_roundtrip, stop_dist,
{group, trap_bif}, {group, dist_auto_connect},
dist_parallel_send, atom_roundtrip, unicode_atom_roundtrip,
@@ -92,6 +102,7 @@ groups() ->
[dist_auto_connect_never, dist_auto_connect_once]},
{bad_dist_ext, [],
[bad_dist_ext_receive, bad_dist_ext_process_info,
+ bad_dist_ext_size,
bad_dist_ext_control, bad_dist_ext_connection_id]}].
%% Tests pinging a node in different ways.
@@ -122,6 +133,96 @@ ping(Config) when is_list(Config) ->
ok.
+%% Test erlang:group_leader(_, ExternalPid), i.e. DOP_GROUP_LEADER
+group_leader(Config) when is_list(Config) ->
+ ?Line Sock = start_relay_node(group_leader_1, []),
+ ?Line Sock2 = start_relay_node(group_leader_2, []),
+ try
+ ?Line Node2 = inet_rpc_nodename(Sock2),
+ ?Line {ok, ok} = do_inet_rpc(Sock, ?MODULE, group_leader_1, [Node2])
+ after
+ ?Line stop_relay_node(Sock),
+ ?Line stop_relay_node(Sock2)
+ end,
+ ok.
+
+group_leader_1(Node2) ->
+ ?Line ExtPid = spawn(Node2, fun F() ->
+ receive {From, group_leader} ->
+ From ! {self(), group_leader, group_leader()}
+ end,
+ F()
+ end),
+ ?Line GL1 = self(),
+ ?Line group_leader(GL1, ExtPid),
+ ?Line ExtPid ! {self(), group_leader},
+ ?Line {ExtPid, group_leader, GL1} = receive_one(),
+
+ %% Kill connection and repeat test when group_leader/2 triggers auto-connect
+ ?Line net_kernel:monitor_nodes(true),
+ ?Line net_kernel:disconnect(Node2),
+ ?Line {nodedown, Node2} = receive_one(),
+ ?Line GL2 = spawn(fun() -> dummy end),
+ ?Line group_leader(GL2, ExtPid),
+ ?Line {nodeup, Node2} = receive_one(),
+ ?Line ExtPid ! {self(), group_leader},
+ ?Line {ExtPid, group_leader, GL2} = receive_one(),
+ ok.
+
+%% Test optimistic distribution flags toward pending connections (DFLAG_DIST_HOPEFULLY)
+optimistic_dflags(Config) when is_list(Config) ->
+ ?Line Sender = start_relay_node(optimistic_dflags_sender, []),
+ ?Line Echo = start_relay_node(optimistic_dflags_echo, []),
+ try
+ ?Line {ok, ok} = do_inet_rpc(Echo, ?MODULE, optimistic_dflags_echo, []),
+
+ ?Line EchoNode = inet_rpc_nodename(Echo),
+ ?Line {ok, ok} = do_inet_rpc(Sender, ?MODULE, optimistic_dflags_sender, [EchoNode])
+ after
+ ?Line stop_relay_node(Sender),
+ ?Line stop_relay_node(Echo)
+ end,
+ ok.
+
+optimistic_dflags_echo() ->
+ P = spawn(fun F() ->
+ receive {From, Term} ->
+ From ! {self(), Term}
+ end,
+ F()
+ end),
+ register(optimistic_dflags_echo, P),
+ optimistic_dflags_echo ! {self(), hello},
+ {P, hello} = receive_one(),
+ ok.
+
+optimistic_dflags_sender(EchoNode) ->
+ ?Line net_kernel:monitor_nodes(true),
+
+ optimistic_dflags_do(EchoNode, <<1:1>>),
+ optimistic_dflags_do(EchoNode, fun lists:map/2),
+ ok.
+
+optimistic_dflags_do(EchoNode, Term) ->
+ ?Line {optimistic_dflags_echo, EchoNode} ! {self(), Term},
+ ?Line {nodeup, EchoNode} = receive_one(),
+ ?Line {EchoPid, Term} = receive_one(),
+ %% repeat with pid destination
+ ?Line net_kernel:disconnect(EchoNode),
+ ?Line {nodedown, EchoNode} = receive_one(),
+ ?Line EchoPid ! {self(), Term},
+ ?Line {nodeup, EchoNode} = receive_one(),
+ ?Line {EchoPid, Term} = receive_one(),
+
+ ?Line net_kernel:disconnect(EchoNode),
+ ?Line {nodedown, EchoNode} = receive_one(),
+ ok.
+
+
+receive_one() ->
+ receive M -> M after 1000 -> timeout end.
+
+
bulk_send_small(Config) when is_list(Config) ->
bulk_send(64, 32).
@@ -637,31 +738,11 @@ link_to_dead_new_node(Config) when is_list(Config) ->
end,
ok.
-%% Test that monitor_node/2 works when applied.
-applied_monitor_node(Config) when is_list(Config) ->
- NonExisting = list_to_atom("__non_existing__@" ++ hostname()),
-
- %% Tail-recursive call to apply (since the node is non-existing,
- %% there will be a trap).
-
- true = tail_apply(erlang, monitor_node, [NonExisting, true]),
- [{nodedown, NonExisting}] = test_server:messages_get(),
-
- %% Ordinary call (with trap).
-
- true = apply(erlang, monitor_node, [NonExisting, true]),
- [{nodedown, NonExisting}] = test_server:messages_get(),
-
- ok.
-
-tail_apply(M, F, A) ->
- apply(M, F, A).
-
%% Test that sending a port or reference to another node and back again
%% doesn't correct them in any way.
ref_port_roundtrip(Config) when is_list(Config) ->
process_flag(trap_exit, true),
- Port = open_port({spawn, efile}, []),
+ Port = make_port(),
Ref = make_ref(),
{ok, Node} = start_node(ref_port_roundtrip),
net_adm:ping(Node),
@@ -682,6 +763,9 @@ ref_port_roundtrip(Config) when is_list(Config) ->
end,
ok.
+make_port() ->
+ hd(erlang:ports()).
+
roundtrip(Term) ->
exit(Term).
@@ -784,8 +868,8 @@ dist_auto_connect_once(Config) when is_list(Config) ->
{ok, pong} = do_inet_rpc(Sock2,net_adm,ping,[NN]),
{ok,[NN2]} = do_inet_rpc(Sock,erlang,nodes,[]),
{ok,[NN]} = do_inet_rpc(Sock2,erlang,nodes,[]),
- [_,HostPartPeer] = string:tokens(atom_to_list(NN),"@"),
- [_,MyHostPart] = string:tokens(atom_to_list(node()),"@"),
+ [_,HostPartPeer] = string:lexemes(atom_to_list(NN),"@"),
+ [_,MyHostPart] = string:lexemes(atom_to_list(node()),"@"),
% Give net_kernel a chance to change the state of the node to up to.
receive after 1000 -> ok end,
case HostPartPeer of
@@ -1156,8 +1240,6 @@ contended_atom_cache_entry_test(Config, Type) ->
spawn_link(
SNode,
fun () ->
- erts_debug:set_internal_state(available_internal_state,
- true),
Master = self(),
CIX = get_cix(),
TestAtoms = case Type of
@@ -1242,7 +1324,7 @@ get_cix(CIX) when is_integer(CIX), CIX < 0 ->
get_cix(CIX) when is_integer(CIX) ->
get_cix(CIX,
unwanted_cixs(),
- erts_debug:get_internal_state(max_atom_out_cache_index)).
+ get_internal_state(max_atom_out_cache_index)).
get_cix(CIX, Unwanted, MaxCIX) when CIX > MaxCIX ->
get_cix(0, Unwanted, MaxCIX);
@@ -1254,8 +1336,8 @@ get_cix(CIX, Unwanted, MaxCIX) ->
unwanted_cixs() ->
lists:map(fun (Node) ->
- erts_debug:get_internal_state({atom_out_cache_index,
- Node})
+ get_internal_state({atom_out_cache_index,
+ Node})
end,
nodes()).
@@ -1264,7 +1346,7 @@ get_conflicting_atoms(_CIX, 0) ->
[];
get_conflicting_atoms(CIX, N) ->
Atom = list_to_atom("atom" ++ integer_to_list(erlang:unique_integer([positive]))),
- case erts_debug:get_internal_state({atom_out_cache_index, Atom}) of
+ case get_internal_state({atom_out_cache_index, Atom}) of
CIX ->
[Atom|get_conflicting_atoms(CIX, N-1)];
_ ->
@@ -1275,7 +1357,7 @@ get_conflicting_unicode_atoms(_CIX, 0) ->
[];
get_conflicting_unicode_atoms(CIX, N) ->
Atom = string_to_atom([16#1f608] ++ "atom" ++ integer_to_list(erlang:unique_integer([positive]))),
- case erts_debug:get_internal_state({atom_out_cache_index, Atom}) of
+ case get_internal_state({atom_out_cache_index, Atom}) of
CIX ->
[Atom|get_conflicting_unicode_atoms(CIX, N-1)];
_ ->
@@ -1363,81 +1445,59 @@ bad_dist_structure(Config) when is_list(Config) ->
start_monitor(Offender,P),
P ! one,
send_bad_structure(Offender, P,{?DOP_MONITOR_P_EXIT,'replace',P,normal},2),
- pong = rpc:call(Victim, net_adm, ping, [Offender]),
+
start_monitor(Offender,P),
send_bad_structure(Offender, P,{?DOP_MONITOR_P_EXIT,'replace',P,normal,normal},2),
- pong = rpc:call(Victim, net_adm, ping, [Offender]),
+
start_link(Offender,P),
send_bad_structure(Offender, P,{?DOP_LINK},0),
- pong = rpc:call(Victim, net_adm, ping, [Offender]),
+
start_link(Offender,P),
send_bad_structure(Offender, P,{?DOP_UNLINK,'replace'},2),
- pong = rpc:call(Victim, net_adm, ping, [Offender]),
+
start_link(Offender,P),
send_bad_structure(Offender, P,{?DOP_UNLINK,'replace',make_ref()},2),
- pong = rpc:call(Victim, net_adm, ping, [Offender]),
+
start_link(Offender,P),
send_bad_structure(Offender, P,{?DOP_UNLINK,make_ref(),P},0),
- pong = rpc:call(Victim, net_adm, ping, [Offender]),
+
start_link(Offender,P),
send_bad_structure(Offender, P,{?DOP_UNLINK,normal,normal},0),
- pong = rpc:call(Victim, net_adm, ping, [Offender]),
+
start_monitor(Offender,P),
send_bad_structure(Offender, P,{?DOP_MONITOR_P,'replace',P},2),
- pong = rpc:call(Victim, net_adm, ping, [Offender]),
+
start_monitor(Offender,P),
send_bad_structure(Offender, P,{?DOP_MONITOR_P,'replace',P,normal},2),
- pong = rpc:call(Victim, net_adm, ping, [Offender]),
+
start_monitor(Offender,P),
send_bad_structure(Offender, P,{?DOP_DEMONITOR_P,'replace',P},2),
- pong = rpc:call(Victim, net_adm, ping, [Offender]),
+
start_monitor(Offender,P),
send_bad_structure(Offender, P,{?DOP_DEMONITOR_P,'replace',P,normal},2),
- pong = rpc:call(Victim, net_adm, ping, [Offender]),
+
send_bad_structure(Offender, P,{?DOP_EXIT,'replace',P},2),
- pong = rpc:call(Victim, net_adm, ping, [Offender]),
send_bad_structure(Offender, P,{?DOP_EXIT,make_ref(),normal,normal},0),
- pong = rpc:call(Victim, net_adm, ping, [Offender]),
send_bad_structure(Offender, P,{?DOP_EXIT_TT,'replace',token,P},2),
- pong = rpc:call(Victim, net_adm, ping, [Offender]),
send_bad_structure(Offender, P,{?DOP_EXIT_TT,make_ref(),token,normal,normal},0),
- pong = rpc:call(Victim, net_adm, ping, [Offender]),
send_bad_structure(Offender, P,{?DOP_EXIT2,'replace',P},2),
- pong = rpc:call(Victim, net_adm, ping, [Offender]),
send_bad_structure(Offender, P,{?DOP_EXIT2,make_ref(),normal,normal},0),
- pong = rpc:call(Victim, net_adm, ping, [Offender]),
send_bad_structure(Offender, P,{?DOP_EXIT2_TT,'replace',token,P},2),
- pong = rpc:call(Victim, net_adm, ping, [Offender]),
send_bad_structure(Offender, P,{?DOP_EXIT2_TT,make_ref(),token,normal,normal},0),
- pong = rpc:call(Victim, net_adm, ping, [Offender]),
send_bad_structure(Offender, P,{?DOP_GROUP_LEADER,'replace'},2),
- pong = rpc:call(Victim, net_adm, ping, [Offender]),
send_bad_structure(Offender, P,{?DOP_GROUP_LEADER,'replace','atomic'},2),
- pong = rpc:call(Victim, net_adm, ping, [Offender]),
send_bad_structure(Offender, P,{?DOP_GROUP_LEADER,'replace',P},0),
- pong = rpc:call(Victim, net_adm, ping, [Offender]),
send_bad_structure(Offender, P,{?DOP_REG_SEND_TT,'replace','',name},2,{message}),
- pong = rpc:call(Victim, net_adm, ping, [Offender]),
send_bad_structure(Offender, P,{?DOP_REG_SEND_TT,'replace','',name,token},0,{message}),
- pong = rpc:call(Victim, net_adm, ping, [Offender]),
send_bad_structure(Offender, P,{?DOP_REG_SEND,'replace',''},2,{message}),
- pong = rpc:call(Victim, net_adm, ping, [Offender]),
send_bad_structure(Offender, P,{?DOP_REG_SEND,'replace','',P},0,{message}),
- pong = rpc:call(Victim, net_adm, ping, [Offender]),
send_bad_structure(Offender, P,{?DOP_REG_SEND,'replace','',name},0,{message}),
- pong = rpc:call(Victim, net_adm, ping, [Offender]),
send_bad_structure(Offender, P,{?DOP_REG_SEND,'replace','',name,{token}},2,{message}),
- pong = rpc:call(Victim, net_adm, ping, [Offender]),
send_bad_structure(Offender, P,{?DOP_SEND_TT,'',P},0,{message}),
- pong = rpc:call(Victim, net_adm, ping, [Offender]),
send_bad_structure(Offender, P,{?DOP_SEND_TT,'',name,token},0,{message}),
- pong = rpc:call(Victim, net_adm, ping, [Offender]),
send_bad_structure(Offender, P,{?DOP_SEND,''},0,{message}),
- pong = rpc:call(Victim, net_adm, ping, [Offender]),
send_bad_structure(Offender, P,{?DOP_SEND,'',name},0,{message}),
- pong = rpc:call(Victim, net_adm, ping, [Offender]),
send_bad_structure(Offender, P,{?DOP_SEND,'',P,{token}},0,{message}),
- pong = rpc:call(Victim, net_adm, ping, [Offender]),
P ! two,
P ! check_msgs,
receive
@@ -1674,6 +1734,61 @@ bad_dist_ext_connection_id(Config) when is_list(Config) ->
stop_node(Offender),
stop_node(Victim).
+%% OTP-14661: Bad message is discovered by erts_msg_attached_data_size
+bad_dist_ext_size(Config) when is_list(Config) ->
+ {ok, Offender} = start_node(bad_dist_ext_process_info_offender),
+ %%Prog = "Prog=/home/uabseri/src/otp_new3/bin/cerl -rr -debug",
+ Prog = [],
+ {ok, Victim} = start_node(bad_dist_ext_process_info_victim, [], Prog),
+ start_node_monitors([Offender,Victim]),
+
+ Parent = self(),
+ P = spawn_opt(Victim,
+ fun () ->
+ Parent ! {self(), started},
+ receive check_msgs -> ok end, %% DID CRASH HERE
+ bad_dist_ext_check_msgs([one]),
+ Parent ! {self(), messages_checked}
+ end,
+ [link,
+ %% on_heap to force total_heap_size to inspect msg queue
+ {message_queue_data, on_heap}]),
+
+ receive {P, started} -> ok end,
+ P ! one,
+
+ Suspended = make_ref(),
+ S = spawn(Victim,
+ fun () ->
+ erlang:suspend_process(P),
+ Parent ! Suspended,
+ receive after infinity -> ok end
+ end),
+
+ receive Suspended -> ok end,
+ pong = rpc:call(Victim, net_adm, ping, [Offender]),
+ verify_up(Offender, Victim),
+ send_bad_msgs(Offender, P, 1, dmsg_bad_tag()),
+
+ %% Make sure bad msgs has reached Victim
+ rpc:call(Offender, rpc, call, [Victim, erlang, node, []]),
+
+ verify_still_up(Offender, Victim),
+
+ %% Let process_info(P, total_heap_size) find bad msg and disconnect
+ rpc:call(Victim, erlang, process_info, [P, total_heap_size]),
+
+ verify_down(Offender, connection_closed, Victim, killed),
+
+ P ! check_msgs,
+ exit(S, bang), % resume Victim
+ receive {P, messages_checked} -> ok end,
+
+ unlink(P),
+ verify_no_down(Offender, Victim),
+ stop_node(Offender),
+ stop_node(Victim).
+
bad_dist_struct_check_msgs([]) ->
receive
@@ -1742,10 +1857,11 @@ send_bad_structure(Offender,Victim,Bad,WhereToPutSelf) ->
send_bad_structure(Offender,Victim,Bad,WhereToPutSelf,PayLoad) ->
Parent = self(),
Done = make_ref(),
- spawn(Offender,
+ spawn_link(Offender,
fun () ->
Node = node(Victim),
pong = net_adm:ping(Node),
+ erlang:monitor_node(Node, true),
DCtrl = dctrl(Node),
Bad1 = case WhereToPutSelf of
0 ->
@@ -1759,7 +1875,16 @@ send_bad_structure(Offender,Victim,Bad,WhereToPutSelf,PayLoad) ->
[] -> [];
_Other -> [dmsg_ext(PayLoad)]
end,
+
+ receive {nodedown, Node} -> exit("premature nodedown")
+ after 10 -> ok
+ end,
+
dctrl_send(DCtrl, DData),
+
+ receive {nodedown, Node} -> ok
+ after 5000 -> exit("missing nodedown")
+ end,
Parent ! {DData,Done}
end),
receive
@@ -1778,9 +1903,12 @@ send_bad_structure(Offender,Victim,Bad,WhereToPutSelf,PayLoad) ->
send_bad_msg(BadNode, To) ->
send_bad_msgs(BadNode, To, 1).
-send_bad_msgs(BadNode, To, Repeat) when is_atom(BadNode),
- is_pid(To),
- is_integer(Repeat) ->
+send_bad_msgs(BadNode, To, Repeat) ->
+ send_bad_msgs(BadNode, To, Repeat, dmsg_bad_atom_cache_ref()).
+
+send_bad_msgs(BadNode, To, Repeat, BadTerm) when is_atom(BadNode),
+ is_pid(To),
+ is_integer(Repeat) ->
Parent = self(),
Done = make_ref(),
spawn_link(BadNode,
@@ -1790,7 +1918,7 @@ send_bad_msgs(BadNode, To, Repeat) when is_atom(BadNode),
DCtrl = dctrl(Node),
DData = [dmsg_hdr(),
dmsg_ext({?DOP_SEND, ?COOKIE, To}),
- dmsg_bad_atom_cache_ref()],
+ BadTerm],
repeat(fun () -> dctrl_send(DCtrl, DData) end, Repeat),
Parent ! Done
end),
@@ -1837,11 +1965,26 @@ send_bad_dhdr(BadNode, ToNode) when is_atom(BadNode), is_atom(ToNode) ->
receive Done -> ok end.
dctrl(Node) when is_atom(Node) ->
- case catch erts_debug:get_internal_state(available_internal_state) of
- true -> true;
- _ -> erts_debug:set_internal_state(available_internal_state, true)
- end,
- erts_debug:get_internal_state({dist_ctrl, Node}).
+ get_internal_state({dist_ctrl, Node}).
+
+get_internal_state(Op) ->
+ try erts_debug:get_internal_state(Op) of
+ R -> R
+ catch
+ error:undef ->
+ erts_debug:set_internal_state(available_internal_state, true),
+ erts_debug:get_internal_state(Op)
+ end.
+
+set_internal_state(Op, Val) ->
+ try erts_debug:set_internal_state(Op, Val) of
+ R -> R
+ catch
+ error:undef ->
+ erts_debug:set_internal_state(available_internal_state, true),
+ erts_debug:set_internal_state(Op, Val)
+ end.
+
dmsg_hdr() ->
[131, % Version Magic
@@ -1878,6 +2021,9 @@ dmsg_ext(Term) ->
dmsg_bad_atom_cache_ref() ->
[$R, 137].
+dmsg_bad_tag() -> %% Will fail early at heap size calculation
+ [$?, 66].
+
start_epmd_false(Config) when is_list(Config) ->
%% Start a node with the option -start_epmd false.
{ok, OtherNode} = start_node(start_epmd_false, "-start_epmd false"),
@@ -1981,11 +2127,9 @@ freeze_node(Node, MS) ->
Freezer = self(),
spawn_link(Node,
fun () ->
- erts_debug:set_internal_state(available_internal_state,
- true),
dctrl_dop_send(Freezer, DoingIt),
receive after Own -> ok end,
- erts_debug:set_internal_state(block, MS+Own)
+ set_internal_state(block, MS+Own)
end),
receive DoingIt -> ok end,
receive after Own -> ok end.
@@ -2035,7 +2179,7 @@ start_relay_node(Node, Args) ->
[{args, Args ++
" -setcookie "++Cookie++" -pa "++Pa++" "++
RunArg}]),
- [N,H] = string:tokens(atom_to_list(NN),"@"),
+ [N,H] = string:lexemes(atom_to_list(NN),"@"),
{ok, Sock} = gen_tcp:accept(LSock),
pang = net_adm:ping(NN),
{N,H,Sock}.
@@ -2189,8 +2333,7 @@ forever(Fun) ->
forever(Fun).
abort(Why) ->
- erts_debug:set_internal_state(available_internal_state, true),
- erts_debug:set_internal_state(abort, Why).
+ set_internal_state(abort, Why).
start_busy_dist_port_tracer() ->
diff --git a/erts/emulator/test/driver_SUITE.erl b/erts/emulator/test/driver_SUITE.erl
index 33d0b708cf..40c7cc11e1 100644
--- a/erts/emulator/test/driver_SUITE.erl
+++ b/erts/emulator/test/driver_SUITE.erl
@@ -80,6 +80,7 @@
async_blast/1,
thr_msg_blast/1,
consume_timeslice/1,
+ env/1,
z_test/1]).
-export([bin_prefix/2]).
@@ -166,6 +167,7 @@ all() -> %% Keep a_test first and z_test last...
async_blast,
thr_msg_blast,
consume_timeslice,
+ env,
z_test].
groups() ->
@@ -1192,9 +1194,9 @@ check_driver_system_info_result(Result) ->
io:format("All names: ~p~n", [?EXPECTED_SYSTEM_INFO_NAMES]),
io:format("Result: ~p~n", [Result]),
{[], Ns, DDVSN} = chk_sis(lists:map(fun (Str) ->
- string:tokens(Str, "=")
+ string:lexemes(Str, "=")
end,
- string:tokens(Result, " ")),
+ string:lexemes(Result, " ")),
?EXPECTED_SYSTEM_INFO_NAMES),
case {DDVSN,
drv_vsn_str2tup(erlang:system_info(driver_version))} of
@@ -1768,7 +1770,10 @@ smp_select0(Config) ->
smp_select_loop(_, 0) ->
ok;
smp_select_loop(Port, N) ->
- "ok" = erlang:port_control(Port, ?CHKIO_SMP_SELECT, []),
+ case erlang:port_control(Port, ?CHKIO_SMP_SELECT, []) of
+ "yield" -> erlang:yield();
+ "ok" -> ok
+ end,
receive
stop ->
io:format("Worker ~p stopped with ~p laps left\n",[self(), N]),
@@ -2360,6 +2365,51 @@ count_proc_sched(Ps, PNs) ->
PNs
end.
+%%
+%% Tests whether erl_drv_putenv reflects in os:getenv and vice versa.
+%%
+env(Config) when is_list(Config) ->
+ ok = load_driver(proplists:get_value(data_dir, Config), env_drv),
+ Port = open_port({spawn_driver, env_drv}, []),
+ true = is_port(Port),
+
+ Keys = ["env_drv_a_key", "env_drv_b_key", "env_drv_c_key"],
+ Values = ["a_value", "b_value", "c_value"],
+
+ [env_put_test(Port, Key, Value) || Key <- Keys, Value <- Values],
+ [env_get_test(Port, Key, Value) || Key <- Keys, Value <- Values],
+ [env_oversize_test(Port, Key) || Key <- Keys],
+ [env_notfound_test(Port, Key) || Key <- Keys],
+
+ true = port_close(Port),
+ erl_ddll:unload_driver(env_drv),
+ ok.
+
+env_control(Port, Command, Key, Value) ->
+ KeyBin = list_to_binary(Key),
+ ValueBin = list_to_binary(Value),
+ Header = <<(byte_size(KeyBin)), (byte_size(ValueBin))>>,
+ Payload = <<KeyBin/binary, ValueBin/binary>>,
+ port_control(Port, Command, <<Header/binary, Payload/binary>>).
+
+env_put_test(Port, Key, Value) ->
+ os:unsetenv(Key),
+ [0] = env_control(Port, 0, Key, Value),
+ Value = os:getenv(Key).
+
+env_get_test(Port, Key, ExpectedValue) ->
+ true = os:putenv(Key, ExpectedValue),
+ [0] = env_control(Port, 1, Key, ExpectedValue).
+
+env_oversize_test(Port, Key) ->
+ os:putenv(Key, [$A || _ <- lists:seq(1, 1024)]),
+ [127] = env_control(Port, 1, Key, "").
+
+env_notfound_test(Port, Key) ->
+ true = os:unsetenv(Key),
+ [255] = env_control(Port, 1, Key, "").
+
+
a_test(Config) when is_list(Config) ->
rpc(Config, fun check_io_debug/0).
@@ -2438,7 +2488,7 @@ wait_until(Fun) ->
end.
drv_vsn_str2tup(Str) ->
- [Major, Minor] = string:tokens(Str, "."),
+ [Major, Minor] = string:lexemes(Str, "."),
{list_to_integer(Major), list_to_integer(Minor)}.
%% Build port data from a template.
@@ -2539,8 +2589,18 @@ stop_driver(Port, Name) ->
ok = erl_ddll:stop().
load_driver(Dir, Driver) ->
+ Before = erlang:system_info(taints),
case erl_ddll:load_driver(Dir, Driver) of
- ok -> ok;
+ ok ->
+ After = erlang:system_info(taints),
+ case lists:member(Driver, Before) of
+ true ->
+ After = Before;
+ false ->
+ true = lists:member(Driver, After),
+ Before = lists:delete(Driver, After)
+ end,
+ ok;
{error, Error} = Res ->
io:format("~s\n", [erl_ddll:format_error(Error)]),
Res
@@ -2614,8 +2674,8 @@ rpc(Config, Fun) ->
Result
= try Fun() of
Res -> Res
- catch E:R ->
- {'EXIT',E,R,erlang:get_stacktrace()}
+ catch E:R:Stk ->
+ {'EXIT',E,R,Stk}
end,
Self ! {Ref, Result}
end),
diff --git a/erts/emulator/test/driver_SUITE_data/Makefile.src b/erts/emulator/test/driver_SUITE_data/Makefile.src
index 1fedd72200..bcabaa689d 100644
--- a/erts/emulator/test/driver_SUITE_data/Makefile.src
+++ b/erts/emulator/test/driver_SUITE_data/Makefile.src
@@ -16,7 +16,8 @@ MISC_DRVS = outputv_drv@dll@ \
thr_free_drv@dll@ \
async_blast_drv@dll@ \
thr_msg_blast_drv@dll@ \
- consume_timeslice_drv@dll@
+ consume_timeslice_drv@dll@ \
+ env_drv@dll@
SYS_INFO_DRVS = sys_info_base_drv@dll@ \
sys_info_prev_drv@dll@ \
diff --git a/erts/emulator/test/driver_SUITE_data/chkio_drv.c b/erts/emulator/test/driver_SUITE_data/chkio_drv.c
index d548c4b1dc..ee8f28e8b1 100644
--- a/erts/emulator/test/driver_SUITE_data/chkio_drv.c
+++ b/erts/emulator/test/driver_SUITE_data/chkio_drv.c
@@ -961,7 +961,6 @@ chkio_drv_control(ErlDrvData drv_data,
break;
}
case CHKIO_SMP_SELECT: {
- int rounds = 1; /*rand(); */
ChkioSmpSelect* pip = (ChkioSmpSelect*) cddp->test_data;
if (pip == NULL) {
erl_drv_mutex_lock(smp_pipes_mtx);
@@ -978,7 +977,8 @@ chkio_drv_control(ErlDrvData drv_data,
}
erl_drv_mutex_unlock(smp_pipes_mtx);
}
- while (rounds--) {
+ res_str = NULL;
+ {
int op = rand_r(&pip->rand_state);
switch (pip->state) {
case Closed: {
@@ -988,7 +988,6 @@ chkio_drv_control(ErlDrvData drv_data,
fcntl(fds[0], F_SETFL, flags|O_NONBLOCK) < 0) {
driver_failure_posix(cddp->port, errno);
- rounds = 0;
break;
}
TRACEF(("%T: Created pipe [%d->%d]\n", cddp->id, fds[1], fds[0]));
@@ -1075,7 +1074,9 @@ chkio_drv_control(ErlDrvData drv_data,
pip->next_write++;
}
break;
- case Waiting:
+ case Waiting:
+ res_str = "yield";
+ res_len = -1;
break;
default:
fprintf(stderr, "Strange state %d\n", pip->state);
@@ -1091,9 +1092,11 @@ chkio_drv_control(ErlDrvData drv_data,
else {
cddp->test_data = pip;
}
- }
- res_str = "ok";
- res_len = -1;
+ }
+ if (!res_str) {
+ res_str = "ok";
+ res_len = -1;
+ }
break;
}
case CHKIO_DRV_USE:
diff --git a/erts/emulator/test/driver_SUITE_data/env_drv.c b/erts/emulator/test/driver_SUITE_data/env_drv.c
new file mode 100644
index 0000000000..0e910eeb84
--- /dev/null
+++ b/erts/emulator/test/driver_SUITE_data/env_drv.c
@@ -0,0 +1,108 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2017. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * %CopyrightEnd%
+ */
+
+/* Tests whether erl_drv_putenv/erl_drv_getenv work correctly and reflect
+ * changes to os:putenv/getenv. */
+
+#include <string.h>
+#include <stdio.h>
+
+#include "erl_driver.h"
+
+static ErlDrvSSizeT env_drv_ctl(ErlDrvData drv_data, unsigned int cmd,
+ char* buf, ErlDrvSizeT len, char** rbuf, ErlDrvSizeT rsize);
+
+static ErlDrvEntry env_drv_entry = {
+ NULL /* init */,
+ NULL /* start */,
+ NULL /* stop */,
+ NULL /* output */,
+ NULL /* ready_input */,
+ NULL /* ready_output */,
+ "env_drv",
+ NULL /* finish */,
+ NULL /* handle */,
+ env_drv_ctl,
+ NULL /* timeout */,
+ NULL /* outputv*/,
+ NULL /* ready_async */,
+ NULL /* flush */,
+ NULL /* call*/,
+ NULL /* event */,
+ ERL_DRV_EXTENDED_MARKER,
+ ERL_DRV_EXTENDED_MAJOR_VERSION,
+ ERL_DRV_EXTENDED_MINOR_VERSION,
+ ERL_DRV_FLAG_USE_PORT_LOCKING,
+ NULL /* handle2 */,
+ NULL /* handle_monitor */
+};
+
+DRIVER_INIT(env_drv) {
+ return &env_drv_entry;
+}
+
+static int test_putenv(ErlDrvData drv_data, char *buf, ErlDrvSizeT len) {
+ char key[256], value[256];
+ int key_len, value_len;
+
+ key_len = buf[0];
+ value_len = buf[1];
+
+ sprintf(key, "%.*s", key_len, &buf[2]);
+ sprintf(value, "%.*s", value_len, &buf[2 + key_len]);
+
+ return erl_drv_putenv(key, value);
+}
+
+static int test_getenv(ErlDrvData drv_data, char *buf, ErlDrvSizeT len) {
+ char expected_value[256], stored_value[256], key[256];
+ int expected_value_len, key_len;
+ size_t stored_value_len;
+ int res;
+
+ key_len = buf[0];
+ sprintf(key, "%.*s", key_len, &buf[2]);
+
+ expected_value_len = buf[1];
+ sprintf(expected_value, "%.*s", expected_value_len, &buf[2 + key_len]);
+
+ stored_value_len = sizeof(stored_value);
+ res = erl_drv_getenv(key, stored_value, &stored_value_len);
+
+ if(res == 0) {
+ return strcmp(stored_value, expected_value) != 0;
+ } else if(res == 1) {
+ return 127;
+ }
+
+ return 255;
+}
+
+static ErlDrvSSizeT env_drv_ctl(ErlDrvData drv_data, unsigned int cmd,
+ char* buf, ErlDrvSizeT len, char** rbuf, ErlDrvSizeT rsize) {
+
+ if(cmd == 0) {
+ (**rbuf) = (char)test_putenv(drv_data, buf, len);
+ } else {
+ (**rbuf) = (char)test_getenv(drv_data, buf, len);
+ }
+
+ return 1;
+}
diff --git a/erts/emulator/test/dump_SUITE.erl b/erts/emulator/test/dump_SUITE.erl
new file mode 100644
index 0000000000..8d18d46d92
--- /dev/null
+++ b/erts/emulator/test/dump_SUITE.erl
@@ -0,0 +1,125 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2005-2017. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(dump_SUITE).
+
+-include_lib("common_test/include/ct.hrl").
+
+-export([all/0, suite/0, init_per_testcase/2, end_per_testcase/2]).
+
+-export([signal_abort/1]).
+
+-export([load/0]).
+
+-include_lib("kernel/include/file.hrl").
+
+suite() ->
+ [{ct_hooks,[ts_install_cth]},
+ {timetrap, {minutes, 2}}].
+
+all() ->
+ [signal_abort].
+
+init_per_testcase(signal_abort, Config) ->
+ SO = erlang:system_info(schedulers_online),
+ erts_debug:set_internal_state(available_internal_state, true),
+ Dump = erts_debug:get_internal_state(scheduler_dump),
+ erts_debug:set_internal_state(available_internal_state, false),
+ if SO < 3 ->
+ {skip, "not enough schedulers"};
+ not Dump ->
+ {skip, "the platform does not support scheduler dump"};
+ Dump ->
+ Config
+ end.
+
+end_per_testcase(_, Config) ->
+ Config.
+
+%%%
+%%% The test cases -------------------------------------------------------------
+%%%
+
+%% Test that a snapshot is taken of other schedulers using a signal
+%% when a crash dump is generated.
+signal_abort(Config) ->
+
+ Dump = filename:join(proplists:get_value(priv_dir, Config),"signal_abort.dump"),
+
+ {ok, Node} = start_node(Config),
+
+ _P1 = spawn(Node, ?MODULE, load, []),
+ _P2 = spawn(Node, ?MODULE, load, []),
+ _P3 = spawn(Node, ?MODULE, load, []),
+ _P4 = spawn(Node, ?MODULE, load, []),
+ _P5 = spawn(Node, ?MODULE, load, []),
+ _P6 = spawn(Node, ?MODULE, load, []),
+
+ timer:sleep(500),
+
+ true = rpc:call(Node, os, putenv, ["ERL_CRASH_DUMP",Dump]),
+ rpc:call(Node, erlang, halt, ["dump"]),
+
+ {ok, Bin} = get_dump_when_done(Dump),
+
+ ct:log("~s",[Bin]),
+
+ {match, Matches} = re:run(Bin,"Current Process: <",[global]),
+
+ ct:log("Found ~p",[Matches]),
+
+ true = length(Matches) > 1,
+
+ file:delete(Dump),
+
+ ok.
+
+get_dump_when_done(Dump) ->
+ case file:read_file_info(Dump) of
+ {ok, #file_info{ size = Sz }} ->
+ get_dump_when_done(Dump, Sz);
+ {error, enoent} ->
+ timer:sleep(1000),
+ get_dump_when_done(Dump)
+ end.
+
+get_dump_when_done(Dump, Sz) ->
+ timer:sleep(1000),
+ case file:read_file_info(Dump) of
+ {ok, #file_info{ size = Sz }} ->
+ file:read_file(Dump);
+ {ok, #file_info{ size = NewSz }} ->
+ get_dump_when_done(Dump, NewSz)
+ end.
+
+load() ->
+ lists:seq(1,10000),
+ load().
+
+start_node(Config) 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]))),
+ test_server:start_node(Name, slave, [{args, "-pa "++Pa}]).
diff --git a/erts/emulator/test/efile_SUITE.erl b/erts/emulator/test/efile_SUITE.erl
index f0e1bcf04b..821381bf0d 100644
--- a/erts/emulator/test/efile_SUITE.erl
+++ b/erts/emulator/test/efile_SUITE.erl
@@ -19,93 +19,17 @@
-module(efile_SUITE).
-export([all/0, suite/0]).
--export([iter_max_files/1, async_dist/1]).
+-export([iter_max_files/1, proc_zero_sized_files/1]).
--export([do_iter_max_files/2, do_async_dist/1]).
+-export([do_iter_max_files/2]).
-include_lib("common_test/include/ct.hrl").
+-include_lib("stdlib/include/assert.hrl").
suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
- [iter_max_files, async_dist].
-
-do_async_dist(Dir) ->
- X = 100,
- AT = erlang:system_info(thread_pool_size),
- Keys = file_keys(Dir,AT*X,[],[]),
- Tab = ets:new(x,[ordered_set]),
- [ ets:insert(Tab,{N,0}) || N <- lists:seq(0,AT-1) ],
- [ ets:update_counter(Tab,(N rem AT),1) || N <- Keys ],
- Res = [ V || {_,V} <- ets:tab2list(Tab) ],
- ets:delete(Tab),
- {Res, sdev(Res)/X}.
-
-sdev(List) ->
- Len = length(List),
- Mean = lists:sum(List)/Len,
- math:sqrt(lists:sum([ (X - Mean) * (X - Mean) || X <- List ]) / Len).
-
-file_keys(_,0,FdList,FnList) ->
- [ file:close(FD) || FD <- FdList ],
- [ file:delete(FN) || FN <- FnList ],
- [];
-file_keys(Dir,Num,FdList,FnList) ->
- Name = "dummy"++integer_to_list(Num),
- FN = filename:join([Dir,Name]),
- case file:open(FN,[write,raw]) of
- {ok,FD} ->
- {file_descriptor,prim_file,{Port,_}} = FD,
- <<X:32/integer-big>> =
- iolist_to_binary(erlang:port_control(Port,$K,[])),
- [X | file_keys(Dir,Num-1,[FD|FdList],[FN|FnList])];
- {error,_} ->
- % Try freeing up FD's if there are any
- case FdList of
- [] ->
- exit({cannot_open_file,FN});
- _ ->
- [ file:close(FD) || FD <- FdList ],
- [ file:delete(F) || F <- FnList ],
- file_keys(Dir,Num,[],[])
- end
- end.
-
-%% Check that the distribution of files over async threads is fair
-async_dist(Config) when is_list(Config) ->
- DataDir = proplists:get_value(data_dir,Config),
- Dir = filename:dirname(code:which(?MODULE)),
- AsyncSizes = [7,10,100,255,256,64,63,65],
- Max = 0.5,
-
- lists:foreach(fun(Size) ->
- {ok,Node} =
- test_server:start_node
- (test_iter_max_files,slave,
- [{args,
- "+A "++integer_to_list(Size)++
- " -pa " ++ Dir}]),
- {Distr,SD} = rpc:call(Node,?MODULE,do_async_dist,
- [DataDir]),
- test_server:stop_node(Node),
- if
- SD > Max ->
- io:format("Bad async queue distribution for "
- "~p async threads:~n"
- " Standard deviation is ~p~n"
- " Key distribution:~n ~lp~n",
- [Size,SD,Distr]),
- exit({bad_async_dist,Size,SD,Distr});
- true ->
- io:format("OK async queue distribution for "
- "~p async threads:~n"
- " Standard deviation is ~p~n"
- " Key distribution:~n ~lp~n",
- [Size,SD,Distr]),
- ok
- end
- end, AsyncSizes),
- ok.
+ [iter_max_files, proc_zero_sized_files].
%%
%% Open as many files as possible. Do this several times and check
@@ -113,17 +37,23 @@ async_dist(Config) when is_list(Config) ->
%%
iter_max_files(Config) when is_list(Config) ->
+ case os:type() of
+ {win32, _} -> {skip, "Windows lacks a hard limit on file handles"};
+ _ -> iter_max_files_1(Config)
+ end.
+
+iter_max_files_1(Config) ->
DataDir = proplists:get_value(data_dir,Config),
TestFile = filename:join(DataDir, "existing_file"),
N = 10,
- %% Run on a different node in order to set the max ports
+ %% Run on a different node in order to make the test more stable.
Dir = filename:dirname(code:which(?MODULE)),
{ok,Node} = test_server:start_node(test_iter_max_files,slave,
- [{args,"+Q 1524 -pa " ++ Dir}]),
+ [{args,"-pa " ++ Dir}]),
L = rpc:call(Node,?MODULE,do_iter_max_files,[N, TestFile]),
test_server:stop_node(Node),
io:format("Number of files opened in each test:~n~w\n", [L]),
- all_equal(L),
+ verify_max_files(L),
Head = hd(L),
if Head >= 2 -> ok;
true -> ct:fail(too_few_files)
@@ -135,12 +65,15 @@ do_iter_max_files(N, Name) when N > 0 ->
do_iter_max_files(_, _) ->
[].
-all_equal([E, E| T]) ->
- all_equal([E| T]);
-all_equal([_]) ->
- ok;
-all_equal([]) ->
- ok.
+%% The attempts shouldn't vary too much; we used to require that they were all
+%% exactly equal, but after we reimplemented the file driver as a NIF we
+%% noticed that the only reason it was stable on Darwin was because the port
+%% limit was hit before ulimit.
+verify_max_files(Attempts) ->
+ N = length(Attempts),
+ Mean = lists:sum(Attempts) / N,
+ Variance = lists:sum([(X - Mean) * (X - Mean) || X <- Attempts]) / N,
+ true = math:sqrt(Variance) =< 1 + (Mean / 1000).
max_files(Name) ->
Fds = open_files(Name),
@@ -162,3 +95,44 @@ open_files(Name) ->
% io:format("Error reason: ~p", [_Reason]),
[]
end.
+
+%% @doc If /proc filesystem exists (no way to know if it is real proc or just
+%% a /proc directory), let's read some zero sized files 500 times each, while
+%% ensuring that response isn't empty << >>
+proc_zero_sized_files(Config) when is_list(Config) ->
+ {Type, Flavor} = os:type(),
+ %% Some files which exist on Linux but might be missing on other systems
+ Inputs = ["/proc/cpuinfo",
+ "/proc/meminfo",
+ "/proc/partitions",
+ "/proc/swaps",
+ "/proc/version",
+ "/proc/uptime",
+ %% curproc is present on freebsd
+ "/proc/curproc/cmdline"],
+ case filelib:is_dir("/proc") of
+ false -> {skip, "/proc not found"}; % skip the test if no /proc
+ _ when Type =:= unix andalso Flavor =:= sunos ->
+ %% SunOS has a /proc, but no zero sized special files
+ {skip, "sunos does not have any zero sized special files"};
+ true ->
+ %% Take away files which do not exist in proc
+ Inputs1 = lists:filter(fun filelib:is_file/1, Inputs),
+
+ %% Fail if none of mentioned files exist in /proc, did we just get
+ %% a normal /proc directory without any special files?
+ ?assertNotEqual([], Inputs1),
+
+ %% For 6 inputs and 500 attempts each this do run anywhere
+ %% between 500 and 3000 function calls.
+ lists:foreach(
+ fun(Filename) -> do_proc_zero_sized(Filename, 500) end,
+ Inputs1)
+ end.
+
+%% @doc Test one file N times to also trigger possible leaking fds and memory
+do_proc_zero_sized(_Filename, 0) -> ok;
+do_proc_zero_sized(Filename, N) ->
+ Data = file:read_file(Filename),
+ ?assertNotEqual(<<>>, Data),
+ do_proc_zero_sized(Filename, N-1).
diff --git a/erts/emulator/test/emulator_smoke.spec b/erts/emulator/test/emulator_smoke.spec
index b2d0de8835..fc98ba6823 100644
--- a/erts/emulator/test/emulator_smoke.spec
+++ b/erts/emulator/test/emulator_smoke.spec
@@ -7,3 +7,4 @@
[consistency],"Not reliable in October and March"}.
{cases,'Dir',crypto_SUITE,[t_md5]}.
{cases,'Dir',float_SUITE,[fpe,cmp_integer]}.
+{cases,'Dir',erts_debug_SUITE,[df]}.
diff --git a/erts/emulator/test/erl_link_SUITE.erl b/erts/emulator/test/erl_link_SUITE.erl
index d8c5b663e3..ed444f2599 100644
--- a/erts/emulator/test/erl_link_SUITE.erl
+++ b/erts/emulator/test/erl_link_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2001-2017. All Rights Reserved.
+%% Copyright Ericsson AB 2001-2018. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -53,26 +53,17 @@
-export([test_proc/0]).
--define(LINK_UNDEF, 0).
--define(LINK_PID, 1).
--define(LINK_NODE, 3).
-
-
-% These are to be kept in sync with erl_monitors.h
--define(MON_ORIGIN, 1).
--define(MON_TARGET, 2).
-
-
--record(erl_link, {type = ?LINK_UNDEF,
+-record(erl_link, {type, % process | port | dist_process
pid = [],
- targets = []}).
+ id}).
% This is to be kept in sync with erl_bif_info.c (make_monitor_list)
--record(erl_monitor, {type, % MON_ORIGIN or MON_TARGET
- ref,
+-record(erl_monitor, {type, % process | port | time_offset | dist_process | resource | node | nodes | suspend
+ dir, % origin | target
+ ref, % Reference | []
pid, % Process or nodename
- name = []}). % registered name or []
+ extra = []}). % registered name, integer or, []
suite() ->
@@ -106,7 +97,7 @@ end_per_suite(_Config) ->
links(Config) when is_list(Config) ->
common_link_test(node(), node()),
true = link(self()),
- [] = find_erl_link(self(), ?LINK_PID, self()),
+ [] = find_erl_link(self(), process, self()),
true = unlink(self()),
ok.
@@ -191,6 +182,7 @@ monitor_nodes(Config) when is_list(Config) ->
monitor_node(A, false),
monitor_node(B, true),
monitor_node(C, true),
+ receive after 1000 -> ok end,
monitor_node(C, false),
monitor_node(C, true),
monitor_node(B, true),
@@ -200,13 +192,16 @@ monitor_nodes(Config) when is_list(Config) ->
monitor_node(A, true),
check_monitor_node(self(), A, 1),
check_monitor_node(self(), B, 3),
+ ok = receive {nodedown, C} -> ok after 1000 -> timeout end,
+ %%OTP-21: monitor_node(_,false) does not trigger auto-connect anymore
+ %% and therefore no nodedown if it fails.
+ %%ok = receive {nodedown, C} -> ok after 1000 -> timeout end,
+ ok = receive {nodedown, C} -> ok after 1000 -> timeout end,
+ ok = receive {nodedown, D} -> ok after 1000 -> timeout end,
+ ok = receive {nodedown, D} -> ok after 1000 -> timeout end,
check_monitor_node(self(), C, 0),
check_monitor_node(self(), D, 0),
- receive {nodedown, C} -> ok end,
- receive {nodedown, C} -> ok end,
- receive {nodedown, C} -> ok end,
- receive {nodedown, D} -> ok end,
- receive {nodedown, D} -> ok end,
+
stop_node(A),
receive {nodedown, A} -> ok end,
check_monitor_node(self(), A, 0),
@@ -301,7 +296,8 @@ run_common_process_monitors(TP1, TP2) ->
wait_until(fun () -> is_proc_dead(TP2) end),
ok = tp_call(TP1, fun () ->
receive
- {'DOWN',R2,process,TP2O,bye} ->
+ {'DOWN',R2,process,TP2O,Reason1} ->
+ bye = Reason1,
ok
end
end),
@@ -310,7 +306,8 @@ run_common_process_monitors(TP1, TP2) ->
R3 = tp_call(TP1, fun () -> erlang:monitor(process, TP2) end),
ok = tp_call(TP1, fun () ->
receive
- {'DOWN',R3,process,TP2O,noproc} ->
+ {'DOWN',R3,process,TP2O,Reason2} ->
+ noproc = Reason2,
ok
end
end),
@@ -695,22 +692,10 @@ test_proc() ->
end,
test_proc().
-expand_link_list([#erl_link{type = ?LINK_NODE, targets = N} = Rec | T]) ->
- lists:duplicate(N,Rec#erl_link{targets = []}) ++ expand_link_list(T);
-expand_link_list([#erl_link{targets = [#erl_link{pid = Pid}]} = Rec | T]) ->
- [Rec#erl_link{targets = [Pid]} | expand_link_list(T)];
-expand_link_list([#erl_link{targets = [#erl_link{pid = Pid}|TT]} = Rec | T]) ->
- [ Rec#erl_link{targets = [Pid]} | expand_link_list(
- [Rec#erl_link{targets = TT} | T])];
-expand_link_list([#erl_link{targets = []} = Rec | T]) ->
- [Rec | expand_link_list(T)];
-expand_link_list([]) ->
- [].
-
get_local_link_list(Obj) ->
case catch erts_debug:get_internal_state({link_list, Obj}) of
LL when is_list(LL) ->
- expand_link_list(LL);
+ LL;
_ ->
[]
end.
@@ -719,7 +704,7 @@ get_remote_link_list(Node, Obj) ->
case catch rpc:call(Node, erts_debug, get_internal_state,
[{link_list, Obj}]) of
LL when is_list(LL) ->
- expand_link_list(LL);
+ LL;
_ ->
[]
end.
@@ -773,82 +758,106 @@ get_monitor_list(undefined) ->
find_erl_monitor(Pid, Ref) when is_reference(Ref) ->
+ MonitorList = get_monitor_list(Pid),
+ io:format("~p MonitorList: ~p~n", [Pid, MonitorList]),
lists:foldl(fun (#erl_monitor{ref = R} = EL, Acc) when R == Ref ->
[EL|Acc];
(_, Acc) ->
Acc
end,
[],
- get_monitor_list(Pid)).
-
-% find_erl_link(Obj, Ref) when is_reference(Ref) ->
-% lists:foldl(fun (#erl_link{ref = R} = EL, Acc) when R == Ref ->
-% [EL|Acc];
-% (_, Acc) ->
-% Acc
-% end,
-% [],
-% get_link_list(Obj)).
-
-find_erl_link(Obj, Type, [Item, Data]) when is_pid(Item);
- is_port(Item);
- is_atom(Item) ->
- lists:foldl(fun (#erl_link{type = T, pid = I, targets = D} = EL,
+ MonitorList);
+find_erl_monitor(Pid, Item) ->
+ MonitorList = get_monitor_list(Pid),
+ io:format("~p MonitorList: ~p~n", [Pid, MonitorList]),
+ lists:foldl(fun (#erl_monitor{pid = I} = EL, Acc) when I == Item ->
+ [EL|Acc];
+ (_, Acc) ->
+ Acc
+ end,
+ [],
+ MonitorList).
+
+
+find_erl_link(Obj, Type, Item) when is_pid(Item); is_port(Item) ->
+ LinkList = get_link_list(Obj),
+ io:format("~p LinkList: ~p~n", [Obj, LinkList]),
+ lists:foldl(fun (#erl_link{type = T, pid = I} = EL,
Acc) when T == Type, I == Item ->
- case Data of
- D ->
- [EL|Acc];
- [] ->
- [EL|Acc];
- _ ->
- Acc
- end;
+ [EL|Acc];
+ (_, Acc) ->
+ Acc
+ end,
+ [],
+ LinkList);
+find_erl_link(Obj, Type, Id) when is_integer(Id) ->
+ %% Find by Id
+ LinkList = get_link_list(Obj),
+ io:format("~p LinkList: ~p~n", [Obj, LinkList]),
+ lists:foldl(fun (#erl_link{type = T, id = I} = EL,
+ Acc) when T == Type, I == Id ->
+ [EL|Acc];
(_, Acc) ->
Acc
end,
[],
- get_link_list(Obj));
-find_erl_link(Obj, Type, Item) when is_pid(Item); is_port(Item); is_atom(Item) ->
- find_erl_link(Obj, Type, [Item, []]).
+ LinkList).
+get_link_type(A, B) when is_port(A);
+ is_port(B) ->
+ port;
+get_link_type(A, B) when is_pid(A),
+ is_pid(B) ->
+ case node(A) == node(B) of
+ true ->
+ process;
+ false ->
+ dist_process
+ end.
+check_link(A, B) when node(A) == node(B) ->
+ LinkType = get_link_type(A, B),
+ [#erl_link{type = LinkType,
+ pid = B,
+ id = Id}] = find_erl_link(A, LinkType, B),
+ [#erl_link{type = LinkType,
+ pid = A,
+ id = Id}] = find_erl_link(B, LinkType, A),
+ [] = find_erl_link({node(A), node(B)},
+ LinkType,
+ A),
+ [] = find_erl_link({node(B), node(A)},
+ LinkType,
+ B),
+ ok;
check_link(A, B) ->
- [#erl_link{type = ?LINK_PID,
+ [#erl_link{type = dist_process,
pid = B,
- targets = []}] = find_erl_link(A, ?LINK_PID, B),
- [#erl_link{type = ?LINK_PID,
+ id = IdA}] = find_erl_link(A, dist_process, B),
+ [#erl_link{type = dist_process,
pid = A,
- targets = []}] = find_erl_link(B, ?LINK_PID, A),
- case node(A) == node(B) of
- false ->
- [#erl_link{type = ?LINK_PID,
- pid = A,
- targets = [B]}] = find_erl_link({node(A),
- node(B)},
- ?LINK_PID,
- [A, [B]]),
- [#erl_link{type = ?LINK_PID,
- pid = B,
- targets = [A]}] = find_erl_link({node(B),
- node(A)},
- ?LINK_PID,
- [B, [A]]);
- true ->
- [] = find_erl_link({node(A), node(B)},
- ?LINK_PID,
- [A, [B]]),
- [] = find_erl_link({node(B), node(A)},
- ?LINK_PID,
- [B, [A]])
- end,
+ id = IdA}] = find_erl_link({node(A),
+ node(B)},
+ dist_process,
+ IdA),
+ [#erl_link{type = dist_process,
+ pid = A,
+ id = IdB}] = find_erl_link(B, dist_process, A),
+ [#erl_link{type = dist_process,
+ pid = B,
+ id = IdB}] = find_erl_link({node(B),
+ node(A)},
+ dist_process,
+ IdB),
ok.
check_unlink(A, B) ->
- [] = find_erl_link(A, ?LINK_PID, B),
- [] = find_erl_link(B, ?LINK_PID, A),
- [] = find_erl_link({node(A), node(B)}, ?LINK_PID, [A, [B]]),
- [] = find_erl_link({node(B), node(A)}, ?LINK_PID, [B, [A]]),
+ LinkType = get_link_type(A, B),
+ [] = find_erl_link(A, LinkType, B),
+ [] = find_erl_link(B, LinkType, A),
+ [] = find_erl_link({node(A), node(B)}, dist_process, A),
+ [] = find_erl_link({node(B), node(A)}, dist_process, B),
ok.
check_process_monitor(From, {Name, Node}, Ref) when is_pid(From),
@@ -861,22 +870,26 @@ check_process_monitor(From, {Name, Node}, Ref) when is_pid(From),
is_atom(Node),
is_reference(Ref) ->
MonitoredPid = rpc:call(Node, erlang, whereis, [Name]),
- [#erl_monitor{type = ?MON_ORIGIN,
+ [#erl_monitor{type = dist_process,
+ dir = origin,
ref = Ref,
pid = Node,
- name = Name}] = find_erl_monitor(From, Ref),
- [#erl_monitor{type = ?MON_TARGET,
+ extra = Name}] = find_erl_monitor(From, Ref),
+ [#erl_monitor{type = dist_process,
+ dir = target,
ref = Ref,
pid = From,
- name = Name}] = find_erl_monitor({node(From), Node}, Ref),
- [#erl_monitor{type = ?MON_ORIGIN,
+ extra = Name}] = find_erl_monitor({node(From), Node}, Ref),
+ [#erl_monitor{type = dist_process,
+ dir = origin,
ref = Ref,
pid = MonitoredPid,
- name = Name}] = find_erl_monitor({Node, node(From)}, Ref),
- [#erl_monitor{type = ?MON_TARGET,
+ extra = Name}] = find_erl_monitor({Node, node(From)}, Ref),
+ [#erl_monitor{type = dist_process,
+ dir = target,
ref = Ref,
pid = From,
- name = Name}] = find_erl_monitor(MonitoredPid, Ref),
+ extra = Name}] = find_erl_monitor(MonitoredPid, Ref),
ok;
check_process_monitor(From, Name, Ref) when is_pid(From),
is_atom(Name),
@@ -884,27 +897,36 @@ check_process_monitor(From, Name, Ref) when is_pid(From),
is_reference(Ref) ->
MonitoredPid = rpc:call(node(From), erlang, whereis, [Name]),
- [#erl_monitor{type = ?MON_ORIGIN,
+ [#erl_monitor{type = process,
+ dir = origin,
ref = Ref,
pid = MonitoredPid,
- name = Name}] = find_erl_monitor(From, Ref),
+ extra = Name}] = find_erl_monitor(From, Ref),
- [#erl_monitor{type = ?MON_TARGET,
+ [#erl_monitor{type = process,
+ dir = target,
ref = Ref,
pid = From,
- name = Name}] = find_erl_monitor(MonitoredPid,Ref),
+ extra = Name}] = find_erl_monitor(MonitoredPid,Ref),
ok;
check_process_monitor(From, To, Ref) when is_pid(From),
is_pid(To),
is_reference(Ref) ->
- OriMon = [#erl_monitor{type = ?MON_ORIGIN,
+ MonType = case node(From) == node(To) of
+ true -> process;
+ false -> dist_process
+ end,
+
+ OriMon = [#erl_monitor{type = MonType,
+ dir = origin,
ref = Ref,
pid = To}],
OriMon = find_erl_monitor(From, Ref),
- TargMon = [#erl_monitor{type = ?MON_TARGET,
+ TargMon = [#erl_monitor{type = MonType,
+ dir = target,
ref = Ref,
pid = From}],
TargMon = find_erl_monitor(To, Ref),
@@ -912,7 +934,11 @@ check_process_monitor(From, To, Ref) when is_pid(From),
case node(From) == node(To) of
false ->
- TargMon = find_erl_monitor({node(From), node(To)}, Ref),
+ DistTargMon = [#erl_monitor{type = dist_process,
+ dir = target,
+ ref = Ref,
+ pid = From}],
+ DistTargMon = find_erl_monitor({node(From), node(To)}, Ref),
OriMon = find_erl_monitor({node(To), node(From)}, Ref);
true ->
[] = find_erl_monitor({node(From), node(From)}, Ref)
@@ -983,19 +1009,36 @@ check_process_demonitor(From, To, Ref) when is_pid(From),
ok.
no_of_monitor_node(From, Node) when is_pid(From), is_atom(Node) ->
- length(find_erl_link(From, ?LINK_NODE, Node)).
+ case find_erl_monitor(From, Node) of
+ [] -> 0;
+ [#erl_monitor{type = node,
+ dir = origin,
+ pid = Node,
+ extra = N}] -> N
+ end.
+check_monitor_node(From, Node, 0) when is_pid(From),
+ is_atom(Node) ->
+ [] = find_erl_monitor(From, Node),
+ [] = find_erl_monitor({node(From), Node}, From);
check_monitor_node(From, Node, No) when is_pid(From),
is_atom(Node),
is_integer(No),
- No >= 0 ->
- LL = lists:duplicate(No, #erl_link{type = ?LINK_NODE, pid = Node}),
- DLL = lists:duplicate(No, #erl_link{type = ?LINK_NODE, pid = From}),
- LL = find_erl_link(From, ?LINK_NODE, Node),
- DLL = find_erl_link({node(From), Node}, ?LINK_NODE, From),
- ok.
-
-
+ No > 0 ->
+ [#erl_monitor{type = node,
+ dir = origin,
+ pid = Node,
+ extra = No}] = find_erl_monitor(From, Node),
+ [#erl_monitor{type = node,
+ dir = target,
+ pid = From}] = find_erl_monitor({node(From), Node}, From).
+
+connection_id(Node) ->
+ try
+ erts_debug:get_internal_state({connection_id, Node})
+ catch
+ _:_ -> -1
+ end.
hostname() ->
from($@, atom_to_list(node())).
diff --git a/erts/emulator/test/exception_SUITE.erl b/erts/emulator/test/exception_SUITE.erl
index e473a10be7..60d14ce841 100644
--- a/erts/emulator/test/exception_SUITE.erl
+++ b/erts/emulator/test/exception_SUITE.erl
@@ -23,7 +23,8 @@
-export([all/0, suite/0,
badmatch/1, pending_errors/1, nil_arith/1, top_of_stacktrace/1,
stacktrace/1, nested_stacktrace/1, raise/1, gunilla/1, per/1,
- exception_with_heap_frag/1, line_numbers/1]).
+ exception_with_heap_frag/1, backtrace_depth/1,
+ line_numbers/1]).
-export([bad_guy/2]).
-export([crash/1]).
@@ -31,6 +32,10 @@
-include_lib("common_test/include/ct.hrl").
-import(lists, [foreach/2]).
+%% The range analysis of the HiPE compiler results in a system limit error
+%% during compilation instead of at runtime, so do not perform this analysis.
+-compile([{hipe, [no_icode_range]}]).
+
suite() ->
[{ct_hooks,[ts_install_cth]},
{timetrap, {minutes, 1}}].
@@ -38,7 +43,7 @@ suite() ->
all() ->
[badmatch, pending_errors, nil_arith, top_of_stacktrace,
stacktrace, nested_stacktrace, raise, gunilla, per,
- exception_with_heap_frag, line_numbers].
+ exception_with_heap_frag, backtrace_depth, line_numbers].
-define(try_match(E),
catch ?MODULE:bar(),
@@ -391,12 +396,12 @@ raise(Conf) when is_list(Conf) ->
try
try foo({'div',{1,0}})
catch
- error:badarith ->
+ error:badarith:A0 ->
put(raise, A0 = erlang:get_stacktrace()),
erlang:raise(error, badarith, A0)
end
catch
- error:badarith ->
+ error:badarith:A1 ->
A1 = erlang:get_stacktrace(),
A1 = get(raise)
end,
@@ -568,6 +573,57 @@ do_exception_with_heap_frag(Bin, [Sz|Sizes]) ->
do_exception_with_heap_frag(Bin, Sizes);
do_exception_with_heap_frag(_, []) -> ok.
+backtrace_depth(Config) when is_list(Config) ->
+ _ = [do_backtrace_depth(D) || D <- lists:seq(0, 8)],
+ ok.
+
+do_backtrace_depth(D) ->
+ Old = erlang:system_flag(backtrace_depth, D),
+ try
+ Expected = max(1, D),
+ do_backtrace_depth_1(Expected)
+ after
+ _ = erlang:system_flag(backtrace_depth, Old)
+ end.
+
+do_backtrace_depth_1(D) ->
+ Exit = fun() ->
+ error(reason)
+ end,
+ HandCrafted = fun() ->
+ {'EXIT',{_,Stk0}} = (catch error(get_stacktrace)),
+ %% Fool the compiler to force a hand-crafted
+ %% stacktrace.
+ Stk = [hd(Stk0)|tl(Stk0)],
+ erlang:raise(error, reason, Stk)
+ end,
+ PassedOn = fun() ->
+ try error(get_stacktrace)
+ catch error:_:Stk ->
+ %% Just pass on the given stacktrace.
+ erlang:raise(error, reason, Stk)
+ end
+ end,
+ do_backtrace_depth_2(D, Exit),
+ do_backtrace_depth_2(D, HandCrafted),
+ do_backtrace_depth_2(D, PassedOn),
+ ok.
+
+do_backtrace_depth_2(D, Exc) ->
+ try
+ Exc()
+ catch
+ error:reason:Stk ->
+ if
+ length(Stk) =/= D ->
+ io:format("Expected depth: ~p\n", [D]),
+ io:format("~p\n", [Stk]),
+ error(bad_depth);
+ true ->
+ ok
+ end
+ end.
+
line_numbers(Config) when is_list(Config) ->
{'EXIT',{{case_clause,bad_tag},
[{?MODULE,line1,2,
@@ -662,6 +718,15 @@ line_numbers(Config) when is_list(Config) ->
{?MODULE,line_numbers,1,_}|_]}} =
(catch applied_bif_2()),
+ {'EXIT',{badarith,
+ [{?MODULE,increment1,1,[{file,"increment.erl"},{line,45}]},
+ {?MODULE,line_numbers,1,_}|_]}} =
+ (catch increment1(x)),
+ {'EXIT',{badarith,
+ [{?MODULE,increment2,1,[{file,"increment.erl"},{line,48}]},
+ {?MODULE,line_numbers,1,_}|_]}} =
+ (catch increment2(x)),
+
ok.
id(I) -> I.
@@ -762,3 +827,15 @@ applied_bif_2() -> %Line 8
R = process_info(self(), current_location), %Line 9
fail = R, %Line 10
ok. %Line 11
+
+%% The increment instruction used to decrement the instruction
+%% pointer, which would cause the line number in a stack trace to
+%% be the previous line number.
+
+-file("increment.erl", 42).
+increment1(Arg) -> %Line 43
+ Res = id(Arg), %Line 44
+ Res + 1. %Line 45
+increment2(Arg) -> %Line 46
+ _ = id(Arg), %Line 47
+ Arg + 1. %Line 48
diff --git a/erts/emulator/test/guard_SUITE.erl b/erts/emulator/test/guard_SUITE.erl
index 1a93a9f5c2..f2c1595392 100644
--- a/erts/emulator/test/guard_SUITE.erl
+++ b/erts/emulator/test/guard_SUITE.erl
@@ -500,7 +500,7 @@ all_types() ->
{atom, xxxx},
{ref, make_ref()},
{pid, self()},
- {port, open_port({spawn, efile}, [])},
+ {port, make_port()},
{function, fun(_) -> "" end},
{function, fun erlang:abs/1},
{binary, list_to_binary([])},
@@ -551,4 +551,7 @@ type_test(bitstring, X) when is_bitstring(X) ->
type_test(function, X) when is_function(X) ->
function.
+make_port() ->
+ hd(erlang:ports()).
+
id(I) -> I.
diff --git a/erts/emulator/test/iovec_SUITE.erl b/erts/emulator/test/iovec_SUITE.erl
index 28df36d293..963b7e2501 100644
--- a/erts/emulator/test/iovec_SUITE.erl
+++ b/erts/emulator/test/iovec_SUITE.erl
@@ -24,7 +24,9 @@
-export([integer_lists/1, binary_lists/1, empty_lists/1, empty_binary_lists/1,
mixed_lists/1, improper_lists/1, illegal_lists/1, cons_bomb/1,
- iolist_to_iovec_idempotence/1, iolist_to_iovec_correctness/1]).
+ sub_binary_lists/1, iolist_to_iovec_idempotence/1,
+ iolist_to_iovec_correctness/1, unaligned_sub_binaries/1,
+ direct_binary_arg/1]).
-include_lib("common_test/include/ct.hrl").
@@ -34,8 +36,9 @@ suite() ->
all() ->
[integer_lists, binary_lists, empty_lists, empty_binary_lists, mixed_lists,
- illegal_lists, improper_lists, cons_bomb, iolist_to_iovec_idempotence,
- iolist_to_iovec_correctness].
+ sub_binary_lists, illegal_lists, improper_lists, cons_bomb,
+ iolist_to_iovec_idempotence, iolist_to_iovec_correctness,
+ unaligned_sub_binaries, direct_binary_arg].
init_per_suite(Config) ->
Config.
@@ -46,15 +49,16 @@ end_per_suite(Config) ->
integer_lists(Config) when is_list(Config) ->
Variations = gen_variations([I || I <- lists:seq(1, 255)]),
+ equivalence_test(fun erlang:iolist_to_iovec/1, Variations).
- equivalence_test(fun erlang:iolist_to_iovec/1, Variations),
-
- ok.
+sub_binary_lists(Config) when is_list(Config) ->
+ Parent = <<0:256/unit:8, "gazurka">>,
+ <<0:196/unit:8, Child/binary>> = Parent,
+ equivalence_test(fun erlang:iolist_to_iovec/1, gen_variations(Child)).
binary_lists(Config) when is_list(Config) ->
Variations = gen_variations([<<I:8>> || I <- lists:seq(1, 255)]),
- equivalence_test(fun erlang:iolist_to_iovec/1, Variations),
- ok.
+ equivalence_test(fun erlang:iolist_to_iovec/1, Variations).
empty_lists(Config) when is_list(Config) ->
Variations = gen_variations([[] || _ <- lists:seq(1, 256)]),
@@ -70,46 +74,34 @@ empty_binary_lists(Config) when is_list(Config) ->
mixed_lists(Config) when is_list(Config) ->
Variations = gen_variations([<<>>, lists:seq(1, 40), <<12, 45, 78>>]),
- equivalence_test(fun erlang:iolist_to_iovec/1, Variations),
- ok.
+ equivalence_test(fun erlang:iolist_to_iovec/1, Variations).
illegal_lists(Config) when is_list(Config) ->
BitStrs = gen_variations(["gurka", <<1:1>>, "gaffel"]),
BadInts = gen_variations(["gurka", 890, "gaffel"]),
Atoms = gen_variations([gurka, "gaffel"]),
- BadTails = [["test" | 0], ["gurka", gaffel]],
+ BadTails = [["test" | 0], ["gurka" | gaffel], ["gaffel" | <<1:1>>]],
Variations =
BitStrs ++ BadInts ++ Atoms ++ BadTails,
- illegality_test(fun erlang:iolist_to_iovec/1, Variations),
-
- ok.
+ illegality_test(fun erlang:iolist_to_iovec/1, Variations).
improper_lists(Config) when is_list(Config) ->
Variations = [
[[[[1 | <<2>>] | <<3>>] | <<4>>] | <<5>>],
- [[<<"test">>, 3] | <<"improper tail">>],
- [1, 2, 3 | <<"improper tail">>]
+ [[<<1>>, 2] | <<3, 4, 5>>],
+ [1, 2, 3 | <<4, 5>>]
],
- equivalence_test(fun erlang:iolist_to_iovec/1, Variations),
- ok.
+ equivalence_test(fun erlang:iolist_to_iovec/1, Variations).
cons_bomb(Config) when is_list(Config) ->
IntBase = gen_variations([I || I <- lists:seq(1, 255)]),
BinBase = gen_variations([<<I:8>> || I <- lists:seq(1, 255)]),
MixBase = gen_variations([<<12, 45, 78>>, lists:seq(1, 255)]),
- Rounds =
- case system_mem_size() of
- Mem when Mem >= (16 bsl 30) -> 32;
- Mem when Mem >= (3 bsl 30) -> 28;
- _ -> 20
- end,
-
- Variations = gen_variations([IntBase, BinBase, MixBase], Rounds),
- equivalence_test(fun erlang:iolist_to_iovec/1, Variations),
- ok.
+ Variations = gen_variations([IntBase, BinBase, MixBase], 16),
+ equivalence_test(fun erlang:iolist_to_iovec/1, Variations).
iolist_to_iovec_idempotence(Config) when is_list(Config) ->
IntVariations = gen_variations([I || I <- lists:seq(1, 255)]),
@@ -133,22 +125,48 @@ iolist_to_iovec_correctness(Config) when is_list(Config) ->
true = is_iolist_equal(Optimized, Variations),
ok.
+unaligned_sub_binaries(Config) when is_list(Config) ->
+ UnalignedBins = [gen_unaligned_binary(I) || I <- lists:seq(32, 4 bsl 10, 512)],
+ UnalignedVariations = gen_variations(UnalignedBins),
+
+ Optimized = erlang:iolist_to_iovec(UnalignedVariations),
+
+ true = is_iolist_equal(Optimized, UnalignedVariations),
+ ok.
+
+direct_binary_arg(Config) when is_list(Config) ->
+ {'EXIT',{badarg, _}} = (catch erlang:iolist_to_iovec(<<1:1>>)),
+ [<<1>>] = erlang:iolist_to_iovec(<<1>>),
+ [] = erlang:iolist_to_iovec(<<>>),
+ ok.
+
illegality_test(Fun, Variations) ->
- [{'EXIT',{badarg, _}} = (catch Fun(Variation)) || Variation <- Variations].
+ [{'EXIT',{badarg, _}} = (catch Fun(Variation)) || Variation <- Variations],
+ ok.
equivalence_test(Fun, [Head | _] = Variations) ->
+ %% Check that each variation is equal to the others, and that the sum of
+ %% them is equal to the input.
Comparand = Fun(Head),
- [is_iolist_equal(Comparand, Fun(Variation)) || Variation <- Variations],
+ [true = is_iolist_equal(Comparand, Fun(V)) || V <- Variations],
+ true = is_iolist_equal(Variations, Fun(Variations)),
ok.
is_iolist_equal(A, B) ->
iolist_to_binary(A) =:= iolist_to_binary(B).
+gen_unaligned_binary(Size) ->
+ Bin0 = << <<I>> || I <- lists:seq(1, Size) >>,
+ <<0:3,Bin:Size/binary,31:5>> = id(<<0:3,Bin0/binary,31:5>>),
+ Bin.
+
+id(I) -> I.
+
%% Generates a bunch of lists whose contents will be equal to Base repeated a
%% few times. The lists only differ by their structure, so their reduction to
%% a simpler format should yield the same result.
gen_variations(Base) ->
- gen_variations(Base, 16).
+ gen_variations(Base, 12).
gen_variations(Base, N) ->
[gen_flat_list(Base, N),
gen_nested_list(Base, N),
@@ -168,8 +186,3 @@ gen_nasty_list_1([Head | Base], Result) when is_list(Head) ->
gen_nasty_list_1(Base, [[Result], [gen_nasty_list_1(Head, [])]]);
gen_nasty_list_1([Head | Base], Result) ->
gen_nasty_list_1(Base, [[Result], [Head]]).
-
-system_mem_size() ->
- application:ensure_all_started(os_mon),
- {Tot,_Used,_} = memsup:get_memory_data(),
- Tot.
diff --git a/erts/emulator/test/lcnt_SUITE.erl b/erts/emulator/test/lcnt_SUITE.erl
index 504b9b54cf..dfffd662e2 100644
--- a/erts/emulator/test/lcnt_SUITE.erl
+++ b/erts/emulator/test/lcnt_SUITE.erl
@@ -28,14 +28,16 @@
init_per_testcase/2, end_per_testcase/2]).
-export(
- [toggle_lock_counting/1, error_on_invalid_category/1, preserve_locks/1]).
+ [toggle_lock_counting/1, error_on_invalid_category/1, preserve_locks/1,
+ registered_processes/1, registered_db_tables/1]).
suite() ->
[{ct_hooks,[ts_install_cth]},
{timetrap, {seconds, 10}}].
all() ->
- [toggle_lock_counting, error_on_invalid_category, preserve_locks].
+ [toggle_lock_counting, error_on_invalid_category, preserve_locks,
+ registered_processes, registered_db_tables].
init_per_suite(Config) ->
case erlang:system_info(lock_counting) of
@@ -85,8 +87,9 @@ wait_for_empty_lock_list() ->
wait_for_empty_lock_list(10).
wait_for_empty_lock_list(Tries) when Tries > 0 ->
try_flush_cleanup_ops(),
- case erts_debug:lcnt_collect() of
- [{duration, _}, {locks, []}] ->
+ [{duration, _}, {locks, Locks}] = erts_debug:lcnt_collect(),
+ case remove_untoggleable_locks(Locks) of
+ [] ->
ok;
_ ->
timer:sleep(50),
@@ -122,7 +125,7 @@ toggle_lock_counting(Config) when is_list(Config) ->
get_lock_info_for(Categories) when is_list(Categories) ->
ok = erts_debug:lcnt_control(mask, Categories),
[{duration, _}, {locks, Locks}] = erts_debug:lcnt_collect(),
- Locks;
+ remove_untoggleable_locks(Locks);
get_lock_info_for(Category) when is_atom(Category) ->
get_lock_info_for([Category]).
@@ -154,3 +157,35 @@ preserve_locks(Config) when is_list(Config) ->
error_on_invalid_category(Config) when is_list(Config) ->
{error, badarg, q_invalid} = erts_debug:lcnt_control(mask, [q_invalid]),
ok.
+
+registered_processes(Config) when is_list(Config) ->
+ %% There ought to be at least one registered process (init/code_server)
+ erts_debug:lcnt_control(mask, [process]),
+ [_, {locks, ProcLocks}] = erts_debug:lcnt_collect(),
+ true = lists:any(
+ fun
+ ({proc_main, RegName, _, _}) when is_atom(RegName) -> true;
+ (_Lock) -> false
+ end, ProcLocks),
+ ok.
+
+registered_db_tables(Config) when is_list(Config) ->
+ %% There ought to be at least one registered table (code)
+ erts_debug:lcnt_control(mask, [db]),
+ [_, {locks, DbLocks}] = erts_debug:lcnt_collect(),
+ true = lists:any(
+ fun
+ ({db_tab, RegName, _, _}) when is_atom(RegName) -> true;
+ (_Lock) -> false
+ end, DbLocks),
+ ok.
+
+%% Not all locks can be toggled on or off due to technical limitations, so we
+%% need to filter them out when checking whether we successfully disabled lock
+%% counting.
+remove_untoggleable_locks([]) ->
+ [];
+remove_untoggleable_locks([{resource_monitors, _, _, _} | T]) ->
+ remove_untoggleable_locks(T);
+remove_untoggleable_locks([H | T]) ->
+ [H | remove_untoggleable_locks(T)].
diff --git a/erts/emulator/test/map_SUITE.erl b/erts/emulator/test/map_SUITE.erl
index 02f3c89318..c9e971af8a 100644
--- a/erts/emulator/test/map_SUITE.erl
+++ b/erts/emulator/test/map_SUITE.erl
@@ -52,7 +52,7 @@
t_bif_map_values/1,
t_bif_map_to_list/1,
t_bif_map_from_list/1,
- t_bif_erts_internal_maps_to_list/1,
+ t_bif_map_next/1,
%% erlang
t_erlang_hash/1,
@@ -119,7 +119,7 @@ all() -> [t_build_and_match_literals, t_build_and_match_literals_large,
t_bif_map_update,
t_bif_map_values,
t_bif_map_to_list, t_bif_map_from_list,
- t_bif_erts_internal_maps_to_list,
+ t_bif_map_next,
%% erlang
t_erlang_hash, t_map_encode_decode,
@@ -2364,41 +2364,71 @@ t_bif_map_from_list(Config) when is_list(Config) ->
{'EXIT', {badarg,_}} = (catch maps:from_list(id(42))),
ok.
-t_bif_erts_internal_maps_to_list(Config) when is_list(Config) ->
- %% small maps
- [] = erts_internal:maps_to_list(#{},-1),
- [] = erts_internal:maps_to_list(#{},-2),
- [] = erts_internal:maps_to_list(#{},10),
- [{a,1},{b,2}] = lists:sort(erts_internal:maps_to_list(#{a=>1,b=>2}, 2)),
- [{a,1},{b,2}] = lists:sort(erts_internal:maps_to_list(#{a=>1,b=>2}, -1)),
- [{_,_}] = erts_internal:maps_to_list(#{a=>1,b=>2}, 1),
- [{a,1},{b,2},{c,3}] = lists:sort(erts_internal:maps_to_list(#{c=>3,a=>1,b=>2},-2)),
- [{a,1},{b,2},{c,3}] = lists:sort(erts_internal:maps_to_list(#{c=>3,a=>1,b=>2},3)),
- [{a,1},{b,2},{c,3}] = lists:sort(erts_internal:maps_to_list(#{c=>3,a=>1,b=>2},5)),
- [{_,_},{_,_}] = erts_internal:maps_to_list(#{c=>3,a=>1,b=>2},2),
- [{_,_}] = erts_internal:maps_to_list(#{c=>3,a=>1,b=>2},1),
- [] = erts_internal:maps_to_list(#{c=>3,a=>1,b=>2},0),
-
- %% big maps
- M = maps:from_list([{I,ok}||I <- lists:seq(1,500)]),
- [] = erts_internal:maps_to_list(M,0),
- [{_,_}] = erts_internal:maps_to_list(M,1),
- [{_,_},{_,_}] = erts_internal:maps_to_list(M,2),
- Ls1 = erts_internal:maps_to_list(M,10),
- 10 = length(Ls1),
- Ls2 = erts_internal:maps_to_list(M,20),
- 20 = length(Ls2),
- Ls3 = erts_internal:maps_to_list(M,120),
- 120 = length(Ls3),
- Ls4 = erts_internal:maps_to_list(M,-1),
- 500 = length(Ls4),
+t_bif_map_next(Config) when is_list(Config) ->
- %% error cases
- {'EXIT', {{badmap,[{a,b},b]},_}} = (catch erts_internal:maps_to_list(id([{a,b},b]),id(1))),
- {'EXIT', {badarg,_}} = (catch erts_internal:maps_to_list(id(#{}),id(a))),
- {'EXIT', {badarg,_}} = (catch erts_internal:maps_to_list(id(#{1=>2}),id(<<>>))),
+ erts_debug:set_internal_state(available_internal_state, true),
+
+ try
+
+ none = maps:next(maps:iterator(id(#{}))),
+
+ verify_iterator(#{}),
+ verify_iterator(#{a => 1, b => 2, c => 3}),
+
+ %% Use fatmap in order to test iterating in very deep maps
+ FM = fatmap(43),
+ verify_iterator(FM),
+
+ {'EXIT', {{badmap,[{a,b},b]},_}} = (catch maps:iterator(id([{a,b},b]))),
+ {'EXIT', {badarg,_}} = (catch maps:next(id(a))),
+ {'EXIT', {badarg,_}} = (catch maps:next(id([a|FM]))),
+ {'EXIT', {badarg,_}} = (catch maps:next(id([1|#{}]))),
+ {'EXIT', {badarg,_}} = (catch maps:next(id([-1|#{}]))),
+ {'EXIT', {badarg,_}} = (catch maps:next(id([-1|FM]))),
+ {'EXIT', {badarg,_}} = (catch maps:next(id([16#FFFFFFFFFFFFFFFF|FM]))),
+ {'EXIT', {badarg,_}} = (catch maps:next(id([-16#FFFFFFFFFFFFFFFF|FM]))),
+
+ %% This us a whitebox test that the error code works correctly.
+ %% It uses a path for a tree of depth 4 and tries to do next on
+ %% each of those paths.
+ (fun F(0) -> ok;
+ F(N) ->
+ try maps:next([N|FM]) of
+ none ->
+ F(N-1);
+ {_K,_V,_I} ->
+ F(N-1)
+ catch error:badarg ->
+ F(N-1)
+ end
+ end)(16#FFFF),
+
+ ok
+ after
+ erts_debug:set_internal_state(available_internal_state, false)
+ end.
+
+verify_iterator(Map) ->
+ KVs = t_fold(fun(K, V, A) -> [{K, V} | A] end, [], Map),
+
+ %% Verify that KVs created by iterating Map is of
+ %% correct size and contains all elements
+ true = length(KVs) == maps:size(Map),
+ [maps:get(K, Map) || {K, _} <- KVs],
ok.
+
+t_fold(Fun, Init, Map) ->
+ t_fold_1(Fun, Init, maps:iterator(Map)).
+
+t_fold_1(Fun, Acc, Iter) ->
+ case maps:next(Iter) of
+ {K, V, NextIter} ->
+ t_fold_1(Fun, Fun(K,V,Acc), NextIter);
+ none ->
+ Acc
+ end.
+
t_bif_build_and_check(Config) when is_list(Config) ->
ok = check_build_and_remove(750,[fun(K) -> [K,K] end,
fun(K) -> [float(K),K] end,
diff --git a/erts/emulator/test/match_spec_SUITE.erl b/erts/emulator/test/match_spec_SUITE.erl
index 92ddc23592..08a7b4560c 100644
--- a/erts/emulator/test/match_spec_SUITE.erl
+++ b/erts/emulator/test/match_spec_SUITE.erl
@@ -21,7 +21,7 @@
-module(match_spec_SUITE).
-export([all/0, suite/0, not_run/1]).
--export([test_1/1, test_2/1, test_3/1, bad_match_spec_bin/1,
+-export([test_1/1, test_2/1, test_3/1, caller_and_return_to/1, bad_match_spec_bin/1,
trace_control_word/1, silent/1, silent_no_ms/1, silent_test/1,
ms_trace2/1, ms_trace3/1, ms_trace_dead/1, boxed_and_small/1,
destructive_in_test_bif/1, guard_exceptions/1,
@@ -47,7 +47,7 @@ suite() ->
all() ->
case test_server:is_native(match_spec_SUITE) of
false ->
- [test_1, test_2, test_3, bad_match_spec_bin,
+ [test_1, test_2, test_3, caller_and_return_to, bad_match_spec_bin,
trace_control_word, silent, silent_no_ms, silent_test, ms_trace2,
ms_trace3, ms_trace_dead, boxed_and_small, destructive_in_test_bif,
guard_exceptions, unary_plus, unary_minus, fpe,
@@ -180,6 +180,50 @@ test_3(Config) when is_list(Config) ->
collect(P1, [{trace, P1, call, {?MODULE, f2, [a, b]}, [true]}]),
ok.
+%% Test that caller and return to work as they should
+%% There was a bug where caller would be undefined when return_to was set
+%% for r the bif erlang:put().
+caller_and_return_to(Config) ->
+ tr(
+ fun do_put_wrapper/0,
+ fun (Tracee) ->
+ MsgCaller = [{'_',[],[{message,{caller}}]}],
+ 1 = erlang:trace(Tracee, true, [call,return_to]),
+ 1 = erlang:trace_pattern( {?MODULE,do_put,1}, MsgCaller, [local]),
+ 1 = erlang:trace_pattern( {?MODULE,do_the_put,1}, MsgCaller, [local]),
+ 1 = erlang:trace_pattern( {erlang,integer_to_list,1}, MsgCaller, [local]),
+ 1 = erlang:trace_pattern( {erlang,put,2}, MsgCaller, [local]),
+
+ [{trace,Tracee,call,{?MODULE,do_put,[test]},{?MODULE,do_put_wrapper,0}},
+ {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,return_to,{?MODULE,do_put,1}},
+
+ %% These last trace messages are a bit strange...
+ %% if call tracing had been enabled for do_put_wrapper
+ %% then caller and return_to would have been {?MODULE,do_put_wrapper,1}
+ %% but since it is not, they are set to do_put instead, but we still
+ %% get the do_put_wrapper return_to message...
+ {trace,Tracee,call,{erlang,integer_to_list,[2]},{?MODULE,do_put,1}},
+ {trace,Tracee,return_to,{?MODULE,do_put,1}},
+ {trace,Tracee,return_to,{?MODULE,do_put_wrapper,0}}
+ ]
+ end),
+ ok.
+
+do_put_wrapper() ->
+ do_put(test),
+ ok.
+
+do_put(Var) ->
+ do_the_put(Var),
+ erlang:integer_to_list(id(2)).
+do_the_put(Var) ->
+ Lst = erlang:integer_to_list(id(1)),
+ erlang:put(Var, Lst).
+
otp_9422(Config) when is_list(Config) ->
Laps = 10000,
Fun1 = fun() -> otp_9422_tracee() end,
diff --git a/erts/emulator/test/module_info_SUITE.erl b/erts/emulator/test/module_info_SUITE.erl
index ba9b564fdc..46a3bba732 100644
--- a/erts/emulator/test/module_info_SUITE.erl
+++ b/erts/emulator/test/module_info_SUITE.erl
@@ -23,7 +23,7 @@
-include_lib("common_test/include/ct.hrl").
-export([all/0, suite/0,
- exports/1,functions/1,deleted/1,native/1,info/1]).
+ exports/1,functions/1,deleted/1,native/1,info/1,nifs/1]).
%%-compile(native).
@@ -38,7 +38,7 @@ all() ->
modules().
modules() ->
- [exports, functions, deleted, native, info].
+ [exports, functions, deleted, native, info, nifs].
%% Should return all functions exported from this module. (local)
all_exported() ->
@@ -62,12 +62,24 @@ exports(Config) when is_list(Config) ->
All = lists:sort(?MODULE:module_info(exports)),
ok.
-%% Test that the list of exported functions from this module is correct.
+%% Test that the list of local and exported functions from this module is
+%% correct.
functions(Config) when is_list(Config) ->
All = all_functions(),
All = lists:sort(?MODULE:module_info(functions)),
ok.
+nifs(Config) when is_list(Config) ->
+ [] = ?MODULE:module_info(nifs),
+
+ %% erl_tracer is guaranteed to be present and contain these NIFs
+ TraceNIFs = erl_tracer:module_info(nifs),
+ true = lists:member({enabled, 3}, TraceNIFs),
+ true = lists:member({trace, 5}, TraceNIFs),
+ 2 = length(TraceNIFs),
+
+ ok.
+
%% Test that deleted modules cause badarg
deleted(Config) when is_list(Config) ->
Data = proplists:get_value(data_dir, Config),
diff --git a/erts/emulator/test/monitor_SUITE.erl b/erts/emulator/test/monitor_SUITE.erl
index 9d772480d9..c7250a9d26 100644
--- a/erts/emulator/test/monitor_SUITE.erl
+++ b/erts/emulator/test/monitor_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-2017. All Rights Reserved.
+%% Copyright Ericsson AB 1999-2018. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -86,7 +86,7 @@ case_2(Config) when is_list(Config) ->
R = erlang:monitor(process, B),
B ! R,
receive
- {'EXIT', _} -> ok;
+ true -> ok;
Other ->
ct:fail({rec, Other})
end,
@@ -98,7 +98,7 @@ case_2a(Config) when is_list(Config) ->
{B,R} = spawn_monitor(?MODULE, y2, [self()]),
B ! R,
receive
- {'EXIT', _} -> ok;
+ true -> ok;
Other ->
ct:fail({rec, Other})
end,
@@ -182,7 +182,7 @@ demon_e_1(Config) when is_list(Config) ->
end ),
receive
{P2, ref, R2} ->
- demon_error(R2, badarg),
+ true = erlang:demonitor(R2),
P2 ! {self(), stop};
Other2 ->
ct:fail({rec, Other2})
@@ -729,8 +729,8 @@ named_down(Config) when is_list(Config) ->
end),
?assertEqual(true, register(Name, NamedProc)),
unlink(NamedProc),
- exit(NamedProc, bang),
Mon = erlang:monitor(process, Name),
+ exit(NamedProc, bang),
receive {'DOWN',Mon, _, _, bang} -> ok
after 3000 -> ?assert(false) end,
?assertEqual(true, register(Name, self())),
diff --git a/erts/emulator/test/nif_SUITE.erl b/erts/emulator/test/nif_SUITE.erl
index e259e39c86..df521311e3 100644
--- a/erts/emulator/test/nif_SUITE.erl
+++ b/erts/emulator/test/nif_SUITE.erl
@@ -1170,6 +1170,12 @@ maps(Config) when is_list(Config) ->
{1, M2} = make_map_remove_nif(M2, "key3"),
{0, undefined} = make_map_remove_nif(self(), key),
+ M1 = maps_from_list_nif(maps:to_list(M1)),
+ M2 = maps_from_list_nif(maps:to_list(M2)),
+ M3 = maps_from_list_nif(maps:to_list(M3)),
+
+ has_duplicate_keys = maps_from_list_nif([{1,1},{1,1}]),
+
verify_tmpmem(TmpMem),
ok.
@@ -2257,9 +2263,8 @@ nif_schedule(Config) when is_list(Config) ->
{B,A} = call_nif_schedule(A, B),
ok = try call_nif_schedule(1, 2)
catch
- error:badarg ->
- [{?MODULE,call_nif_schedule,[1,2],_}|_] =
- erlang:get_stacktrace(),
+ error:badarg:Stk ->
+ [{?MODULE,call_nif_schedule,[1,2],_}|_] = Stk,
ok
end,
ok.
@@ -2429,8 +2434,8 @@ nif_raise_exceptions(NifFunc) ->
erlang:apply(?MODULE,NifFunc,[Term]),
ct:fail({expected,Term})
catch
- error:Term ->
- [{?MODULE,NifFunc,[Term],_}|_] = erlang:get_stacktrace(),
+ error:Term:Stk ->
+ [{?MODULE,NifFunc,[Term],_}|_] = Stk,
ok
end
end, ok, ExcTerms).
@@ -3037,18 +3042,37 @@ nif_ioq(Config) ->
{enqbraw,a},
{enqbraw,a, 5},
{peek, a},
+ {peek_head, a},
{deq, a, 42},
%% Test enqv
{enqv, a, 2, 100},
+ {peek_head, a},
{deq, a, all},
%% This skips all elements but one in the iolist
{enqv, a, 5, iolist_size(nif_ioq_payload(5)) - 1},
+ {peek_head, a},
{peek, a},
- %% Test to enqueue a bunch of refc binaries
+ %% Ensure that enqueued refc binaries are intact after a roundtrip.
+ %%
+ %% This test and the ones immediately following it does not go through
+ %% erlang:iolist_to_iovec/1
{enqv, a, [nif_ioq_payload(refcbin) || _ <- lists:seq(1,20)], 0},
+ {peek, a},
+
+ %% ... heap binaries
+ {enqv, a, [nif_ioq_payload(heapbin) || _ <- lists:seq(1,20)], 0},
+ {peek, a},
+
+ %% ... plain sub-binaries
+ {enqv, a, [nif_ioq_payload(subbin) || _ <- lists:seq(1,20)], 0},
+ {peek, a},
+
+ %% ... unaligned binaries
+ {enqv, a, [nif_ioq_payload(unaligned_bin) || _ <- lists:seq(1,20)], 0},
+ {peek, a},
%% Enq stuff to destroy with data in queue
{enqv, a, 2, 100},
@@ -3074,9 +3098,13 @@ nif_ioq(Config) ->
Q = ioq_nif(create),
+ false = ioq_nif(peek_head, Q),
+
{'EXIT', {badarg, _}} = (catch ioq_nif(deq, Q, 1)),
{'EXIT', {badarg, _}} = (catch ioq_nif(enqv, Q, 1, 1234)),
+ false = ioq_nif(peek_head, Q),
+
{'EXIT', {badarg, _}} = (catch ioq_nif(enqv, Q, [atom_in_list], 0)),
{'EXIT', {badarg, _}} = (catch ioq_nif(enqv, Q, [make_ref()], 0)),
{'EXIT', {badarg, _}} = (catch ioq_nif(enqv, Q, [256], 0)),
@@ -3085,6 +3113,8 @@ nif_ioq(Config) ->
{'EXIT', {badarg, _}} = (catch ioq_nif(enqv, Q, [1 bsl 64], 0)),
{'EXIT', {badarg, _}} = (catch ioq_nif(enqv, Q, [{tuple}], 0)),
+ false = ioq_nif(peek_head, Q),
+
{'EXIT', {badarg, _}} = (catch ioq_nif(inspect, [atom_in_list], use_stack)),
{'EXIT', {badarg, _}} = (catch ioq_nif(inspect, [make_ref()], no_stack)),
{'EXIT', {badarg, _}} = (catch ioq_nif(inspect, [256], use_stack)),
@@ -3145,6 +3175,20 @@ nif_ioq_run([{peek, Name} = H|T], State) ->
true = iolist_to_binary(B) == iolist_to_binary(Data),
nif_ioq_run(T, State);
+nif_ioq_run([{peek_head, Name} = H|T], State) ->
+ #{ q := IOQ, b := B } = maps:get(Name, State),
+ RefData = iolist_to_binary(B),
+
+ ct:log("~p", [H]),
+
+ {true, QueueHead} = ioq_nif(peek_head, IOQ),
+ true = byte_size(QueueHead) > 0,
+
+ {RefHead, _Tail} = split_binary(RefData, byte_size(QueueHead)),
+
+ true = QueueHead =:= RefHead,
+
+ nif_ioq_run(T, State);
nif_ioq_run([{deq, Name, all}|T], State) ->
#{ q := IOQ, b := B } = maps:get(Name, State),
Size = ioq_nif(size, IOQ),
@@ -3184,13 +3228,16 @@ nif_ioq_payload(N) when is_integer(N) ->
Tail = if N > 3 -> nif_ioq_payload(N-3); true -> [] end,
Head = element(1, lists:split(N,[nif_ioq_payload(subbin),
nif_ioq_payload(heapbin),
- nif_ioq_payload(refcbin) | Tail])),
+ nif_ioq_payload(refcbin),
+ nif_ioq_payload(unaligned_bin) | Tail])),
erlang:iolist_to_iovec(Head);
nif_ioq_payload(subbin) ->
Bin = nif_ioq_payload(refcbin),
Sz = size(Bin) - 1,
<<_:8,SubBin:Sz/binary,_/bits>> = Bin,
SubBin;
+nif_ioq_payload(unaligned_bin) ->
+ make_unaligned_binary(<< <<I>> || I <- lists:seq(1, 255) >>);
nif_ioq_payload(heapbin) ->
<<"a literal heap binary">>;
nif_ioq_payload(refcbin) ->
@@ -3198,6 +3245,13 @@ nif_ioq_payload(refcbin) ->
nif_ioq_payload(Else) ->
Else.
+make_unaligned_binary(Bin0) ->
+ Size = byte_size(Bin0),
+ <<0:3,Bin:Size/binary,31:5>> = id(<<0:3,Bin0/binary,31:5>>),
+ Bin.
+
+id(I) -> I.
+
%% The NIFs:
lib_version() -> undefined.
call_history() -> ?nif_stub.
diff --git a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c
index 79560a38aa..a0aef60cf1 100644
--- a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c
+++ b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c
@@ -2140,24 +2140,45 @@ static ERL_NIF_TERM make_map_remove_nif(ErlNifEnv* env, int argc, const ERL_NIF_
/* maps */
static ERL_NIF_TERM maps_from_list_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
- ERL_NIF_TERM cell = argv[0];
- ERL_NIF_TERM map = enif_make_new_map(env);
- ERL_NIF_TERM tuple;
- const ERL_NIF_TERM *pair;
- int arity = -1;
+ ERL_NIF_TERM *keys, *values;
+ ERL_NIF_TERM result, cell;
+ unsigned count;
+
+ if (argc != 1 || !enif_get_list_length(env, argv[0], &count)) {
+ return enif_make_badarg(env);
+ }
- if (argc != 1 && !enif_is_list(env, cell)) return enif_make_badarg(env);
+ keys = enif_alloc(sizeof(ERL_NIF_TERM) * count * 2);
+ values = keys + count;
- /* assume sorted keys */
+ cell = argv[0];
+ count = 0;
- while (!enif_is_empty_list(env,cell)) {
- if (!enif_get_list_cell(env, cell, &tuple, &cell)) return enif_make_badarg(env);
- if (enif_get_tuple(env,tuple,&arity,&pair)) {
- enif_make_map_put(env, map, pair[0], pair[1], &map);
- }
+ while (!enif_is_empty_list(env, cell)) {
+ const ERL_NIF_TERM *pair;
+ ERL_NIF_TERM tuple;
+ int arity;
+
+ if (!enif_get_list_cell(env, cell, &tuple, &cell)
+ || !enif_get_tuple(env, tuple, &arity, &pair)
+ || arity != 2) {
+ enif_free(keys);
+ return enif_make_badarg(env);
+ }
+
+ keys[count] = pair[0];
+ values[count] = pair[1];
+
+ count++;
}
- return map;
+ if (!enif_make_map_from_arrays(env, keys, values, count, &result)) {
+ result = enif_make_atom(env, "has_duplicate_keys");
+ }
+
+ enif_free(keys);
+
+ return result;
}
static ERL_NIF_TERM sorted_list_from_maps_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) {
@@ -2838,7 +2859,7 @@ unsigned rand_bits(struct frenzy_rand_bits* rnd, unsigned int nbits)
struct frenzy_monitor {
ErlNifMutex* lock;
- enum {
+ volatile enum {
MON_FREE, MON_FREE_DOWN, MON_FREE_DEMONITOR,
MON_TRYING, MON_ACTIVE, MON_PENDING
} state;
@@ -3200,13 +3221,24 @@ static void frenzy_resource_down(ErlNifEnv* env, void* obj, ErlNifPid* pid,
DBG_TRACE3("DOWN pid=%T, r=%p rix=%u\n", pid->pid, r, r->rix);
for (mix = 0; mix < FRENZY_MONITORS_MAX; mix++) {
- if (r->monv[mix].pid.pid == pid->pid && r->monv[mix].state >= MON_TRYING) {
+ int state1 = r->monv[mix].state;
+ /* First do dirty access of pid and state without the lock */
+ if (r->monv[mix].pid.pid == pid->pid && state1 >= MON_TRYING) {
+ int state2;
enif_mutex_lock(r->monv[mix].lock);
- if (enif_compare_monitors(mon, &r->monv[mix].mon) == 0) {
- assert(r->monv[mix].state >= MON_ACTIVE);
- r->monv[mix].state = MON_FREE_DOWN;
- enif_mutex_unlock(r->monv[mix].lock);
- return;
+ state2 = r->monv[mix].state;
+ if (state2 >= MON_ACTIVE) {
+ if (enif_compare_monitors(mon, &r->monv[mix].mon) == 0) {
+ r->monv[mix].state = MON_FREE_DOWN;
+ enif_mutex_unlock(r->monv[mix].lock);
+ return;
+ }
+ }
+ else {
+ assert(state2 != MON_TRYING);
+ assert(state1 == MON_TRYING || /* racing monitor failed */
+ state2 == MON_FREE_DEMONITOR || /* racing demonitor */
+ state2 == MON_FREE_DOWN); /* racing down */
}
enif_mutex_unlock(r->monv[mix].lock);
}
@@ -3403,6 +3435,15 @@ static ERL_NIF_TERM ioq(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
return enif_make_badarg(env);
else
return enif_make_atom(env, "true");
+ } else if (enif_is_identical(argv[0], enif_make_atom(env, "peek_head"))) {
+ ERL_NIF_TERM head_term;
+
+ if(enif_ioq_peek_head(env, ioq->q, NULL, &head_term)) {
+ return enif_make_tuple2(env,
+ enif_make_atom(env, "true"), head_term);
+ }
+
+ return enif_make_atom(env, "false");
} else if (enif_is_identical(argv[0], enif_make_atom(env, "peek"))) {
int iovlen, num, i, off = 0;
SysIOVec *iov = enif_ioq_peek(ioq->q, &iovlen);
diff --git a/erts/emulator/test/node_container_SUITE.erl b/erts/emulator/test/node_container_SUITE.erl
index be90f929df..7df001fec5 100644
--- a/erts/emulator/test/node_container_SUITE.erl
+++ b/erts/emulator/test/node_container_SUITE.erl
@@ -966,6 +966,8 @@ check_refc(ThisNodeName,ThisCreation,Table,EntryList) when is_list(EntryList) ->
{case Referrer of
{system,delayed_delete_timer} ->
true;
+ {system,thread_progress_delete_timer} ->
+ true;
_ ->
DDT
end,
diff --git a/erts/emulator/test/num_bif_SUITE.erl b/erts/emulator/test/num_bif_SUITE.erl
index 1c76eb8019..290bb61fc8 100644
--- a/erts/emulator/test/num_bif_SUITE.erl
+++ b/erts/emulator/test/num_bif_SUITE.erl
@@ -118,6 +118,7 @@ t_float(Config) when is_list(Config) ->
%% Tests float_to_list/1, float_to_list/2, float_to_binary/1, float_to_binary/2
t_float_to_string(Config) when is_list(Config) ->
+ rand_seed(),
test_fts("0.00000000000000000000e+00", 0.0),
test_fts("2.50000000000000000000e+01", 25.0),
test_fts("2.50000000000000000000e+00", 2.5),
@@ -145,7 +146,7 @@ t_float_to_string(Config) when is_list(Config) ->
123456789012345678.0, [{decimals, 237}])),
{'EXIT', {badarg, _}} = (catch float_to_binary(
123456789012345678.0, [{decimals, 237}])),
- test_fts("1." ++ string:copies("0", 249) ++ "e+00",
+ test_fts("1." ++ lists:duplicate(249, $0) ++ "e+00",
1.0, [{scientific, 249}, compact]),
X1 = float_to_list(1.0),
@@ -167,8 +168,8 @@ t_float_to_string(Config) when is_list(Config) ->
test_fts("1.12300",1.123, [{decimals, 5}]),
test_fts("1.123",1.123, [{decimals, 5}, compact]),
test_fts("1.1234",1.1234,[{decimals, 6}, compact]),
- test_fts("1.01",1.005, [{decimals, 2}]),
- test_fts("-1.01",-1.005,[{decimals, 2}]),
+ test_fts("1.00",1.005, [{decimals, 2}]), %% 1.005 is really 1.0049999999...
+ test_fts("-1.00",-1.005,[{decimals, 2}]),
test_fts("0.999",0.999, [{decimals, 3}]),
test_fts("-0.999",-0.999,[{decimals, 3}]),
test_fts("1.0",0.999, [{decimals, 2}, compact]),
@@ -184,6 +185,9 @@ t_float_to_string(Config) when is_list(Config) ->
test_fts("123000000000000000000.0",1.23e20, [{decimals, 10}, compact]),
test_fts("1.2300000000e+20",1.23e20, [{scientific, 10}, compact]),
test_fts("1.23000000000000000000e+20",1.23e20, []),
+
+ fts_rand_float_decimals(1000),
+
ok.
test_fts(Expect, Float) ->
@@ -197,6 +201,83 @@ test_fts(Expect, Float, Args) ->
BinExpect = float_to_binary(Float,Args).
+rand_float_reasonable() ->
+ F = rand_float(),
+ case abs(F) > 1.0e238 of
+ true -> rand_float_reasonable();
+ false -> F
+ end.
+
+fts_rand_float_decimals(0) -> ok;
+fts_rand_float_decimals(N) ->
+ [begin
+ F0 = rand_float_reasonable(),
+ L0 = float_to_list(F0, [{decimals, D}]),
+ case conform_with_io_lib_format_os(F0,D) of
+ false -> ok;
+ true ->
+ IOL = lists:flatten(io_lib:format("~.*f", [D, F0])),
+ true = case L0 =:= IOL of
+ true -> true;
+ false ->
+ io:format("F0 = ~w ~w\n", [F0, <<F0/float>>]),
+ io:format("decimals = ~w\n", [D]),
+ io:format("float_to_list = ~s\n", [L0]),
+ io:format("io_lib:format = ~s\n", [IOL]),
+ false
+ end
+ end,
+ L1 = case D of
+ 0 -> L0 ++ ".0";
+ _ -> L0
+ end,
+ F1 = list_to_float(L1),
+ Diff = abs(F0-F1),
+ MaxDiff = max_diff_decimals(F0, D),
+ ok = case Diff =< MaxDiff of
+ true -> ok;
+ false ->
+ io:format("F0 = ~w ~w\n", [F0, <<F0/float>>]),
+ io:format("L1 = ~s\n", [L1]),
+ io:format("F1 = ~w ~w\n", [F1, <<F1/float>>]),
+ io:format("Diff = ~w, MaxDiff = ~w\n", [Diff, MaxDiff]),
+ error
+ end
+ end
+ || D <- lists:seq(0,15)],
+
+ fts_rand_float_decimals(N-1).
+
+conform_with_io_lib_format_os(F, D) ->
+ case os:type() of
+ {win32,_} ->
+ %% io_lib:format("~.*f") buggy on windows? OTP-15010
+ false;
+ _ ->
+ conform_with_io_lib_format(F, D)
+ end.
+
+conform_with_io_lib_format(_, 0) ->
+ %% io_lib:format("~.*f") does not support zero decimals
+ false;
+conform_with_io_lib_format(_, D) when D > 10 ->
+ %% Seems float_to_list gets it slightly wrong sometimes for many decimals
+ false;
+conform_with_io_lib_format(F, D) ->
+ %% io_lib:format prints '0' for input bits beyond mantissa precision
+ %% float_to_list treats those unknown input bits as if they were zeros.
+ math:log2(abs(F) * math:pow(10,D)) < 54.
+
+max_diff_decimals(F, D) ->
+ IntBits = floor(math:log2(abs(F))) + 1,
+ FracBits = (52 - IntBits),
+ Log10_2 = 0.3010299956639812, % math:log10(2)
+ MaxDec = floor(FracBits * Log10_2),
+
+ Resolution = math:pow(2, IntBits - 53),
+
+ (math:pow(10, -min(D,MaxDec)) / 2) + Resolution.
+
%% Tests list_to_float/1.
t_string_to_float_safe(Config) when is_list(Config) ->
@@ -331,18 +412,26 @@ t_trunc_and_friends(_Config) ->
-18446744073709551616 = trunc_and_friends(-float(1 bsl 64)),
%% Random.
+ rand_seed(),
t_trunc_and_friends_rand(100),
ok.
+rand_seed() ->
+ rand:seed(exrop),
+ io:format("\n*** rand:export_seed() = ~w\n\n", [rand:export_seed()]),
+ ok.
+
+rand_float() ->
+ F0 = rand:uniform() * math:pow(10, 50*rand:normal()),
+ case rand:uniform() of
+ U when U < 0.5 -> -F0;
+ _ -> F0
+ end.
+
t_trunc_and_friends_rand(0) ->
ok;
t_trunc_and_friends_rand(N) ->
- F0 = rand:uniform() * math:pow(10, 50*rand:normal()),
- F = case rand:uniform() of
- U when U < 0.5 -> -F0;
- _ -> F0
- end,
- _ = trunc_and_friends(F),
+ _ = trunc_and_friends(rand_float()),
t_trunc_and_friends_rand(N-1).
trunc_and_friends(F) ->
@@ -491,7 +580,7 @@ t_string_to_integer(Config) when is_list(Config) ->
list_to_binary(Value),Base)),
{'EXIT', {badarg, _}} =
(catch erlang:list_to_integer(Value,Base))
- end,[{" 1",1},{" 1",37},{"2",2},{"C",11},
+ end,[{" 1",1},{" 1",37},{"2",2},{"B",11},{"b",11},{":", 16},
{"1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111z",16},
{"1z111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111",16},
{"111z11111111",16}]),
diff --git a/erts/emulator/test/port_SUITE.erl b/erts/emulator/test/port_SUITE.erl
index 730a17d7e8..5b39d05df8 100644
--- a/erts/emulator/test/port_SUITE.erl
+++ b/erts/emulator/test/port_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2017. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2018. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -109,7 +109,6 @@
mon_port_pid_demonitor/1,
mon_port_remote_on_remote/1,
mon_port_driver_die/1,
- mon_port_driver_die_demonitor/1,
mul_basic/1,
mul_slow_writes/1,
name1/1,
@@ -180,8 +179,7 @@ all() ->
mon_port_bad_named,
mon_port_pid_demonitor,
mon_port_name_demonitor,
- mon_port_driver_die,
- mon_port_driver_die_demonitor
+ mon_port_driver_die
].
groups() ->
@@ -1035,6 +1033,9 @@ huge_env(Config) when is_list(Config) ->
try erlang:open_port({spawn,Cmd},[exit_status, {env, Env}]) of
P ->
receive
+ {P, {exit_status,N}} = M when N > 127->
+ %% If exit status is > 127 something went very wrong
+ ct:fail("Open port failed got ~p",[M]);
{P, {exit_status,N}} = M ->
%% We test that the exit status is an integer, this means
%% that the child program has started. If we get an atom
@@ -1662,13 +1663,7 @@ spawn_executable(Config) when is_list(Config) ->
[ExactFile2,"hello world","dlrow olleh"] =
run_echo_args_2(unicode:characters_to_binary("\""++ExactFile2++"\" "++"\"hello world\" \"dlrow olleh\"")),
- ExeExt =
- case string:to_lower(lists:last(string:tokens(ExactFile2,"."))) of
- "exe" ->
- ".exe";
- _ ->
- ""
- end,
+ ExeExt = filename:extension(ExactFile2),
Executable2 = "spoky name"++ExeExt,
file:copy(ExactFile1,filename:join([SpaceDir,Executable2])),
ExactFile3 = filename:nativename(filename:join([SpaceDir,Executable2])),
@@ -1836,7 +1831,7 @@ collect_data(Port) ->
end.
parse_echo_args_output(Data) ->
- [lists:last(string:tokens(S,"|")) || S <- string:tokens(Data,"\r\n")].
+ [lists:last(string:lexemes(S,"|")) || S <- string:lexemes(Data,["\r\n",$\n])].
%% Test that the emulator does not mix up ports when the port table wraps
mix_up_ports(Config) when is_list(Config) ->
@@ -1962,7 +1957,7 @@ max_ports() ->
erlang:system_info(port_limit).
port_ix(Port) when is_port(Port) ->
- ["#Port",_,PortIxStr] = string:tokens(erlang:port_to_list(Port),
+ ["#Port",_,PortIxStr] = string:lexemes(erlang:port_to_list(Port),
"<.>"),
list_to_integer(PortIxStr).
@@ -2786,7 +2781,7 @@ mon_port_driver_die(Config) ->
end,
ok.
-
+-ifdef(DISABLED_TESTCASE).
%% 1. Spawn a port which will sleep 3 seconds
%% 2. Monitor port
%% 3. Port driver and dies horribly (via C driver_failure call). This should
@@ -2822,6 +2817,7 @@ mon_port_driver_die_demonitor(Config) ->
after 5000 -> ?assert(false)
end,
ok.
+-endif.
%% @doc Makes a controllable port for testing. Underlying mechanism of this
%% port is not important, only important is our ability to close/kill it or
diff --git a/erts/emulator/test/port_trace_SUITE.erl b/erts/emulator/test/port_trace_SUITE.erl
index a1986397a8..eba8f194e0 100644
--- a/erts/emulator/test/port_trace_SUITE.erl
+++ b/erts/emulator/test/port_trace_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-2017. All Rights Reserved.
+%% Copyright Ericsson AB 1999-2018. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -202,8 +202,7 @@ ports(_Config) ->
erlang:port_close(Prt),
[{trace,Prt,closed,normal},
- {trace,Prt,unregister,port_trace_SUITE},
- {trace,Prt,unlink,S}] = flush(),
+ {trace,Prt,unregister,port_trace_SUITE}] = flush(),
ok.
@@ -475,8 +474,7 @@ failure_test(Failure, Reason) ->
process_flag(trap_exit, false)
end,
[{trace, Prt, 'receive', {S, {command, Failure}}},
- {trace, Prt, closed, Reason},
- {trace, Prt, unlink, S}] = flush(),
+ {trace, Prt, closed, Reason}] = flush(),
ok.
@@ -599,13 +597,11 @@ close(Prt, Flags) ->
if Recv, Ports ->
[{trace, Prt, 'receive', {S, close}},
- {trace, Prt, closed, normal},
- {trace, Prt, unlink, S}] = flush();
+ {trace, Prt, closed, normal}] = flush();
Recv ->
[{trace, Prt, 'receive', {S, close}}] = flush();
Ports ->
- [{trace, Prt, closed, normal},
- {trace, Prt, unlink, S}] = flush();
+ [{trace, Prt, closed, normal}] = flush();
true ->
[] = flush()
end.
diff --git a/erts/emulator/test/process_SUITE.erl b/erts/emulator/test/process_SUITE.erl
index a9f20f9928..585c5a1871 100644
--- a/erts/emulator/test/process_SUITE.erl
+++ b/erts/emulator/test/process_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2017. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2018. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -42,8 +42,9 @@
process_info_lock_reschedule2/1,
process_info_lock_reschedule3/1,
process_info_garbage_collection/1,
+ process_info_smoke_all/1,
+ process_info_status_handled_signal/1,
bump_reductions/1, low_prio/1, binary_owner/1, yield/1, yield2/1,
- process_status_exiting/1,
otp_4725/1, bad_register/1, garbage_collect/1, otp_6237/1,
process_info_messages/1, process_flag_badarg/1, process_flag_heap_size/1,
spawn_opt_heap_size/1, spawn_opt_max_heap_size/1,
@@ -80,7 +81,8 @@ all() ->
process_info_lock_reschedule2,
process_info_lock_reschedule3,
process_info_garbage_collection,
- process_status_exiting,
+ process_info_smoke_all,
+ process_info_status_handled_signal,
bump_reductions, low_prio, yield, yield2, otp_4725,
bad_register, garbage_collect, process_info_messages,
process_flag_badarg, process_flag_heap_size,
@@ -512,14 +514,20 @@ pio_current_location(N, Pid, Pi, Looper) ->
case Where of
{erlang,process_info,2,[]} ->
pio_current_location(N-1, Pid, Pi+1, Looper);
+ {erts_internal,await_result,1, Loc} when is_list(Loc) ->
+ pio_current_location(N-1, Pid, Pi+1, Looper);
{?MODULE,process_info_looper,1,Loc} when is_list(Loc) ->
- pio_current_location(N-1, Pid, Pi, Looper+1)
+ pio_current_location(N-1, Pid, Pi, Looper+1);
+ _ ->
+ exit({unexpected_location, Where})
end.
pio_current_stacktrace() ->
L = [begin
- {current_stacktrace,Stk} = process_info(P, current_stacktrace),
- {P,Stk}
+ case process_info(P, current_stacktrace) of
+ {current_stacktrace, Stk} -> {P,Stk};
+ undefined -> {P, []}
+ end
end || P <- processes()],
[erlang:garbage_collect(P) || {P,_} <- L],
erlang:garbage_collect(),
@@ -841,28 +849,6 @@ process_info_lock_reschedule3(Config) when is_list(Config) ->
ct:fail(BadStatus)
end.
-process_status_exiting(Config) when is_list(Config) ->
- %% Make sure that erts_debug:get_internal_state({process_status,P})
- %% returns exiting if it is in status P_EXITING.
- erts_debug:set_internal_state(available_internal_state,true),
- Prio = process_flag(priority, max),
- P = spawn_opt(fun () -> receive after infinity -> ok end end,
- [{priority, normal}]),
- erlang:yield(),
- %% The tok_loop processes are here to make it hard for the exiting
- %% process to be scheduled in for exit...
- TokLoops = lists:map(fun (_) ->
- spawn_opt(fun tok_loop/0,
- [link,{priority, high}])
- end, lists:seq(1, erlang:system_info(schedulers_online))),
- exit(P, boom),
- wait_until(fun() ->
- exiting =:= erts_debug:get_internal_state({process_status,P})
- end),
- lists:foreach(fun (Tok) -> unlink(Tok), exit(Tok,bang) end, TokLoops),
- process_flag(priority, Prio),
- ok.
-
otp_4725(Config) when is_list(Config) ->
Tester = self(),
Ref1 = make_ref(),
@@ -997,10 +983,110 @@ process_info_garbage_collection(_Config) ->
gv(Key,List) ->
proplists:get_value(Key,List).
+process_info_smoke_all_tester() ->
+ register(process_info_smoke_all_tester, self()),
+ put(ets_ref, ets:new(blupp, [])),
+ put(binary, [list_to_binary(lists:duplicate(1000, 1)),
+ list_to_binary(lists:duplicate(1000, 2))]),
+ process_info_smoke_all_tester_loop().
+
+process_info_smoke_all_tester_loop() ->
+ receive
+ {other_process, Pid} ->
+ case get(procs) of
+ undefined -> put(procs, [Pid]);
+ Procs -> put(procs, [Pid|Procs])
+ end,
+ erlang:monitor(process, Pid),
+ link(Pid),
+ process_info_smoke_all_tester_loop()
+ end.
+
+process_info_smoke_all(Config) when is_list(Config) ->
+ AllPIOptions = [registered_name,
+ current_function,
+ initial_call,
+ messages,
+ message_queue_len,
+ links,
+ monitors,
+ monitored_by,
+ dictionary,
+ trap_exit,
+ error_handler,
+ heap_size,
+ stack_size,
+ memory,
+ garbage_collection,
+ group_leader,
+ reductions,
+ priority,
+ trace,
+ binary,
+ sequential_trace_token,
+ catchlevel,
+ backtrace,
+ last_calls,
+ total_heap_size,
+ suspending,
+ min_heap_size,
+ min_bin_vheap_size,
+ max_heap_size,
+ current_location,
+ current_stacktrace,
+ message_queue_data,
+ garbage_collection_info,
+ magic_ref,
+ fullsweep_after],
+
+ {ok, Node} = start_node(Config, ""),
+ RP = spawn_link(Node, fun process_info_smoke_all_tester/0),
+ LP = spawn_link(fun process_info_smoke_all_tester/0),
+ RP ! {other_process, LP},
+ LP ! {other_process, RP},
+ LP ! {other_process, self()},
+ LP ! ets:new(blapp, []),
+ LP ! ets:new(blipp, []),
+ LP ! list_to_binary(lists:duplicate(1000, 3)),
+ receive after 1000 -> ok end,
+ _MLP = erlang:monitor(process, LP),
+ true = is_process_alive(LP),
+ PI = process_info(LP, AllPIOptions),
+ io:format("~p~n", [PI]),
+ garbage_collect(),
+ unlink(RP),
+ unlink(LP),
+ exit(RP, kill),
+ exit(LP, kill),
+ false = is_process_alive(LP),
+ stop_node(Node),
+ ok.
+
+process_info_status_handled_signal(Config) when is_list(Config) ->
+ P = spawn_link(fun () ->
+ receive after infinity -> ok end
+ end),
+ wait_until(fun () ->
+ process_info(P, status) == {status, waiting}
+ end),
+ %%
+ %% The 'messages' option will force a process-info-request
+ %% signal to be scheduled on the process. Ensure that status
+ %% 'waiting' is reported even though it is actually running
+ %% when handling the request. We want it to report the status
+ %% it would have had if it had not been handling the
+ %% process-info-request...
+ %%
+ [{status, waiting}, {messages, []}] = process_info(P, [status, messages]),
+ unlink(P),
+ exit(P, kill),
+ false = erlang:is_process_alive(P),
+ ok.
+
%% Tests erlang:bump_reductions/1.
bump_reductions(Config) when is_list(Config) ->
erlang:garbage_collect(),
- receive after 1 -> ok end, % Clear reductions.
+ erlang:yield(), % Clear reductions.
{reductions,R1} = process_info(self(), reductions),
true = erlang:bump_reductions(100),
{reductions,R2} = process_info(self(), reductions),
@@ -2131,36 +2217,44 @@ handle_event(Event, Pid) ->
processes_term_proc_list(Config) when is_list(Config) ->
Tester = self(),
- as_expected = processes_term_proc_list_test(false),
- {ok, Node} = start_node(Config, "+Mis true"),
- RT = spawn_link(Node, fun () ->
- receive after 1000 -> ok end,
- processes_term_proc_list_test(false),
- Tester ! {it_worked, self()}
- end),
- receive {it_worked, RT} -> ok end,
- stop_node(Node),
+
+ Run = fun(Args) ->
+ {ok, Node} = start_node(Config, Args),
+ RT = spawn_link(Node, fun () ->
+ receive after 1000 -> ok end,
+ as_expected = processes_term_proc_list_test(false),
+ Tester ! {it_worked, self()}
+ end),
+ receive {it_worked, RT} -> ok end,
+ stop_node(Node)
+ end,
+
+ %% We have to run this test case with +S1 since instrument:allocations()
+ %% will report a free()'d block as present until it's actually deallocated
+ %% by its employer.
+ Run("+MSe true +MSatags false +S1"),
+ Run("+MSe true +MSatags true +S1"),
+
ok.
-
+
-define(CHK_TERM_PROC_LIST(MC, XB),
chk_term_proc_list(?LINE, MC, XB)).
chk_term_proc_list(Line, MustChk, ExpectBlks) ->
- case {MustChk, instrument:memory_status(types)} of
- {false, false} ->
+ Allocs = instrument:allocations(#{ allocator_types => [sl_alloc] }),
+ case {MustChk, Allocs} of
+ {false, {error, not_enabled}} ->
not_enabled;
- {_, MS} ->
- {value,
- {ptab_list_deleted_el,
- DL}} = lists:keysearch(ptab_list_deleted_el, 1, MS),
- case lists:keysearch(blocks, 1, DL) of
- {value, {blocks, ExpectBlks, _, _}} ->
- ok;
- {value, {blocks, Blks, _, _}} ->
- exit({line, Line,
- mismatch, expected, ExpectBlks, actual, Blks});
- Unexpected ->
- exit(Unexpected)
+ {_, {ok, {_Shift, _Unscanned, ByOrigin}}} ->
+ ByType = maps:get(system, ByOrigin, #{}),
+ Hist = maps:get(ptab_list_deleted_el, ByType, {}),
+ case lists:sum(tuple_to_list(Hist)) of
+ ExpectBlks ->
+ ok;
+ Blks ->
+ exit({line, Line, mismatch,
+ expected, ExpectBlks,
+ actual, Blks})
end
end,
ok.
@@ -2532,8 +2626,13 @@ system_task_on_suspended(Config) when is_list(Config) ->
end.
gc_request_when_gc_disabled(Config) when is_list(Config) ->
- Master = self(),
AIS = erts_debug:set_internal_state(available_internal_state, true),
+ gc_request_when_gc_disabled_do(ref),
+ gc_request_when_gc_disabled_do(immed),
+ erts_debug:set_internal_state(available_internal_state, AIS).
+
+gc_request_when_gc_disabled_do(ReqIdType) ->
+ Master = self(),
{P, M} = spawn_opt(fun () ->
true = erts_debug:set_internal_state(gc_state,
false),
@@ -2545,7 +2644,10 @@ gc_request_when_gc_disabled(Config) when is_list(Config) ->
receive after 100 -> ok end
end, [monitor, link]),
receive {P, gc_state, false} -> ok end,
- ReqId = make_ref(),
+ ReqId = case ReqIdType of
+ ref -> make_ref();
+ immed -> immed
+ end,
async = garbage_collect(P, [{async, ReqId}]),
receive
{garbage_collect, ReqId, Result} ->
@@ -2554,7 +2656,6 @@ gc_request_when_gc_disabled(Config) when is_list(Config) ->
ok
end,
receive {garbage_collect, ReqId, true} -> ok end,
- erts_debug:set_internal_state(available_internal_state, AIS),
receive {'DOWN', M, process, P, _Reason} -> ok end,
ok.
diff --git a/erts/emulator/test/receive_SUITE.erl b/erts/emulator/test/receive_SUITE.erl
index a12019ec83..1fe11428b4 100644
--- a/erts/emulator/test/receive_SUITE.erl
+++ b/erts/emulator/test/receive_SUITE.erl
@@ -25,14 +25,16 @@
-include_lib("common_test/include/ct.hrl").
-export([all/0, suite/0,
- call_with_huge_message_queue/1,receive_in_between/1]).
+ call_with_huge_message_queue/1,receive_in_between/1,
+ receive_opt_exception/1,receive_opt_recursion/1]).
suite() ->
[{ct_hooks,[ts_install_cth]},
{timetrap, {minutes, 3}}].
-all() ->
- [call_with_huge_message_queue, receive_in_between].
+all() ->
+ [call_with_huge_message_queue, receive_in_between,
+ receive_opt_exception, receive_opt_recursion].
call_with_huge_message_queue(Config) when is_list(Config) ->
Pid = spawn_link(fun echo_loop/0),
@@ -113,6 +115,60 @@ receive_one() ->
dummy -> ok
end.
+receive_opt_exception(_Config) ->
+ Recurse = fun() ->
+ %% Overwrite with the same mark,
+ %% and never consume it.
+ ThrowFun = fun() -> throw(aborted) end,
+ aborted = (catch do_receive_opt_exception(ThrowFun)),
+ ok
+ end,
+ do_receive_opt_exception(Recurse),
+
+ %% Eat the second message.
+ receive
+ Ref when is_reference(Ref) -> ok
+ end.
+
+do_receive_opt_exception(Disturber) ->
+ %% Create a receive mark.
+ Ref = make_ref(),
+ self() ! Ref,
+ Disturber(),
+ receive
+ Ref ->
+ ok
+ after 0 ->
+ error(the_expected_message_was_not_there)
+ end.
+
+receive_opt_recursion(_Config) ->
+ Recurse = fun() ->
+ %% Overwrite with the same mark,
+ %% and never consume it.
+ NoOp = fun() -> ok end,
+ BlackHole = spawn(NoOp),
+ expected = do_receive_opt_recursion(BlackHole, NoOp, true),
+ ok
+ end,
+ do_receive_opt_recursion(self(), Recurse, false),
+ ok.
+
+do_receive_opt_recursion(Recipient, Disturber, IsInner) ->
+ Ref = make_ref(),
+ Recipient ! Ref,
+ Disturber(),
+ receive
+ Ref -> ok
+ after 0 ->
+ case IsInner of
+ true ->
+ expected;
+ false ->
+ error(the_expected_message_was_not_there)
+ end
+ end.
+
%%%
%%% Common helpers.
%%%
diff --git a/erts/emulator/test/scheduler_SUITE.erl b/erts/emulator/test/scheduler_SUITE.erl
index 7afb82a1b8..7eebbe8b19 100644
--- a/erts/emulator/test/scheduler_SUITE.erl
+++ b/erts/emulator/test/scheduler_SUITE.erl
@@ -894,11 +894,9 @@ adjust_schedulers_online() ->
read_affinity(Data) ->
Exp = "pid " ++ os:getpid() ++ "'s current affinity mask",
- case string:tokens(Data, ":") of
+ case string:lexemes(Data, ":") of
[Exp, DirtyAffinityStr] ->
- AffinityStr = string:strip(string:strip(DirtyAffinityStr,
- both, $ ),
- both, $\n),
+ AffinityStr = string:trim(DirtyAffinityStr),
case catch erlang:list_to_integer(AffinityStr, 16) of
Affinity when is_integer(Affinity) ->
Affinity;
diff --git a/erts/emulator/test/signal_SUITE.erl b/erts/emulator/test/signal_SUITE.erl
index 61a8617165..fab2f45f28 100644
--- a/erts/emulator/test/signal_SUITE.erl
+++ b/erts/emulator/test/signal_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2006-2017. All Rights Reserved.
+%% Copyright Ericsson AB 2006-2018. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -34,23 +34,9 @@
-export([init_per_testcase/2, end_per_testcase/2]).
% Test cases
--export([xm_sig_order/1,
- pending_exit_unlink_process/1,
- pending_exit_unlink_dist_process/1,
- pending_exit_unlink_port/1,
- pending_exit_trap_exit/1,
- pending_exit_receive/1,
- pending_exit_exit/1,
- pending_exit_gc/1,
- pending_exit_is_process_alive/1,
- pending_exit_process_display/1,
- pending_exit_process_info_1/1,
- pending_exit_process_info_2/1,
- pending_exit_group_leader/1,
- exit_before_pending_exit/1]).
+-export([xm_sig_order/1]).
init_per_testcase(Func, Config) when is_atom(Func), is_list(Config) ->
- available_internal_state(true),
[{testcase, Func}|Config].
end_per_testcase(_Func, _Config) ->
@@ -60,24 +46,14 @@ init_per_suite(Config) ->
Config.
end_per_suite(_Config) ->
- available_internal_state(true),
- catch erts_debug:set_internal_state(not_running_optimization, true),
- available_internal_state(false).
+ ok.
suite() ->
[{ct_hooks,[ts_install_cth]},
{timetrap, {minutes, 2}}].
all() ->
- [xm_sig_order, pending_exit_unlink_process,
- pending_exit_unlink_dist_process,
- pending_exit_unlink_port, pending_exit_trap_exit,
- pending_exit_receive, pending_exit_trap_exit,
- pending_exit_gc, pending_exit_is_process_alive,
- pending_exit_process_display,
- pending_exit_process_info_1,
- pending_exit_process_info_2, pending_exit_group_leader,
- exit_before_pending_exit].
+ [xm_sig_order].
%% Test that exit signals and messages are received in correct order
@@ -113,362 +89,9 @@ xm_sig_order_proc() ->
end,
xm_sig_order_proc().
-pending_exit_unlink_process(Config) when is_list(Config) ->
- pending_exit_test(self(), unlink).
-
-pending_exit_unlink_dist_process(Config) when is_list(Config) ->
- {ok, Node} = start_node(Config),
- From = spawn(Node, fun () -> receive after infinity -> ok end end),
- Res = pending_exit_test(From, unlink),
- stop_node(Node),
- Res.
-
-pending_exit_unlink_port(Config) when is_list(Config) ->
- pending_exit_test(hd(erlang:ports()), unlink).
-
-pending_exit_trap_exit(Config) when is_list(Config) ->
- pending_exit_test(self(), trap_exit).
-
-pending_exit_receive(Config) when is_list(Config) ->
- pending_exit_test(self(), 'receive').
-
-pending_exit_exit(Config) when is_list(Config) ->
- pending_exit_test(self(), exit).
-
-pending_exit_gc(Config) when is_list(Config) ->
- pending_exit_test(self(), gc).
-
-pending_exit_test(From, Type) ->
- OTE = process_flag(trap_exit, true),
- Ref = make_ref(),
- Master = self(),
- ExitBySignal = case Type of
- gc ->
- lists:duplicate(10000,
- exit_by_signal);
- _ ->
- exit_by_signal
- end,
- Pid = spawn_link(
- fun () ->
- receive go -> ok end,
- false = have_pending_exit(),
- exit = fake_exit(From,
- self(),
- ExitBySignal),
- true = have_pending_exit(),
- Master ! {self(), Ref, Type},
- case Type of
- gc ->
- force_gc(),
- erlang:yield();
- unlink ->
- unlink(From);
- trap_exit ->
- process_flag(trap_exit, true);
- 'receive' ->
- receive _ -> ok
- after 0 -> ok
- end;
- exit ->
- ok
- end,
- exit(exit_by_myself)
- end),
- Mon = erlang:monitor(process, Pid),
- Pid ! go,
- Reason = receive
- {'DOWN', Mon, process, Pid, R} ->
- receive
- {Pid, Ref, Type} ->
- ok
- after 0 ->
- ct:fail(premature_exit)
- end,
- case Type of
- exit ->
- exit_by_myself = R;
- _ ->
- ExitBySignal = R
- end
- end,
- receive
- {'EXIT', Pid, R2} ->
- Reason = R2
- end,
- process_flag(trap_exit, OTE),
- ok,
- {comment, "Test only valid with current SMP emulator."}.
-
-
-
-exit_before_pending_exit(Config) when is_list(Config) ->
- %% This is a testcase testcase very specific to the smp
- %% implementation as it is of the time of writing.
- %%
- %% The testcase tries to check that a process can
- %% exit by itself even though it has a pending exit.
- OTE = process_flag(trap_exit, true),
- Master = self(),
- Tester = spawn_link(
- fun () ->
- Opts = case {erlang:system_info(run_queues),
- erlang:system_info(schedulers_online)} of
- {RQ, SO} when RQ =:= 1; SO =:= 1 -> [];
- _ ->
- process_flag(scheduler, 1),
- [{scheduler, 2}]
- end,
- P = self(),
- Exiter = spawn_opt(fun () ->
- receive
- {exit_me, P, R} ->
- exit(P, R)
- end
- end, Opts),
- erlang:yield(),
- Exiter ! {exit_me, self(), exited_by_exiter},
- %% We want to get a pending exit
- %% before we exit ourselves. We
- %% don't want to be scheduled out
- %% since we will then see the
- %% pending exit.
- %%
- %% Do something that takes
- %% relatively long time but
- %% consumes few reductions...
- repeat(fun() -> erlang:system_info(procs) end,10),
- %% ... then exit.
- Master ! {self(),
- pending_exit,
- have_pending_exit()},
- exit(exited_by_myself)
- end),
- PendingExit = receive {Tester, pending_exit, PE} -> PE end,
- receive
- {'EXIT', Tester, exited_by_myself} ->
- process_flag(trap_exit, OTE),
- ok;
- Msg ->
- ct:fail({unexpected_message, Msg})
- end,
- NoScheds = integer_to_list(erlang:system_info(schedulers_online)),
- {comment,
- "Was "
- ++ case PendingExit of
- true -> "";
- false ->"*not*"
- end ++ " able to trigger a pending exit. "
- ++ "Running on " ++ NoScheds ++ " scheduler(s). "
- ++ "This test is only interesting with at least two schedulers."}.
-
--define(PE_INFO_REPEAT, 100).
-
-pending_exit_is_process_alive(Config) when is_list(Config) ->
- S = exit_op_test_init(),
- TestFun = fun (P) -> false = is_process_alive(P) end,
- repeated_exit_op_test(TestFun, ?PE_INFO_REPEAT),
- verify_pending_exit_success(S),
- comment().
-
-pending_exit_process_info_1(Config) when is_list(Config) ->
- S = exit_op_test_init(),
- TestFun = fun (P) ->
- undefined = process_info(P)
- end,
- repeated_exit_op_test(TestFun, ?PE_INFO_REPEAT),
- verify_pending_exit_success(S),
- comment().
-
-pending_exit_process_info_2(Config) when is_list(Config) ->
- S0 = exit_op_test_init(),
- repeated_exit_op_test(fun (P) ->
- undefined = process_info(P, messages)
- end, ?PE_INFO_REPEAT),
- S1 = verify_pending_exit_success(S0),
- repeated_exit_op_test(fun (P) ->
- undefined = process_info(P, status)
- end, ?PE_INFO_REPEAT),
- S2 = verify_pending_exit_success(S1),
- repeated_exit_op_test(fun (P) ->
- undefined = process_info(P, links)
- end, ?PE_INFO_REPEAT),
- S3 = verify_pending_exit_success(S2),
- repeated_exit_op_test(fun (P) ->
- undefined = process_info(P, [messages])
- end, ?PE_INFO_REPEAT),
- S4 = verify_pending_exit_success(S3),
- repeated_exit_op_test(fun (P) ->
- undefined = process_info(P, [status])
- end, ?PE_INFO_REPEAT),
- S5 = verify_pending_exit_success(S4),
- repeated_exit_op_test(fun (P) ->
- undefined = process_info(P, [links])
- end, ?PE_INFO_REPEAT),
- S6 = verify_pending_exit_success(S5),
- repeated_exit_op_test(fun (P) ->
- undefined = process_info(P, [status,
- links])
- end, ?PE_INFO_REPEAT),
- S7 = verify_pending_exit_success(S6),
- repeated_exit_op_test(fun (P) ->
- undefined = process_info(P, [messages,
- status])
- end, ?PE_INFO_REPEAT),
- S8 = verify_pending_exit_success(S7),
- repeated_exit_op_test(fun (P) ->
- undefined = process_info(P, [messages,
- links])
- end, ?PE_INFO_REPEAT),
- S9 = verify_pending_exit_success(S8),
- repeated_exit_op_test(
- fun (P) ->
- undefined = process_info(P, [message_queue_len,
- status])
- end, ?PE_INFO_REPEAT),
- S10 = verify_pending_exit_success(S9),
- repeated_exit_op_test(fun (P) ->
- undefined = process_info(P, [messages,
- links,
- status])
- end, ?PE_INFO_REPEAT),
- verify_pending_exit_success(S10),
- comment().
-
-pending_exit_process_display(Config) when is_list(Config) ->
- S = exit_op_test_init(),
- TestFun = fun (P) ->
- badarg = try
- erlang:process_display(P, backtrace)
- catch
- error:badarg -> badarg
- end
- end,
- repeated_exit_op_test(TestFun, ?PE_INFO_REPEAT),
- verify_pending_exit_success(S),
- comment().
-
-pending_exit_group_leader(Config) when is_list(Config) ->
- S = exit_op_test_init(),
- TestFun = fun (P) ->
- badarg = try
- group_leader(self(), P)
- catch
- error:badarg -> badarg
- end
- end,
- repeated_exit_op_test(TestFun, ?PE_INFO_REPEAT),
- verify_pending_exit_success(S),
- comment().
-
%%
%% -- Internal utils --------------------------------------------------------
%%
-exit_op_test_init() ->
- put(no_pending_exit_success, 0),
- put(no_pending_exit_tries, 0),
- {case {erlang:system_info(run_queues),
- erlang:system_info(schedulers_online)} of
- {RQ, SO} when RQ =:= 1; SO =:= 1 -> false;
- _ -> true
- end, 0, 0}.
-
-verify_pending_exit_success({false, _, _} = S) ->
- S;
-verify_pending_exit_success({true, S, T}) ->
- NewS = get(no_pending_exit_success),
- NewT = get(no_pending_exit_tries),
- case NewT =:= T of
- true -> ok;
- _ -> case NewS > S of
- true -> ok;
- _ -> exit(no_pending_exits)
- end
- end,
- {true, NewS, NewT}.
-
-comment() ->
- {comment,
- "Pending exit trigger ratio "
- ++ integer_to_list(get(no_pending_exit_success))
- ++ "/"
- ++ integer_to_list(get(no_pending_exit_tries))
- ++ "."
- ++ case get(not_running_opt_test) of
- true -> " No 'not running optimization' to disable.";
- _ -> ""
- end}.
-
-repeated_exit_op_test(TestFun, N) ->
- WorkFun0 = fun () ->
- lists:sort(lists:reverse(lists:seq(1, 1000)))
- end,
- repeat(fun () -> exit_op_test(TestFun, WorkFun0) end, N),
- try erts_debug:set_internal_state(not_running_optimization, false) of
- Bool when Bool == true; Bool == false ->
- WorkFun1 = fun () ->
- erts_debug:set_internal_state(sleep, 0),
- lists:sort(lists:reverse(lists:seq(1, 1000)))
- end,
- repeat(fun () ->
- exit_op_test(TestFun, WorkFun1)
- end, N)
- catch
- error:notsup -> put(not_running_opt_test, true)
- after
- catch erts_debug:set_internal_state(not_running_optimization, true)
- end.
-
-exit_op_test(TestFun, WorkFun) ->
- Opts = case {erlang:system_info(run_queues),
- erlang:system_info(schedulers_online)} of
- {RQ, SO} when RQ =:= 1; SO =:= 1 -> [];
- _ ->
- process_flag(scheduler, 1),
- [{scheduler, 2}]
- end,
- Master = self(),
- Going = make_ref(),
- P = spawn_opt(fun () ->
- loop(10, WorkFun),
- Master ! Going,
- loop(infinity, WorkFun)
- end, Opts),
- receive Going -> ok end,
- loop(10, WorkFun),
- erlang:yield(),
- exit(P, bang),
- PE0 = have_pending_exit(P),
- TestFun(P),
- PE = case PE0 of
- true -> true;
- _ -> false
- end,
- case {PE, get(no_pending_exit_success), get(no_pending_exit_tries)} of
- {true, undefined, undefined} ->
- put(no_pending_exit_success, 1),
- put(no_pending_exit_tries, 1);
- {false, undefined, undefined} ->
- put(no_pending_exit_success, 0),
- put(no_pending_exit_tries, 1);
- {true, S, T} ->
- put(no_pending_exit_success, S+1),
- put(no_pending_exit_tries, T+1);
- {false, _S, T} ->
- put(no_pending_exit_tries, T+1)
- end,
- ok.
-
-loop(infinity, WorkFun) ->
- do_loop(infinity, WorkFun);
-loop(0, _WorkFun) ->
- ok;
-loop(N, WorkFun) when is_integer(N) ->
- do_loop(N-1, WorkFun).
-
-do_loop(N, WorkFun) ->
- WorkFun(),
- loop(N, WorkFun).
repeat(_Fun, N) when is_integer(N), N =< 0 ->
ok;
@@ -486,30 +109,3 @@ start_node(Config) ->
stop_node(Node) ->
test_server:stop_node(Node).
-
-have_pending_exit() ->
- have_pending_exit(self()).
-
-have_pending_exit(Pid) ->
- erts_debug:get_internal_state({have_pending_exit, Pid}).
-
-force_gc() ->
- erts_debug:set_internal_state(force_gc, self()).
-
-fake_exit(From, To, Reason) ->
- erts_debug:set_internal_state(send_fake_exit_signal, {From, To, Reason}).
-
-available_internal_state(Bool) when Bool == true; Bool == false ->
- case {Bool,
- (catch erts_debug:get_internal_state(available_internal_state))} of
- {true, true} ->
- true;
- {false, true} ->
- erts_debug:set_internal_state(available_internal_state, false),
- true;
- {true, _} ->
- erts_debug:set_internal_state(available_internal_state, true),
- false;
- {false, _} ->
- false
- end.
diff --git a/erts/emulator/test/smoke_test_SUITE.erl b/erts/emulator/test/smoke_test_SUITE.erl
index adc6f56c06..b3d34103f1 100644
--- a/erts/emulator/test/smoke_test_SUITE.erl
+++ b/erts/emulator/test/smoke_test_SUITE.erl
@@ -70,6 +70,20 @@ boot_combo(Config) when is_list(Config) ->
chk_boot(Config, "+Ktrue", NOOP),
chk_boot(Config, "+A42", A42),
chk_boot(Config, "+Ktrue +A42", A42),
+
+ WBTArgs = ["very_short", "short", "medium", "long", "very_long"],
+ WTArgs = ["very_low", "low", "medium", "high", "very_high"],
+ [chk_boot(Config,
+ " +sbwt " ++ WBT ++
+ " +sbwtdcpu " ++ WBT ++
+ " +sbwtdio " ++ WBT ++
+ " +swt " ++ WT ++
+ " +swtdcpu " ++ WT ++
+ " +swtdio " ++ WT, NOOP) || WBT <- WBTArgs, WT <- WTArgs],
+
+ WSArgs = ["legacy", "default"],
+ [chk_boot(Config, " +sws " ++ WS, NOOP) || WS <- WSArgs],
+
%% A lot more combos could be implemented...
ok
after
diff --git a/erts/emulator/test/system_info_SUITE.erl b/erts/emulator/test/system_info_SUITE.erl
index 56522039da..fdf4aab24d 100644
--- a/erts/emulator/test/system_info_SUITE.erl
+++ b/erts/emulator/test/system_info_SUITE.erl
@@ -308,9 +308,9 @@ memory_test(_Config) ->
mem_workers_call(MWs,
fun () ->
- list_to_atom("an ugly atom "++integer_to_list(erlang:system_info(scheduler_id))),
- list_to_atom("another ugly atom "++integer_to_list(erlang:system_info(scheduler_id))),
- list_to_atom("yet another ugly atom "++integer_to_list(erlang:system_info(scheduler_id)))
+ _ = list_to_atom("an ugly atom "++integer_to_list(erlang:system_info(scheduler_id))),
+ _ = list_to_atom("another ugly atom "++integer_to_list(erlang:system_info(scheduler_id))),
+ _ = list_to_atom("yet another ugly atom "++integer_to_list(erlang:system_info(scheduler_id)))
end, []),
cmp_memory(MWs, "new atoms"),
diff --git a/erts/emulator/test/trace_SUITE.erl b/erts/emulator/test/trace_SUITE.erl
index 72acd33033..def25dba7d 100644
--- a/erts/emulator/test/trace_SUITE.erl
+++ b/erts/emulator/test/trace_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2017. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2018. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -38,7 +38,7 @@
system_monitor_long_gc_1/1, system_monitor_long_gc_2/1,
system_monitor_large_heap_1/1, system_monitor_large_heap_2/1,
system_monitor_long_schedule/1,
- bad_flag/1, trace_delivered/1]).
+ bad_flag/1, trace_delivered/1, trap_exit_self_receive/1]).
-include_lib("common_test/include/ct.hrl").
@@ -61,7 +61,8 @@ all() ->
more_system_monitor_args, system_monitor_long_gc_1,
system_monitor_long_gc_2, system_monitor_large_heap_1,
system_monitor_long_schedule,
- system_monitor_large_heap_2, bad_flag, trace_delivered].
+ system_monitor_large_heap_2, bad_flag, trace_delivered,
+ trap_exit_self_receive].
init_per_testcase(_Case, Config) ->
[{receiver,spawn(fun receiver/0)}|Config].
@@ -234,7 +235,7 @@ link_receive_call_correlation(Config) when is_list(Config) ->
1 = erlang:trace(Receiver, true, ['receive', procs, call, timestamp, scheduler_id]),
1 = erlang:trace_pattern({?MODULE, receive_msg, '_'}, [], [local]),
- Num = 100000,
+ Num = 100,
(fun F(0) -> [];
F(N) ->
@@ -254,7 +255,7 @@ link_receive_call_correlation(Config) when is_list(Config) ->
Msgs = (fun F() -> receive M -> [M | F()] after 1 -> [] end end)(),
- case check_consistent(Receiver, Num, Num, Num, Msgs) of
+ case check_consistent(Receiver, Num, Num, Num, Msgs, false, undefined) of
ok ->
ok;
{error, Reason} ->
@@ -264,20 +265,63 @@ link_receive_call_correlation(Config) when is_list(Config) ->
-define(schedid, , _).
-check_consistent(_Pid, Recv, Call, _LU, [Msg | _]) when Recv > Call ->
+check_consistent(_Pid, Recv, Call, _LU, [Msg | _], _Received, _LinkedN) when Recv > Call ->
{error, Msg};
-check_consistent(Pid, Recv, Call, LU, [Msg | Msgs]) ->
+check_consistent(Pid, Recv, Call, LU, [Msg | Msgs], false, undefined) ->
case Msg of
{trace, Pid, 'receive', Recv ?schedid} ->
- check_consistent(Pid,Recv - 1, Call, LU, Msgs);
+ check_consistent(Pid,Recv - 1, Call, LU, Msgs, true, undefined);
{trace_ts, Pid, 'receive', Recv ?schedid, _} ->
- check_consistent(Pid,Recv - 1, Call, LU, Msgs);
+ check_consistent(Pid,Recv - 1, Call, LU, Msgs, true, undefined);
{trace, Pid, call, {?MODULE, receive_msg, [Call]} ?schedid} ->
- check_consistent(Pid,Recv, Call - 1, LU, Msgs);
+ check_consistent(Pid,Recv, Call - 1, LU, Msgs, false, undefined);
{trace_ts, Pid, call, {?MODULE, receive_msg, [Call]} ?schedid, _} ->
- check_consistent(Pid,Recv, Call - 1, LU, Msgs);
+ check_consistent(Pid,Recv, Call - 1, LU, Msgs, false, undefined);
+
+ {trace, Pid, _, _Self ?schedid} ->
+ check_consistent(Pid, Recv, Call, LU, Msgs, false, undefined);
+ {trace_ts, Pid, _, _Self ?schedid, _} ->
+ check_consistent(Pid, Recv, Call, LU, Msgs, false, undefined);
+
+ Msg ->
+ {error, Msg}
+ end;
+check_consistent(Pid, Recv, Call, LU, [Msg | Msgs], true, undefined) ->
+
+ case Msg of
+ {trace, Pid, call, {?MODULE, receive_msg, [Call]} ?schedid} ->
+ check_consistent(Pid,Recv, Call - 1, LU, Msgs, true, undefined);
+ {trace_ts, Pid, call, {?MODULE, receive_msg, [Call]} ?schedid, _} ->
+ check_consistent(Pid,Recv, Call - 1, LU, Msgs, true, undefined);
+
+ {trace, Pid, getting_linked, _Self ?schedid} ->
+ check_consistent(Pid, Recv, Call, LU - 1, Msgs, true, Recv rem 2);
+ {trace_ts, Pid, getting_linked, _Self ?schedid, _} ->
+ check_consistent(Pid, Recv, Call, LU - 1, Msgs, true, Recv rem 2);
+
+ {trace, Pid, getting_unlinked, _Self ?schedid} ->
+ check_consistent(Pid, Recv, Call, LU - 1, Msgs, true, (Recv+1) rem 2);
+ {trace_ts, Pid, getting_unlinked, _Self ?schedid, _} ->
+ check_consistent(Pid, Recv, Call, LU - 1, Msgs, true, (Recv+1) rem 2);
+
+ Msg ->
+ {error, Msg}
+ end;
+check_consistent(Pid, Recv, Call, LU, [Msg | Msgs], true, LinkedN) ->
+ UnlinkedN = (LinkedN + 1) rem 2,
+
+ case Msg of
+ {trace, Pid, 'receive', Recv ?schedid} when Recv == LU ->
+ check_consistent(Pid,Recv - 1, Call, LU, Msgs, true, LinkedN);
+ {trace_ts, Pid, 'receive', Recv ?schedid, _} when Recv == LU ->
+ check_consistent(Pid,Recv - 1, Call, LU, Msgs, true, LinkedN);
+
+ {trace, Pid, call, {?MODULE, receive_msg, [Call]} ?schedid} ->
+ check_consistent(Pid,Recv, Call - 1, LU, Msgs, true, LinkedN);
+ {trace_ts, Pid, call, {?MODULE, receive_msg, [Call]} ?schedid, _} ->
+ check_consistent(Pid,Recv, Call - 1, LU, Msgs, true, LinkedN);
%% We check that for each receive we have gotten a
%% getting_linked or getting_unlinked message. Also
@@ -285,38 +329,38 @@ check_consistent(Pid, Recv, Call, LU, [Msg | Msgs]) ->
%% message we expect to receive is an even number
%% and odd number for getting_unlinked.
{trace, Pid, getting_linked, _Self ?schedid}
- when Recv rem 2 == 0, Recv == LU ->
- check_consistent(Pid, Recv, Call, LU - 1, Msgs);
+ when Recv rem 2 == LinkedN ->
+ check_consistent(Pid, Recv, Call, LU - 1, Msgs, true, LinkedN);
{trace_ts, Pid, getting_linked, _Self ?schedid, _}
- when Recv rem 2 == 0, Recv == LU ->
- check_consistent(Pid, Recv, Call, LU - 1, Msgs);
+ when Recv rem 2 == LinkedN ->
+ check_consistent(Pid, Recv, Call, LU - 1, Msgs, true, LinkedN);
{trace, Pid, getting_unlinked, _Self ?schedid}
- when Recv rem 2 == 1, Recv == LU ->
- check_consistent(Pid, Recv, Call, LU - 1, Msgs);
+ when Recv rem 2 == UnlinkedN ->
+ check_consistent(Pid, Recv, Call, LU - 1, Msgs, true, LinkedN);
{trace_ts, Pid, getting_unlinked, _Self ?schedid, _}
- when Recv rem 2 == 1, Recv == LU ->
- check_consistent(Pid, Recv, Call, LU - 1, Msgs);
+ when Recv rem 2 == UnlinkedN ->
+ check_consistent(Pid, Recv, Call, LU - 1, Msgs, true, LinkedN);
{trace,Pid,'receive',Ignore ?schedid}
when Ignore == stop; Ignore == timeout ->
- check_consistent(Pid, Recv, Call, LU, Msgs);
+ check_consistent(Pid, Recv, Call, LU, Msgs, true, LinkedN);
{trace_ts,Pid,'receive',Ignore ?schedid,_}
when Ignore == stop; Ignore == timeout ->
- check_consistent(Pid, Recv, Call, LU, Msgs);
+ check_consistent(Pid, Recv, Call, LU, Msgs, true, LinkedN);
{trace, Pid, exit, normal ?schedid} ->
- check_consistent(Pid, Recv, Call, LU, Msgs);
+ check_consistent(Pid, Recv, Call, LU, Msgs, true, LinkedN);
{trace_ts, Pid, exit, normal ?schedid, _} ->
- check_consistent(Pid, Recv, Call, LU, Msgs);
+ check_consistent(Pid, Recv, Call, LU, Msgs, true, LinkedN);
{'EXIT', Pid, normal} ->
- check_consistent(Pid, Recv, Call, LU, Msgs);
+ check_consistent(Pid, Recv, Call, LU, Msgs, true, LinkedN);
Msg ->
{error, Msg}
end;
-check_consistent(_, 0, 0, 0, []) ->
+check_consistent(_, 0, 0, 1, [], true, _) ->
ok;
-check_consistent(_, Recv, Call, LU, []) ->
+check_consistent(_, Recv, Call, LU, [], _, _) ->
{error,{Recv, Call, LU}}.
receive_msg(M) ->
@@ -1709,6 +1753,31 @@ trace_delivered(Config) when is_list(Config) ->
ok
end.
+%% This testcase checks that receive trace works on exit signal messages
+%% when the sender of the exit signal is the process itself.
+trap_exit_self_receive(Config) ->
+ Parent = self(),
+ Proc = spawn_link(fun() -> process(Parent) end),
+
+ 1 = erlang:trace(Proc, true, ['receive']),
+ Proc ! {trap_exit_please, true},
+ {trace, Proc, 'receive', {trap_exit_please, true}} = receive_first_trace(),
+
+ %% Make the process call exit(self(), signal)
+ Reason1 = make_ref(),
+ Proc ! {exit_signal_please, Reason1},
+ {trace, Proc, 'receive', {exit_signal_please, Reason1}} = receive_first_trace(),
+ {trace, Proc, 'receive', {'EXIT', Proc, Reason1}} = receive_first_trace(),
+ receive {Proc, {'EXIT', Proc, Reason1}} -> ok end,
+ receive_nothing(),
+
+ unlink(Proc),
+ Reason2 = make_ref(),
+ Proc ! {exit_please, Reason2},
+ {trace, Proc, 'receive', {exit_please, Reason2}} = receive_first_trace(),
+ receive_nothing(),
+ ok.
+
drop_trace_until_down(Proc, Mon) ->
drop_trace_until_down(Proc, Mon, false, 0, 0).
@@ -1791,6 +1860,9 @@ process(Dest) ->
process(Dest);
{exit_please, Reason} ->
exit(Reason);
+ {exit_signal_please, Reason} ->
+ exit(self(), Reason),
+ process(Dest);
{trap_exit_please, State} ->
process_flag(trap_exit, State),
process(Dest);
diff --git a/erts/emulator/test/tracer_SUITE.erl b/erts/emulator/test/tracer_SUITE.erl
index ab7d047bc3..e1362ef07a 100644
--- a/erts/emulator/test/tracer_SUITE.erl
+++ b/erts/emulator/test/tracer_SUITE.erl
@@ -30,7 +30,8 @@
-export([load/1, unload/1, reload/1, invalid_tracers/1]).
-export([send/1, recv/1, call/1, call_return/1, spawn/1, exit/1,
link/1, unlink/1, getting_linked/1, getting_unlinked/1,
- register/1, unregister/1, in/1, out/1, gc_start/1, gc_end/1]).
+ register/1, unregister/1, in/1, out/1, gc_start/1, gc_end/1,
+ seq_trace/1]).
suite() -> [{ct_hooks,[ts_install_cth]},
{timetrap, {minutes, 1}}].
@@ -41,7 +42,8 @@ all() ->
groups() ->
[{ basic, [], [send, recv, call, call_return, spawn, exit,
link, unlink, getting_linked, getting_unlinked,
- register, unregister, in, out, gc_start, gc_end]}].
+ register, unregister, in, out, gc_start, gc_end,
+ seq_trace]}].
init_per_suite(Config) ->
erlang:trace_pattern({'_','_','_'}, false, [local]),
@@ -583,6 +585,24 @@ gc_end(_Config) ->
test(gc_major_end, garbage_collection, Tc, Expect, false).
+seq_trace(_Config) ->
+
+ seq_trace:set_system_tracer({tracer_test,
+ {#{ seq_trace => trace }, self(), []}}),
+ erlang:spawn(fun() ->
+ seq_trace:set_token(label,17),
+ seq_trace:set_token(print,true),
+ seq_trace:print(17,"**** Trace Started ****")
+ end),
+ receive
+ {seq_trace, _, 17, {print, _, _, _, _}, _} ->
+ ok;
+ M ->
+ ct:fail("~p~n",[M])
+ after 100 ->
+ ct:fail(timeout)
+ end.
+
test(Event, Tc, Expect) ->
test(Event, Tc, Expect, false).
test(Event, Tc, Expect, Removes) ->
diff --git a/erts/emulator/test/z_SUITE.erl b/erts/emulator/test/z_SUITE.erl
index ac3df8bfbf..103f9f1550 100644
--- a/erts/emulator/test/z_SUITE.erl
+++ b/erts/emulator/test/z_SUITE.erl
@@ -37,6 +37,7 @@
-export([schedulers_alive/1, node_container_refc_check/1,
long_timers/1, pollset_size/1,
check_io_debug/1, get_check_io_info/0,
+ lc_graph/1,
leaked_processes/1]).
suite() ->
@@ -46,6 +47,7 @@ suite() ->
all() ->
[schedulers_alive, node_container_refc_check,
long_timers, pollset_size, check_io_debug,
+ lc_graph,
%% Make sure that the leaked_processes/1 is always
%% run last.
leaked_processes].
@@ -289,6 +291,12 @@ has_gethost([P|T]) ->
has_gethost([]) ->
false.
+lc_graph(Config) when is_list(Config) ->
+ %% Create "lc_graph" file in current working dir
+ %% if lock checker is enabled
+ erts_debug:lc_graph(),
+ ok.
+
leaked_processes(Config) when is_list(Config) ->
%% Replace the defualt timetrap with a timetrap with
%% known pid.
diff --git a/erts/emulator/utils/beam_emu_vars b/erts/emulator/utils/beam_emu_vars
new file mode 100755
index 0000000000..c798a4dada
--- /dev/null
+++ b/erts/emulator/utils/beam_emu_vars
@@ -0,0 +1,122 @@
+#!/usr/bin/perl -w
+use strict;
+
+# Analyse beam_emu.s and try to find out the registers
+# used for the important variables in process_main().
+#
+# Works for .s files from clang or gcc. For gcc, the -fverbose-asm
+# option must be used.
+#
+# Example:
+#
+# $ beam-emu-vars -vars 'c_p E HTOP FCALLS I reg freg' beam_emu.s
+# E: %r13
+# FCALLS: %rcx:98 %rax:88 16(%rsp):50 %rdi:6
+# HTOP: %r10:382 64(%rsp):88 72(%rsp):9 24(%rsp):7 %rcx:6 %r15:6 80(%rsp):3 88(%rsp):2
+# I: %rbx
+# c_p: %rbp
+# freg: 48(%rsp):11 %rcx:8 %rdi:5 %rax:4
+# reg: %r12
+#
+# That means that E, I, c_p, reg seems to be assigned to permanent registers.
+# HTOP seems to be assigned %r10, but it is saved to a scratch location
+# before any function calls. FCALLS and freg seems to be saved in a location on
+# the stack and loaded into a register when used.
+#
+# The exit status will be 0 if all variables are assigned to registers (most of
+# the time), and 1 if one or more variables are assigned to a stack location.
+
+my $vars = 'c_p E FCALLS freg HTOP I reg';
+
+while (@ARGV and $ARGV[0] =~ /^-(.*)/) {
+ $_ = $1;
+ shift;
+ ($vars = shift), next if /^vars/;
+ die "$0: Bad option: -$_\n";
+}
+
+my @vars = split(" ", $vars);
+my %vars;
+@vars{@vars} = @vars;
+
+my $inside;
+my %count;
+
+if (@ARGV != 1) {
+ usage();
+}
+
+while (<>) {
+ if (!$inside && /[.]globl\s*_?process_main/) {
+ $inside = 1;
+ } elsif ($inside && /[.]globl/) {
+ last;
+ }
+ if ($inside) {
+ if (/##DEBUG_VALUE:\s*process_main:([A-Za-z]*)\s*<-\s*(.*)/) {
+ # clang
+ my($var,$reg) = ($1,$2);
+ next if $reg =~ /^[-\d]+$/; # Ignore if number.
+ $count{$var}->{$reg}++ if $vars{$var};
+ next;
+ }
+
+ # Parse gcc verbose arguments. Comments are marked with
+ # one '#' (clang marks its comments with two '#').
+ my($src,$dst,$comment) = /movq\s+([^#]+), ([^#]+)#(?!#)\s*(.*)/;
+ next unless $comment;
+ $dst =~ s/\s*$//;
+ my($vsrc,$vdst) = split /,/, $comment, 2;
+ $vdst =~ s/^\s//;
+ update_count(\%count, $vsrc, $src);
+ update_count(\%count, $vdst, $dst);
+ if ($vars{$vdst} and $vsrc eq '%sfp') {
+ $count{$vdst}->{$src}++;
+ }
+ }
+}
+
+my @first;
+
+OUTER:
+for my $var (sort keys %count) {
+ my $total = 0;
+
+ foreach my $reg (keys %{$count{$var}}) {
+ $total += $count{$var}->{$reg}++;
+ }
+
+ foreach my $reg (keys %{$count{$var}}) {
+ if ($count{$var}->{$reg} > 0.9*$total) {
+ print "$var: $reg\n";
+ push @first, $var;
+ next OUTER;
+ }
+ }
+
+ my @r;
+ foreach my $reg (keys %{$count{$var}}) {
+ push @r, $reg;
+ }
+ @r = sort { $count{$var}->{$b} <=> $count{$var}->{$a} } @r;
+ @r = map { "$_:$count{$var}->{$_}" } @r;
+ push @first, $r[0];
+ print "$var: ", join(' ', @r), "\n";
+}
+
+foreach (@first) {
+ exit 1 if /%rsp/;
+}
+exit 0;
+
+sub update_count {
+ my($count_ref,$var,$reg) = @_;
+ return unless $vars{$var};
+ ${${$count_ref}{$var}}{$reg}++;
+}
+
+sub usage {
+ die qq[usage: beam_emu_vars [ -vars "var1 var2..." ] <filename>.s\n\n] .
+ "The exit status is 0 if all variables are assigned to registers,\n" .
+ "and 1 if one or more variables are allocated to a stack location.\n";
+}
diff --git a/erts/emulator/utils/beam_makeops b/erts/emulator/utils/beam_makeops
index bb31db7eb5..da994fae3e 100755
--- a/erts/emulator/utils/beam_makeops
+++ b/erts/emulator/utils/beam_makeops
@@ -19,12 +19,24 @@
# %CopyrightEnd%
#
use strict;
-use vars qw($BEAM_FORMAT_NUMBER);
+use vars qw($BEAM_FORMAT_NUMBER $GC_REGEXP);
use constant COLD => 0;
use constant WARM => 1;
use constant HOT => 2;
+# Instructions for packing
+use constant PACK_JUMP => 1;
+use constant PACK_IN_INSTR_WORD => 2;
+use constant PACK_OPT_IN_INSTR_WORD => 4;
+
+# Packing commands
+use constant PACK_CMD_TIGHTEST => '1';
+use constant PACK_CMD_TIGHT => '2';
+use constant PACK_CMD_LOOSE => '3';
+use constant PACK_CMD_WIDE => '4';
+
$BEAM_FORMAT_NUMBER = undef;
+$GC_REGEXP = undef;
my $target = \&emulator_output;
my $outdir = "."; # Directory for output files.
@@ -32,30 +44,15 @@ my $verbose = 0;
my $hotness = 1;
my $num_file_opcodes = 0;
my $wordsize = 32;
-my %defs; # Defines (from command line).
+my $code_pointers_are_short = 0; # Whether code pointers (to C code) are short.
+my $code_model = 'unknown';
+my %defs; # Defines (from command line).
# This is shift counts and mask for the packer.
my $WHOLE_WORD = '';
-my @pack_instr;
-my @pack_shift;
-my @pack_mask;
-
-$pack_instr[2] = ['6', 'i'];
-$pack_instr[3] = ['0', '0', 'i'];
-$pack_instr[4] = ['6', '6', '6', 'i']; # Only for 64 bit wordsize
-
-$pack_shift[2] = ['0', 'BEAM_LOOSE_SHIFT'];
-$pack_shift[3] = ['0', 'BEAM_TIGHT_SHIFT', '(2*BEAM_TIGHT_SHIFT)'];
-$pack_shift[4] = ['0', 'BEAM_LOOSE_SHIFT', # Only for 64 bit wordsize
- '(2*BEAM_LOOSE_SHIFT)',
- '(3*BEAM_LOOSE_SHIFT)'];
-
-$pack_mask[2] = ['BEAM_LOOSE_MASK', $WHOLE_WORD];
-$pack_mask[3] = ['BEAM_TIGHT_MASK', 'BEAM_TIGHT_MASK', 'BEAM_TIGHT_MASK'];
-$pack_mask[4] = ['BEAM_LOOSE_MASK', # Only for 64 bit wordsize
- 'BEAM_LOOSE_MASK',
- 'BEAM_LOOSE_MASK',
- $WHOLE_WORD];
+
+my @basic_pack_options = (0);
+my @extended_pack_options = @basic_pack_options;
# There are two types of instructions: generic and specific.
# The generic instructions are those generated by the Beam compiler.
@@ -80,7 +77,14 @@ my %gen_opnum;
my %num_specific;
my %gen_to_spec;
my %specific_op;
-my %group_size; # Group size for specific operators.
+
+# The following hashes are used for error checking.
+my %print_name;
+my %specific_op_arity;
+
+# Information about each specific operator. Key is the print name (e.g. get_list_xxy).
+# Value is a hash.
+my %spec_op_info;
my %gen_arity;
my @gen_arity;
@@ -132,7 +136,10 @@ my $loader_types = "nprvlqo";
my $genop_types = $compiler_types . $loader_types;
#
-# Defines the argument types and their loaded size assuming no packing.
+# Define the operand types and their loaded size assuming no packing.
+#
+# Those are the types that can be used in the definition of a specific
+# instruction.
#
my %arg_size = ('r' => 0, # x(0) - x register zero
'x' => 1, # x(N), N > 0 - x register
@@ -155,12 +162,35 @@ my %arg_size = ('r' => 0, # x(0) - x register zero
'A' => 1, # arity value
'P' => 1, # byte offset into tuple or stack
'Q' => 1, # like 'P', but packable
- 'h' => 1, # character
+ 'h' => 1, # character (not used)
'l' => 1, # float reg
'q' => 1, # literal term
);
#
+# Define the types that may be used in a transformation rule.
+#
+# %pattern_type defines the types that may be used in a pattern
+# on the left side.
+#
+# %construction_type defines the types that may be used when
+# constructing a new instruction on the right side (a subset of
+# the pattern types that are possible to construct).
+#
+my $pattern_types = "acdfjilnopqsuxy";
+my %pattern_type;
+@pattern_type{split("", $pattern_types)} = (1) x length($pattern_types);
+
+my %construction_type;
+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') {
+ delete $construction_type{$makes_no_sense};
+}
+
+#
# Generate bits.
#
my %type_bit;
@@ -195,7 +225,8 @@ sub define_type_bit {
define_type_bit('S', $type_bit{'d'});
define_type_bit('j', $type_bit{'f'} | $type_bit{'p'});
- # Aliases (for matching purposes).
+ # Aliases of 'u'. Those specify how to load the operand and
+ # what kind of packing can be done.
define_type_bit('t', $type_bit{'u'});
define_type_bit('I', $type_bit{'u'});
define_type_bit('W', $type_bit{'u'});
@@ -247,6 +278,7 @@ while (@ARGV && $ARGV[0] =~ /^-(.*)/) {
($target = \&compiler_output), next if /^compiler/;
($outdir = shift), next if /^outdir/;
($wordsize = shift), next if /^wordsize/;
+ ($code_model = shift), next if /^code-model/;
($verbose = 1), next if /^v/;
($defs{$1} = $2), next if /^D(\w+)=(\w+)/;
die "$0: Bad option: -$_\n";
@@ -258,23 +290,36 @@ if ($wordsize == 32) {
} elsif ($wordsize == 64) {
$defs{'ARCH_32'} = 0;
$defs{'ARCH_64'} = 1;
+ $code_pointers_are_short = $code_model eq 'small';
}
#
-# Initialize number of arguments per packed word.
+# Initialize pack options.
#
if ($wordsize == 64) {
- $pack_mask[3] = ['BEAM_TIGHT_MASK', 'BEAM_TIGHT_MASK', $WHOLE_WORD];
+ @basic_pack_options = (0,PACK_JUMP);
+ @extended_pack_options = @basic_pack_options;
+ if ($code_pointers_are_short) {
+ foreach (@basic_pack_options) {
+ push @extended_pack_options, $_ | PACK_IN_INSTR_WORD;
+ }
+ }
}
#
# Add placeholders for built-in macros.
#
-$c_code{'IS_PACKED'} = ['$Expr',"built-in macro",('Expr')];
-$c_code{'ARG_POSITION'} = ['$Expr',"built-in macro",('Expr')];
-foreach my $name (keys %c_code) {
+my %predef_macros =
+ (OPERAND_POSITION => ['Expr'],
+ IF => ['Expr','IfTrue','IfFalse'],
+ REFRESH_GEN_DEST => [],
+ );
+foreach my $name (keys %predef_macros) {
+ my @args = @{$predef_macros{$name}};
+ my $body = join(':', map { '$' . $_ } @args);
+ $c_code{$name} = [$body,"built-in macro",@args],
$c_code_used{$name} = 1;
}
@@ -352,8 +397,10 @@ while (<>) {
#
if (/^([\w_][\w\d_]+)=(.*)/) {
no strict 'refs';
- my($name) = $1;
- $$name = $2;
+ my $name = $1;
+ my $value = $2;
+ $value =~ s/;\s*$//;
+ $$name = $value;
next;
}
@@ -447,15 +494,7 @@ while (<>) {
# Parse specific instructions (only present in emulator/loader):
# Name Arg1 Arg2...
#
- my($name, @args) = split;
- error("too many operands")
- if @args > $max_spec_operands;
- syntax_check($name, @args);
- my $arity = @args;
- if (defined $gen_opnum{$name,$arity} and $obsolete[$gen_opnum{$name,$arity}]) {
- error("specific instructions may not be specified for obsolete instructions");
- }
- save_specific_ops($name, $arity, $hotness, @args);
+ my($name,$arity) = parse_specific_op($_);
if (defined $op_num) {
error("specific instructions must not be numbered");
} elsif (!defined($gen_arity{$name}) && !defined($unnumbered{$name,$arity})) {
@@ -523,6 +562,36 @@ sub emulator_output {
my $key; # Loop variable.
#
+ # Generate code and meta information for all instructions.
+ #
+ foreach $key (keys %specific_op) {
+ foreach (@{$specific_op{$key}}) {
+ my($name, $hotness, @args) = @$_;
+ my $print_name = print_name($name, @args);
+
+ my($size, $code, $pack_spec) = cg_basic(name => $name, args => \@args);
+ if (defined $code) {
+ $code = "OpCase($print_name):\n$code";
+ push @generated_code, [$hotness,$code,($print_name)];
+ }
+
+ # Note: Some of the information below will be modified
+ # for combined instructions.
+ my %info = ('size' => $size,
+ 'pack_spec' => $pack_spec,
+ 'adj' => 0,
+ 'args' => \@args);
+ $spec_op_info{$print_name} = \%info;
+ }
+ }
+
+ #
+ # Combine micro instruction into instruction blocks and generate
+ # code for them.
+ #
+ combine_micro_instructions();
+
+ #
# Information about opcodes (beam_opcodes.c).
#
$name = "$outdir/beam_opcodes.c";
@@ -551,14 +620,9 @@ sub emulator_output {
print "\n";
#
- # Combine micro instruction into instruction blocks.
- #
- combine_micro_instructions();
-
- #
# Generate code for specific ops.
#
- my($spec_opnum) = 0;
+ my $spec_opnum = 0;
print "const OpEntry opc[] = {\n";
foreach $key (sort keys %specific_op) {
$gen_to_spec{$key} = $spec_opnum;
@@ -573,38 +637,26 @@ sub emulator_output {
foreach (@{$specific_op{$key}}) {
my($name, $hot, @args) = @{$_};
my($sign) = join('', @args);
+ $sign =~ s/[?]//g;
# The primitive types should sort before other types.
- my($sort_key) = $sign;
+ my $sort_key = $sign;
eval "\$sort_key =~ tr/$genop_types/./";
$sort_key .= ":$sign";
- $items{$sort_key} = [$name, $hot, $sign, @args];
+ my $print_name = print_name($name, @args);
+ $items{$sort_key} = $print_name;
}
#
# Now call the generator for the sorted result.
#
- foreach (sort keys %items) {
- my($name, $hot, $sign, @args) = @{$items{$_}};
+ foreach my $sort_key (sort keys %items) {
+ my $print_name = $items{$sort_key};
+ my $info = $spec_op_info{$print_name};
+ my(@args) = @{$info->{'args'}};
+ @args = map { s/[?]$//; $_ } @args;
my $arity = @args;
- my($instr) = "${name}_$sign";
- $instr =~ s/_$//;
-
- #
- # Call a generator to calculate size and generate macros
- # for the emulator.
- #
- my($size, $code, $pack) =
- basic_generator($name, 1, '', 0, undef, @args);
-
- #
- # Save the generated $code for later.
- #
- if (defined $code) {
- $code = "OpCase($instr):\n$code";
- push @generated_code, [$hot,$code,($instr)];
- }
#
# Calculate the bit mask which should be used to match this
@@ -626,7 +678,6 @@ sub emulator_output {
}
printf "/* %3d */ ", $spec_opnum;
- my $print_name = $sign ne '' ? "${name}_$sign" : $name;
my $init = "{";
my $sep = "";
foreach (@bits) {
@@ -634,12 +685,12 @@ sub emulator_output {
$sep = ",";
}
$init .= "}";
- my $adj = 0;
- if (defined $group_size{$print_name}) {
- $adj = $size - $group_size{$print_name};
- }
- init_item($print_name, $init, $involves_r, $size, $adj, $pack, $sign);
- $op_to_name[$spec_opnum] = $instr;
+ my $adj = $info->{'adj'};
+ my $size = $info->{'size'};
+ my $pack_spec = $info->{'pack_spec'};
+ my $sign = join '', @args;
+ init_item($print_name, $init, $involves_r, $size, $adj, $pack_spec, $sign);
+ $op_to_name[$spec_opnum] = $print_name;
$spec_opnum++;
}
}
@@ -718,12 +769,19 @@ sub emulator_output {
print "#if !defined(ARCH_64)\n";
print qq[ #error "64-bit architecture assumed, but ARCH_64 not defined"\n];
print "#endif\n";
+ if ($code_pointers_are_short) {
+ print "#if !defined(CODE_MODEL_SMALL)\n";
+ print qq[ #error "small code model assumed, but CODE_MODEL_SMALL not defined"\n];
+ print "#endif\n";
+ }
print "#define BEAM_WIDE_MASK 0xFFFFFFFFull\n";
print "#define BEAM_LOOSE_MASK 0xFFFFull\n";
print "#define BEAM_TIGHT_MASK 0xFFFFull\n";
+ print "#define BEAM_TIGHTEST_MASK 0x3FFull\n";
print "#define BEAM_WIDE_SHIFT 32\n";
print "#define BEAM_LOOSE_SHIFT 16\n";
print "#define BEAM_TIGHT_SHIFT 16\n";
+ print "#define BEAM_TIGHTEST_SHIFT 10\n";
}
print "\n";
@@ -835,6 +893,13 @@ sub emulator_output {
print_code(COLD);
}
+sub print_name {
+ my($name,@args) = @_;
+ my $sign = join '', @args;
+ $sign =~ s/[?]//g;
+ $sign ne '' ? "${name}_$sign" : $name;
+}
+
sub init_item {
my($sep) = "";
@@ -951,40 +1016,70 @@ sub compiler_output {
}
#
-# Check an operation for validity.
+# Parse and store a specific operation.
#
-sub syntax_check {
- my($name, @args) = @_;
- my($i);
+sub parse_specific_op {
+ my($name, @args) = split " ", shift;
+ my $arity = @args;
+ # Check for various errors.
error("Bad opcode name '$name'")
unless $name =~ /^[a-z][\w\d_]*$/;
- for ($i = 0; $i < @args; $i++) {
- foreach my $type (split(//, $args[$i])) {
+ error("too many operands")
+ if @args > $max_spec_operands;
+ for (my $i = 0; $i < $arity; $i++) {
+ my $arg = $args[$i];
+ $arg =~ s/[?]$//;
+ foreach my $type (split(//, $arg)) {
error("Argument " . ($i+1) . ": invalid type '$type'")
unless defined $arg_size{$type};
}
}
-}
-
-sub save_specific_ops {
- my($name,$arity,$hot,@args) = @_;
- my(@res) = ("");
+ if (defined $gen_opnum{$name,$arity} and $obsolete[$gen_opnum{$name,$arity}]) {
+ error("specific instructions may not be specified for obsolete instructions");
+ }
+ # Expand operands with multiple types to multiple instructions.
+ # (For example, "get_list xy xy xy" will be expanded to six instructions.)
+ my @res = ([]);
foreach my $arg (@args) {
- my @new_res = ();
+ my @old_res = @res;
+ @res = ();
+ my $marker = ($arg =~ s/[?]$//) ? '?' : '';
foreach my $type (split(//, $arg)) {
- foreach my $args (@res) {
- push @new_res, "$args$type";
+ foreach my $args_ref (@old_res) {
+ my @args = @$args_ref;
+ push @args, "$type$marker";
+ push @res, \@args;
}
}
- @res = @new_res;
}
+
+ # Store each specific instruction.
my $key = "$name/$arity";
- foreach my $args (@res) {
- @args = split //, $args;
- push @{$specific_op{$key}}, [$name,$hot,@args];
+ foreach my $args_ref (@res) {
+ @args = @$args_ref;
+ my $arity = @args;
+ my $loc = "$ARGV($.)";
+ if (defined $specific_op_arity{$name}) {
+ my($prev_arity,$loc) = @{$specific_op_arity{$name}};
+ if ($arity != $prev_arity) {
+ error("$name defined with arity $arity, " .
+ "but previously defined with arity $prev_arity at $loc");
+ }
+ }
+ $specific_op_arity{$name} = [$arity,$loc];
+ my $print_name = print_name($name, @args);
+ if (defined $print_name{$print_name}) {
+ error("$name @args: already defined at " .
+ $print_name{$print_name});
+ }
+ $print_name{$print_name} = $loc;
+ push @{$specific_op{$key}}, [$name,$hotness,@args];
}
+
+ # Done.
+ ($name,$arity);
}
sub parse_c_args {
@@ -1090,8 +1185,27 @@ sub combine_instruction_group {
# Variables.
my %offsets;
my @instrs;
- my %num_references;
- my $group_size = 0;
+ my %num_references; # Number of references from other sub instructions.
+ my $group_size = 999;
+
+ #
+ # Calculate the number of references from other sub instructions.
+ # This number is useful in several ways:
+ #
+ # * If this number is 0, it is only used as the entry point for a
+ # function, implying that it does not need a label and that operands
+ # can be packed into the instruction word.
+ #
+ # * We'll use this number in the sort key, as a tie breaker for sub instructions
+ # at the same instruction offset.
+ #
+ foreach my $ref_instr (@in_instrs) {
+ my(undef,undef,$first_sub,@other_subs) = @$ref_instr;
+ $num_references{$first_sub} += 0; # Make sure it is defined.
+ foreach my $sub (@other_subs) {
+ $num_references{$sub}++;
+ }
+ }
# Do basic error checking. Associate operands of instructions
# with the correct micro instructions. Calculate offsets for micro
@@ -1108,8 +1222,9 @@ sub combine_instruction_group {
my $offset = 0;
my @rest = @args;
my @new_subs;
- my $opcase = $specific;
- $opcase .= "_" . join '', @args if @args;
+ my $print_name = print_name($specific, @args);
+ my $opcase = $print_name;
+ my $last = $subs[$#subs];
foreach my $s (@subs) {
my $code = $c_code{$s};
my(undef,undef,@c_args) = @{$code};
@@ -1117,16 +1232,18 @@ sub combine_instruction_group {
foreach (0..$#c_args) {
push @first, shift @rest;
}
- my($size,undef) = basic_generator($s, 0, '', 0, undef, @first);
+ my $size = cg_combined_size(name => $s,
+ first => $num_references{$s} == 0,
+ args => \@first);
$offsets{$s} = $offset
- unless defined $offsets{$s} and $offsets{$s} >= $offset;
+ unless defined $offsets{$s} and $offsets{$s} < $offset;
$offset += $size - 1;
my $label = micro_label($s);
- $num_references{$label} = 0;
push @new_subs, [$opcase,$label,$s,$size-1,@first];
$opcase = '';
}
- $group_size = $offset if $group_size < $offset;
+ $spec_op_info{$print_name}->{'size'} = $offset + 1;
+ $group_size = $offset if $group_size >= $offset;
push @instrs, [$specific_key,@new_subs];
}
}
@@ -1140,9 +1257,8 @@ sub combine_instruction_group {
my($opcase,$label,$s,$size,@args) = @{$subs[$i]};
my $next = '';
(undef,$next) = @{$subs[$i+1]} if $i < $#subs;
- $num_references{$next}++ if $next;
my $instr_info = "$opcase:$label:$next:$s:$size:@args";
- push @all_instrs, [$label,$offsets{$s},$instr_info];
+ push @all_instrs, [$label,$s,$offsets{$s},$instr_info];
}
}
@@ -1150,8 +1266,8 @@ sub combine_instruction_group {
my %label_to_offset;
my %order_to_offset;
foreach my $instr (@all_instrs) {
- my($label,$offset,$instr_info) = @$instr;
- my $sort_key = sprintf("%02d.%02d", $offset, $num_references{$label});
+ my($label,$s,$offset,$instr_info) = @$instr;
+ my $sort_key = sprintf("%02d.%02d", $offset, $num_references{$s});
push @{$order_to_instrs{$sort_key}}, $instr_info;
$label_to_offset{$label} = $offset;
$order_to_offset{$sort_key} = $offset;
@@ -1162,6 +1278,8 @@ sub combine_instruction_group {
# Now generate the code for the entire group.
my $offset = 0;
my @opcase_labels;
+ my %down;
+ my %up;
for(my $i = 0; $i < @slots; $i++) {
my $key = $slots[$i];
@@ -1182,36 +1300,69 @@ sub combine_instruction_group {
my $seen_key = "$label:$next:" . scalar(@first);
next if $opcase eq '' and $seen{$seen_key};
$seen{$seen_key} = 1;
+ $seen_key .= $opcase;
if ($opcase ne '') {
$gcode .= "OpCase($opcase):\n";
push @opcase_labels, $opcase;
- $group_size{$opcase} = $group_size + 1;
}
- if ($num_references{$label}) {
+ if ($num_references{$s}) {
$gcode .= "$label:\n";
}
my $flags = '';
my $transfer_to_next = '';
- my $dec = 0;
+ my $inc = 0;
unless ($i == $#slots) {
$flags = "-no_next";
my $next_offset = $label_to_offset{$next};
- $dec = $next_offset - ($offset + $size);
- $transfer_to_next = "I -= $dec;\n" if $dec;
+ $inc = ($offset + $size) - $next_offset;
+ $transfer_to_next = "I += $inc;\n" if $inc;
$transfer_to_next .= "goto $next;\n\n";
}
- my(undef,$gen_code) =
- basic_generator($s, 0, $flags, $offset,
- $group_size-$offset-$dec, @first);
+ my($gen_code,$down,$up) =
+ cg_combined_code(name => $s,
+ first => $num_references{$s} == 0,
+ extra_comments => $flags,
+ offset => $offset,
+ comp_size => $group_size-$offset,
+ inc => $inc,
+ args =>\@first);
+ my $spec_label = "$opcase$label";
+ $down{$spec_label} = $down;
+ $up{$spec_label} = $up;
$gcode .= $gen_code . $transfer_to_next;
}
$offset = $order_to_offset{$slots[$i+1]} if $i < $#slots;
}
+ foreach my $print_name (@opcase_labels) {
+ my $info = $spec_op_info{$print_name};
+ $info->{'adj'} = $info->{'size'} - $group_size - 1;
+ }
+
+ #
+ # Assemble pack specifications for all instructions in the group.
+ #
+ foreach my $instr (@instrs) {
+ my(undef,@subs) = @{$instr};
+ my $down = '';
+ my $up = '';
+ for (my $i = 0; $i < @subs; $i++) {
+ my($opcase,$label) = @{$subs[$i]};
+ my $spec_label = "$opcase$label";
+ if (defined $down{$spec_label}) {
+ $down = $down{$spec_label} . $down;
+ $up = $up . $up{$spec_label};
+ }
+ }
+ my $print_name = $subs[0]->[0];
+ my $info = $spec_op_info{$print_name};
+ $info->{'pack_spec'} = build_pack_spec("$down:$up");
+ }
+
($group_hotness,"{\n$gcode\n}\n\n",@opcase_labels);
}
@@ -1223,12 +1374,59 @@ sub micro_label {
#
-# Basic implementation of instruction in emulator loop
-# (assuming no packing).
+# Basic code generation for one instruction.
+#
+
+sub cg_basic {
+ my %params = (@_, pack_options => \@extended_pack_options);
+ my($size,$code,$pack_spec) = code_gen(%params);
+ $pack_spec = build_pack_spec($pack_spec);
+ ($size,$code,$pack_spec);
+}
+
+#
+# Calculate size for a micro instruction.
+#
+
+sub cg_combined_size {
+ my %params = (@_,
+ pack_options => \@basic_pack_options,
+ size_only => 1);
+ $params{pack_options} = \@extended_pack_options
+ if $params{first};
+ my($size) = code_gen(%params);
+ $size;
+}
+
+#
+# Generate code for a micro instruction.
#
-sub basic_generator {
- my($name,$hot,$extra_comments,$offset,$group_size,@args) = @_;
+sub cg_combined_code {
+ my %params = (@_, pack_options => \@basic_pack_options);
+ $params{pack_options} = \@extended_pack_options
+ if $params{first};
+ my($size,$code,$pack_spec) = code_gen(%params);
+ if ($pack_spec eq '') {
+ ($code,'','');
+ } else {
+ my($down,$up) = split /:/, $pack_spec;
+ ($code,$down,$up);
+ }
+}
+
+sub code_gen {
+ my %params = (extra_comments => '',
+ offset => 0,
+ inc => 0,
+ size_only => 0,
+ @_);
+ my $name = $params{name};
+ my $extra_comments = $params{extra_comments};
+ my $offset = $params{offset};
+ my $inc = $params{inc};
+ my @args = @{$params{args}};
+
my $size = 0;
my $flags = '';
my @f;
@@ -1242,8 +1440,9 @@ sub basic_generator {
#
my $c_code_ref = $c_code{$name};
- if ($hot and defined $c_code_ref and $name ne 'catch') {
- ($var_decls, $pack_spec, @args) = do_pack(@args);
+ if (defined $c_code_ref and $name ne 'catch') {
+ my $pack_options = $params{pack_options};
+ ($var_decls, $pack_spec, @args) = do_pack($name, $offset, $pack_options, @args);
}
#
@@ -1253,6 +1452,8 @@ sub basic_generator {
my $need_block = 0;
my $arg_offset = $offset;
+ my $has_gen_dest = 0;
+ @args = map { s/[?]$//g; $_ } @args;
foreach (@args) {
my($this_size) = $arg_size{$_};
SWITCH:
@@ -1262,6 +1463,7 @@ sub basic_generator {
"Eterm* dst_ptr = REG_TARGET_PTR(dst);\n";
push(@f, "*dst_ptr");
$this_size = $1;
+ $has_gen_dest = 1;
last SWITCH;
};
/^packed:[a-zA-z]:(\d):(.*)/ and do {
@@ -1294,6 +1496,7 @@ sub basic_generator {
$var_decls .= "Eterm dst = " . arg_offset($arg_offset) . ";\n" .
"Eterm* dst_ptr = REG_TARGET_PTR(dst);\n";
push(@f, "*dst_ptr");
+ $has_gen_dest = 1;
last SWITCH;
};
defined $arg_size{$_} and do {
@@ -1308,14 +1511,14 @@ sub basic_generator {
}
#
- # If the implementation is in beam_emu.c, there is nothing
- # more to do.
+ # If the implementation is in beam_emu.c or if
+ # the caller only wants the size, we are done.
#
- unless (defined $c_code_ref) {
+ if (not defined $c_code_ref or $params{size_only}) {
return ($size+1, undef, '');
}
- $group_size = $size unless defined $group_size;
+ my $group_size = ($params{comp_size} || $size) + $inc;
#
# Generate main body of the implementation.
@@ -1334,6 +1537,7 @@ sub basic_generator {
$bindings{$var} = $f[$i];
}
$bindings{'NEXT_INSTRUCTION'} = "I+" . ($group_size+$offset+1);
+ $bindings{'IP_ADJUSTMENT'} = $inc;
$c_code = eval { expand_all($c_code, \%bindings) };
unless (defined $c_code) {
warn $@;
@@ -1356,11 +1560,10 @@ sub basic_generator {
"ASSERT(VALID_INSTR(*I));\n" .
"Goto(*I);";
} else {
- $var_decls .= "BeamInstr* _nextpf = " .
- "(BeamInstr *) I[$instr_offset];\n";
+ $var_decls .= "BeamInstr next_pf = BeamCodeAddr(I[$instr_offset]);\n";
$dispatch_next = "\nI += $instr_offset;\n" .
- "ASSERT(VALID_INSTR(_nextpf));\n" .
- "Goto(_nextpf);";
+ "ASSERT(VALID_INSTR(next_pf));\n" .
+ "GotoPF(next_pf);";
}
#
@@ -1376,9 +1579,36 @@ sub basic_generator {
"{",
"$var_decls$body",
"}", "");
+
+ # Make sure that $REFRESH_GEN_DEST() is used when a
+ # general destination ('d') may have been clobbered by
+ # a GC.
+ my $gc_error = verify_gc_code($code, $has_gen_dest);
+ if (defined $gc_error) {
+ warn $gc_error;
+ error("... from the body of $name at $where");
+ }
+
+ # Done.
($size+1, $code, $pack_spec);
}
+sub verify_gc_code {
+ my $code = shift;
+ my $has_gen_dest = shift;
+
+ return unless $has_gen_dest;
+
+ if ($code =~ /$GC_REGEXP/o) {
+ my $code_after_gc = substr($code, $+[0]);
+ unless ($code_after_gc =~ /dst_ptr = REG_TARGET_PTR/) {
+ return "pointer to destination register is invalid after GC -- " .
+ "use \$REFRESH_GEN_DEST()\n";
+ }
+ }
+ return undef;
+}
+
sub arg_offset {
my $offset = shift;
"I[" . ($offset+1) . "]";
@@ -1457,7 +1687,7 @@ sub expand_macro {
my %new_bindings;
# Keep the special, pre-defined bindings.
- foreach my $key (qw(NEXT_INSTRUCTION)) {
+ foreach my $key (qw(NEXT_INSTRUCTION IP_ADJUSTMENT)) {
$new_bindings{$key} = $bindings{$key};
}
@@ -1478,17 +1708,26 @@ sub expand_macro {
}
# Handle built-in macros.
- if ($name eq 'ARG_POSITION') {
+ if ($name eq 'OPERAND_POSITION') {
if ($body =~ /^I\[(\d+)\]$/) {
$body = $1;
} else {
$body = 0;
}
- } elsif ($name eq 'IS_PACKED') {
- $body = ($body =~ /^I\[\d+\]$/) ? 0 : 1;
+ } elsif ($name eq 'IF') {
+ my $expr = $new_bindings{Expr};
+ my $bool = eval $expr;
+ if ($@ ne '') {
+ &error("bad expression '$expr' in \$IF()");
+ }
+ my $part = $bool ? 'IfTrue' : 'IfFalse';
+ $body = $new_bindings{$part};
+ } elsif ($name eq 'REFRESH_GEN_DEST') {
+ $body = "dst_ptr = REG_TARGET_PTR(dst)";
}
- # Wrap body if needed and return resul.t
+
+ # Wrap body if needed and return result.
$body = "do {\n$body\n} while (0)"
if needs_do_wrapper($body);
($body,$rest);
@@ -1519,9 +1758,51 @@ sub needs_do_wrapper {
}
sub do_pack {
- my(@args) = @_;
+ my($name,$offset,$pack_opts_ref,@args) = @_;
+ my @pack_opts = @$pack_opts_ref;
+ my $opt_arg_pos = -1;
+
+ # Look for an optional use operand not as the first argument.
+ if (@args and $args[0] !~ /[?]$/) {
+ for (my $pos = 0; $pos < @args; $pos++) {
+ if ($args[$pos] =~ /[?]$/) {
+ $opt_arg_pos = $pos;
+ last;
+ }
+ }
+ }
+
+ @args = map { s/[?]$//; $_ } @args; # Remove any optional use marker.
+
+ # If there is an optional operand, extend the array of pack options.
+ if ($opt_arg_pos >= 0) {
+ my @new_pack_opts = grep { $_ & PACK_IN_INSTR_WORD } @pack_opts;
+ @new_pack_opts = map {
+ ($_ & ~ PACK_IN_INSTR_WORD) | PACK_OPT_IN_INSTR_WORD;
+ } @new_pack_opts;
+ push @pack_opts, @new_pack_opts;
+ }
+
+ my $ret = ['', ':', @args];
+ my $score = 0;
+
+ foreach my $options (@pack_opts) {
+ my $this_opt_arg_pos = ($options & PACK_OPT_IN_INSTR_WORD) ? $opt_arg_pos : -1;
+ my($this_score,$this_result) =
+ do_pack_one($name, $options, $this_opt_arg_pos, $offset, @args);
+ if ($this_score > $score) {
+ $ret = $this_result;
+ $score = $this_score;
+ }
+ }
+ return @$ret;
+}
+
+sub do_pack_one {
+ my($name,$options,$opt_arg_pos,$offset,@args) = @_;
my($packable_args) = 0;
my @bits_needed; # Bits needed for each argument.
+ my $pack_in_iw = $options & PACK_IN_INSTR_WORD;
#
# Define the minimum number of bits needed for the packable argument types.
@@ -1535,6 +1816,10 @@ sub do_pack {
't' => 16);
if ($wordsize == 64) {
$bits_needed{'I'} = 32;
+ if ($options & PACK_JUMP) {
+ $bits_needed{'f'} = 32;
+ $bits_needed{'j'} = 32;
+ }
}
#
@@ -1547,51 +1832,48 @@ sub do_pack {
} else {
push @bits_needed, 0;
}
- }
-
- #
- # Try to pack 'f' and 'j', but not at expense at worse packing
- # for other operands. For example, given the arguments "f x x", we
- # want the 'x' operands to be packed, not 'f' and 'x' packed and
- # the final 'x' not packed.
- #
-
- if ($wordsize == 64 and $packable_args == 1) {
- for (my $i = 0; $i < @args; $i++) {
- if ($args[$i] =~ /^[fj]$/) {
- $bits_needed[$i] = 32;
- $packable_args++;
- last;
- }
+ if ($arg =~ /^[fj]$/) {
+ # Only pack the first occurrence of 'f' or 'j'.
+ delete $bits_needed{'f'};
+ delete $bits_needed{'j'};
}
}
#
- # Nothing to pack unless there are at least 2 packable arguments.
+ # Return if there is nothing to pack.
#
- return ('', '', @args) if $packable_args < 2;
+ if ($packable_args == 0) {
+ return (-1);
+ } elsif ($packable_args == 1 and $options == 0) {
+ return (-1);
+ }
#
# Determine how many arguments we should pack into each word.
#
my @args_per_word;
my @need_wide_mask;
- my $bits = 0;
- my $word = 0;
- $args_per_word[0] = 0;
- $need_wide_mask[0] = 0;
- for (my $i = 0; $i < @args; $i++) {
- if ($bits_needed[$i]) {
- my $needed = $bits_needed[$i];
-
- my $next_word = sub {
- $word++;
- $args_per_word[$word] = 0;
- $need_wide_mask[$word] = 0;
- $bits = 0;
- };
+ my $bits;
+ my $this_wordsize;
+ my $word = -1;
+
+ my $next_word = sub {
+ $word++;
+ $args_per_word[$word] = 0;
+ $need_wide_mask[$word] = 0;
+ $bits = 0;
+ $this_wordsize = $wordsize;
+ };
+
+ $next_word->();
+ $this_wordsize = 32 if $pack_in_iw;
+ for (my $arg_num = 0; $arg_num < @args; $arg_num++) {
+ my $needed = $bits_needed[$arg_num];
+
+ next unless $needed;
+ next if $arg_num == $opt_arg_pos;
- if ($bits+$needed > $wordsize) { # Does not fit.
+ if ($bits+$needed > $this_wordsize) { # Does not fit.
$next_word->();
}
if ($args_per_word[$word] == 4) { # Can't handle more than 4 args.
@@ -1611,15 +1893,16 @@ sub do_pack {
# Can only pack two things in a word where one
# item is 32 bits. Force the next item into
# the next word.
- $bits = $wordsize;
+ $bits = $this_wordsize;
}
- }
}
#
# Try to balance packing between words.
#
- if ($args_per_word[$#args_per_word] == 1) {
+ if (@args_per_word == 1 and $args_per_word[0] == 1 and $pack_in_iw) {
+ # Don't rebalance.
+ } elsif ($args_per_word[$#args_per_word] == 1) {
if ($args_per_word[$#args_per_word-1] < 3) {
pop @args_per_word;
} else {
@@ -1644,13 +1927,19 @@ sub do_pack {
# beginning).
my $up = ''; # Pack commands (storing back while
# moving forward).
- my $did_some_packing = 0; # Nothing packed yet.
+ my $arg_num = 0; # Number of argument.
- # Skip an unpackable argument.
+ # Skip an unpackable argument. Also handle packing of
+ # an single operand into the instruction word.
my $skip_unpackable = sub {
my($arg) = @_;
- if ($arg_size{$arg} and $did_some_packing) {
+ if ($arg_num == $opt_arg_pos) {
+ my $pack = chr(ord('#') + $arg_num);
+ $down = PACK_CMD_WIDE . "$pack$down";
+ my $unpack = "BeamExtraData(I[0])";
+ $args[$arg_num] = "packed:$arg:0:${arg}b($unpack)";
+ } elsif ($arg_size{$arg}) {
# Save the argument on the pack engine's stack.
my $push = 'g';
if ($type_bit{$arg} & $type_bit{'q'}) {
@@ -1662,11 +1951,6 @@ sub do_pack {
}
$down = "$push${down}";
$up = "${up}p";
- } else {
- # The argument has either zero size (e.g. r(0)),
- # or is to the left of the first packed argument
- # and will never be accessed. No need to do
- # anything.
}
};
@@ -1675,44 +1959,50 @@ sub do_pack {
# the packing engine works from right-to-left, but we must generate
# the instructions from left-to-right because we must calculate
# instruction sizes from left-to-right.
-
- my $arg_num = 0;
for (my $word = 0; $word < @args_per_word; $word++) {
my $ap = 0; # Argument number within word.
my $packed_var = "tmp_packed" . ($word+1);
my $args_per_word = $args_per_word[$word];
- my @shift;
- my @mask;
- my @instr;
-
- if ($need_wide_mask[$word]) {
- @shift = ('0', 'BEAM_WIDE_SHIFT');
- @mask = ('BEAM_WIDE_MASK', $WHOLE_WORD);
- @instr = ('w', 'w');
- } else {
- @shift = @{$pack_shift[$args_per_word]};
- @mask = @{$pack_mask[$args_per_word]};
- @instr = @{$pack_instr[$args_per_word]};
- }
+ my $pack_word_size = ($pack_in_iw && $word == 0) ? 32 : $wordsize;
+
+ my($shref,$mref,$iref,$unpack_suffix) =
+ get_pack_parameters($name, $args_per_word, $pack_word_size,
+ $need_wide_mask[$word]);
+ my @shift = @$shref;
+ my @mask = @$mref;
+ my @instr = @$iref;
while ($ap < $args_per_word) {
my $reg = $args[$arg_num];
my $this_size = $arg_size{$reg};
+
if ($bits_needed[$arg_num]) {
$this_size = 0;
- $did_some_packing = 1;
if ($ap == 0) {
- $pack_prefix .= "Eterm $packed_var = " .
- arg_offset($size) . ";\n";
- $up .= "p";
- $down = "P$down";
- $this_size = 1;
+ my $packed_data;
+ if ($pack_in_iw and $word == 0) {
+ $packed_data = "BeamExtraData(I[0])";
+ if ($args_per_word == 1) {
+ $packed_var = $packed_data;
+ } else {
+ $pack_prefix .= "Eterm $packed_var = $packed_data;\n";
+ }
+ my $pack = chr(ord('#') + $size);
+ $down = "$pack$down";
+ } else {
+ $packed_data = arg_offset($size + $offset);
+ $pack_prefix .= "Eterm $packed_var = $packed_data;\n";
+ $down = "P$down";
+ $up .= "p";
+ $this_size = 1;
+ }
}
$down = "$instr[$ap]$down";
my $unpack = make_unpack($packed_var, $shift[$ap], $mask[$ap]);
- $args[$arg_num] = "packed:$reg:$this_size:$reg" . "b($unpack)";
+ my $macro = "$reg$unpack_suffix";
+ $args[$arg_num] = "packed:$reg:$this_size:$macro($unpack)";
$ap++;
} else {
@@ -1727,12 +2017,107 @@ sub do_pack {
# Skip any unpackable arguments at the end.
#
while ($arg_num < @args) {
- $skip_unpackable->($args[$arg_num]);
+ my $arg = $args[$arg_num];
+ $skip_unpackable->($arg);
+ $size += $arg_size{$arg};
$arg_num++;
}
- my $pack_spec = $down . $up;
- return ($pack_prefix, $pack_spec, @args);
+ my $pack_spec = "$down:$up";
+ my $score = pack_score($options, @args);
+
+ return ($score, [$pack_prefix,$pack_spec,@args]);
+}
+
+sub get_pack_parameters {
+ my($name,$args_per_word,$pack_word_size,$wide_mask) = @_;
+ my(@shift,@mask,@instr);
+ my $unpack_suffix = 'b';
+
+ if ($wide_mask and $args_per_word > 1) {
+ @shift = ('0', 'BEAM_WIDE_SHIFT');
+ @mask = ('BEAM_WIDE_MASK', $WHOLE_WORD);
+ @instr = (PACK_CMD_WIDE) x 2;
+ } elsif ($args_per_word == 1) {
+ @shift = ('0');
+ @mask = ($WHOLE_WORD);
+ @instr = (PACK_CMD_WIDE);
+ } elsif ($args_per_word == 2) {
+ if ($pack_word_size != $wordsize) {
+ # 64-bit word size, pack 32 bits into instruction word.
+ @shift = ('0', 'BEAM_TIGHT_SHIFT');
+ @mask = ('BEAM_TIGHT_MASK', $WHOLE_WORD);
+ @instr = (PACK_CMD_TIGHT) x 2;
+ } else {
+ # 32/64 bit word size
+ @shift = ('0', 'BEAM_LOOSE_SHIFT');
+ @mask = ('BEAM_LOOSE_MASK', $WHOLE_WORD);
+ @instr = (PACK_CMD_LOOSE) x 2;
+ }
+ } elsif ($args_per_word == 3) {
+ if ($pack_word_size != $wordsize) {
+ # 64-bit word size, pack 3 register numbers into instruction word.
+ @shift = ('0', 'BEAM_TIGHTEST_SHIFT', '(2*BEAM_TIGHTEST_SHIFT)');
+ @mask = ('BEAM_TIGHTEST_MASK', 'BEAM_TIGHTEST_MASK', $WHOLE_WORD);
+ @instr = (PACK_CMD_TIGHTEST) x 3;
+ $unpack_suffix = '';
+ } else {
+ # 32/64 bit word size.
+ @shift = ('0', 'BEAM_TIGHT_SHIFT', '(2*BEAM_TIGHT_SHIFT)');
+ if ($wordsize == 32) {
+ @mask = ('BEAM_TIGHT_MASK') x 3;
+ } elsif ($wordsize == 64) {
+ @mask = ('BEAM_TIGHT_MASK', 'BEAM_TIGHT_MASK', $WHOLE_WORD);
+ }
+ @instr = (PACK_CMD_TIGHT) x 3;
+ }
+ } elsif ($args_per_word == 4) {
+ # 64 bit word size only.
+ @shift = ('0',
+ 'BEAM_LOOSE_SHIFT',
+ '(2*BEAM_LOOSE_SHIFT)',
+ '(3*BEAM_LOOSE_SHIFT)');
+ @mask = ('BEAM_LOOSE_MASK', 'BEAM_LOOSE_MASK',
+ 'BEAM_LOOSE_MASK', $WHOLE_WORD);
+ @instr = (PACK_CMD_LOOSE) x 4;
+ }
+
+ unless (@shift) {
+ error("$name: internal packing error: args_per_word=$args_per_word, " .
+ "pack_word_size=$pack_word_size");
+ }
+
+ (\@shift,\@mask,\@instr,$unpack_suffix);
+}
+
+sub pack_score {
+ my($options,@args) = @_;
+ my $size = 0;
+
+ # Calculate the number of words.
+ foreach (@args) {
+ if (/^packed:[^:]*:(\d+)/) {
+ $size += $1;
+ } else {
+ $size += $arg_size{$_}
+ }
+ }
+
+ # Less numbers of words give a higher score; for the same number of
+ # words, using PACK_JUMP or PACK_IN_INSTR_WORD gives a lower score.
+ my $score = 1 + 10*($max_spec_operands - $size);
+ if (($options & PACK_OPT_IN_INSTR_WORD) != 0) {
+ $score += 4;
+ } elsif ($options == PACK_IN_INSTR_WORD) {
+ $score += 0;
+ } elsif ($options == PACK_JUMP) {
+ $score += 1;
+ } elsif ($options == (PACK_JUMP|PACK_IN_INSTR_WORD)) {
+ $score += 2;
+ } elsif ($options == 0) {
+ $score += 3;
+ }
+ $score;
}
sub make_unpack {
@@ -1744,6 +2129,17 @@ sub make_unpack {
$e;
}
+sub build_pack_spec {
+ my $pack_spec = shift;
+ return '' if $pack_spec eq '';
+ my($down,$up) = split /:/, $pack_spec;
+ while ($down =~ /[gfq]$/ and $up =~ /^p/) {
+ $down = substr($down, 0, -1);
+ $up = substr($up, 1);
+ }
+ "$down$up";
+}
+
sub quote {
local($_) = @_;
return "'$_'" if $_ eq 'try';
@@ -1858,12 +2254,19 @@ sub tr_parse_op {
if (/^([a-z*]+)(.*)/) {
$type = $1;
$_ = $2;
+ error("$type: only a single type is allowed on right side of transformations")
+ if not $src and length($type) > 1;
foreach (split('', $type)) {
- error("bad type in $op")
- unless defined $type_bit{$_} or $type eq '*';
- $_ eq 'r' and
- error("$op: 'r' is not allowed in transformations")
- }
+ next if $src and $type eq '*';
+ error("$op: not a type")
+ unless defined $type_bit{$_};
+ error("$op: the type '$_' is not allowed in transformations")
+ unless defined $pattern_type{$_};
+ if (not $src) {
+ error("$op: type '$_' is not allowed on the right side of transformations")
+ unless defined $construction_type{$_};
+ }
+ }
}
# Get an optional condition. (In source.)
@@ -1896,10 +2299,18 @@ sub tr_parse_op {
}
# Get an optional value. (In destination.)
- $type_val = $type eq 'x' ? 1023 : 0;
+ if ($type eq 'x') {
+ $type_val = 1023;
+ } elsif ($type eq 'a') {
+ $type_val = 'am_Empty';
+ } else {
+ $type_val = 0;
+ }
if (/^=(.*)/) {
- error("value not allowed in source: $op")
+ error("$op: value not allowed in source")
if $src;
+ error("$op: the type 'n' must not be given a value")
+ if $type eq 'n';
$type_val = $1;
$_ = '';
}
@@ -1909,13 +2320,16 @@ sub tr_parse_op {
error("garbage '$_' after operand: $op")
unless /^\s*$/;
- # Test that destination has no conditions.
+ # Check the conditions.
- unless ($src) {
- error("condition not allowed in destination: $op")
+ if ($src) {
+ error("$op: the type '$type' is not allowed to be compared with a literal value")
+ if $cond and not $construction_type{$type};
+ } else {
+ error("$op: condition not allowed in destination")
if $cond;
- error("variable name and type cannot be combined in destination: $op")
- if $var && $type;
+ error("$op: variable name and type cannot be combined in destination")
+ if $var and $type;
}
($var,$type,$type_val,$cond,$cond_val);
diff --git a/erts/emulator/utils/make_driver_tab b/erts/emulator/utils/make_driver_tab
index ffb5f58ebf..b7bca1dc3a 100755
--- a/erts/emulator/utils/make_driver_tab
+++ b/erts/emulator/utils/make_driver_tab
@@ -30,6 +30,7 @@ use File::Basename;
my $file = "";
my $nif = "";
my @emu_drivers = ();
+my @emu_nifs = ();
my @static_drivers = ();
my @static_nifs = ();
my $mode = 1;
@@ -61,7 +62,7 @@ while (@ARGV) {
} elsif ($mode == 2) {
$d = basename $d;
$d =~ s/_nif(\..*|)$//; # strip nif.* or just nif
- push(@static_nifs, $d);
+ push(@emu_nifs, $d);
next;
}
$d = basename $d;
@@ -94,37 +95,33 @@ foreach (@static_drivers) {
}
# The array itself
-print "\nErlDrvEntry *driver_tab[] =\n{\n";
+print "\nErtsStaticDriver driver_tab[] =\n{\n";
foreach (@emu_drivers) {
- print " &${_}driver_entry,\n";
+ print " {&${_}driver_entry, 0},\n";
}
foreach (@static_drivers) {
- print " NULL, /* ${_} */\n";
+ print " {NULL, 1}, /* ${_} */\n";
}
-print " NULL\n};\n";
+print " {NULL}\n};\n";
print "void erts_init_static_drivers() {\n";
my $index = 0;
foreach (@static_drivers) {
- print " driver_tab[".(scalar @emu_drivers+$index)."] = ${_}_driver_init();\n";
+ print " driver_tab[".(scalar @emu_drivers+$index)."].de = ${_}_driver_init();\n";
$index++;
}
print "}\n";
-print <<EOF;
-
-typedef struct ErtsStaticNifEntry_ {
- const char *nif_name;
- ErtsStaticNifInitFPtr nif_init;
-} ErtsStaticNifEntry;
-
-EOF
-
# prototypes
+foreach (@emu_nifs) {
+ my $d = ${_};
+ $d =~ s/\.debug//; # strip .debug
+ print "void *".$d."_nif_init(void);\n";
+}
foreach (@static_nifs) {
my $d = ${_};
$d =~ s/\.debug//; # strip .debug
@@ -134,20 +131,25 @@ foreach (@static_nifs) {
# The array itself
print "static ErtsStaticNifEntry static_nif_tab[] =\n{\n";
+foreach (@emu_nifs) {
+ my $d = ${_};
+ $d =~ s/\.debug//; # strip .debug
+ print " {\"${_}\", &".$d."_nif_init, 0},\n";
+}
foreach (@static_nifs) {
my $d = ${_};
$d =~ s/\.debug//; # strip .debug
- print "{\"${_}\",&".$d."_nif_init},\n";
+ print " {\"${_}\", &".$d."_nif_init, 1},\n";
}
print " {NULL,NULL}\n};\n";
print <<EOF;
-ErtsStaticNifInitFPtr erts_static_nif_get_nif_init(const char *name, int len) {
+ErtsStaticNifEntry* erts_static_nif_get_nif_init(const char *name, int len) {
ErtsStaticNifEntry* p;
for (p = static_nif_tab; p->nif_name != NULL; p++)
if (strncmp(p->nif_name, name, len) == 0 && p->nif_name[len] == 0)
- return p->nif_init;
+ return p;
return NULL;
}