diff options
author | Kjell Winblad <[email protected]> | 2019-02-25 13:38:15 +0100 |
---|---|---|
committer | GitHub <[email protected]> | 2019-02-25 13:38:15 +0100 |
commit | 34c88dfb8252a4ab00695ba806c3992788906a8b (patch) | |
tree | 2e326ec9430b9ef62b3f57cce55f13d5f5c646b5 /erts/emulator/beam | |
parent | 7c1388ddaa2d66285446e34c3d3791a67e67893b (diff) | |
parent | 5284c11811e6e410b72f8233dcd8cb443402f767 (diff) | |
download | otp-34c88dfb8252a4ab00695ba806c3992788906a8b.tar.gz otp-34c88dfb8252a4ab00695ba806c3992788906a8b.tar.bz2 otp-34c88dfb8252a4ab00695ba806c3992788906a8b.zip |
Merge pull request #2124 from kjellwinblad/kjell/stdlib/iolist_size_trap
Make iolist_size/1 yield
OTP-15631
Diffstat (limited to 'erts/emulator/beam')
-rw-r--r-- | erts/emulator/beam/bif.c | 113 | ||||
-rw-r--r-- | erts/emulator/beam/erl_alloc.types | 21 | ||||
-rw-r--r-- | erts/emulator/beam/erl_dirty_bif.tab | 1 |
3 files changed, 117 insertions, 18 deletions
diff --git a/erts/emulator/beam/bif.c b/erts/emulator/beam/bif.c index a4c7af9a24..7faba35e1c 100644 --- a/erts/emulator/beam/bif.c +++ b/erts/emulator/beam/bif.c @@ -2375,7 +2375,7 @@ accumulate(Eterm acc, Uint size) * bignum buffer with one extra word to be used if * the bignum grows in the future. */ - Eterm* hp = (Eterm *) erts_alloc(ERTS_ALC_T_TEMP_TERM, + Eterm* hp = (Eterm *) erts_alloc(ERTS_ALC_T_SHORT_LIVED_TERM, (BIG_UINT_HEAP_SIZE+1) * sizeof(Eterm)); return uint_to_big(size, hp); @@ -2395,7 +2395,7 @@ accumulate(Eterm acc, Uint size) * The extra word has been consumed. Grow the * allocation by one word. */ - big = (Eterm *) erts_realloc(ERTS_ALC_T_TEMP_TERM, + big = (Eterm *) erts_realloc(ERTS_ALC_T_SHORT_LIVED_TERM, big_val(acc), (need_heap+1) * sizeof(Eterm)); acc = make_big(big); @@ -2424,29 +2424,85 @@ consolidate(Process* p, Eterm acc, Uint size) while (sz--) { *hp++ = *big++; } - erts_free(ERTS_ALC_T_TEMP_TERM, (void *) big_val(acc)); + erts_free(ERTS_ALC_T_SHORT_LIVED_TERM, (void *) big_val(acc)); return res; } } +typedef struct { + Eterm obj; + Uint size; + Eterm acc; + Eterm input_list; + ErtsEStack stack; + int is_trap_at_L_iter_list; +} ErtsIOListSizeContext; + +static int iolist_size_ctx_bin_dtor(Binary *context_bin) { + ErtsIOListSizeContext* context = ERTS_MAGIC_BIN_DATA(context_bin); + DESTROY_SAVED_ESTACK(&context->stack); + if (context->acc != THE_NON_VALUE) { + erts_free(ERTS_ALC_T_SHORT_LIVED_TERM, (void *) big_val(context->acc)); + } + return 1; +} + BIF_RETTYPE iolist_size_1(BIF_ALIST_1) { - Eterm obj, hd; + static const Uint ITERATIONS_PER_RED = 64; + Eterm input_list, obj, hd; Eterm* objp; Uint size = 0; Uint cur_size; Uint new_size; Eterm acc = THE_NON_VALUE; DECLARE_ESTACK(s); - - obj = BIF_ARG_1; + Uint max_iterations; + Uint iterations_until_trap = max_iterations = + ITERATIONS_PER_RED * ERTS_BIF_REDS_LEFT(BIF_P); + ErtsIOListSizeContext* context = NULL; + Eterm state_mref; + int is_trap_at_L_iter_list; + ESTACK_CHANGE_ALLOCATOR(s, ERTS_ALC_T_SAVED_ESTACK); +#ifdef DEBUG + iterations_until_trap = iterations_until_trap / 10; +#endif + input_list = obj = BIF_ARG_1; + if (is_internal_magic_ref(obj)) { + /* Restore state after a trap */ + Binary* state_bin; + state_mref = obj; + state_bin = erts_magic_ref2bin(state_mref); + if (ERTS_MAGIC_BIN_DESTRUCTOR(state_bin) != iolist_size_ctx_bin_dtor) { + BIF_ERROR(BIF_P, BADARG); + } + context = ERTS_MAGIC_BIN_DATA(state_bin); + obj = context->obj; + size = context->size; + acc = context->acc; + input_list = context->input_list; + ESTACK_RESTORE(s, &context->stack); + ASSERT(BIF_P->flags & F_DISABLE_GC); + erts_set_gc_state(BIF_P, 1); + if (context->is_trap_at_L_iter_list) { + goto L_iter_list; + } + } goto L_again; while (!ESTACK_ISEMPTY(s)) { obj = ESTACK_POP(s); + if (iterations_until_trap == 0) { + is_trap_at_L_iter_list = 0; + goto L_save_state_and_trap; + } L_again: if (is_list(obj)) { L_iter_list: + if (iterations_until_trap == 0) { + is_trap_at_L_iter_list = 1; + goto L_save_state_and_trap; + } objp = list_val(obj); hd = CAR(objp); obj = CDR(objp); @@ -2468,12 +2524,14 @@ BIF_RETTYPE iolist_size_1(BIF_ALIST_1) } else if (is_list(hd)) { ESTACK_PUSH(s, obj); obj = hd; + iterations_until_trap--; goto L_iter_list; } else if (is_not_nil(hd)) { goto L_type_error; } /* Tail */ if (is_list(obj)) { + iterations_until_trap--; goto L_iter_list; } else if (is_binary(obj) && binary_bitsize(obj) == 0) { cur_size = binary_size(obj); @@ -2497,14 +2555,55 @@ BIF_RETTYPE iolist_size_1(BIF_ALIST_1) } else if (is_not_nil(obj)) { goto L_type_error; } + iterations_until_trap--; } DESTROY_ESTACK(s); + BUMP_REDS(BIF_P, (max_iterations - iterations_until_trap) / ITERATIONS_PER_RED); + ASSERT(!(BIF_P->flags & F_DISABLE_GC)); + if (context != NULL) { + /* context->acc needs to be reset so that + iolist_size_ctx_bin_dtor does not deallocate twice */ + context->acc = THE_NON_VALUE; + } BIF_RET(consolidate(BIF_P, acc, size)); L_type_error: DESTROY_ESTACK(s); - BIF_ERROR(BIF_P, BADARG); + if (acc != THE_NON_VALUE) { + erts_free(ERTS_ALC_T_SHORT_LIVED_TERM, (void *) big_val(acc)); + if (context != NULL) { + context->acc = THE_NON_VALUE; + } + } + BUMP_REDS(BIF_P, (max_iterations - iterations_until_trap) / ITERATIONS_PER_RED); + ASSERT(!(BIF_P->flags & F_DISABLE_GC)); + if (context == NULL) { + BIF_ERROR(BIF_P, BADARG); + } else { + ERTS_BIF_ERROR_TRAPPED1(BIF_P, + BADARG, + bif_export[BIF_iolist_size_1], + input_list); + } + + L_save_state_and_trap: + if (context == NULL) { + Binary *state_bin = erts_create_magic_binary(sizeof(ErtsIOListSizeContext), + iolist_size_ctx_bin_dtor); + Eterm* hp = HAlloc(BIF_P, ERTS_MAGIC_REF_THING_SIZE); + state_mref = erts_mk_magic_ref(&hp, &MSO(BIF_P), state_bin); + context = ERTS_MAGIC_BIN_DATA(state_bin); + } + context->obj = obj; + context->size = size; + context->acc = acc; + context->is_trap_at_L_iter_list = is_trap_at_L_iter_list; + context->input_list = input_list; + ESTACK_SAVE(s, &context->stack); + erts_set_gc_state(BIF_P, 0); + BUMP_ALL_REDS(BIF_P); + BIF_TRAP1(bif_export[BIF_iolist_size_1], BIF_P, state_mref); } /**********************************************************************/ diff --git a/erts/emulator/beam/erl_alloc.types b/erts/emulator/beam/erl_alloc.types index 490a033b8a..e7329daa2d 100644 --- a/erts/emulator/beam/erl_alloc.types +++ b/erts/emulator/beam/erl_alloc.types @@ -347,16 +347,17 @@ type COUNTERS STANDARD SYSTEM erl_bif_counters # Types used by system specific code # -type TEMP_TERM TEMPORARY SYSTEM temp_term -type DRV_TAB LONG_LIVED SYSTEM drv_tab -type DRV_EV_STATE LONG_LIVED SYSTEM driver_event_state -type DRV_SEL_D_STATE FIXED_SIZE SYSTEM driver_select_data_state -type NIF_SEL_D_STATE FIXED_SIZE SYSTEM enif_select_data_state -type POLLSET LONG_LIVED SYSTEM pollset -type POLLSET_UPDREQ SHORT_LIVED SYSTEM pollset_update_req -type POLL_FDS LONG_LIVED SYSTEM poll_fds -type FD_STATUS LONG_LIVED SYSTEM fd_status -type SELECT_FDS LONG_LIVED SYSTEM select_fds +type TEMP_TERM TEMPORARY SYSTEM temp_term +type SHORT_LIVED_TERM SHORT_LIVED SYSTEM short_lived_term +type DRV_TAB LONG_LIVED SYSTEM drv_tab +type DRV_EV_STATE LONG_LIVED SYSTEM driver_event_state +type DRV_SEL_D_STATE FIXED_SIZE SYSTEM driver_select_data_state +type NIF_SEL_D_STATE FIXED_SIZE SYSTEM enif_select_data_state +type POLLSET LONG_LIVED SYSTEM pollset +type POLLSET_UPDREQ SHORT_LIVED SYSTEM pollset_update_req +type POLL_FDS LONG_LIVED SYSTEM poll_fds +type FD_STATUS LONG_LIVED SYSTEM fd_status +type SELECT_FDS LONG_LIVED SYSTEM select_fds +if unix diff --git a/erts/emulator/beam/erl_dirty_bif.tab b/erts/emulator/beam/erl_dirty_bif.tab index 609869ad9f..656acfebdb 100644 --- a/erts/emulator/beam/erl_dirty_bif.tab +++ b/erts/emulator/beam/erl_dirty_bif.tab @@ -57,7 +57,6 @@ dirty-cpu erts_debug:lcnt_clear/0 # and debug purposes only. We really do *not* want to execute these # on dirty schedulers on a real system. -dirty-cpu-test erlang:iolist_size/1 dirty-cpu-test erlang:make_tuple/2 dirty-cpu-test erlang:make_tuple/3 dirty-cpu-test erlang:append_element/2 |