BIF_RETTYPE erts_internal_dist_spawn_request_4()

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;
}