From 57551877d85ad7659201235e27498be42809fefb Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Thu, 18 Feb 2016 13:52:54 +0100 Subject: erts: Add enif_send with NULL as msg env This is an optimization for reducing the number of heap fragments allocated when sending a message where the majority of the message payload is on the sending process' heap. --- erts/doc/src/erl_nif.xml | 4 ++- erts/emulator/beam/erl_nif.c | 36 ++++++++++++++++----------- erts/emulator/test/nif_SUITE.erl | 13 ++++++++-- erts/emulator/test/nif_SUITE_data/nif_SUITE.c | 13 ++++++++++ 4 files changed, 48 insertions(+), 18 deletions(-) (limited to 'erts') diff --git a/erts/doc/src/erl_nif.xml b/erts/doc/src/erl_nif.xml index 1e95634d1b..1bfd98f664 100644 --- a/erts/doc/src/erl_nif.xml +++ b/erts/doc/src/erl_nif.xml @@ -1632,7 +1632,7 @@ enif_map_iterator_destroy(env, &iter); msg_env The environment of the message term. Must be a process independent environment allocated with - enif_alloc_env. + enif_alloc_env or NULL. msg The message term to send. @@ -1641,6 +1641,8 @@ enif_map_iterator_destroy(env, &iter); msg) will be invalidated by a successful call to enif_send. The environment should either be freed with enif_free_env of cleared for reuse with enif_clear_env.

+

If msg_env is set to NULL the msg term is copied and + the original term and its environemt is still valid after the call.

This function is only thread-safe when the emulator with SMP support is used. It can only be used in a non-SMP emulator from a NIF-calling thread.

Passing msg_env as NULL is only supported since diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c index d3030070b7..1012ced44b 100644 --- a/erts/emulator/beam/erl_nif.c +++ b/erts/emulator/beam/erl_nif.c @@ -312,7 +312,6 @@ int enif_send(ErlNifEnv* env, const ErlNifPid* to_pid, Process* rp; Process* c_p; ErtsMessage *mp; - ErlHeapFragment* frags; Eterm receiver = to_pid->pid; int flush_me = 0; int scheduler = erts_get_scheduler_id() != 0; @@ -340,25 +339,32 @@ int enif_send(ErlNifEnv* env, const ErlNifPid* to_pid, ASSERT(env == NULL || receiver != c_p->common.id); return 0; } - flush_env(msg_env); - frags = menv->env.heap_frag; - ASSERT(frags == MBUF(&menv->phony_proc)); - if (frags != NULL) { - /* Move all offheap's from phony proc to the first fragment. - Quick and dirty... */ - ASSERT(!is_offheap(&frags->off_heap)); - frags->off_heap = MSO(&menv->phony_proc); - clear_offheap(&MSO(&menv->phony_proc)); - menv->env.heap_frag = NULL; - MBUF(&menv->phony_proc) = NULL; + if (menv) { + flush_env(msg_env); + mp = erts_alloc_message(0, NULL); + mp->data.heap_frag = menv->env.heap_frag; + ASSERT(mp->data.heap_frag == MBUF(&menv->phony_proc)); + if (mp->data.heap_frag != NULL) { + /* Move all offheap's from phony proc to the first fragment. + Quick and dirty... */ + ASSERT(!is_offheap(&mp->data.heap_frag->off_heap)); + mp->data.heap_frag->off_heap = MSO(&menv->phony_proc); + clear_offheap(&MSO(&menv->phony_proc)); + menv->env.heap_frag = NULL; + MBUF(&menv->phony_proc) = NULL; + } + ASSERT(!is_offheap(&MSO(&menv->phony_proc))); + } else { + Uint sz = size_object(msg); + Eterm *hp; + mp = erts_alloc_message(sz, &hp); + msg = copy_struct(msg, sz, &hp, &mp->hfrag.off_heap); + ASSERT(hp == mp->hfrag.mem+mp->hfrag.used_size); } - ASSERT(!is_offheap(&MSO(&menv->phony_proc))); if (flush_me) { flush_env(env); /* Needed for ERTS_HOLE_CHECK */ } - mp = erts_alloc_message(0, NULL); - mp->data.heap_frag = frags; erts_queue_message(rp, &rp_locks, mp, msg, am_undefined); if (c_p == rp) rp_locks &= ~ERTS_PROC_LOCK_MAIN; diff --git a/erts/emulator/test/nif_SUITE.erl b/erts/emulator/test/nif_SUITE.erl index a185b72341..aa512b2360 100644 --- a/erts/emulator/test/nif_SUITE.erl +++ b/erts/emulator/test/nif_SUITE.erl @@ -1282,9 +1282,10 @@ send3_make_blob() -> repeat(N bsr 1, fun(_) -> grow_blob(MsgEnv,other_term(),rand:uniform(1 bsl 20)) end, void), - case (N band 1) of + case (N band 3) of 0 -> {term,copy_blob(MsgEnv)}; - 1 -> {msgenv,MsgEnv} + 1 -> {copy,copy_blob(MsgEnv)}; + _ -> {msgenv,MsgEnv} end end. @@ -1297,6 +1298,9 @@ send3_send(Pid, Msg) -> send3_send_nif(Pid, {term,Blob}) -> %%io:format("~p send term nif\n",[self()]), send_term(Pid, {blob, Blob}) =:= 1; +send3_send_nif(Pid, {copy,Blob}) -> + %%io:format("~p send term nif\n",[self()]), + send_copy_term(Pid, {blob, Blob}) =:= 1; send3_send_nif(Pid, {msgenv,MsgEnv}) -> %%io:format("~p send blob nif\n",[self()]), send3_blob(MsgEnv, Pid, blob) =:= 1. @@ -1305,6 +1309,10 @@ send3_send_bang(Pid, {term,Blob}) -> %%io:format("~p send term bang\n",[self()]), Pid ! {blob, Blob}, true; +send3_send_bang(Pid, {copy,Blob}) -> + %%io:format("~p send term bang\n",[self()]), + Pid ! {blob, Blob}, + true; send3_send_bang(Pid, {msgenv,MsgEnv}) -> %%io:format("~p send blob bang\n",[self()]), Pid ! {blob, copy_blob(MsgEnv)}, @@ -2062,6 +2070,7 @@ send_blob_thread(_,_,_) -> ?nif_stub. join_send_thread(_) -> ?nif_stub. copy_blob(_) -> ?nif_stub. send_term(_,_) -> ?nif_stub. +send_copy_term(_,_) -> ?nif_stub. reverse_list(_) -> ?nif_stub. echo_int(_) -> ?nif_stub. type_sizes() -> ?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 b3c6cc5ba3..e369fb3386 100644 --- a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c +++ b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c @@ -1466,6 +1466,18 @@ static ERL_NIF_TERM send_term(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[ return enif_make_int(env, ret); } +static ERL_NIF_TERM send_copy_term(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ + ErlNifEnv* menv; + ErlNifPid pid; + int ret; + if (!enif_get_local_pid(env, argv[0], &pid)) { + return enif_make_badarg(env); + } + ret = enif_send(env, &pid, NULL, argv[1]); + return enif_make_int(env, ret); +} + static ERL_NIF_TERM reverse_list(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { ERL_NIF_TERM rev_list; @@ -2142,6 +2154,7 @@ static ErlNifFunc nif_funcs[] = {"join_send_thread", 1, join_send_thread}, {"copy_blob", 1, copy_blob}, {"send_term", 2, send_term}, + {"send_copy_term", 2, send_copy_term}, {"reverse_list",1, reverse_list}, {"echo_int", 1, echo_int}, {"type_sizes", 0, type_sizes}, -- cgit v1.2.3