aboutsummaryrefslogtreecommitdiffstats
path: root/erts/emulator/beam
diff options
context:
space:
mode:
Diffstat (limited to 'erts/emulator/beam')
-rw-r--r--erts/emulator/beam/dist.c44
-rw-r--r--erts/emulator/beam/erl_alloc.types1
-rw-r--r--erts/emulator/beam/erl_alloc_util.c8
-rw-r--r--erts/emulator/beam/erl_alloc_util.h7
-rw-r--r--erts/emulator/beam/erl_bif_info.c5
-rw-r--r--erts/emulator/beam/erl_bif_persistent.c530
-rw-r--r--erts/emulator/beam/erl_nif.c11
-rw-r--r--erts/emulator/beam/erl_process.c4
-rw-r--r--erts/emulator/beam/erl_trace.c6
-rw-r--r--erts/emulator/beam/external.c174
-rw-r--r--erts/emulator/beam/external.h15
-rw-r--r--erts/emulator/beam/io.c68
12 files changed, 619 insertions, 254 deletions
diff --git a/erts/emulator/beam/dist.c b/erts/emulator/beam/dist.c
index 30fe13fad3..d15db760a2 100644
--- a/erts/emulator/beam/dist.c
+++ b/erts/emulator/beam/dist.c
@@ -2307,8 +2307,18 @@ erts_dsig_send(ErtsDSigSendContext *ctx)
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);
+ switch (erts_encode_dist_ext_size(ctx->ctl, ctx->flags,
+ ctx->acmp, &ctx->data_size)) {
+ case ERTS_EXT_SZ_OK:
+ break;
+ case ERTS_EXT_SZ_SYSTEM_LIMIT:
+ retval = ERTS_DSIG_SEND_TOO_LRG;
+ goto done;
+ case ERTS_EXT_SZ_YIELD:
+ ERTS_INTERNAL_ERROR("Unexpected yield result");
+ break;
+ }
if (is_non_value(ctx->msg)) {
ctx->phase = ERTS_DSIG_SEND_PHASE_ALLOC;
break;
@@ -2318,17 +2328,31 @@ erts_dsig_send(ErtsDSigSendContext *ctx)
ctx->u.sc.level = 0;
ctx->phase = ERTS_DSIG_SEND_PHASE_MSG_SIZE;
- case ERTS_DSIG_SEND_PHASE_MSG_SIZE:
- if (!ctx->no_trap) {
- if (erts_encode_dist_ext_size_int(ctx->msg, ctx, &ctx->data_size)) {
- retval = ERTS_DSIG_SEND_CONTINUE;
- goto done;
- }
- } else {
- erts_encode_dist_ext_size(ctx->msg, ctx->flags, ctx->acmp, &ctx->data_size);
+ case ERTS_DSIG_SEND_PHASE_MSG_SIZE: {
+ ErtsExtSzRes sz_res;
+ sz_res = (!ctx->no_trap
+ ? erts_encode_dist_ext_size_ctx(ctx->msg,
+ ctx,
+ &ctx->data_size)
+ : erts_encode_dist_ext_size(ctx->msg,
+ ctx->flags,
+ ctx->acmp,
+ &ctx->data_size));
+ switch (sz_res) {
+ case ERTS_EXT_SZ_OK:
+ break;
+ case ERTS_EXT_SZ_SYSTEM_LIMIT:
+ retval = ERTS_DSIG_SEND_TOO_LRG;
+ goto done;
+ case ERTS_EXT_SZ_YIELD:
+ if (ctx->no_trap)
+ ERTS_INTERNAL_ERROR("Unexpected yield result");
+ retval = ERTS_DSIG_SEND_CONTINUE;
+ goto done;
}
ctx->phase = ERTS_DSIG_SEND_PHASE_ALLOC;
+ }
case ERTS_DSIG_SEND_PHASE_ALLOC:
erts_finalize_atom_cache_map(ctx->acmp, ctx->flags);
@@ -3431,6 +3455,8 @@ dist_ctrl_get_data_1(BIF_ALIST_1)
obufsize -= size_obuf(obuf);
if (reds < 0) {
erts_de_runlock(dep);
+ if (obufsize)
+ erts_atomic_add_nob(&dep->qsize, (erts_aint_t) -obufsize);
ERTS_BIF_YIELD1(bif_export[BIF_dist_ctrl_get_data_1],
BIF_P, BIF_ARG_1);
}
diff --git a/erts/emulator/beam/erl_alloc.types b/erts/emulator/beam/erl_alloc.types
index e7329daa2d..92e5069c71 100644
--- a/erts/emulator/beam/erl_alloc.types
+++ b/erts/emulator/beam/erl_alloc.types
@@ -282,6 +282,7 @@ type ENVIRONMENT SYSTEM SYSTEM environment
type PERSISTENT_TERM LONG_LIVED CODE persisten_term
type PERSISTENT_LOCK_Q SHORT_LIVED SYSTEM persistent_lock_q
+type PERSISTENT_TERM_TMP SHORT_LIVED SYSTEM persistent_term_tmp_table
#
# Types used for special emulators
diff --git a/erts/emulator/beam/erl_alloc_util.c b/erts/emulator/beam/erl_alloc_util.c
index 8d4464969a..25ac3bc5af 100644
--- a/erts/emulator/beam/erl_alloc_util.c
+++ b/erts/emulator/beam/erl_alloc_util.c
@@ -6567,6 +6567,14 @@ erts_alcu_start(Allctr_t *allctr, AllctrInit_t *init)
__FILE__, __LINE__);
}
+ /* The various fields packed into the header word must not overlap */
+ ERTS_CT_ASSERT(!(MBC_ABLK_OFFSET_MASK & MBC_ABLK_SZ_MASK));
+ ERTS_CT_ASSERT(!(MBC_ABLK_OFFSET_MASK & BLK_FLG_MASK));
+ ERTS_CT_ASSERT(!(MBC_ABLK_SZ_MASK & BLK_FLG_MASK));
+ ERTS_CT_ASSERT(!(MBC_FBLK_SZ_MASK & BLK_FLG_MASK));
+ ERTS_CT_ASSERT(!(SBC_BLK_SZ_MASK & BLK_FLG_MASK));
+ ERTS_CT_ASSERT(!(CRR_SZ_MASK & CRR_FLG_MASK));
+
if (!initialized)
goto error;
diff --git a/erts/emulator/beam/erl_alloc_util.h b/erts/emulator/beam/erl_alloc_util.h
index ea1afe8f58..b46b311c59 100644
--- a/erts/emulator/beam/erl_alloc_util.h
+++ b/erts/emulator/beam/erl_alloc_util.h
@@ -334,8 +334,11 @@ void erts_alcu_sched_spec_data_init(struct ErtsSchedulerData_ *esdp);
#endif
#if MBC_ABLK_OFFSET_BITS
-# define MBC_ABLK_OFFSET_SHIFT (sizeof(UWord)*8 - MBC_ABLK_OFFSET_BITS)
-# define MBC_ABLK_OFFSET_MASK ((~((UWord)0) << MBC_ABLK_OFFSET_SHIFT) & ~BLK_FLG_MASK)
+/* The shift is reduced by 1 since the highest bit is used for a flag. */
+# define MBC_ABLK_OFFSET_SHIFT (sizeof(UWord)*8 - 1 - MBC_ABLK_OFFSET_BITS)
+# define MBC_ABLK_OFFSET_MASK \
+ (((UWORD_CONSTANT(1) << MBC_ABLK_OFFSET_BITS) - UWORD_CONSTANT(1)) \
+ << MBC_ABLK_OFFSET_SHIFT)
# define MBC_ABLK_SZ_MASK (~MBC_ABLK_OFFSET_MASK & ~BLK_FLG_MASK)
#else
# define MBC_ABLK_SZ_MASK (~BLK_FLG_MASK)
diff --git a/erts/emulator/beam/erl_bif_info.c b/erts/emulator/beam/erl_bif_info.c
index c3d0ea40f6..0339589b79 100644
--- a/erts/emulator/beam/erl_bif_info.c
+++ b/erts/emulator/beam/erl_bif_info.c
@@ -4236,7 +4236,10 @@ BIF_RETTYPE erts_debug_get_internal_state_1(BIF_ALIST_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));
+ Eterm res = erts_term_to_binary(BIF_P, tp[2], 0, dflags);
+ if (is_value(res))
+ BIF_RET(res);
+ BIF_ERROR(BIF_P, SYSTEM_LIMIT);
}
else if (ERTS_IS_ATOM_STR("dist_ctrl", tp[1])) {
Eterm res = am_undefined;
diff --git a/erts/emulator/beam/erl_bif_persistent.c b/erts/emulator/beam/erl_bif_persistent.c
index 5a78a043ce..f38e0cc5cb 100644
--- a/erts/emulator/beam/erl_bif_persistent.c
+++ b/erts/emulator/beam/erl_bif_persistent.c
@@ -37,15 +37,6 @@
#include "erl_binary.h"
/*
- * The limit for the number of persistent terms before
- * a warning is issued.
- */
-
-#define WARNING_LIMIT 20000
-#define XSTR(s) STR(s)
-#define STR(s) #s
-
-/*
* Parameters for the hash table.
*/
#define INITIAL_SIZE 8
@@ -73,14 +64,69 @@ typedef struct trap_data {
Uint memory; /* Used by info/0 to count used memory */
} TrapData;
+typedef enum {
+ ERTS_PERSISTENT_TERM_CPY_PLACE_START,
+ ERTS_PERSISTENT_TERM_CPY_PLACE_1,
+ ERTS_PERSISTENT_TERM_CPY_PLACE_2,
+ ERTS_PERSISTENT_TERM_CPY_PLACE_3
+} ErtsPersistentTermCpyTableLocation;
+
+typedef enum {
+ ERTS_PERSISTENT_TERM_CPY_NO_REHASH = 0,
+ ERTS_PERSISTENT_TERM_CPY_REHASH = 1,
+ ERTS_PERSISTENT_TERM_CPY_TEMP = 2
+} ErtsPersistentTermCpyTableType;
+
+typedef struct {
+ HashTable* old_table; /* in param */
+ Uint new_size; /* in param */
+ ErtsPersistentTermCpyTableType copy_type; /* in param */
+ Uint max_iterations; /* in param */
+ ErtsPersistentTermCpyTableLocation location; /* in/out param */
+ Uint iterations_done; /* in/out param */
+ Uint total_iterations_done; /* in/out param */
+ HashTable* new_table; /* out param */
+} ErtsPersistentTermCpyTableCtx;
+
+typedef enum {
+ PUT2_TRAP_LOCATION_NEW_KEY,
+ PUT2_TRAP_LOCATION_REPLACE_VALUE
+} ErtsPersistentTermPut2TrapLocation;
+
+typedef struct {
+ ErtsPersistentTermPut2TrapLocation trap_location;
+ Eterm key;
+ Eterm term;
+ Uint entry_index;
+ HashTable* hash_table;
+ Eterm heap[3];
+ Eterm tuple;
+ ErtsPersistentTermCpyTableCtx cpy_ctx;
+} ErtsPersistentTermPut2Context;
+
+typedef enum {
+ ERASE1_TRAP_LOCATION_TMP_COPY,
+ ERASE1_TRAP_LOCATION_FINAL_COPY
+} ErtsPersistentTermErase1TrapLocation;
+
+typedef struct {
+ ErtsPersistentTermErase1TrapLocation trap_location;
+ Eterm key;
+ HashTable* old_table;
+ HashTable* new_table;
+ Uint entry_index;
+ Eterm old_term;
+ HashTable* tmp_table;
+ ErtsPersistentTermCpyTableCtx cpy_ctx;
+} ErtsPersistentTermErase1Context;
+
/*
* Declarations of local functions.
*/
static HashTable* create_initial_table(void);
static Uint lookup(HashTable* hash_table, Eterm key);
-static HashTable* copy_table(HashTable* old_table, Uint new_size, int rehash);
-static HashTable* tmp_table_copy(HashTable* old_table);
+static HashTable* copy_table(ErtsPersistentTermCpyTableCtx* ctx);
static int try_seize_update_permission(Process* c_p);
static void release_update_permission(int release_updater);
static void table_updater(void* table);
@@ -127,7 +173,6 @@ static Process* updater_process = NULL;
/* Protected by update_table_permission_mtx */
static ErtsThrPrgrLaterOp thr_prog_op;
-static int issued_warning = 0;
/*
* Queue of hash tables to be deleted.
@@ -139,7 +184,7 @@ static HashTable** delete_queue_tail = &delete_queue_head;
/*
* The following variables are only used during crash dumping. They
- * are intialized by erts_init_persistent_dumping().
+ * are initialized by erts_init_persistent_dumping().
*/
ErtsLiteralArea** erts_persistent_areas;
@@ -188,101 +233,181 @@ void erts_init_bif_persistent_term(void)
&persistent_term_info_trap);
}
+/*
+ * Macro used for trapping in persistent_term_put_2 and
+ * persistent_term_erase_1
+ */
+#define TRAPPING_COPY_TABLE(TABLE_DEST, OLD_TABLE, NEW_SIZE, COPY_TYPE, LOC_NAME, TRAP_CODE) \
+ do { \
+ ctx->cpy_ctx = (ErtsPersistentTermCpyTableCtx){ \
+ .old_table = OLD_TABLE, \
+ .new_size = NEW_SIZE, \
+ .copy_type = COPY_TYPE, \
+ .location = ERTS_PERSISTENT_TERM_CPY_PLACE_START \
+ }; \
+ L_ ## LOC_NAME: \
+ ctx->cpy_ctx.max_iterations = MAX(1, max_iterations); \
+ TABLE_DEST = copy_table(&ctx->cpy_ctx); \
+ iterations_until_trap -= ctx->cpy_ctx.total_iterations_done; \
+ if (TABLE_DEST == NULL) { \
+ ctx->trap_location = LOC_NAME; \
+ erts_set_gc_state(BIF_P, 0); \
+ BUMP_ALL_REDS(BIF_P); \
+ TRAP_CODE; \
+ } \
+ } while (0)
+
+static int persistent_term_put_2_ctx_bin_dtor(Binary *context_bin)
+{
+ ErtsPersistentTermPut2Context* ctx = ERTS_MAGIC_BIN_DATA(context_bin);
+ if (ctx->cpy_ctx.new_table != NULL) {
+ erts_free(ERTS_ALC_T_PERSISTENT_TERM, ctx->cpy_ctx.new_table);
+ release_update_permission(0);
+ }
+ return 1;
+}
+/*
+ * A linear congruential generator that is used in the debug emulator
+ * to trap after a random number of iterations in
+ * persistent_term_put_2 and persistent_term_erase_1.
+ *
+ * https://en.wikipedia.org/wiki/Linear_congruential_generator
+ */
+#define GET_SMALL_RANDOM_INT(SEED) \
+ (1103515245 * (SEED) + 12345) % 227
+
BIF_RETTYPE persistent_term_put_2(BIF_ALIST_2)
{
- Eterm key;
- Eterm term;
- Eterm heap[3];
- Eterm tuple;
- HashTable* hash_table;
- Uint term_size;
- Uint lit_area_size;
- ErlOffHeap code_off_heap;
- ErtsLiteralArea* literal_area;
- erts_shcopy_t info;
- Eterm* ptr;
- Uint entry_index;
+ static const Uint ITERATIONS_PER_RED = 32;
+ ErtsPersistentTermPut2Context* ctx;
+ Eterm state_mref = THE_NON_VALUE;
+ long iterations_until_trap;
+ long max_iterations;
+#define PUT_TRAP_CODE \
+ BIF_TRAP2(bif_export[BIF_persistent_term_put_2], BIF_P, state_mref, BIF_ARG_2)
+#define TRAPPING_COPY_TABLE_PUT(TABLE_DEST, OLD_TABLE, NEW_SIZE, COPY_TYPE, LOC_NAME) \
+ TRAPPING_COPY_TABLE(TABLE_DEST, OLD_TABLE, NEW_SIZE, COPY_TYPE, LOC_NAME, PUT_TRAP_CODE)
+
+#ifdef DEBUG
+ (void)ITERATIONS_PER_RED;
+ iterations_until_trap = max_iterations =
+ GET_SMALL_RANDOM_INT(ERTS_BIF_REDS_LEFT(BIF_P) + (Uint)&ctx);
+#else
+ iterations_until_trap = max_iterations =
+ ITERATIONS_PER_RED * ERTS_BIF_REDS_LEFT(BIF_P);
+#endif
+ if (is_internal_magic_ref(BIF_ARG_1) &&
+ (ERTS_MAGIC_BIN_DESTRUCTOR(erts_magic_ref2bin(BIF_ARG_1)) ==
+ persistent_term_put_2_ctx_bin_dtor)) {
+ /* Restore state after a trap */
+ Binary* state_bin;
+ state_mref = BIF_ARG_1;
+ state_bin = erts_magic_ref2bin(state_mref);
+ ctx = ERTS_MAGIC_BIN_DATA(state_bin);
+ ASSERT(BIF_P->flags & F_DISABLE_GC);
+ erts_set_gc_state(BIF_P, 1);
+ switch (ctx->trap_location) {
+ case PUT2_TRAP_LOCATION_NEW_KEY:
+ goto L_PUT2_TRAP_LOCATION_NEW_KEY;
+ case PUT2_TRAP_LOCATION_REPLACE_VALUE:
+ goto L_PUT2_TRAP_LOCATION_REPLACE_VALUE;
+ }
+ } else {
+ /* Save state in magic bin in case trapping is necessary */
+ Eterm* hp;
+ Binary* state_bin = erts_create_magic_binary(sizeof(ErtsPersistentTermPut2Context),
+ persistent_term_put_2_ctx_bin_dtor);
+ hp = HAlloc(BIF_P, ERTS_MAGIC_REF_THING_SIZE);
+ state_mref = erts_mk_magic_ref(&hp, &MSO(BIF_P), state_bin);
+ ctx = ERTS_MAGIC_BIN_DATA(state_bin);
+ /*
+ * IMPORTANT: The following field is used to detect if
+ * persistent_term_put_2_ctx_bin_dtor needs to free memory
+ */
+ ctx->cpy_ctx.new_table = NULL;
+ }
+
if (!try_seize_update_permission(BIF_P)) {
ERTS_BIF_YIELD2(bif_export[BIF_persistent_term_put_2],
BIF_P, BIF_ARG_1, BIF_ARG_2);
}
+ ctx->hash_table = (HashTable *) erts_atomic_read_nob(&the_hash_table);
- hash_table = (HashTable *) erts_atomic_read_nob(&the_hash_table);
+ ctx->key = BIF_ARG_1;
+ ctx->term = BIF_ARG_2;
- key = BIF_ARG_1;
- term = BIF_ARG_2;
+ ctx->entry_index = lookup(ctx->hash_table, ctx->key);
- entry_index = lookup(hash_table, key);
-
- heap[0] = make_arityval(2);
- heap[1] = key;
- heap[2] = term;
- tuple = make_tuple(heap);
+ ctx->heap[0] = make_arityval(2);
+ ctx->heap[1] = ctx->key;
+ ctx->heap[2] = ctx->term;
+ ctx->tuple = make_tuple(ctx->heap);
- if (is_nil(hash_table->term[entry_index])) {
- Uint size = hash_table->allocated;
- if (MUST_GROW(hash_table)) {
- size *= 2;
+ if (is_nil(ctx->hash_table->term[ctx->entry_index])) {
+ Uint new_size = ctx->hash_table->allocated;
+ if (MUST_GROW(ctx->hash_table)) {
+ new_size *= 2;
}
- hash_table = copy_table(hash_table, size, 0);
- entry_index = lookup(hash_table, key);
- hash_table->num_entries++;
+ TRAPPING_COPY_TABLE_PUT(ctx->hash_table,
+ ctx->hash_table,
+ new_size,
+ ERTS_PERSISTENT_TERM_CPY_NO_REHASH,
+ PUT2_TRAP_LOCATION_NEW_KEY);
+ ctx->entry_index = lookup(ctx->hash_table, ctx->key);
+ ctx->hash_table->num_entries++;
} else {
- Eterm tuple = hash_table->term[entry_index];
+ Eterm tuple = ctx->hash_table->term[ctx->entry_index];
Eterm old_term;
ASSERT(is_tuple_arity(tuple, 2));
old_term = boxed_val(tuple)[2];
- if (EQ(term, old_term)) {
+ if (EQ(ctx->term, old_term)) {
/* Same value. No need to update anything. */
release_update_permission(0);
BIF_RET(am_ok);
} else {
/* Mark the old term for deletion. */
- mark_for_deletion(hash_table, entry_index);
- hash_table = copy_table(hash_table, hash_table->allocated, 0);
+ mark_for_deletion(ctx->hash_table, ctx->entry_index);
+ TRAPPING_COPY_TABLE_PUT(ctx->hash_table,
+ ctx->hash_table,
+ ctx->hash_table->allocated,
+ ERTS_PERSISTENT_TERM_CPY_NO_REHASH,
+ PUT2_TRAP_LOCATION_REPLACE_VALUE);
}
}
- /*
- * Preserve internal sharing in the term by using the
- * sharing-preserving functions. However, literals must
- * be copied in case the module holding them are unloaded.
- */
- INITIALIZE_SHCOPY(info);
- info.copy_literals = 1;
- term_size = copy_shared_calculate(tuple, &info);
- ERTS_INIT_OFF_HEAP(&code_off_heap);
- lit_area_size = ERTS_LITERAL_AREA_ALLOC_SIZE(term_size);
- literal_area = erts_alloc(ERTS_ALC_T_LITERAL, lit_area_size);
- ptr = &literal_area->start[0];
- literal_area->end = ptr + term_size;
- tuple = copy_shared_perform(tuple, term_size, &info, &ptr, &code_off_heap);
- ASSERT(tuple_val(tuple) == literal_area->start);
- literal_area->off_heap = code_off_heap.first;
- DESTROY_SHCOPY(info);
- erts_set_literal_tag(&tuple, literal_area->start, term_size);
- hash_table->term[entry_index] = tuple;
-
- erts_schedule_thr_prgr_later_op(table_updater, hash_table, &thr_prog_op);
- suspend_updater(BIF_P);
-
- /*
- * Issue a warning once if the warning limit has been exceeded.
- */
-
- if (hash_table->num_entries > WARNING_LIMIT && issued_warning == 0) {
- static char w[] =
- "More than " XSTR(WARNING_LIMIT) " persistent terms "
- "have been created.\n"
- "It is recommended to avoid creating an excessive number of\n"
- "persistent terms, as creation and deletion of persistent terms\n"
- "will be slower as the number of persistent terms increases.\n";
- issued_warning = 1;
- erts_send_warning_to_logger_str(BIF_P->group_leader, w);
+ {
+ Uint term_size;
+ Uint lit_area_size;
+ ErlOffHeap code_off_heap;
+ ErtsLiteralArea* literal_area;
+ erts_shcopy_t info;
+ Eterm* ptr;
+ /*
+ * Preserve internal sharing in the term by using the
+ * sharing-preserving functions. However, literals must
+ * be copied in case the module holding them are unloaded.
+ */
+ INITIALIZE_SHCOPY(info);
+ info.copy_literals = 1;
+ term_size = copy_shared_calculate(ctx->tuple, &info);
+ ERTS_INIT_OFF_HEAP(&code_off_heap);
+ lit_area_size = ERTS_LITERAL_AREA_ALLOC_SIZE(term_size);
+ literal_area = erts_alloc(ERTS_ALC_T_LITERAL, lit_area_size);
+ ptr = &literal_area->start[0];
+ literal_area->end = ptr + term_size;
+ ctx->tuple = copy_shared_perform(ctx->tuple, term_size, &info, &ptr, &code_off_heap);
+ ASSERT(tuple_val(ctx->tuple) == literal_area->start);
+ literal_area->off_heap = code_off_heap.first;
+ DESTROY_SHCOPY(info);
+ erts_set_literal_tag(&ctx->tuple, literal_area->start, term_size);
+ ctx->hash_table->term[ctx->entry_index] = ctx->tuple;
+
+ erts_schedule_thr_prgr_later_op(table_updater, ctx->hash_table, &thr_prog_op);
+ suspend_updater(BIF_P);
}
-
+ BUMP_REDS(BIF_P, (max_iterations - iterations_until_trap) / ITERATIONS_PER_RED);
ERTS_BIF_YIELD_RETURN(BIF_P, am_ok);
}
@@ -349,26 +474,84 @@ BIF_RETTYPE persistent_term_get_2(BIF_ALIST_2)
BIF_RET(result);
}
-BIF_RETTYPE persistent_term_erase_1(BIF_ALIST_1)
+static int persistent_term_erase_1_ctx_bin_dtor(Binary *context_bin)
{
- Eterm key = BIF_ARG_1;
- HashTable* old_table;
- HashTable* new_table;
- Uint entry_index;
- Eterm old_term;
+ ErtsPersistentTermErase1Context* ctx = ERTS_MAGIC_BIN_DATA(context_bin);
+ if (ctx->cpy_ctx.new_table != NULL) {
+ if (ctx->cpy_ctx.copy_type == ERTS_PERSISTENT_TERM_CPY_TEMP) {
+ erts_free(ERTS_ALC_T_PERSISTENT_TERM_TMP, ctx->cpy_ctx.new_table);
+ } else {
+ erts_free(ERTS_ALC_T_PERSISTENT_TERM, ctx->cpy_ctx.new_table);
+ }
+ if (ctx->tmp_table != NULL) {
+ erts_free(ERTS_ALC_T_PERSISTENT_TERM_TMP, ctx->tmp_table);
+ }
+ release_update_permission(0);
+ }
+ return 1;
+}
+BIF_RETTYPE persistent_term_erase_1(BIF_ALIST_1)
+{
+ static const Uint ITERATIONS_PER_RED = 32;
+ ErtsPersistentTermErase1Context* ctx;
+ Eterm state_mref = THE_NON_VALUE;
+ long iterations_until_trap;
+ long max_iterations;
+#ifdef DEBUG
+ (void)ITERATIONS_PER_RED;
+ iterations_until_trap = max_iterations =
+ GET_SMALL_RANDOM_INT(ERTS_BIF_REDS_LEFT(BIF_P) + (Uint)&ctx);
+#else
+ iterations_until_trap = max_iterations =
+ ITERATIONS_PER_RED * ERTS_BIF_REDS_LEFT(BIF_P);
+#endif
+#define ERASE_TRAP_CODE \
+ BIF_TRAP1(bif_export[BIF_persistent_term_erase_1], BIF_P, state_mref);
+#define TRAPPING_COPY_TABLE_ERASE(TABLE_DEST, OLD_TABLE, NEW_SIZE, REHASH, LOC_NAME) \
+ TRAPPING_COPY_TABLE(TABLE_DEST, OLD_TABLE, NEW_SIZE, REHASH, LOC_NAME, ERASE_TRAP_CODE)
+ if (is_internal_magic_ref(BIF_ARG_1) &&
+ (ERTS_MAGIC_BIN_DESTRUCTOR(erts_magic_ref2bin(BIF_ARG_1)) ==
+ persistent_term_erase_1_ctx_bin_dtor)) {
+ /* Restore the state after a trap */
+ Binary* state_bin;
+ state_mref = BIF_ARG_1;
+ state_bin = erts_magic_ref2bin(state_mref);
+ ctx = ERTS_MAGIC_BIN_DATA(state_bin);
+ ASSERT(BIF_P->flags & F_DISABLE_GC);
+ erts_set_gc_state(BIF_P, 1);
+ switch (ctx->trap_location) {
+ case ERASE1_TRAP_LOCATION_TMP_COPY:
+ goto L_ERASE1_TRAP_LOCATION_TMP_COPY;
+ case ERASE1_TRAP_LOCATION_FINAL_COPY:
+ goto L_ERASE1_TRAP_LOCATION_FINAL_COPY;
+ }
+ } else {
+ /* Save state in magic bin in case trapping is necessary */
+ Eterm* hp;
+ Binary* state_bin = erts_create_magic_binary(sizeof(ErtsPersistentTermErase1Context),
+ persistent_term_erase_1_ctx_bin_dtor);
+ hp = HAlloc(BIF_P, ERTS_MAGIC_REF_THING_SIZE);
+ state_mref = erts_mk_magic_ref(&hp, &MSO(BIF_P), state_bin);
+ ctx = ERTS_MAGIC_BIN_DATA(state_bin);
+ /*
+ * IMPORTANT: The following two fields are used to detect if
+ * persistent_term_erase_1_ctx_bin_dtor needs to free memory
+ */
+ ctx->cpy_ctx.new_table = NULL;
+ ctx->tmp_table = NULL;
+ }
if (!try_seize_update_permission(BIF_P)) {
ERTS_BIF_YIELD1(bif_export[BIF_persistent_term_erase_1],
BIF_P, BIF_ARG_1);
}
- old_table = (HashTable *) erts_atomic_read_nob(&the_hash_table);
- entry_index = lookup(old_table, key);
- old_term = old_table->term[entry_index];
- if (is_boxed(old_term)) {
+ ctx->key = BIF_ARG_1;
+ ctx->old_table = (HashTable *) erts_atomic_read_nob(&the_hash_table);
+ ctx->entry_index = lookup(ctx->old_table, ctx->key);
+ ctx->old_term = ctx->old_table->term[ctx->entry_index];
+ if (is_boxed(ctx->old_term)) {
Uint new_size;
- HashTable* tmp_table;
-
/*
* Since we don't use any delete markers, we must rehash
* the table when deleting terms to ensure that all terms
@@ -378,8 +561,12 @@ BIF_RETTYPE persistent_term_erase_1(BIF_ALIST_1)
* temporary table copy of the same size as the old one.
*/
- ASSERT(is_tuple_arity(old_term, 2));
- tmp_table = tmp_table_copy(old_table);
+ ASSERT(is_tuple_arity(ctx->old_term, 2));
+ TRAPPING_COPY_TABLE_ERASE(ctx->tmp_table,
+ ctx->old_table,
+ ctx->old_table->allocated,
+ ERTS_PERSISTENT_TERM_CPY_TEMP,
+ ERASE1_TRAP_LOCATION_TMP_COPY);
/*
* Delete the term from the temporary table. Then copy the
@@ -387,18 +574,28 @@ BIF_RETTYPE persistent_term_erase_1(BIF_ALIST_1)
* while copying.
*/
- tmp_table->term[entry_index] = NIL;
- tmp_table->num_entries--;
- new_size = tmp_table->allocated;
- if (MUST_SHRINK(tmp_table)) {
+ ctx->tmp_table->term[ctx->entry_index] = NIL;
+ ctx->tmp_table->num_entries--;
+ new_size = ctx->tmp_table->allocated;
+ if (MUST_SHRINK(ctx->tmp_table)) {
new_size /= 2;
}
- new_table = copy_table(tmp_table, new_size, 1);
- erts_free(ERTS_ALC_T_TMP, tmp_table);
+ TRAPPING_COPY_TABLE_ERASE(ctx->new_table,
+ ctx->tmp_table,
+ new_size,
+ ERTS_PERSISTENT_TERM_CPY_REHASH,
+ ERASE1_TRAP_LOCATION_FINAL_COPY);
+ erts_free(ERTS_ALC_T_PERSISTENT_TERM_TMP, ctx->tmp_table);
+ /*
+ * IMPORTANT: Memory management depends on that ctx->tmp_table
+ * is set to NULL on the line below
+ */
+ ctx->tmp_table = NULL;
- mark_for_deletion(old_table, entry_index);
- erts_schedule_thr_prgr_later_op(table_updater, new_table, &thr_prog_op);
+ mark_for_deletion(ctx->old_table, ctx->entry_index);
+ erts_schedule_thr_prgr_later_op(table_updater, ctx->new_table, &thr_prog_op);
suspend_updater(BIF_P);
+ BUMP_REDS(BIF_P, (max_iterations - iterations_until_trap) / ITERATIONS_PER_RED);
ERTS_BIF_YIELD_RETURN(BIF_P, am_true);
}
@@ -406,7 +603,7 @@ BIF_RETTYPE persistent_term_erase_1(BIF_ALIST_1)
* Key is not present. Nothing to do.
*/
- ASSERT(is_nil(old_term));
+ ASSERT(is_nil(ctx->old_term));
release_update_permission(0);
BIF_RET(am_false);
}
@@ -740,65 +937,104 @@ lookup(HashTable* hash_table, Eterm key)
}
static HashTable*
-tmp_table_copy(HashTable* old_table)
+copy_table(ErtsPersistentTermCpyTableCtx* ctx)
{
- Uint size = old_table->allocated;
- HashTable* tmp_table;
+ Uint old_size = ctx->old_table->allocated;
Uint i;
-
- tmp_table = (HashTable *) erts_alloc(ERTS_ALC_T_TMP,
- sizeof(HashTable) +
- sizeof(Eterm) * (size-1));
- *tmp_table = *old_table;
- for (i = 0; i < size; i++) {
- tmp_table->term[i] = old_table->term[i];
+ ErtsAlcType_t alloc_type;
+ ctx->total_iterations_done = 0;
+ switch(ctx->location) {
+ case ERTS_PERSISTENT_TERM_CPY_PLACE_1: goto L_copy_table_place_1;
+ case ERTS_PERSISTENT_TERM_CPY_PLACE_2: goto L_copy_table_place_2;
+ case ERTS_PERSISTENT_TERM_CPY_PLACE_3: goto L_copy_table_place_3;
+ case ERTS_PERSISTENT_TERM_CPY_PLACE_START:
+ ctx->iterations_done = 0;
}
- return tmp_table;
-}
-
-static HashTable*
-copy_table(HashTable* old_table, Uint new_size, int rehash)
-{
- HashTable* new_table;
- Uint old_size = old_table->allocated;
- Uint i;
-
- new_table = (HashTable *) erts_alloc(ERTS_ALC_T_PERSISTENT_TERM,
- sizeof(HashTable) +
- sizeof(Eterm) * (new_size-1));
- if (old_table->allocated == new_size && !rehash) {
+ if (ctx->copy_type == ERTS_PERSISTENT_TERM_CPY_TEMP) {
+ alloc_type = ERTS_ALC_T_PERSISTENT_TERM_TMP;
+ } else {
+ alloc_type = ERTS_ALC_T_PERSISTENT_TERM;
+ }
+ ctx->new_table = (HashTable *) erts_alloc(alloc_type,
+ sizeof(HashTable) +
+ sizeof(Eterm) * (ctx->new_size-1));
+ if (ctx->old_table->allocated == ctx->new_size &&
+ (ctx->copy_type == ERTS_PERSISTENT_TERM_CPY_NO_REHASH ||
+ ctx->copy_type == ERTS_PERSISTENT_TERM_CPY_TEMP)) {
/*
* Same size and no key deleted. Make an exact copy of the table.
*/
- *new_table = *old_table;
- for (i = 0; i < new_size; i++) {
- new_table->term[i] = old_table->term[i];
+ *ctx->new_table = *ctx->old_table;
+ L_copy_table_place_1:
+ for (i = ctx->iterations_done;
+ i < MIN(ctx->iterations_done + ctx->max_iterations,
+ ctx->new_size);
+ i++) {
+ ctx->new_table->term[i] = ctx->old_table->term[i];
}
+ ctx->total_iterations_done = (i - ctx->iterations_done);
+ if (i < ctx->new_size) {
+ ctx->iterations_done = i;
+ ctx->location = ERTS_PERSISTENT_TERM_CPY_PLACE_1;
+ return NULL;
+ }
+ ctx->iterations_done = 0;
} else {
/*
* The size of the table has changed or an element has been
* deleted. Must rehash, by inserting all old terms into the
* new (empty) table.
*/
- new_table->allocated = new_size;
- new_table->num_entries = old_table->num_entries;
- new_table->mask = new_size - 1;
- for (i = 0; i < new_size; i++) {
- new_table->term[i] = NIL;
+ ctx->new_table->allocated = ctx->new_size;
+ ctx->new_table->num_entries = ctx->old_table->num_entries;
+ ctx->new_table->mask = ctx->new_size - 1;
+ L_copy_table_place_2:
+ for (i = ctx->iterations_done;
+ i < MIN(ctx->iterations_done + ctx->max_iterations,
+ ctx->new_size);
+ i++) {
+ ctx->new_table->term[i] = NIL;
+ }
+ ctx->total_iterations_done = (i - ctx->iterations_done);
+ ctx->max_iterations -= ctx->total_iterations_done;
+ if (i < ctx->new_size) {
+ ctx->iterations_done = i;
+ ctx->location = ERTS_PERSISTENT_TERM_CPY_PLACE_2;
+ return NULL;
}
- for (i = 0; i < old_size; i++) {
- if (is_tuple(old_table->term[i])) {
- Eterm key = tuple_val(old_table->term[i])[1];
- Uint entry_index = lookup(new_table, key);
- ASSERT(is_nil(new_table->term[entry_index]));
- new_table->term[entry_index] = old_table->term[i];
+ ctx->iterations_done = 0;
+ L_copy_table_place_3:
+ for (i = ctx->iterations_done;
+ i < MIN(ctx->iterations_done + ctx->max_iterations,
+ old_size);
+ i++) {
+ if (is_tuple(ctx->old_table->term[i])) {
+ Eterm key = tuple_val(ctx->old_table->term[i])[1];
+ Uint entry_index = lookup(ctx->new_table, key);
+ ASSERT(is_nil(ctx->new_table->term[entry_index]));
+ ctx->new_table->term[entry_index] = ctx->old_table->term[i];
}
}
+ ctx->total_iterations_done += (i - ctx->iterations_done);
+ if (i < old_size) {
+ ctx->iterations_done = i;
+ ctx->location = ERTS_PERSISTENT_TERM_CPY_PLACE_3;
+ return NULL;
+ }
+ ctx->iterations_done = 0;
+ }
+ ctx->new_table->first_to_delete = 0;
+ ctx->new_table->num_to_delete = 0;
+ erts_atomic_init_nob(&ctx->new_table->refc, (erts_aint_t)1);
+ {
+ HashTable* new_table = ctx->new_table;
+ /*
+ * IMPORTANT: Memory management depends on that ctx->new_table is
+ * set to NULL on the line below
+ */
+ ctx->new_table = NULL;
+ return new_table;
}
- new_table->first_to_delete = 0;
- new_table->num_to_delete = 0;
- erts_atomic_init_nob(&new_table->refc, (erts_aint_t)1);
- return new_table;
}
static void
diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c
index deaf35c2a1..1fbe362330 100644
--- a/erts/emulator/beam/erl_nif.c
+++ b/erts/emulator/beam/erl_nif.c
@@ -1344,11 +1344,18 @@ unsigned char* enif_make_new_binary(ErlNifEnv* env, size_t size,
int enif_term_to_binary(ErlNifEnv *dst_env, ERL_NIF_TERM term,
ErlNifBinary *bin)
{
- Sint size;
+ Uint size;
byte *bp;
Binary* refbin;
- size = erts_encode_ext_size(term);
+ switch (erts_encode_ext_size(term, &size)) {
+ case ERTS_EXT_SZ_SYSTEM_LIMIT:
+ return 0; /* system limit */
+ case ERTS_EXT_SZ_YIELD:
+ ERTS_INTERNAL_ERROR("Unexpected yield");
+ case ERTS_EXT_SZ_OK:
+ break;
+ }
if (!enif_alloc_binary(size, bin))
return 0;
diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c
index 76eec96372..1f6adb98ef 100644
--- a/erts/emulator/beam/erl_process.c
+++ b/erts/emulator/beam/erl_process.c
@@ -12151,6 +12151,7 @@ erts_proc_exit_handle_dist_monitor(ErtsMonitor *mon, void *vctxt, Sint reds)
case ERTS_DSIG_SEND_OK:
break;
case ERTS_DSIG_SEND_TOO_LRG:
+ erts_kill_dist_connection(dep, dist->connection_id);
erts_set_gc_state(c_p, 1);
break;
default:
@@ -12401,6 +12402,7 @@ erts_proc_exit_handle_dist_link(ErtsLink *lnk, void *vctxt, Sint reds)
case ERTS_DSIG_SEND_OK:
break;
case ERTS_DSIG_SEND_TOO_LRG:
+ erts_kill_dist_connection(dep, dist->connection_id);
erts_set_gc_state(c_p, 1);
break;
default:
@@ -12897,7 +12899,9 @@ restart:
switch (result) {
case ERTS_DSIG_SEND_OK:
+ break;
case ERTS_DSIG_SEND_TOO_LRG: /*SEND_SYSTEM_LIMIT*/
+ erts_kill_dist_connection(ctx->dep, ctx->connection_id);
break;
case ERTS_DSIG_SEND_YIELD: /*SEND_YIELD_RETURN*/
case ERTS_DSIG_SEND_CONTINUE: { /*SEND_YIELD_CONTINUE*/
diff --git a/erts/emulator/beam/erl_trace.c b/erts/emulator/beam/erl_trace.c
index ae7084b7f4..c85a7df5ec 100644
--- a/erts/emulator/beam/erl_trace.c
+++ b/erts/emulator/beam/erl_trace.c
@@ -635,9 +635,11 @@ write_sys_msg_to_port(Eterm unused_to,
Eterm message) {
byte *buffer;
byte *ptr;
- unsigned size;
+ Uint size;
+
+ if (erts_encode_ext_size(message, &size) != ERTS_EXT_SZ_OK)
+ erts_exit(ERTS_ERROR_EXIT, "Internal error: System limit\n");
- size = erts_encode_ext_size(message);
buffer = (byte *) erts_alloc(ERTS_ALC_T_TMP, size);
ptr = buffer;
diff --git a/erts/emulator/beam/external.c b/erts/emulator/beam/external.c
index 395ff51ad3..ec67ab2aed 100644
--- a/erts/emulator/beam/external.c
+++ b/erts/emulator/beam/external.c
@@ -112,13 +112,13 @@ static byte* dec_pid(ErtsDistExternal *, ErtsHeapFactory*, byte*, Eterm*, byte t
static Sint decoded_size(byte *ep, byte* endp, int internal_tags, struct B2TContext_t*);
static BIF_RETTYPE term_to_binary_trap_1(BIF_ALIST_1);
-static Eterm erts_term_to_binary_int(Process* p, Eterm Term, int level, Uint flags,
- Binary *context_b);
+static Eterm erts_term_to_binary_int(Process* p, Eterm Term, Eterm opts, int level,
+ Uint flags, Binary *context_b);
static Uint encode_size_struct2(ErtsAtomCacheMap *, Eterm, unsigned);
struct TTBSizeContext_;
-static int encode_size_struct_int(struct TTBSizeContext_*, ErtsAtomCacheMap *acmp, Eterm obj,
- unsigned dflags, Sint *reds, Uint *res);
+static ErtsExtSzRes encode_size_struct_int(struct TTBSizeContext_*, ErtsAtomCacheMap *acmp,
+ Eterm obj, unsigned dflags, Sint *reds, Uint *res);
static Export binary_to_term_trap_export;
static BIF_RETTYPE binary_to_term_trap_1(BIF_ALIST_1);
@@ -603,49 +603,50 @@ done:
return reds < 0 ? 0 : reds;
}
-int erts_encode_dist_ext_size(Eterm term, Uint32 flags, ErtsAtomCacheMap *acmp,
- Uint* szp)
+ErtsExtSzRes
+erts_encode_dist_ext_size(Eterm term, Uint32 flags, ErtsAtomCacheMap *acmp, Uint* szp)
{
Uint sz;
- if (encode_size_struct_int(NULL, acmp, term, flags, NULL, &sz)) {
- return -1;
- } else {
+ ErtsExtSzRes res = encode_size_struct_int(NULL, acmp, term, flags, NULL, &sz);
+ if (res == ERTS_EXT_SZ_OK) {
#ifndef ERTS_DEBUG_USE_DIST_SEP
if (!(flags & (DFLAG_DIST_HDR_ATOM_CACHE | DFLAG_NO_MAGIC)))
#endif
sz++ /* VERSION_MAGIC */;
*szp += sz;
- return 0;
}
+ return res;
}
-int erts_encode_dist_ext_size_int(Eterm term, ErtsDSigSendContext *ctx, Uint* szp)
+ErtsExtSzRes
+erts_encode_dist_ext_size_ctx(Eterm term, ErtsDSigSendContext *ctx, Uint* szp)
{
Uint sz;
- if (encode_size_struct_int(&ctx->u.sc, ctx->acmp, term, ctx->flags, &ctx->reds, &sz)) {
- return -1;
- } else {
+ ErtsExtSzRes res = encode_size_struct_int(&ctx->u.sc, ctx->acmp, term,
+ ctx->flags, &ctx->reds, &sz);
+ if (res == ERTS_EXT_SZ_OK) {
#ifndef ERTS_DEBUG_USE_DIST_SEP
if (!(ctx->flags & (DFLAG_DIST_HDR_ATOM_CACHE | DFLAG_NO_MAGIC)))
#endif
sz++ /* VERSION_MAGIC */;
*szp += sz;
- return 0;
}
+ return res;
}
-Uint erts_encode_ext_size(Eterm term)
+ErtsExtSzRes erts_encode_ext_size_2(Eterm term, unsigned dflags, Uint *szp)
{
- return encode_size_struct2(NULL, term, TERM_TO_BINARY_DFLAGS)
- + 1 /* VERSION_MAGIC */;
+ ErtsExtSzRes res = encode_size_struct_int(NULL, NULL, term, dflags,
+ NULL, szp);
+ (*szp)++ /* VERSION_MAGIC */;
+ return res;
}
-Uint erts_encode_ext_size_2(Eterm term, unsigned dflags)
+ErtsExtSzRes erts_encode_ext_size(Eterm term, Uint *szp)
{
- return encode_size_struct2(NULL, term, dflags)
- + 1 /* VERSION_MAGIC */;
+ return erts_encode_ext_size_2(term, TERM_TO_BINARY_DFLAGS, szp);
}
Uint erts_encode_ext_size_ets(Eterm term)
@@ -1253,9 +1254,22 @@ static BIF_RETTYPE term_to_binary_trap_1(BIF_ALIST_1)
{
Eterm *tp = tuple_val(BIF_ARG_1);
Eterm Term = tp[1];
- Eterm bt = tp[2];
+ Eterm Opts = tp[2];
+ Eterm bt = tp[3];
Binary *bin = erts_magic_ref2bin(bt);
- Eterm res = erts_term_to_binary_int(BIF_P, Term, 0, 0,bin);
+ Eterm res = erts_term_to_binary_int(BIF_P, Term, Opts, 0, 0,bin);
+ if (is_non_value(res)) {
+ if (erts_set_gc_state(BIF_P, 1)
+ || MSO(BIF_P).overhead > BIN_VHEAP_SZ(BIF_P)) {
+ ERTS_VBUMP_ALL_REDS(BIF_P);
+ }
+ if (Opts == am_undefined)
+ ERTS_BIF_ERROR_TRAPPED1(BIF_P, SYSTEM_LIMIT,
+ bif_export[BIF_term_to_binary_1], Term);
+ else
+ ERTS_BIF_ERROR_TRAPPED2(BIF_P, SYSTEM_LIMIT,
+ bif_export[BIF_term_to_binary_2], Term, Opts);
+ }
if (is_tuple(res)) {
ASSERT(BIF_P->flags & F_DISABLE_GC);
BIF_TRAP1(&term_to_binary_trap_export,BIF_P,res);
@@ -1272,7 +1286,12 @@ HIPE_WRAPPER_BIF_DISABLE_GC(term_to_binary, 1)
BIF_RETTYPE term_to_binary_1(BIF_ALIST_1)
{
- Eterm res = erts_term_to_binary_int(BIF_P, BIF_ARG_1, 0, TERM_TO_BINARY_DFLAGS, NULL);
+ Eterm res = erts_term_to_binary_int(BIF_P, BIF_ARG_1, am_undefined,
+ 0, TERM_TO_BINARY_DFLAGS, NULL);
+ if (is_non_value(res)) {
+ ASSERT(!(BIF_P->flags & F_DISABLE_GC));
+ BIF_ERROR(BIF_P, SYSTEM_LIMIT);
+ }
if (is_tuple(res)) {
erts_set_gc_state(BIF_P, 0);
BIF_TRAP1(&term_to_binary_trap_export,BIF_P,res);
@@ -1331,7 +1350,12 @@ BIF_RETTYPE term_to_binary_2(BIF_ALIST_2)
goto error;
}
- res = erts_term_to_binary_int(p, Term, level, flags, NULL);
+ res = erts_term_to_binary_int(p, Term, BIF_ARG_2,
+ level, flags, NULL);
+ if (is_non_value(res)) {
+ ASSERT(!(BIF_P->flags & F_DISABLE_GC));
+ BIF_ERROR(BIF_P, SYSTEM_LIMIT);
+ }
if (is_tuple(res)) {
erts_set_gc_state(p, 0);
BIF_TRAP1(&term_to_binary_trap_export,BIF_P,res);
@@ -1880,8 +1904,17 @@ external_size_1(BIF_ALIST_1)
{
Process* p = BIF_P;
Eterm Term = BIF_ARG_1;
+ Uint size;
+
+ switch (erts_encode_ext_size(Term, &size)) {
+ case ERTS_EXT_SZ_SYSTEM_LIMIT:
+ BIF_ERROR(BIF_P, SYSTEM_LIMIT);
+ case ERTS_EXT_SZ_YIELD:
+ ERTS_INTERNAL_ERROR("Unexpected yield");
+ case ERTS_EXT_SZ_OK:
+ break;
+ }
- Uint size = erts_encode_ext_size(Term);
if (IS_USMALL(0, size)) {
BIF_RET(make_small(size));
} else {
@@ -1924,7 +1957,15 @@ external_size_2(BIF_ALIST_2)
goto error;
}
- size = erts_encode_ext_size_2(BIF_ARG_1, flags);
+ switch (erts_encode_ext_size_2(BIF_ARG_1, flags, &size)) {
+ case ERTS_EXT_SZ_SYSTEM_LIMIT:
+ BIF_ERROR(BIF_P, SYSTEM_LIMIT);
+ case ERTS_EXT_SZ_YIELD:
+ ERTS_INTERNAL_ERROR("Unexpected yield");
+ case ERTS_EXT_SZ_OK:
+ break;
+ }
+
if (IS_USMALL(0, size)) {
BIF_RET(make_small(size));
} else {
@@ -2012,7 +2053,15 @@ erts_term_to_binary_simple(Process* p, Eterm Term, Uint size, int level, Uint fl
Eterm
erts_term_to_binary(Process* p, Eterm Term, int level, Uint flags) {
Uint size;
- size = encode_size_struct2(NULL, Term, flags) + 1 /* VERSION_MAGIC */;
+ switch (encode_size_struct_int(NULL, NULL, Term, flags, NULL, &size)) {
+ case ERTS_EXT_SZ_SYSTEM_LIMIT:
+ return THE_NON_VALUE;
+ case ERTS_EXT_SZ_YIELD:
+ ERTS_INTERNAL_ERROR("Unexpected yield");
+ case ERTS_EXT_SZ_OK:
+ break;
+ }
+ size++; /* VERSION_MAGIC */;
return erts_term_to_binary_simple(p, Term, size, level, flags);
}
@@ -2062,8 +2111,8 @@ static int ttb_context_destructor(Binary *context_bin)
return 1;
}
-static Eterm erts_term_to_binary_int(Process* p, Eterm Term, int level, Uint flags,
- Binary *context_b)
+static Eterm erts_term_to_binary_int(Process* p, Eterm Term, Eterm opts, int level,
+ Uint flags, Binary *context_b)
{
Eterm *hp;
Eterm res;
@@ -2081,18 +2130,17 @@ static Eterm erts_term_to_binary_int(Process* p, Eterm Term, int level, Uint fla
do { \
if (context_b == NULL) { \
context_b = erts_create_magic_binary(sizeof(TTBContext), \
- ttb_context_destructor); \
+ ttb_context_destructor);\
context = ERTS_MAGIC_BIN_DATA(context_b); \
- sys_memcpy(context,&c_buff,sizeof(TTBContext)); \
+ sys_memcpy(context,&c_buff,sizeof(TTBContext)); \
} \
} while (0)
#define RETURN_STATE() \
do { \
- static const int TUPLE2_SIZE = 2 + 1; \
- hp = HAlloc(p, ERTS_MAGIC_REF_THING_SIZE + TUPLE2_SIZE); \
+ hp = HAlloc(p, ERTS_MAGIC_REF_THING_SIZE + 1 + 3); \
c_term = erts_mk_magic_ref(&hp, &MSO(p), context_b); \
- res = TUPLE2(hp, Term, c_term); \
+ res = TUPLE3(hp, Term, opts, c_term); \
BUMP_ALL_REDS(p); \
return res; \
} while (0);
@@ -2118,11 +2166,17 @@ static Eterm erts_term_to_binary_int(Process* p, Eterm Term, int level, Uint fla
int level;
Uint flags;
/* Try for fast path */
- if (encode_size_struct_int(&context->s.sc, NULL, Term,
- context->s.sc.flags, &reds, &size) < 0) {
+ switch (encode_size_struct_int(&context->s.sc, NULL, Term,
+ context->s.sc.flags, &reds, &size)) {
+ case ERTS_EXT_SZ_SYSTEM_LIMIT:
+ BUMP_REDS(p, (initial_reds - reds) / TERM_TO_BINARY_LOOP_FACTOR);
+ return THE_NON_VALUE;
+ case ERTS_EXT_SZ_YIELD:
EXPORT_CONTEXT();
/* Same state */
RETURN_STATE();
+ case ERTS_EXT_SZ_OK:
+ break;
}
++size; /* VERSION_MAGIC */
/* Move these to next state */
@@ -4184,13 +4238,21 @@ error_hamt:
to a sequence of bytes
N.B. That this must agree with to_external2() above!!!
(except for cached atoms) */
-static Uint encode_size_struct2(ErtsAtomCacheMap *acmp, Eterm obj, unsigned dflags) {
- Uint res;
- (void) encode_size_struct_int(NULL, acmp, obj, dflags, NULL, &res);
- return res;
+static Uint encode_size_struct2(ErtsAtomCacheMap *acmp,
+ Eterm obj,
+ unsigned dflags) {
+ Uint size;
+ ErtsExtSzRes res = encode_size_struct_int(NULL, acmp, obj,
+ dflags, NULL, &size);
+ /*
+ * encode_size_struct2() only allowed when
+ * we know the result will always be OK!
+ */
+ ASSERT(res == ERTS_EXT_SZ_OK); (void) res;
+ return (Uint) size;
}
-static int
+static ErtsExtSzRes
encode_size_struct_int(TTBSizeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj,
unsigned dflags, Sint *reds, Uint *res)
{
@@ -4223,7 +4285,7 @@ encode_size_struct_int(TTBSizeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj,
ctx->obj = obj;
ctx->result = result;
WSTACK_SAVE(s, &ctx->wstack);
- return -1;
+ return ERTS_EXT_SZ_YIELD;
}
switch (tag_val_def(obj)) {
case NIL_DEF:
@@ -4399,11 +4461,26 @@ encode_size_struct_int(TTBSizeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj,
result += 32; /* Yes, including the tag */
}
break;
- case BINARY_DEF:
- if (dflags & DFLAG_INTERNAL_TAGS) {
+ case BINARY_DEF: {
+ ProcBin* pb = (ProcBin*) binary_val(obj);
+ Uint tot_bytes = pb->size;
+ if (!(dflags & DFLAG_INTERNAL_TAGS)) {
+#ifdef ARCH_64
+ if (tot_bytes >= (Uint) 0xffffffff) {
+ if (pb->thing_word == HEADER_SUB_BIN) {
+ ErlSubBin* sub = (ErlSubBin*) pb;
+ tot_bytes += (sub->bitoffs + sub->bitsize+ 7) / 8;
+ }
+ if (tot_bytes > (Uint) 0xffffffff) {
+ WSTACK_DESTROY(s);
+ return ERTS_EXT_SZ_SYSTEM_LIMIT;
+ }
+ }
+#endif
+ }
+ else {
ProcBin* pb = (ProcBin*) binary_val(obj);
Uint sub_extra = 0;
- Uint tot_bytes = pb->size;
if (pb->thing_word == HEADER_SUB_BIN) {
ErlSubBin* sub = (ErlSubBin*) pb;
pb = (ProcBin*) binary_val(sub->orig);
@@ -4420,6 +4497,7 @@ encode_size_struct_int(TTBSizeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj,
result += 1 + 4 + binary_size(obj) +
5; /* For unaligned binary */
break;
+ }
case FUN_DEF:
{
ErlFunThing* funp = (ErlFunThing *) fun_val(obj);
@@ -4452,7 +4530,7 @@ encode_size_struct_int(TTBSizeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj,
break;
default:
- erts_exit(ERTS_ERROR_EXIT,"Internal data structure error (in encode_size_struct2)%x\n",
+ erts_exit(ERTS_ERROR_EXIT,"Internal data structure error (in encode_size_struct_int) %x\n",
obj);
}
@@ -4492,7 +4570,7 @@ encode_size_struct_int(TTBSizeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj,
*reds = r < 0 ? 0 : r;
}
*res = result;
- return 0;
+ return ERTS_EXT_SZ_OK;
}
diff --git a/erts/emulator/beam/external.h b/erts/emulator/beam/external.h
index f2cc9bf98f..b556c9076c 100644
--- a/erts/emulator/beam/external.h
+++ b/erts/emulator/beam/external.h
@@ -164,14 +164,21 @@ byte *erts_encode_ext_dist_header_setup(byte *, ErtsAtomCacheMap *, Uint, Eterm)
byte *erts_encode_ext_dist_header_fragment(byte **, Uint, Eterm);
Sint erts_encode_ext_dist_header_finalize(ErtsDistOutputBuf*, DistEntry *, Uint32 dflags, Sint reds);
struct erts_dsig_send_context;
-int erts_encode_dist_ext_size(Eterm, Uint32, ErtsAtomCacheMap*, Uint* szp);
-int erts_encode_dist_ext_size_int(Eterm term, struct erts_dsig_send_context* ctx, Uint* szp);
+
+typedef enum {
+ ERTS_EXT_SZ_OK,
+ ERTS_EXT_SZ_YIELD,
+ ERTS_EXT_SZ_SYSTEM_LIMIT
+} ErtsExtSzRes;
+
+ErtsExtSzRes erts_encode_dist_ext_size(Eterm, Uint32, ErtsAtomCacheMap*, Uint* szp);
+ErtsExtSzRes erts_encode_dist_ext_size_ctx(Eterm term, struct erts_dsig_send_context* ctx, Uint* szp);
struct TTBEncodeContext_;
int erts_encode_dist_ext(Eterm, byte **, Uint32, ErtsAtomCacheMap *,
struct TTBEncodeContext_ *, Sint* reds);
-Uint erts_encode_ext_size(Eterm);
-Uint erts_encode_ext_size_2(Eterm, unsigned);
+ErtsExtSzRes erts_encode_ext_size(Eterm, Uint *szp);
+ErtsExtSzRes erts_encode_ext_size_2(Eterm, unsigned, Uint *szp);
Uint erts_encode_ext_size_ets(Eterm);
void erts_encode_ext(Eterm, byte **);
byte* erts_encode_ext_ets(Eterm, byte *, struct erl_off_heap_header** ext_off_heap);
diff --git a/erts/emulator/beam/io.c b/erts/emulator/beam/io.c
index b961c639f5..45fef0c0e5 100644
--- a/erts/emulator/beam/io.c
+++ b/erts/emulator/beam/io.c
@@ -4450,6 +4450,7 @@ erts_port_call(Process* c_p,
char input_buf[256];
char *bufp;
byte *endp;
+ Uint uintsz;
ErlDrvSizeT size;
int try_call;
erts_aint32_t sched_flags;
@@ -4462,7 +4463,9 @@ erts_port_call(Process* c_p,
try_call = !(sched_flags & ERTS_PTS_FLGS_FORCE_SCHEDULE_OP);
- size = erts_encode_ext_size(data);
+ if (erts_encode_ext_size(data, &uintsz) != ERTS_EXT_SZ_OK)
+ return ERTS_PORT_OP_BADARG;
+ size = (ErlDrvSizeT) uintsz;
if (!try_call)
bufp = erts_alloc(ERTS_ALC_T_DRV_CALL_DATA, size);
@@ -5295,44 +5298,31 @@ erts_get_port_names(Eterm id, ErlDrvPort drv_port)
pnp->driver_name = NULL;
}
else {
- int do_realloc = 1;
- int len = -1;
- size_t pnp_len = sizeof(ErtsPortNames);
-#ifndef DEBUG
- pnp_len += 100; /* In most cases 100 characters will be enough... */
- ASSERT(prt->common.id == id);
-#endif
- pnp = erts_alloc(ERTS_ALC_T_PORT_NAMES, pnp_len);
- do {
- int nlen;
- char *name, *driver_name;
- if (len > 0) {
- erts_free(ERTS_ALC_T_PORT_NAMES, pnp);
- pnp_len = sizeof(ErtsPortNames) + len;
- pnp = erts_alloc(ERTS_ALC_T_PORT_NAMES, pnp_len);
- }
- name = prt->name;
- len = nlen = name ? sys_strlen(name) + 1 : 0;
- driver_name = (prt->drv_ptr ? prt->drv_ptr->name : NULL);
- len += driver_name ? sys_strlen(driver_name) + 1 : 0;
- if (len <= pnp_len - sizeof(ErtsPortNames)) {
- if (!name)
- pnp->name = NULL;
- else {
- pnp->name = ((char *) pnp) + sizeof(ErtsPortNames);
- sys_strcpy(pnp->name, name);
- }
- if (!driver_name)
- pnp->driver_name = NULL;
- else {
- pnp->driver_name = (((char *) pnp)
- + sizeof(ErtsPortNames)
- + nlen);
- sys_strcpy(pnp->driver_name, driver_name);
- }
- do_realloc = 0;
- }
- } while (do_realloc);
+ int len;
+ int nlen;
+ char *driver_name;
+
+ len = nlen = prt->name ? sys_strlen(prt->name) + 1 : 0;
+ driver_name = (prt->drv_ptr ? prt->drv_ptr->name : NULL);
+ len += driver_name ? sys_strlen(driver_name) + 1 : 0;
+
+ pnp = erts_alloc(ERTS_ALC_T_PORT_NAMES,
+ sizeof(ErtsPortNames) + len);
+
+ if (!prt->name)
+ pnp->name = NULL;
+ else {
+ pnp->name = ((char *) pnp) + sizeof(ErtsPortNames);
+ sys_strcpy(pnp->name, prt->name);
+ }
+ if (!driver_name)
+ pnp->driver_name = NULL;
+ else {
+ pnp->driver_name = (((char *) pnp)
+ + sizeof(ErtsPortNames)
+ + nlen);
+ sys_strcpy(pnp->driver_name, driver_name);
+ }
}
return pnp;
}