diff --git a/erts/doc/src/erlang.xml b/erts/doc/src/erlang.xml index 345e8f000a22..c93987c943b1 100644 --- a/erts/doc/src/erlang.xml +++ b/erts/doc/src/erlang.xml @@ -6923,6 +6923,11 @@ receive_replies(ReqId, N, Acc) ->

Dictionary is the process dictionary.

+ {{dictionary, Key}, Value} + +

Value associated with Key + in the process dictionary.

+
{error_handler, Module}

Module is the error handler module used by diff --git a/erts/emulator/beam/erl_alloc.h b/erts/emulator/beam/erl_alloc.h index 00ab6284d78b..dc931e825450 100644 --- a/erts/emulator/beam/erl_alloc.h +++ b/erts/emulator/beam/erl_alloc.h @@ -355,6 +355,16 @@ erts_alloc_get_verify_unused_temp_alloc(Allctr_t **allctr); #define ERTS_ALC_DATA_ALIGN_SIZE(SZ) \ (((((SZ) - 1) / 8) + 1) * 8) +#if defined(ARCH_64) +#define ERTS_ALC_WORD_ALIGN_SIZE(SZ) \ + ERTS_ALC_DATA_ALIGN_SIZE((SZ)) +#elif defined(ARCH_32) +#define ERTS_ALC_WORD_ALIGN_SIZE(SZ) \ + (((((SZ) - 1) / 4) + 1) * 4) +#else +#error "Not supported word size" +#endif + #define ERTS_ALC_CACHE_LINE_ALIGN_SIZE(SZ) \ (((((SZ) - 1) / ERTS_CACHE_LINE_SIZE) + 1) * ERTS_CACHE_LINE_SIZE) diff --git a/erts/emulator/beam/erl_bif_info.c b/erts/emulator/beam/erl_bif_info.c index 867db56f390c..8e47f4183045 100644 --- a/erts/emulator/beam/erl_bif_info.c +++ b/erts/emulator/beam/erl_bif_info.c @@ -778,6 +778,7 @@ collect_one_suspend_monitor(ErtsMonitor *mon, void *vsmicp, Sint reds) #define ERTS_PI_IX_FULLSWEEP_AFTER 35 #define ERTS_PI_IX_PARENT 36 #define ERTS_PI_IX_ASYNC_DIST 37 +#define ERTS_PI_IX_DICTIONARY_LOOKUP 38 #define ERTS_PI_FLAG_SINGELTON (1 << 0) #define ERTS_PI_FLAG_ALWAYS_WRAP (1 << 1) @@ -785,6 +786,7 @@ collect_one_suspend_monitor(ErtsMonitor *mon, void *vsmicp, Sint reds) #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_FLAG_KEY_TUPLE2 (1 << 6) #define ERTS_PI_UNRESERVE(RS, SZ) \ (ASSERT((RS) >= (SZ)), (RS) -= (SZ)) @@ -835,7 +837,8 @@ static ErtsProcessInfoArgs pi_args[] = { {am_magic_ref, 0, ERTS_PI_FLAG_FORCE_SIG_SEND, ERTS_PROC_LOCK_MAIN}, {am_fullsweep_after, 0, 0, ERTS_PROC_LOCK_MAIN}, {am_parent, 0, 0, ERTS_PROC_LOCK_MAIN}, - {am_async_dist, 0, 0, ERTS_PROC_LOCK_MAIN} + {am_async_dist, 0, 0, ERTS_PROC_LOCK_MAIN}, + {am_dictionary, 3, ERTS_PI_FLAG_FORCE_SIG_SEND|ERTS_PI_FLAG_KEY_TUPLE2, ERTS_PROC_LOCK_MAIN}, }; #define ERTS_PI_ARGS ((int) (sizeof(pi_args)/sizeof(pi_args[0]))) @@ -847,11 +850,19 @@ static ErtsProcessInfoArgs pi_args[] = { #endif static ERTS_INLINE Eterm -pi_ix2arg(int ix) +pi_ix2arg(Eterm **hpp, int ix, Eterm extra) { + Eterm res; if (ix < 0 || ERTS_PI_ARGS <= ix) return am_undefined; - return pi_args[ix].name; + + if (!(pi_args[ix].flags & ERTS_PI_FLAG_KEY_TUPLE2)) + return pi_args[ix].name; + + ASSERT(is_value(extra)); + res = TUPLE2(*hpp, pi_args[ix].name, extra); + *hpp += 3; + return res; } static ERTS_INLINE int @@ -879,7 +890,7 @@ pi_ix2locks(int ix) } static ERTS_INLINE int -pi_arg2ix(Eterm arg) +pi_arg2ix(Eterm arg, Eterm *extrap) { switch (arg) { case am_registered_name: @@ -959,6 +970,17 @@ pi_arg2ix(Eterm arg) case am_async_dist: return ERTS_PI_IX_ASYNC_DIST; default: + if (is_tuple_arity(arg, 2)) { + Eterm *tpl = tuple_val(arg); + if (extrap) + *extrap = tpl[2]; + switch (tpl[1]) { + case am_dictionary: + return ERTS_PI_IX_DICTIONARY_LOOKUP; + default: + break; + } + } return -1; } } @@ -1005,7 +1027,10 @@ process_info_init(void) { /* Make sure the process_info argument mappings are consistent */ int ix; for (ix = 0; ix < ERTS_PI_ARGS; ix++) { - ASSERT(pi_arg2ix(pi_ix2arg(ix)) == ix); + Eterm heap[3]; + Eterm *hp = &heap[0]; + ASSERT(pi_arg2ix(pi_ix2arg(&hp, ix, am_ok), NULL) == ix); + ASSERT(hp <= &heap[3]); } } #endif @@ -1018,6 +1043,7 @@ process_info_aux(Process *c_p, Process *rp, ErtsProcLocks rp_locks, int item_ix, + Eterm extra, Sint *msgq_len_p, int flags, Uint *reserve_sizep, @@ -1029,7 +1055,8 @@ erts_process_info(Process *c_p, Process *rp, ErtsProcLocks rp_locks, int *item_ix, - int item_ix_len, + Eterm *item_extra, + int item_len, int flags, Uint reserve_size, Uint *reds) @@ -1040,8 +1067,27 @@ erts_process_info(Process *c_p, Sint msgq_len = -1; if (ERTS_PI_FLAG_SINGELTON & flags) { - ASSERT(item_ix_len == 1); - res = process_info_aux(c_p, hfact, rp, rp_locks, item_ix[0], + Eterm extra; + ASSERT(item_len == 1); + if (!item_extra) { + extra = THE_NON_VALUE; + ASSERT(!(pi_ix2flags(item_ix[0]) & ERTS_PI_FLAG_KEY_TUPLE2)); + } + else { + extra = item_extra[0]; + ASSERT(pi_ix2flags(item_ix[0]) & ERTS_PI_FLAG_KEY_TUPLE2); + ASSERT(is_value(extra)); + if ((flags & ERTS_PI_FLAG_REQUEST_FOR_OTHER) + && is_not_immed(extra)) { + Eterm *hp; + Uint sz = size_object(extra); + ASSERT(sz); + ERTS_PI_UNRESERVE(reserve_size, sz); + hp = erts_produce_heap(hfact, sz, reserve_size); + extra = copy_struct(extra, sz, &hp, hfact->off_heap); + } + } + res = process_info_aux(c_p, hfact, rp, rp_locks, item_ix[0], extra, &msgq_len, flags, &reserve_size, reds); return res; } @@ -1057,19 +1103,21 @@ erts_process_info(Process *c_p, * the queue contain bad distribution messages). */ if (flags & ERTS_PI_FLAG_WANT_MSGS) { - ix = pi_arg2ix(am_messages); + ix = pi_arg2ix(am_messages, NULL); ASSERT(part_res[ix] == THE_NON_VALUE); - res = process_info_aux(c_p, hfact, rp, rp_locks, ix, + res = process_info_aux(c_p, hfact, rp, rp_locks, ix, THE_NON_VALUE, &msgq_len, flags, &reserve_size, reds); ASSERT(res != am_undefined); ASSERT(res != THE_NON_VALUE); part_res[ix] = res; } - for (item_ix_ix = item_ix_len - 1; item_ix_ix >= 0; item_ix_ix--) { + for (item_ix_ix = item_len - 1; item_ix_ix >= 0; item_ix_ix--) { ix = item_ix[item_ix_ix]; + if (pi_ix2flags(ix) & ERTS_PI_FLAG_KEY_TUPLE2) + continue; if (part_res[ix] == THE_NON_VALUE) { - res = process_info_aux(c_p, hfact, rp, rp_locks, ix, + res = process_info_aux(c_p, hfact, rp, rp_locks, ix, THE_NON_VALUE, &msgq_len, flags, &reserve_size, reds); ASSERT(res != am_undefined); ASSERT(res != THE_NON_VALUE); @@ -1079,31 +1127,53 @@ erts_process_info(Process *c_p, res = NIL; - for (item_ix_ix = item_ix_len - 1; item_ix_ix >= 0; item_ix_ix--) { + for (item_ix_ix = item_len - 1; item_ix_ix >= 0; item_ix_ix--) { + Eterm *hp, val; ix = item_ix[item_ix_ix]; - ASSERT(part_res[ix] != THE_NON_VALUE); - /* - * If we should ignore the value of registered_name, - * its value is nil. For more info, see comment in the - * beginning of process_info_aux(). - */ - if (is_nil(part_res[ix])) { - 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); + if (pi_ix2flags(ix) & ERTS_PI_FLAG_KEY_TUPLE2) { + Eterm extra = item_extra[item_ix_ix]; + ASSERT(is_value(extra)); + ASSERT(part_res[ix] == THE_NON_VALUE); + if ((flags & ERTS_PI_FLAG_REQUEST_FOR_OTHER) + && is_not_immed(extra)) { + Uint sz = size_object(extra); + ASSERT(sz); + ERTS_PI_UNRESERVE(reserve_size, sz); + hp = erts_produce_heap(hfact, sz, reserve_size); + extra = copy_struct(extra, sz, &hp, hfact->off_heap); + } + val = process_info_aux(c_p, hfact, rp, rp_locks, ix, extra, + &msgq_len, flags, &reserve_size, reds); + } + else { + ASSERT(part_res[ix] != THE_NON_VALUE); + /* + * If we should ignore the value of registered_name, + * its value is nil. For more info, see comment in the + * beginning of process_info_aux(). + */ + if (is_nil(part_res[ix])) { + ASSERT(!(flags & ERTS_PI_FLAG_ALWAYS_WRAP)); + ASSERT(pi_ix2arg(NULL, ix, THE_NON_VALUE) == am_registered_name); + continue; + } + val = part_res[ix]; } + + ERTS_PI_UNRESERVE(reserve_size, 2); + hp = erts_produce_heap(hfact, 2, reserve_size); + res = CONS(hp, val, res); } return res; } static void -pi_setup_grow(int **arr, int *def_arr, Uint *sz, int ix); +pi_setup_grow(int **arr, int *def_arr, Eterm **extra_arr, + Eterm *def_extra_arr, Uint *sz, int ix); +static void +pi_setup_init_extra(Eterm **extra_arr, Eterm *def_extra_arr, Uint sz); + #ifdef DEBUG static int @@ -1225,7 +1295,9 @@ 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]; + Eterm def_extra_arr[ERTS_PI_DEF_ARR_SZ]; int *item_ix = &def_arr[0]; + Eterm *item_extra = NULL; Process *rp = NULL; erts_aint32_t state; BIF_RETTYPE ret; @@ -1238,8 +1310,9 @@ process_info_bif(Process *c_p, Eterm pid, Eterm opt, int always_wrap, int pi2) ERTS_CT_ASSERT(ERTS_PI_DEF_ARR_SZ > 0); - if (is_atom(opt)) { - int ix = pi_arg2ix(opt); + if (is_atom(opt) || is_tuple_arity(opt, 2)) { + Eterm extra = THE_NON_VALUE; + int ix = pi_arg2ix(opt, &extra); item_ix[0] = ix; len = 1; locks = pi_ix2locks(ix); @@ -1248,6 +1321,11 @@ process_info_bif(Process *c_p, Eterm pid, Eterm opt, int always_wrap, int pi2) flags |= pi_ix2flags(ix); if (ix < 0) goto badarg; + if (flags & ERTS_PI_FLAG_KEY_TUPLE2) { + ASSERT(is_value(extra)); + item_extra = &def_extra_arr[0]; + item_extra[0] = extra; + } } else { Eterm list = opt; @@ -1259,23 +1337,34 @@ process_info_bif(Process *c_p, Eterm pid, Eterm opt, int always_wrap, int pi2) flags = 0; while (is_list(list)) { + int item_flags; + Eterm extra = THE_NON_VALUE; Eterm *consp = list_val(list); Eterm arg = CAR(consp); - int ix = pi_arg2ix(arg); + int ix = pi_arg2ix(arg, &extra); if (ix < 0) goto badarg; if (len >= size) - pi_setup_grow(&item_ix, def_arr, &size, len); + pi_setup_grow(&item_ix, def_arr, &item_extra, + def_extra_arr, &size, len); - item_ix[len++] = ix; + item_ix[len] = ix; locks |= pi_ix2locks(ix); - flags |= pi_ix2flags(ix); + item_flags = pi_ix2flags(ix); + flags |= item_flags; reserve_size += pi_ix2rsz(ix); reserve_size += 3; /* 2-tuple */ reserve_size += 2; /* cons */ - + if (item_flags & ERTS_PI_FLAG_KEY_TUPLE2) { + ASSERT(is_value(extra)); + if (!item_extra) + pi_setup_init_extra(&item_extra, def_extra_arr, size); + ASSERT(item_extra); + item_extra[len] = extra; + } + len++; list = CDR(consp); } @@ -1345,8 +1434,8 @@ process_info_bif(Process *c_p, Eterm pid, Eterm opt, int always_wrap, int pi2) erts_factory_proc_init(&hfact, c_p); - res = erts_process_info(c_p, &hfact, rp, locks, item_ix, len, - flags, reserve_size, &reds); + res = erts_process_info(c_p, &hfact, rp, locks, item_ix, item_extra, + len, flags, reserve_size, &reds); erts_factory_close(&hfact); @@ -1384,6 +1473,9 @@ process_info_bif(Process *c_p, Eterm pid, Eterm opt, int always_wrap, int pi2) if (item_ix != def_arr) erts_free(ERTS_ALC_T_TMP, item_ix); + if (item_extra && item_extra != def_extra_arr) + erts_free(ERTS_ALC_T_TMP, item_extra); + return ret; badarg: @@ -1418,6 +1510,7 @@ send_signal: { */ erts_msgq_set_save_end(c_p); enqueued = erts_proc_sig_send_process_info_request(c_p, pid, item_ix, + item_extra, len, need_msgq_len, flags, reserve_size, ref); @@ -1432,19 +1525,54 @@ send_signal: { } static void -pi_setup_grow(int **arr, int *def_arr, Uint *sz, int ix) +pi_setup_grow(int **arr, int *def_arr, Eterm **extra_arr, + Eterm *def_extra_arr, Uint *sz, int ix) { *sz = (ix+1) + ERTS_PI_DEF_ARR_SZ; - if (*arr != def_arr) + if (*arr != def_arr) { *arr = erts_realloc(ERTS_ALC_T_TMP, *arr, (*sz)*sizeof(int)); + if (*extra_arr) { + Eterm *new_extra_arr; + int i; + ASSERT(*extra_arr != def_extra_arr); + new_extra_arr = erts_realloc(ERTS_ALC_T_TMP, *extra_arr, + (*sz)*sizeof(Eterm)); + for (i = ix; i < *sz; i++) + new_extra_arr[i] = THE_NON_VALUE; + *extra_arr = new_extra_arr; + } + } 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; + if (*extra_arr) { + int i; + Eterm *new_extra_arr = erts_alloc(ERTS_ALC_T_TMP, + (*sz)*sizeof(Eterm)); + ASSERT(*extra_arr == def_extra_arr); + sys_memcpy((void *) new_extra_arr, (void *) def_extra_arr, + sizeof(Eterm)*ERTS_PI_DEF_ARR_SZ); + for (i = ERTS_PI_DEF_ARR_SZ; i < *sz; i++) + new_extra_arr[i] = THE_NON_VALUE; + *extra_arr = new_extra_arr; + } } } +static void +pi_setup_init_extra(Eterm **extra_arr, Eterm *def_extra_arr, Uint sz) +{ + Eterm *new_extra_arr; + int i; + new_extra_arr = (sz > ERTS_PI_DEF_ARR_SZ + ? (Eterm *) erts_alloc(ERTS_ALC_T_TMP, sz*sizeof(Eterm)) + : def_extra_arr); + for (i = 0; i < sz; i++) + new_extra_arr[i] = THE_NON_VALUE; + *extra_arr = new_extra_arr; +} BIF_RETTYPE process_info_2(BIF_ALIST_2) { @@ -1462,12 +1590,14 @@ process_info_aux(Process *c_p, Process *rp, ErtsProcLocks rp_locks, int item_ix, + Eterm extra, Sint *msgq_len_p, int flags, Uint *reserve_sizep, Uint *reds) { Eterm *hp; + Eterm key; Eterm res = NIL; Uint reserved; Uint reserve_size = *reserve_sizep; @@ -2174,16 +2304,39 @@ process_info_aux(Process *c_p, break; } + case ERTS_PI_IX_DICTIONARY_LOOKUP: { + Uint sz; + + ASSERT(is_value(extra)); + res = ((ERTS_TRACE_FLAGS(rp) & F_SENSITIVE) + ? am_undefined + : erts_pd_hash_get(rp, extra)); + sz = (!(flags & ERTS_PI_FLAG_REQUEST_FOR_OTHER) || is_immed(res) + ? 0 + : size_object(res)); + + /* Allocate 3 words extra for key 2-tuple... */ + hp = erts_produce_heap(hfact, sz + 3, reserve_size); + + if (sz) + res = copy_struct(res, sz, &hp, hfact->off_heap); + + break; + } + default: return THE_NON_VALUE; /* will produce badarg */ } + key = pi_ix2arg(&hp, item_ix, extra); + ERTS_PI_UNRESERVE(reserve_size, 3); *reserve_sizep = reserve_size; hp = erts_produce_heap(hfact, 3, reserve_size); + res = TUPLE2(hp, key, res); - return TUPLE2(hp, pi_ix2arg(item_ix), res); + return res; } #undef MI_INC diff --git a/erts/emulator/beam/erl_proc_sig_queue.c b/erts/emulator/beam/erl_proc_sig_queue.c index c35729edfd01..f490abd41657 100644 --- a/erts/emulator/beam/erl_proc_sig_queue.c +++ b/erts/emulator/beam/erl_proc_sig_queue.c @@ -200,7 +200,9 @@ typedef struct { Uint reserve_size; Uint len; int flags; - int item_ix[1]; /* of len size in reality... */ + int *item_ix; + Eterm *item_extra; + ErlHeapFragment *extra_hfrag; } ErtsProcessInfoSig; #define ERTS_PROC_SIG_PI_MSGQ_LEN_IGNORE ((Sint) -1) @@ -509,6 +511,14 @@ destroy_sig_group_leader(ErtsSigGroupLeader *sgl) erts_free(ERTS_ALC_T_SIG_DATA, sgl); } +static void +destroy_process_info_sig(ErtsProcessInfoSig *pis) +{ + if (pis->extra_hfrag) + erts_cleanup_offheap(&pis->extra_hfrag->off_heap); + erts_free(ERTS_ALC_T_SIG_DATA, pis); +} + static ERTS_INLINE void sig_enqueue_trace(ErtsPTabElementCommon *sender, Eterm from, ErtsMessage **sigp, int op, Process *rp, @@ -2684,16 +2694,89 @@ int erts_proc_sig_send_process_info_request(Process *c_p, Eterm to, int *item_ix, + Eterm *item_extra, 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); + Uint size, item_ix_offs, extra_offs, extra_hfrag_offs, + extra_hsz = 0, *extra_hszs = NULL; + ErtsProcessInfoSig *pis; int res; + if (item_extra) { + int i; + extra_hszs = erts_alloc(ERTS_ALC_T_TMP, sizeof(Uint)*len); + for (i = 0; i < len; i++) { + Eterm extra = item_extra[i]; + extra_hszs[i] = (is_non_value(extra) || is_immed(extra) + ? 0 + : size_object(extra)); + extra_hsz += extra_hszs[i]; + } + } + + size = ERTS_ALC_WORD_ALIGN_SIZE(sizeof(ErtsProcessInfoSig)); + item_ix_offs = size; + size += ERTS_ALC_WORD_ALIGN_SIZE(len * sizeof(int)); + if (!item_extra) { + extra_offs = 0; + extra_hfrag_offs = 0; + } + else { + extra_offs = size; + size += ERTS_ALC_WORD_ALIGN_SIZE(len * sizeof(Eterm)); + extra_hfrag_offs = size; + if (extra_hsz) + size += ERTS_HEAP_FRAG_SIZE(extra_hsz); + } + pis = erts_alloc(ERTS_ALC_T_SIG_DATA, size); + pis->item_ix = (int *) (((char *) pis) + item_ix_offs); + if (!item_extra) { + ASSERT(!extra_offs); + pis->item_extra = NULL; + pis->extra_hfrag = NULL; + } + else { + int i; + Eterm *extra_hp; + ErlOffHeap *extra_off_heap; +#ifdef DEBUG + Eterm *end_hp = NULL; +#endif + ASSERT(extra_offs); + pis->item_extra = (Eterm *) (((char *) pis) + extra_offs); + if (!extra_hsz) { + extra_hp = NULL; + extra_off_heap = NULL; + pis->extra_hfrag = NULL; + } + else { + pis->extra_hfrag = (ErlHeapFragment *) (((char *) pis) + + extra_hfrag_offs); + ERTS_INIT_HEAP_FRAG(pis->extra_hfrag, extra_hsz, extra_hsz); + extra_hp = &pis->extra_hfrag->mem[0]; +#ifdef DEBUG + end_hp = extra_hp + extra_hsz; +#endif + extra_off_heap = &pis->extra_hfrag->off_heap; + } + for (i = 0; i < len; i++) { + if (!extra_hsz || !extra_hszs[i]) { + pis->item_extra[i] = item_extra[i]; + } + else { + pis->item_extra[i] = copy_struct(item_extra[i], extra_hszs[i], + &extra_hp, extra_off_heap); + } + } + ASSERT(extra_hp == end_hp); + erts_free(ERTS_ALC_T_TMP, extra_hszs); + reserve_size += sizeof(Eterm)*extra_hsz; + } + ASSERT(c_p); ASSERT(item_ix); ASSERT(len > 0); @@ -2730,7 +2813,7 @@ erts_proc_sig_send_process_info_request(Process *c_p, if (res) { (void) maybe_elevate_sig_handling_prio(c_p, -1, to); } else { - erts_free(ERTS_ALC_T_SIG_DATA, pis); + destroy_process_info_sig(pis); } return res; @@ -4713,12 +4796,17 @@ destroy_process_info_request(Process *c_p, ErtsProcessInfoSig *pisig) = (ErtsProcessInfoSig *) (((char *) marker) - offsetof(ErtsProcessInfoSig, marker)); - erts_free(ERTS_ALC_T_SIG_DATA, pisig2); + destroy_process_info_sig(pisig2); } } - if (dealloc_pisig) - erts_free(ERTS_ALC_T_SIG_DATA, pisig); + if (dealloc_pisig) { + destroy_process_info_sig(pisig); + } + else if (pisig->extra_hfrag) { + erts_cleanup_offheap(&pisig->extra_hfrag->off_heap); + pisig->extra_hfrag = NULL; + } } static int @@ -4826,7 +4914,7 @@ handle_process_info(Process *c_p, ErtsSigRecvTracing *tracing, 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->item_ix, pisig->item_extra, pisig->len, pisig->flags, reserve_size, &reds); hp = erts_produce_heap(&hfact, diff --git a/erts/emulator/beam/erl_proc_sig_queue.h b/erts/emulator/beam/erl_proc_sig_queue.h index 663d42d59fad..187b8d7a164a 100644 --- a/erts/emulator/beam/erl_proc_sig_queue.h +++ b/erts/emulator/beam/erl_proc_sig_queue.h @@ -866,7 +866,11 @@ erts_proc_sig_send_is_alive_request(Process *c_p, Eterm to, * @param[in] item_ix Info index array to pass to * erts_process_info() * - * @param[in] len Length of info index array + * @param[in] item_extra Extra terms array to pass to + * erts_process_info() + * + * @param[in] len Length of info index array and + * extra array if such is provided * * @param[in] need_msgq_len Non-zero if message queue * length is needed; otherwise, @@ -892,6 +896,7 @@ int erts_proc_sig_send_process_info_request(Process *c_p, Eterm to, int *item_ix, + Eterm *item_extra, int len, int need_msgq_len, int flags, diff --git a/erts/emulator/beam/erl_process.h b/erts/emulator/beam/erl_process.h index b266dc556182..6f166e372c51 100644 --- a/erts/emulator/beam/erl_process.h +++ b/erts/emulator/beam/erl_process.h @@ -2031,7 +2031,7 @@ void erts_dump_extended_process_state(fmtfn_t to, void *to_arg, erts_aint32_t ps 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 *item_ix, Eterm *item_extra, int item_len, int flags, Uint reserve_size, Uint *reds); typedef struct { diff --git a/erts/emulator/test/process_SUITE.erl b/erts/emulator/test/process_SUITE.erl index 1d546ff36e8b..10cddfdfa137 100644 --- a/erts/emulator/test/process_SUITE.erl +++ b/erts/emulator/test/process_SUITE.erl @@ -54,6 +54,7 @@ process_info_self_msgq_len_messages/1, process_info_self_msgq_len_more/1, process_info_msgq_len_no_very_long_delay/1, + process_info_dict_lookup/1, bump_reductions/1, low_prio/1, binary_owner/1, yield/1, yield2/1, otp_4725/1, dist_unlink_ack_exit_leak/1, bad_register/1, garbage_collect/1, otp_6237/1, @@ -178,7 +179,8 @@ groups() -> process_info_self_msgq_len, process_info_self_msgq_len_messages, process_info_self_msgq_len_more, - process_info_msgq_len_no_very_long_delay]}, + process_info_msgq_len_no_very_long_delay, + process_info_dict_lookup]}, {otp_7738, [], [otp_7738_waiting, otp_7738_suspended, otp_7738_resume]}, @@ -1260,7 +1262,8 @@ process_info_smoke_all(Config) when is_list(Config) -> message_queue_data, garbage_collection_info, magic_ref, - fullsweep_after], + fullsweep_after, + {dictionary, ets_ref}], {ok, Peer, Node} = ?CT_PEER(), RP = spawn_link(Node, fun process_info_smoke_all_tester/0), @@ -1554,6 +1557,200 @@ process_info_msgq_len_no_very_long_delay(Config) when is_list(Config) -> false = is_process_alive(P2), ok. +process_info_dict_lookup(Config) when is_list(Config) -> + Pid = spawn_link(fun proc_dict_helper/0), + {async_dist, AsyncDist} = process_info(Pid, async_dist), + Ref = make_ref(), + Bin = <<17:4096>>, + Int0 = 9999999999999999999999999999999999, + Int1 = 1111111111111111111111111111111111, + Tuple = {make_ref(), erlang:monotonic_time()}, + + %% Check that we can lookup dictionary values on another process... + pdh(Pid, put_async, [hej, hopp]), + pdh(Pid, put_async, [hopp, hej]), + pdh(Pid, put_async, [Ref, Int0]), + pdh(Pid, put_async, [Int0, Int1]), + pdh(Pid, put_async, [Pid, Ref]), + pdh(Pid, put_async, [Tuple, Bin]), + undefined = pdh(Pid, put, [Bin, Ref]), + + erlang:garbage_collect(Pid), + + {{dictionary, Ref}, Int0} = process_info(Pid, {dictionary, Ref}), + [{{dictionary, Ref}, Int0}] = process_info(Pid, [{dictionary, Ref}]), + + PIRes = process_info(Pid, [async_dist, + trap_exit, + {dictionary, hej}, + {dictionary, hopp}, + {dictionary, Ref}, + {dictionary, Int0}, + async_dist, + trap_exit, + {dictionary, Pid}, + {dictionary, Tuple}, + {dictionary, Bin}]), + ct:log("PIRes = ~p", [PIRes]), + PIRes = [{async_dist, AsyncDist}, + {trap_exit, false}, + {{dictionary, hej}, hopp}, + {{dictionary, hopp}, hej}, + {{dictionary, Ref}, Int0}, + {{dictionary, Int0}, Int1}, + {async_dist, AsyncDist}, + {trap_exit, false}, + {{dictionary, Pid}, Ref}, + {{dictionary, Tuple}, Bin}, + {{dictionary, Bin}, Ref}], + + pdh(Pid, erase_async, [hej]), + pdh(Pid, erase_async, [hopp]), + pdh(Pid, erase_async, [Ref]), + pdh(Pid, erase_async, [Int0]), + pdh(Pid, erase_async, [Pid]), + pdh(Pid, erase_async, [Tuple]), + Ref = pdh(Pid, erase, [Bin]), + + erlang:garbage_collect(Pid), + + {{dictionary, Ref}, undefined} = process_info(Pid, {dictionary, Ref}), + [{{dictionary, Ref}, undefined}] = process_info(Pid, [{dictionary, Ref}]), + + PIRes2 = process_info(Pid, [async_dist, + trap_exit, + {dictionary, hej}, + {dictionary, hopp}, + {dictionary, Ref}, + {dictionary, Int0}, + async_dist, + trap_exit, + {dictionary, Pid}, + {dictionary, Tuple}, + {dictionary, Bin}]), + ct:log("PIRes2 = ~p", [PIRes2]), + + PIRes2 = [{async_dist, AsyncDist}, + {trap_exit, false}, + {{dictionary, hej}, undefined}, + {{dictionary, hopp}, undefined}, + {{dictionary, Ref}, undefined}, + {{dictionary, Int0}, undefined}, + {async_dist, AsyncDist}, + {trap_exit, false}, + {{dictionary, Pid}, undefined}, + {{dictionary, Tuple}, undefined}, + {{dictionary, Bin}, undefined}], + + unlink(Pid), + exit(Pid,kill), + + %% Also check that it works on ourself... + + put(hej, hopp), + put(hopp, hej), + put(Ref, Int0), + put(Int0, Int1), + put(Pid, Ref), + put(Tuple, Bin), + undefined = put(Bin, Ref), + + erlang:garbage_collect(), + + {{dictionary, Ref}, Int0} = process_info(self(), {dictionary, Ref}), + [{{dictionary, Ref}, Int0}] = process_info(self(), [{dictionary, Ref}]), + + PIRes3 = process_info(self(), [async_dist, + trap_exit, + {dictionary, hej}, + {dictionary, hopp}, + {dictionary, Ref}, + {dictionary, Int0}, + async_dist, + trap_exit, + {dictionary, Pid}, + {dictionary, Tuple}, + {dictionary, Bin}]), + ct:log("PIRes3 = ~p", [PIRes3]), + PIRes3 = [{async_dist, AsyncDist}, + {trap_exit, false}, + {{dictionary, hej}, hopp}, + {{dictionary, hopp}, hej}, + {{dictionary, Ref}, Int0}, + {{dictionary, Int0}, Int1}, + {async_dist, AsyncDist}, + {trap_exit, false}, + {{dictionary, Pid}, Ref}, + {{dictionary, Tuple}, Bin}, + {{dictionary, Bin}, Ref}], + + erase(hej), + erase(hopp), + erase(Ref), + erase(Int0), + erase(Pid), + erase(Tuple), + Ref = erase(Bin), + + erlang:garbage_collect(), + + {{dictionary, Ref}, undefined} = process_info(self(), {dictionary, Ref}), + [{{dictionary, Ref}, undefined}] = process_info(self(), [{dictionary, Ref}]), + + PIRes4 = process_info(self(), [async_dist, + trap_exit, + {dictionary, hej}, + {dictionary, hopp}, + {dictionary, Ref}, + {dictionary, Int0}, + async_dist, + trap_exit, + {dictionary, Pid}, + {dictionary, Tuple}, + {dictionary, Bin}]), + ct:log("PIRes4 = ~p", [PIRes4]), + + PIRes4 = [{async_dist, AsyncDist}, + {trap_exit, false}, + {{dictionary, hej}, undefined}, + {{dictionary, hopp}, undefined}, + {{dictionary, Ref}, undefined}, + {{dictionary, Int0}, undefined}, + {async_dist, AsyncDist}, + {trap_exit, false}, + {{dictionary, Pid}, undefined}, + {{dictionary, Tuple}, undefined}, + {{dictionary, Bin}, undefined}], + + false = is_process_alive(Pid), + ok. + +pdh(Pid, AsyncOp, Args) when AsyncOp == put_async; + AsyncOp == erase_async -> + Pid ! {AsyncOp, Args}, + ok; +pdh(Pid, SyncOp, Args) -> + Ref = make_ref(), + Pid ! {SyncOp, self(), Ref, Args}, + receive {Ref, Res} -> Res end. + +proc_dict_helper() -> + receive + {put, From, Ref, [Key, Value]} -> + From ! {Ref, put(Key, Value)}; + {get, From, Ref, [Key]} -> + From ! {Ref, get(Key)}; + {get, From, Ref, []} -> + From ! {Ref, get()}; + {erase, From, Ref, [Key]} -> + From ! {Ref, erase(Key)}; + {put_async, [Key, Value]} -> + _ = put(Key, Value); + {erase_async, [Key]} -> + _ = erase(Key) + end, + proc_dict_helper(). + %% Tests erlang:bump_reductions/1. bump_reductions(Config) when is_list(Config) -> erlang:garbage_collect(), diff --git a/erts/preloaded/ebin/erlang.beam b/erts/preloaded/ebin/erlang.beam index 00ea9e5775fb..01476d307184 100644 Binary files a/erts/preloaded/ebin/erlang.beam and b/erts/preloaded/ebin/erlang.beam differ diff --git a/erts/preloaded/src/erlang.erl b/erts/preloaded/src/erlang.erl index 110e6e62894b..1ba97db9e034 100644 --- a/erts/preloaded/src/erlang.erl +++ b/erts/preloaded/src/erlang.erl @@ -2690,6 +2690,7 @@ process_flag(_Flag, _Value) -> current_location | current_stacktrace | dictionary | + {dictionary, Key :: term()} | error_handler | garbage_collection | garbage_collection_info | @@ -2733,6 +2734,7 @@ process_flag(_Flag, _Value) -> {line, Line :: pos_integer()}]}} | {current_stacktrace, Stack :: [stack_item()]} | {dictionary, Dictionary :: [{Key :: term(), Value :: term()}]} | + {{dictionary, Key :: term()}, Value :: term()} | {error_handler, Module :: module()} | {garbage_collection, GCInfo :: [{atom(),non_neg_integer()}]} | {garbage_collection_info, GCInfo :: [{atom(),non_neg_integer()}]} |