diff options
Diffstat (limited to 'erts/emulator/beam/external.c')
-rw-r--r-- | erts/emulator/beam/external.c | 113 |
1 files changed, 107 insertions, 6 deletions
diff --git a/erts/emulator/beam/external.c b/erts/emulator/beam/external.c index 3f40dcfca3..045fee5b06 100644 --- a/erts/emulator/beam/external.c +++ b/erts/emulator/beam/external.c @@ -125,12 +125,15 @@ static ErtsExtSzRes encode_size_struct_int(TTBSizeContext*, ErtsAtomCacheMap *ac static Export binary_to_term_trap_export; static BIF_RETTYPE binary_to_term_trap_1(BIF_ALIST_1); static Sint transcode_dist_obuf(ErtsDistOutputBuf*, DistEntry*, Uint64 dflags, Sint reds); +static byte *begin_hopefull_data(TTBEncodeContext *ctx, byte *ep); +static byte *end_hopefull_data(TTBEncodeContext *ctx, byte *ep, Uint fallback_size); static byte *hopefull_bit_binary(TTBEncodeContext* ctx, byte **epp, Binary *pb_val, Eterm pb_term, byte *bytes, byte bitoffs, byte bitsize, Uint sz); static void hopefull_export(TTBEncodeContext* ctx, byte **epp, Export* exp, Uint32 dflags, struct erl_off_heap_header** off_heap); static void store_in_vec(TTBEncodeContext *ctx, byte *ep, Binary *ohbin, Eterm ohpb, byte *ohp, Uint ohsz); +static Uint32 calc_iovec_fun_size(SysIOVec* iov, Uint32 fun_high_ix, byte* size_p); void erts_init_external(void) { erts_init_trap_export(&term_to_binary_trap_export, @@ -3098,8 +3101,47 @@ enc_term_int(TTBEncodeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, byte* ep, break; case ENC_PATCH_FUN_SIZE: { - byte* size_p = (byte *) obj; - put_int32(ep - size_p, size_p); + byte* size_p = (byte *) obj; + Sint32 fun_sz; + + if (use_iov && !ErtsInArea(size_p, ctx->cptr, ep - ctx->cptr)) { + ASSERT(ctx->vlen > 0); + fun_sz = (ep - ctx->cptr) + + calc_iovec_fun_size(ctx->iov, ctx->vlen-1, size_p); + + if (dflags & DFLAG_PENDING_CONNECT) { + /* + * Problem: The fun may contain hopefully encoded stuff + * in its environment. This makes the correct fun size + * may not be known until a final fallback transcoding + * has been done in transcode_dist_obuf(). + */ + ep = begin_hopefull_data(ctx, ep); + *ep++ = HOPEFUL_END_OF_FUN; + sys_memcpy(ep, &size_p, sizeof(size_p)); + ep += sizeof(size_p); + ep = end_hopefull_data(ctx, ep, 0); + ASSERT(ctx->iov[ctx->vlen - 1].iov_len + == 1 + sizeof(size_p)); + ASSERT(*(byte*)ctx->iov[ctx->vlen - 1].iov_base + == HOPEFUL_END_OF_FUN); + /* + * The HOPEFUL_END_OF_FUN iovec data entry encoded above + * contains no actual payload, only meta data to patch + * the correct fun size in transcode_dist_obuf(). + * Therefor reset its iov_len to zero to avoid output as + * payload. + */ + ctx->fragment_eiovs[ctx->frag_ix].size -= 1 + sizeof(size_p); + ctx->iov[ctx->vlen - 1].iov_len = 0; + } + } + else { + /* No iovec encoding or still in same iovec buffer as start + * of fun. Easy to calculate fun size. */ + fun_sz = ep - size_p; + } + put_int32(fun_sz, size_p); } goto outer_loop; case ENC_BIN_COPY: { @@ -3606,6 +3648,7 @@ enc_term_int(TTBEncodeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, byte* ep, int 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 */ @@ -4967,7 +5010,8 @@ encode_size_struct_int(TTBSizeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, } #define LIST_TAIL_OP ((0 << _TAG_PRIMARY_SIZE) | TAG_PRIMARY_HEADER) -#define TERM_ARRAY_OP(N) (((N) << _TAG_PRIMARY_SIZE) | TAG_PRIMARY_HEADER) +#define PATCH_FUN_SIZE_OP ((1 << _TAG_PRIMARY_SIZE) | TAG_PRIMARY_HEADER) +#define TERM_ARRAY_OP(N) (((N+1) << _TAG_PRIMARY_SIZE) | TAG_PRIMARY_HEADER) #define TERM_ARRAY_OP_DEC(OP) ((OP) - (1 << _TAG_PRIMARY_SIZE)) @@ -5212,7 +5256,8 @@ encode_size_struct_int(TTBSizeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, + 1 /* 2 tuple size */ + 1 /* BINARY_EXT */ + 4 /* binary size */); - trailing_result = (1 /* SMALL_INTEGER_EXT */ + trailing_result = (1 /* trailing bits */ + + 1 /* SMALL_INTEGER_EXT */ + 1 /* bitsize */); } csz = result - ctx->last_result; @@ -5248,6 +5293,7 @@ encode_size_struct_int(TTBSizeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, during a pending connect. */ Uint csz = result - ctx->last_result; ASSERT(dflags & DFLAG_BIT_BINARIES); + ASSERT(vlen >= 0); /* potentially multiple elements leading up to binary */ vlen += (csz + MAX_SYSIOVEC_IOVLEN - 1)/MAX_SYSIOVEC_IOVLEN; @@ -5284,6 +5330,10 @@ encode_size_struct_int(TTBSizeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, ErlFunThing* funp = (ErlFunThing *) fun_val(obj); ASSERT(dflags & DFLAG_NEW_FUN_TAGS); + if (dflags & DFLAG_PENDING_CONNECT) { + ASSERT(vlen >= 0); + WSTACK_PUSH(s, PATCH_FUN_SIZE_OP); + } result += 20+1+1+4; /* New ID + Tag */ result += 4; /* Length field (number of free variables */ result += encode_size_struct2(acmp, funp->creator, dflags); @@ -5315,6 +5365,7 @@ encode_size_struct_int(TTBSizeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, * the hopefull index + hopefull encoding is larger... */ ASSERT(dflags & DFLAG_EXPORT_PTR_TAG); + ASSERT(vlen >= 0); csz = tmp_result - ctx->last_result; /* potentially multiple elements leading up to hopefull entry */ vlen += (csz/MAX_SYSIOVEC_IOVLEN + 1 @@ -5330,6 +5381,7 @@ encode_size_struct_int(TTBSizeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, obj); } + pop_next: if (WSTACK_ISEMPTY(s)) { break; } @@ -5347,6 +5399,19 @@ encode_size_struct_int(TTBSizeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, } break; + case PATCH_FUN_SIZE_OP: { + Uint csz; + ASSERT(vlen >= 0 && (dflags & DFLAG_PENDING_CONNECT)); + csz = result - ctx->last_result; + /* potentially multiple elements leading up to hopefull entry */ + vlen += (csz/MAX_SYSIOVEC_IOVLEN + 1 + + 1); /* hopefull entry */ + result += (4 /* hopefull index */ + + 1 /* HOPEFUL_END_OF_FUN */ + + sizeof(byte*)); /* size_p */ + ctx->last_result = result; + goto pop_next; + } case TERM_ARRAY_OP(1): obj = *(Eterm*)WSTACK_POP(s); break; @@ -5781,6 +5846,29 @@ transcode_decode_state_destroy(ErtsTranscodeDecodeState *state) erts_free(ERTS_ALC_T_TMP, state->hp); } +static Uint32 +calc_iovec_fun_size(SysIOVec* iov, Uint32 fun_high_ix, byte* size_p) +{ + Sint32 ix; + Uint32 fun_size = 0; + + ASSERT(size_p[-1] == NEW_FUN_EXT); + + /* + * Search backwards for start of fun while adding up its total byte size. + */ + for (ix = fun_high_ix; ix >= 0; ix--) { + fun_size += iov[ix].iov_len; + + if (ErtsInArea(size_p, iov[ix].iov_base, iov[ix].iov_len)) { + fun_size -= (size_p - (byte*)iov[ix].iov_base); + break; + } + } + ERTS_ASSERT(ix >= 0); + return fun_size; +} + static Sint transcode_dist_obuf(ErtsDistOutputBuf* ob, DistEntry* dep, @@ -6061,7 +6149,7 @@ Sint transcode_dist_obuf(ErtsDistOutputBuf* ob, case EXPORT_EXT: { byte *start_ep, *end_ep; Eterm module, function; - if (!(hopefull_flags & DFLAG_EXPORT_PTR_TAG)) + if (dflags & DFLAG_EXPORT_PTR_TAG) break; /* Read original encoding... */ ep++; @@ -6106,7 +6194,7 @@ Sint transcode_dist_obuf(ErtsDistOutputBuf* ob, Uint bin_sz; byte bitsize, epilog_byte; ASSERT(hopefull_ix != ERTS_NO_HIX); - if (!(hopefull_flags & DFLAG_BIT_BINARIES)) { + if (dflags & DFLAG_BIT_BINARIES) { /* skip to epilog... */ hopefull_ix = new_hopefull_ix; ep = (byte *) iov[hopefull_ix].iov_base; @@ -6185,6 +6273,19 @@ Sint transcode_dist_obuf(ErtsDistOutputBuf* ob, break; } + case HOPEFUL_END_OF_FUN: { + byte* size_p; + Uint32 fun_sz; + + ASSERT(iov[hopefull_ix].iov_len == 0); + ep++; + sys_memcpy(&size_p, ep, sizeof(size_p)); + + fun_sz = calc_iovec_fun_size(iov, hopefull_ix-1, size_p); + put_int32(fun_sz, size_p); + break; + } + default: ERTS_INTERNAL_ERROR("Unexpected external tag"); break; |