in erts/emulator/beam/dist.c [5285:5609]
BIF_RETTYPE erts_internal_dist_spawn_request_4(BIF_ALIST_4)
{
BIF_RETTYPE ret_val;
Eterm node = BIF_ARG_1;
Eterm mfa = BIF_ARG_2;
Eterm opts = BIF_ARG_3;
Eterm tag = am_spawn_reply;
Eterm mod, func, alist, new_opts, error, ref,
ok_result;
Uint nargs, nopts, rm_opts, rebuild_opts;
DistEntry *dep = NULL;
Eterm list;
ErtsDSigSendContext ctx;
int code, link = 0, monitor = 0, success_message, error_message;
ok_result = THE_NON_VALUE;
success_message = error_message = !0;
if (!is_atom(node))
goto badarg;
dep = erts_find_or_insert_dist_entry(node);
if (dep == erts_this_dist_entry)
goto badarg;
if (!is_tuple_arity(mfa, 3))
goto badarg;
else {
Eterm *tp = tuple_val(mfa);
mod = tp[1];
func = tp[2];
alist = tp[3];
if (!is_atom(mod))
goto badarg;
if (!is_atom(func))
goto badarg;
nargs = 0;
list = alist;
while (is_list(list)) {
Eterm *cp = list_val(list);
list = CDR(cp);
nargs++;
}
if (!is_nil(list))
goto badarg;
}
new_opts = list = opts;
nopts = 0;
rm_opts = 0;
rebuild_opts = 0;
while (is_list(list)) {
Eterm *cp = list_val(list);
Eterm car = CAR(cp);
list = CDR(cp);
nopts++;
switch (car) {
case am_link:
link = !0;
break;
case am_monitor:
monitor = !0;
break;
default:
if (is_tuple_arity(car, 2)) {
Eterm *tp = tuple_val(car);
switch (tp[1]) {
case am_reply_tag:
tag = tp[2];
if (0) {
case am_reply:
switch (tp[2]) {
case am_error_only:
success_message = 0;
error_message = !0;
break;
case am_success_only:
success_message = !0;
error_message = 0;
break;
case am_no:
success_message = 0;
error_message = 0;
break;
case am_yes:
success_message = !0;
error_message = !0;
break;
default:
goto badarg;
}
}
if (BIF_ARG_4 != am_spawn_request)
goto badarg;
rm_opts++;
new_opts = list;
rebuild_opts = nopts - rm_opts;
break;
default:
break;
}
}
break;
}
}
if (!is_nil(list))
goto badarg;
if (rm_opts) {
/*
* If no 'rebuild_opts', all options to drop were in
* the head of the 'opts' list. 'new_opts' now contain
* the tail of original option list without the dropped
* options.
*/
if (rebuild_opts) {
#ifdef DEBUG
Eterm reusable_tail, *hp_start;
#endif
Uint rm_cnt;
Eterm *hp, *prev_cp;
/*
* Remove 'reply_tag' option in option list.
* This options are mixed with other options.
*
* We build the list backwards and reuse tail
* without options to remove, if such exist.
*/
hp = HAlloc(BIF_P, 2*rebuild_opts);
#ifdef DEBUG
hp_start = hp;
reusable_tail = new_opts;
#endif
hp += 2*(rebuild_opts - 1);
new_opts = make_list(hp);
prev_cp = NULL;
list = opts;
rm_cnt = 0;
while (is_list(list)) {
Eterm *cp = list_val(list);
Eterm car = CAR(cp);
list = CDR(cp);
if (is_tuple_arity(car, 2)) {
Eterm *tp = tuple_val(car);
if (am_reply_tag == tp[1]
|| am_reply == tp[1]) {
rm_cnt++;
/* skip option */
if (rm_cnt == rm_opts) {
ASSERT(prev_cp);
ASSERT(list == reusable_tail);
CDR(prev_cp) = list;
break; /* last option to skip */
}
continue;
}
}
#ifdef DEBUG
rebuild_opts--;
#endif
CAR(hp) = car;
prev_cp = hp;
hp -= 2;
CDR(prev_cp) = make_list(hp);
}
ASSERT(hp == hp_start - 2);
ASSERT(rebuild_opts == 0);
}
opts = new_opts;
}
/* Arguments checked; do it... */
ref = erts_make_ref(BIF_P);
if (BIF_ARG_4 == am_spawn_request)
ok_result = ref;
else {
Eterm *hp = HAlloc(BIF_P, 3);
ASSERT(BIF_ARG_4 == am_spawn_opt);
ok_result = TUPLE2(hp, ref, monitor ? am_true : am_false);
}
code = erts_dsig_prepare(&ctx, dep,
BIF_P, ERTS_PROC_LOCK_MAIN,
ERTS_DSP_RLOCK, 0, 0, 1);
switch (code) {
case ERTS_DSIG_PREP_NOT_ALIVE:
case ERTS_DSIG_PREP_NOT_CONNECTED:
goto noconnection;
case ERTS_DSIG_PREP_CONNECTED:
if (!(dep->dflags & DFLAG_SPAWN)) {
erts_de_runlock(dep);
goto notsup;
}
/* Fall through... */
case ERTS_DSIG_PREP_PENDING: {
int inserted;
ErtsMonitorData *mdp;
Eterm nargs_term, mfna, *hp;
if (IS_USMALL(0, nargs)) {
hp = HAlloc(BIF_P, 4);
nargs_term = make_small(nargs);
}
else {
hp = HAlloc(BIF_P, 4+BIG_UINT_HEAP_SIZE);
nargs_term = uint_to_big(nargs, hp);
hp += BIG_UINT_HEAP_SIZE;
}
mfna = TUPLE3(hp, mod, func, nargs_term);
mdp = erts_monitor_create(ERTS_MON_TYPE_DIST_PROC, ref,
BIF_P->common.id, am_pending,
tag);
if (monitor)
mdp->origin.flags |= ERTS_ML_FLG_SPAWN_MONITOR;
if (link)
mdp->origin.flags |= ERTS_ML_FLG_SPAWN_LINK;
if (!success_message)
mdp->origin.flags |= ERTS_ML_FLG_SPAWN_NO_SMSG;
if (!error_message)
mdp->origin.flags |= ERTS_ML_FLG_SPAWN_NO_EMSG;
erts_monitor_tree_insert(&ERTS_P_MONITORS(BIF_P),
&mdp->origin);
inserted = erts_monitor_dist_insert(&mdp->target, dep->mld);
ASSERT(inserted); (void)inserted;
erts_de_runlock(dep);
ctx.reds = (Sint) (ERTS_BIF_REDS_LEFT(BIF_P) * TERM_TO_BINARY_LOOP_FACTOR);
code = dsig_send_spawn_request(&ctx, ref, BIF_P->common.id,
BIF_P->group_leader, mfna,
alist, opts);
switch (code) {
case ERTS_DSIG_SEND_OK:
ERTS_BIF_PREP_RET(ret_val, ok_result);
break;
case ERTS_DSIG_SEND_YIELD:
ERTS_BIF_PREP_YIELD_RETURN(ret_val, BIF_P, ok_result);
break;
case ERTS_DSIG_SEND_CONTINUE: {
Eterm ctx_term;
/* Keep dist entry alive over trap... */
ctx.deref_dep = 1;
dep = NULL;
erts_set_gc_state(BIF_P, 0);
ctx_term = erts_dsend_export_trap_context(BIF_P, &ctx);
BUMP_ALL_REDS(BIF_P);
ERTS_BIF_PREP_TRAP3(ret_val, spawn_request_yield_export,
BIF_P, ctx_term, ok_result, tag);
break;
}
case ERTS_DSIG_SEND_TOO_LRG: {
ErtsMonitor *mon;
ErtsMonitorData *mdp;
mon = erts_monitor_tree_lookup(ERTS_P_MONITORS(BIF_P), ref);
ASSERT(mon);
erts_monitor_tree_delete(&ERTS_P_MONITORS(BIF_P), mon);
mdp = erts_monitor_to_data(mon);
if (erts_monitor_dist_delete(&mdp->target))
erts_monitor_release_both(mdp);
else
erts_monitor_release(mon);
goto system_limit;
}
default:
ERTS_INTERNAL_ERROR("Invalid spawn request result");
ERTS_BIF_PREP_RET(ret_val, am_internal_error);
}
break;
}
default:
ERTS_INTERNAL_ERROR("Invalid dsig prepare result");
ERTS_BIF_PREP_RET(ret_val, am_internal_error);
break;
}
do_return:
if (dep)
erts_deref_dist_entry(dep);
return ret_val;
badarg:
ERTS_BIF_PREP_RET(ret_val, am_badarg);
goto do_return;
system_limit:
error = am_system_limit;
goto send_error;
noconnection:
error = am_noconnection;
goto send_error;
notsup:
error = am_notsup;
/* fall through... */
send_error:
ASSERT(is_value(ok_result));
if (error_message)
erts_send_local_spawn_reply(BIF_P, ERTS_PROC_LOCK_MAIN, NULL,
tag, ref, error, am_undefined);
ERTS_BIF_PREP_RET(ret_val, ok_result);
goto do_return;
}