BIF_RETTYPE erts_internal_open_port_2(BIF_ALIST_2) { Port *port; Eterm res; char *str; int err_type, err_num; port = open_port(BIF_P, BIF_ARG_1, BIF_ARG_2, &err_type, &err_num); if (!port) { if (err_type == -3) { ASSERT(err_num == BADARG || err_num == SYSTEM_LIMIT); if (err_num == BADARG) res = am_badarg; else if (err_num == SYSTEM_LIMIT) res = am_system_limit; else /* this is only here to silence gcc, it should not happen */ BIF_ERROR(BIF_P, EXC_INTERNAL_ERROR); } else if (err_type == -2) { str = erl_errno_id(err_num); res = erts_atom_put((byte *) str, strlen(str), ERTS_ATOM_ENC_LATIN1, 1); } else { res = am_einval; } BIF_RET(res); } if (port->drv_ptr->flags & ERL_DRV_FLAG_USE_INIT_ACK) { /* Copied from erl_port_task.c */ port->async_open_port = erts_alloc(ERTS_ALC_T_PRTSD, sizeof(*port->async_open_port)); erts_make_ref_in_array(port->async_open_port->ref); port->async_open_port->to = BIF_P->common.id; erts_smp_proc_lock(BIF_P, ERTS_PROC_LOCKS_MSG_RECEIVE | ERTS_PROC_LOCK_LINK); if (ERTS_PROC_PENDING_EXIT(BIF_P)) { /* need to exit caller instead */ erts_smp_proc_unlock(BIF_P, ERTS_PROC_LOCKS_MSG_RECEIVE | ERTS_PROC_LOCK_LINK); KILL_CATCHES(BIF_P); BIF_P->freason = EXC_EXIT; erts_port_release(port); BIF_RET(am_badarg); } ERTS_SMP_MSGQ_MV_INQ2PRIVQ(BIF_P); BIF_P->msg.save = BIF_P->msg.last; erts_smp_proc_unlock(BIF_P, ERTS_PROC_LOCKS_MSG_RECEIVE); res = erts_proc_store_ref(BIF_P, port->async_open_port->ref); } else { res = port->common.id; erts_smp_proc_lock(BIF_P, ERTS_PROC_LOCK_LINK); } erts_add_link(&ERTS_P_LINKS(port), LINK_PID, BIF_P->common.id); erts_add_link(&ERTS_P_LINKS(BIF_P), LINK_PID, port->common.id); if (IS_TRACED_FL(BIF_P, F_TRACE_PROCS)) trace_proc(BIF_P, ERTS_PROC_LOCK_MAIN|ERTS_PROC_LOCK_LINK, BIF_P, am_link, port->common.id); erts_smp_proc_unlock(BIF_P, ERTS_PROC_LOCK_LINK); erts_port_release(port); BIF_RET(res); }
/* * Unregister a name * Return 0 if not registered * Otherwise returns 1 * */ int erts_unregister_name(Process *c_p, ErtsProcLocks c_p_locks, Port *c_prt, Eterm name) { int res = 0; RegProc r, *rp; Port *port = c_prt; ErtsProcLocks current_c_p_locks = 0; #ifdef ERTS_SMP /* * SMP note: If 'c_prt != NULL' and 'c_prt->reg->name == name', * we are *not* allowed to temporarily release the lock * on c_prt. */ if (!c_p) { c_p_locks = 0; } current_c_p_locks = c_p_locks; restart: reg_safe_write_lock(c_p, ¤t_c_p_locks); #endif r.name = name; if (is_non_value(name)) { /* Unregister current process name */ ASSERT(c_p); #ifdef ERTS_SMP if (current_c_p_locks != c_p_locks) { erts_smp_proc_lock(c_p, c_p_locks); current_c_p_locks = c_p_locks; } #endif if (c_p->common.u.alive.reg) { r.name = c_p->common.u.alive.reg->name; } else { /* Name got unregistered while main lock was released */ res = 0; goto done; } } if ((rp = (RegProc*) hash_get(&process_reg, (void*) &r)) != NULL) { if (rp->pt) { if (port != rp->pt) { #ifdef ERTS_SMP if (port) { ASSERT(port != c_prt); erts_port_release(port); port = NULL; } if (erts_smp_port_trylock(rp->pt) == EBUSY) { Eterm id = rp->pt->common.id; /* id read only... */ /* Unlock all locks, acquire port lock, and restart... */ if (current_c_p_locks) { erts_smp_proc_unlock(c_p, current_c_p_locks); current_c_p_locks = 0; } reg_write_unlock(); port = erts_id2port(id); goto restart; } #endif port = rp->pt; } ASSERT(rp->pt == port); ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(port)); rp->pt->common.u.alive.reg = NULL; if (IS_TRACED_FL(port, F_TRACE_PORTS)) { if (current_c_p_locks) { erts_smp_proc_unlock(c_p, current_c_p_locks); current_c_p_locks = 0; } trace_port(port, am_unregister, r.name); } } else if (rp->p) { #ifdef ERTS_SMP erts_proc_safelock(c_p, current_c_p_locks, c_p_locks, rp->p, (c_p == rp->p) ? current_c_p_locks : 0, ERTS_PROC_LOCK_MAIN); current_c_p_locks = c_p_locks; #endif rp->p->common.u.alive.reg = NULL; if (IS_TRACED_FL(rp->p, F_TRACE_PROCS)) { trace_proc(rp->p, (c_p == rp->p) ? c_p_locks : ERTS_PROC_LOCK_MAIN, rp->p, am_unregister, r.name); } #ifdef ERTS_SMP if (rp->p != c_p) { erts_smp_proc_unlock(rp->p, ERTS_PROC_LOCK_MAIN); } #endif } hash_erase(&process_reg, (void*) &r); res = 1; } done: reg_write_unlock(); if (c_prt != port) { if (port) { erts_port_release(port); } if (c_prt) { erts_smp_port_lock(c_prt); } } #ifdef ERTS_SMP if (c_p && !current_c_p_locks) { erts_smp_proc_lock(c_p, c_p_locks); } #endif return res; }
BIF_RETTYPE erts_internal_open_port_2(BIF_ALIST_2) { BIF_RETTYPE ret; Port *port; Eterm res; char *str; int err_type, err_num; ErtsLinkData *ldp; ErtsLink *lnk; port = open_port(BIF_P, BIF_ARG_1, BIF_ARG_2, &err_type, &err_num); if (!port) { if (err_type == -3) { ASSERT(err_num == BADARG || err_num == SYSTEM_LIMIT); if (err_num == BADARG) res = am_badarg; else if (err_num == SYSTEM_LIMIT) res = am_system_limit; else /* this is only here to silence gcc, it should not happen */ BIF_ERROR(BIF_P, EXC_INTERNAL_ERROR); } else if (err_type == -2) { str = erl_errno_id(err_num); res = erts_atom_put((byte *) str, sys_strlen(str), ERTS_ATOM_ENC_LATIN1, 1); } else { res = am_einval; } BIF_RET(res); } ldp = erts_link_create(ERTS_LNK_TYPE_PORT, BIF_P->common.id, port->common.id); ASSERT(ldp->a.other.item == port->common.id); ASSERT(ldp->b.other.item == BIF_P->common.id); /* * This link should not already be present, but can potentially * due to id wrapping... */ lnk = erts_link_tree_lookup_insert(&ERTS_P_LINKS(BIF_P), &ldp->a); erts_link_tree_insert(&ERTS_P_LINKS(port), &ldp->b); if (port->drv_ptr->flags & ERL_DRV_FLAG_USE_INIT_ACK) { /* Copied from erl_port_task.c */ port->async_open_port = erts_alloc(ERTS_ALC_T_PRTSD, sizeof(*port->async_open_port)); erts_make_ref_in_array(port->async_open_port->ref); port->async_open_port->to = BIF_P->common.id; /* * We unconditionaly *must* do a receive on a message * containing the reference after this... */ ERTS_RECV_MARK_SAVE(BIF_P); ERTS_RECV_MARK_SET(BIF_P); res = erts_proc_store_ref(BIF_P, port->async_open_port->ref); } else { res = port->common.id; } if (IS_TRACED_FL(BIF_P, F_TRACE_PROCS)) trace_proc(BIF_P, ERTS_PROC_LOCK_MAIN, BIF_P, am_link, port->common.id); ERTS_BIF_PREP_RET(ret, res); erts_port_release(port); if (lnk) erts_link_release(lnk); return ret; }
/* * Register a process or port (can't be registered twice). * Returns 0 if name, process or port is already registered. * * When smp support is enabled: * * Assumes that main lock is locked (and only main lock) * on c_p. * */ int erts_register_name(Process *c_p, Eterm name, Eterm id) { int res = 0; Process *proc = NULL; Port *port = NULL; RegProc r, *rp; ERTS_SMP_CHK_HAVE_ONLY_MAIN_PROC_LOCK(c_p); if (is_not_atom(name) || name == am_undefined) return res; if (c_p->common.id == id) /* A very common case I think... */ proc = c_p; else { if (is_not_internal_pid(id) && is_not_internal_port(id)) return res; erts_smp_proc_unlock(c_p, ERTS_PROC_LOCK_MAIN); if (is_internal_port(id)) { port = erts_id2port(id); if (!port) goto done; } } #ifdef ERTS_SMP { ErtsProcLocks proc_locks = proc ? ERTS_PROC_LOCK_MAIN : 0; reg_safe_write_lock(proc, &proc_locks); if (proc && !proc_locks) erts_smp_proc_lock(c_p, ERTS_PROC_LOCK_MAIN); } #endif if (is_internal_pid(id)) { if (!proc) proc = erts_pid2proc(NULL, 0, id, ERTS_PROC_LOCK_MAIN); r.p = proc; if (!proc) goto done; if (proc->common.u.alive.reg) goto done; r.pt = NULL; } else { ASSERT(!INVALID_PORT(port, id)); ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(port)); r.pt = port; if (r.pt->common.u.alive.reg) goto done; r.p = NULL; } r.name = name; rp = (RegProc*) hash_put(&process_reg, (void*) &r); if (proc && rp->p == proc) { if (IS_TRACED_FL(proc, F_TRACE_PROCS)) { trace_proc(proc, ERTS_PROC_LOCK_MAIN, proc, am_register, name); } proc->common.u.alive.reg = rp; } else if (port && rp->pt == port) { if (IS_TRACED_FL(port, F_TRACE_PORTS)) { trace_port(port, am_register, name); } port->common.u.alive.reg = rp; } if ((rp->p && rp->p->common.id == id) || (rp->pt && rp->pt->common.id == id)) { res = 1; } done: reg_write_unlock(); if (port) erts_port_release(port); if (c_p != proc) { if (proc) erts_smp_proc_unlock(proc, ERTS_PROC_LOCK_MAIN); erts_smp_proc_lock(c_p, ERTS_PROC_LOCK_MAIN); } return res; }
/* * Unregister a name * Return 0 if not registered * Otherwise returns 1 * */ int erts_unregister_name(Process *c_p, ErtsProcLocks c_p_locks, Port *c_prt, Eterm name) { int res = 0; RegProc r, *rp; Port *port = c_prt; #ifdef ERTS_SMP ErtsProcLocks current_c_p_locks; /* * SMP note: If 'c_prt != NULL' and 'c_prt->reg->name == name', * we are *not* allowed to temporarily release the lock * on c_prt. */ if (!c_p) c_p_locks = 0; current_c_p_locks = c_p_locks; restart: reg_safe_write_lock(c_p, ¤t_c_p_locks); #endif r.name = name; if ((rp = (RegProc*) hash_get(&process_reg, (void*) &r)) != NULL) { if (rp->pt) { #ifdef ERTS_SMP if (port != rp->pt) { if (port) { ERTS_SMP_LC_ASSERT(port != c_prt); erts_smp_port_unlock(port); port = NULL; } if (erts_smp_port_trylock(rp->pt) == EBUSY) { Eterm id = rp->pt->id; /* id read only... */ /* Unlock all locks, acquire port lock, and restart... */ if (current_c_p_locks) { erts_smp_proc_unlock(c_p, current_c_p_locks); current_c_p_locks = 0; } reg_read_unlock(); port = erts_id2port(id, NULL, 0); goto restart; } port = rp->pt; } #endif ERTS_SMP_LC_ASSERT(rp->pt == port && erts_lc_is_port_locked(port)); rp->pt->reg = NULL; if (IS_TRACED_FL(port, F_TRACE_PORTS)) { trace_port(port, am_unregister, name); } } else if (rp->p) { Process* p = rp->p; #ifdef ERTS_SMP erts_proc_safelock(c_p, current_c_p_locks, c_p_locks, rp->p, 0, ERTS_PROC_LOCK_MAIN); current_c_p_locks = c_p_locks; #endif p->reg = NULL; #ifdef ERTS_SMP if (rp->p != c_p) erts_smp_proc_unlock(rp->p, ERTS_PROC_LOCK_MAIN); #endif if (IS_TRACED_FL(p, F_TRACE_PROCS)) { trace_proc(c_p, p, am_unregister, name); } } hash_erase(&process_reg, (void*) &r); res = 1; } reg_write_unlock(); if (c_prt != port) { if (port) erts_smp_port_unlock(port); if (c_prt) erts_smp_port_lock(c_prt); } #ifdef ERTS_SMP if (c_p && !current_c_p_locks) erts_smp_proc_lock(c_p, c_p_locks); #endif return res; }