Eterm check_process_code_2(BIF_ALIST_2) { Process* rp; Module* modp; if (is_not_atom(BIF_ARG_2)) { goto error; } if (is_internal_pid(BIF_ARG_1)) { Eterm res; if (internal_pid_index(BIF_ARG_1) >= erts_max_processes) goto error; rp = erts_pid2proc_not_running(BIF_P, ERTS_PROC_LOCK_MAIN, BIF_ARG_1, ERTS_PROC_LOCK_MAIN); if (!rp) { ERTS_BIF_CHK_EXITED(BIF_P); ERTS_SMP_BIF_CHK_RESCHEDULE(BIF_P); BIF_RET(am_false); } modp = erts_get_module(BIF_ARG_2); res = check_process_code(rp, modp); #ifdef ERTS_SMP if (BIF_P != rp) erts_smp_proc_unlock(rp, ERTS_PROC_LOCK_MAIN); #endif BIF_RET(res); } else if (is_external_pid(BIF_ARG_1) && external_pid_dist_entry(BIF_ARG_1) == erts_this_dist_entry) { BIF_RET(am_false); } error: BIF_ERROR(BIF_P, BADARG); }
/* You have to have loaded the driver and the pid state is LOADED or AWAIT_LOAD. You will be removed from the list regardless of driver state. If the driver is loaded by someone else to, return is {ok, pending_process} If the driver is loaded but locked by a port, return is {ok, pending_driver} If the driver is loaded and free to unload (you're the last holding it) {ok, unloaded} If it's not loaded or not loaded by you {error, not_loaded} or {error, not_loaded_by_you} Internally, if its in state UNLOADING, just return {ok, pending_driver} and remove/decrement this pid (which should be an LOADED tagged one). If the state is RELOADING, this pid should be in list as LOADED tagged, only AWAIT_LOAD would be possible but not allowed for unloading, remove it and, if the last LOADED tagged, change from RELOAD to UNLOAD and notify any AWAIT_LOAD-waiters with {'DOWN', ref(), driver, name(), load_cancelled} If the driver made itself permanent, {'UP', ref(), driver, name(), permanent} */ Eterm erl_ddll_try_unload_2(BIF_ALIST_2) { Eterm name_term = BIF_ARG_1; Eterm options = BIF_ARG_2; char *name = NULL; Eterm ok_term = NIL; Eterm soft_error_term = NIL; erts_driver_t *drv; DE_Handle *dh; DE_ProcEntry *pe; Eterm *hp; Eterm t; int monitor = 0; Eterm l; int kill_ports = 0; erts_proc_unlock(BIF_P, ERTS_PROC_LOCK_MAIN); for(l = options; is_list(l); l = CDR(list_val(l))) { Eterm opt = CAR(list_val(l)); Eterm *tp; if (is_not_tuple(opt)) { if (opt == am_kill_ports) { kill_ports = 1; continue; } else { goto error; } } tp = tuple_val(opt); if (*tp != make_arityval(2) || tp[1] != am_monitor) { goto error; } if (tp[2] == am_pending_driver) { monitor = 1; } else if (tp[2] == am_pending) { monitor = 2; } else { goto error; } } if (is_not_nil(l)) { goto error; } if ((name = pick_list_or_atom(name_term)) == NULL) { goto error; } lock_drv_list(); if ((drv = lookup_driver(name)) == NULL) { soft_error_term = am_not_loaded; goto soft_error; } if (drv->handle == NULL) { soft_error_term = am_linked_in_driver; goto soft_error; } else if (drv->handle->status == ERL_DE_PERMANENT) { soft_error_term = am_permanent; goto soft_error; } dh = drv->handle; if (dh->flags & ERL_DE_FL_KILL_PORTS) { kill_ports = 1; } if ((pe = find_proc_entry(dh, BIF_P, ERL_DE_PROC_LOADED)) == NULL) { if (num_procs(dh, ERL_DE_PROC_LOADED) > 0) { soft_error_term = am_not_loaded_by_this_process; goto soft_error; } } else { remove_proc_entry(dh, pe); if (!(pe->flags & ERL_DE_FL_DEREFERENCED)) { erts_ddll_dereference_driver(dh); } erts_free(ERTS_ALC_T_DDLL_PROCESS, pe); } if (num_procs(dh, ERL_DE_PROC_LOADED) > 0) { ok_term = am_pending_process; --monitor; goto done; } if (dh->status == ERL_DE_RELOAD || dh->status == ERL_DE_FORCE_RELOAD) { notify_all(dh, drv->name, ERL_DE_PROC_AWAIT_LOAD, am_DOWN, am_load_cancelled); erts_free(ERTS_ALC_T_DDLL_HANDLE,dh->reload_full_path); erts_free(ERTS_ALC_T_DDLL_HANDLE,dh->reload_driver_name); dh->reload_full_path = dh->reload_driver_name = NULL; dh->reload_flags = 0; } if (erts_atomic32_read_nob(&dh->port_count) > 0) { ++kill_ports; } dh->status = ERL_DE_UNLOAD; ok_term = am_pending_driver; done: assert_drv_list_rwlocked(); if (kill_ports > 1) { /* Avoid closing the driver by referencing it */ erts_ddll_reference_driver(dh); dh->status = ERL_DE_FORCE_UNLOAD; unlock_drv_list(); kill_ports_driver_unloaded(dh); lock_drv_list(); erts_ddll_dereference_driver(dh); } erts_ddll_reference_driver(dh); unlock_drv_list(); erts_proc_lock(BIF_P, ERTS_PROC_LOCK_MAIN); lock_drv_list(); erts_ddll_dereference_driver(dh); erts_free(ERTS_ALC_T_DDLL_TMP_BUF, (void *) name); BIF_P->flags |= F_USING_DDLL; if (monitor > 0) { Eterm mref = add_monitor(BIF_P, dh, ERL_DE_PROC_AWAIT_UNLOAD); hp = HAlloc(BIF_P, 4); t = TUPLE3(hp, am_ok, ok_term, mref); } else { hp = HAlloc(BIF_P, 3); t = TUPLE2(hp, am_ok, ok_term); } if (kill_ports > 1) { ERTS_BIF_CHK_EXITED(BIF_P); /* May be exited by port killing */ } unlock_drv_list(); BIF_RET(t); soft_error: unlock_drv_list(); erts_free(ERTS_ALC_T_DDLL_TMP_BUF, (void *) name); erts_proc_lock(BIF_P, ERTS_PROC_LOCK_MAIN); hp = HAlloc(BIF_P, 3); t = TUPLE2(hp, am_error, soft_error_term); BIF_RET(t); error: /* No lock fiddling before going here */ assert_drv_list_not_locked(); if (name != NULL) { erts_free(ERTS_ALC_T_DDLL_TMP_BUF, (void *) name); } erts_proc_lock(BIF_P, ERTS_PROC_LOCK_MAIN); BIF_ERROR(BIF_P, BADARG); }
BIF_RETTYPE port_control_3(BIF_ALIST_3) { Port* p; Uint op; Eterm res = THE_NON_VALUE; /* Virtual schedule out calling process before lock wait */ if (IS_TRACED_FL(BIF_P, F_TRACE_SCHED_PROCS)) { trace_virtual_sched(BIF_P, am_out); } if (erts_system_profile_flags.runnable_procs && erts_system_profile_flags.exclusive) { profile_runnable_proc(BIF_P, am_inactive); } p = id_or_name2port(BIF_P, BIF_ARG_1); if (!p) { /* Schedule the process before exiting */ if (IS_TRACED_FL(BIF_P, F_TRACE_SCHED_PROCS)) { trace_virtual_sched(BIF_P, am_in); } if (erts_system_profile_flags.runnable_procs && erts_system_profile_flags.exclusive) { profile_runnable_proc(BIF_P, am_active); } BIF_ERROR(BIF_P, BADARG); } /* Trace the port for scheduling in */ if (IS_TRACED_FL(p, F_TRACE_SCHED_PORTS)) { trace_sched_ports_where(p, am_in, am_control); } if (erts_system_profile_flags.runnable_ports && !erts_port_is_scheduled(p)) { profile_runnable_port(p, am_active); } if (term_to_Uint(BIF_ARG_2, &op)) res = erts_port_control(BIF_P, p, op, BIF_ARG_3); /* Trace the port for scheduling out */ if (IS_TRACED_FL(p, F_TRACE_SCHED_PORTS)) { trace_sched_ports_where(p, am_out, am_control); } if (erts_system_profile_flags.runnable_ports && !erts_port_is_scheduled(p)) { profile_runnable_port(p, am_inactive); } erts_port_release(p); #ifdef ERTS_SMP ERTS_SMP_BIF_CHK_PENDING_EXIT(BIF_P, ERTS_PROC_LOCK_MAIN); #else ERTS_BIF_CHK_EXITED(BIF_P); #endif if (IS_TRACED_FL(BIF_P, F_TRACE_SCHED_PROCS)) { trace_virtual_sched(BIF_P, am_in); } if (erts_system_profile_flags.runnable_procs && erts_system_profile_flags.exclusive) { profile_runnable_proc(BIF_P, am_active); } if (is_non_value(res)) { BIF_ERROR(BIF_P, BADARG); } BIF_RET(res); }
static BIF_RETTYPE port_call(Process* c_p, Eterm arg1, Eterm arg2, Eterm arg3) { Uint op; Port *p; Uint size; byte *bytes; byte *endp; ErlDrvSizeT real_size; erts_driver_t *drv; byte port_input[256]; /* Default input buffer to encode in */ byte port_result[256]; /* Buffer for result from port. */ byte* port_resp; /* Pointer to result buffer. */ char *prc; ErlDrvSSizeT ret; Eterm res; Sint result_size; Eterm *hp; Eterm *hp_end; /* To satisfy hybrid heap architecture */ unsigned ret_flags = 0U; int fpe_was_unmasked; bytes = &port_input[0]; port_resp = port_result; /* trace of port scheduling with virtual process descheduling * lock wait */ if (IS_TRACED_FL(c_p, F_TRACE_SCHED_PROCS)) { trace_virtual_sched(c_p, am_out); } if (erts_system_profile_flags.runnable_procs && erts_system_profile_flags.exclusive) { profile_runnable_proc(c_p, am_inactive); } p = id_or_name2port(c_p, arg1); if (!p) { error: if (port_resp != port_result && !(ret_flags & DRIVER_CALL_KEEP_BUFFER)) { driver_free(port_resp); } if (bytes != &port_input[0]) erts_free(ERTS_ALC_T_PORT_CALL_BUF, bytes); /* Need to virtual schedule in the process if there * was an error. */ if (IS_TRACED_FL(c_p, F_TRACE_SCHED_PROCS)) { trace_virtual_sched(c_p, am_in); } if (erts_system_profile_flags.runnable_procs && erts_system_profile_flags.exclusive) { profile_runnable_proc(c_p, am_active); } if (p) erts_port_release(p); #ifdef ERTS_SMP ERTS_SMP_BIF_CHK_PENDING_EXIT(c_p, ERTS_PROC_LOCK_MAIN); #else ERTS_BIF_CHK_EXITED(c_p); #endif BIF_ERROR(c_p, BADARG); } if ((drv = p->drv_ptr) == NULL) { goto error; } if (drv->call == NULL) { goto error; } if (!term_to_Uint(arg2, &op)) { goto error; } p->caller = c_p->id; /* Lock taken, virtual schedule of port */ if (IS_TRACED_FL(p, F_TRACE_SCHED_PORTS)) { trace_sched_ports_where(p, am_in, am_call); } if (erts_system_profile_flags.runnable_ports && !erts_port_is_scheduled(p)) { profile_runnable_port(p, am_active); } size = erts_encode_ext_size(arg3); if (size > sizeof(port_input)) bytes = erts_alloc(ERTS_ALC_T_PORT_CALL_BUF, size); endp = bytes; erts_encode_ext(arg3, &endp); real_size = endp - bytes; if (real_size > size) { erl_exit(1, "%s, line %d: buffer overflow: %d word(s)\n", __FILE__, __LINE__, endp - (bytes + size)); } erts_smp_proc_unlock(c_p, ERTS_PROC_LOCK_MAIN); #ifdef USE_VM_PROBES if (DTRACE_ENABLED(driver_call)) { DTRACE_CHARBUF(process_str, DTRACE_TERM_BUF_SIZE); DTRACE_CHARBUF(port_str, DTRACE_TERM_BUF_SIZE); dtrace_pid_str(p->connected, process_str); dtrace_port_str(p, port_str); DTRACE5(driver_call, process_str, port_str, p->name, op, real_size); } #endif prc = (char *) port_resp; fpe_was_unmasked = erts_block_fpe(); ret = drv->call((ErlDrvData)p->drv_data, (unsigned) op, (char *) bytes, (int) real_size, &prc, (int) sizeof(port_result), &ret_flags); erts_unblock_fpe(fpe_was_unmasked); if (IS_TRACED_FL(p, F_TRACE_SCHED_PORTS)) { trace_sched_ports_where(p, am_out, am_call); } if (erts_system_profile_flags.runnable_ports && !erts_port_is_scheduled(p)) { profile_runnable_port(p, am_inactive); } port_resp = (byte *) prc; p->caller = NIL; erts_smp_proc_lock(c_p, ERTS_PROC_LOCK_MAIN); #ifdef HARDDEBUG { ErlDrvSizeT z; printf("real_size = %ld,%d, ret = %ld,%d\r\n", (unsigned long) real_size, (int) real_size, (unsigned long)ret, (int) ret); printf("["); for(z = 0; z < real_size; ++z) { printf("%d, ",(int) bytes[z]); } printf("]\r\n"); printf("["); for(z = 0; z < ret; ++z) { printf("%d, ",(int) port_resp[z]); } printf("]\r\n"); } #endif if (ret <= 0 || port_resp[0] != VERSION_MAGIC) { /* Error or a binary without magic/ with wrong magic */ goto error; } result_size = erts_decode_ext_size(port_resp, ret); if (result_size < 0) { goto error; } hp = HAlloc(c_p, result_size); hp_end = hp + result_size; endp = port_resp; res = erts_decode_ext(&hp, &MSO(c_p), &endp); if (res == THE_NON_VALUE) { goto error; } HRelease(c_p, hp_end, hp); if (port_resp != port_result && !(ret_flags & DRIVER_CALL_KEEP_BUFFER)) { driver_free(port_resp); } if (bytes != &port_input[0]) erts_free(ERTS_ALC_T_PORT_CALL_BUF, bytes); if (p) erts_port_release(p); #ifdef ERTS_SMP ERTS_SMP_BIF_CHK_PENDING_EXIT(c_p, ERTS_PROC_LOCK_MAIN); #else ERTS_BIF_CHK_EXITED(c_p); #endif if (IS_TRACED_FL(c_p, F_TRACE_SCHED_PROCS)) { trace_virtual_sched(c_p, am_in); } if (erts_system_profile_flags.runnable_procs && erts_system_profile_flags.exclusive) { profile_runnable_proc(c_p, am_active); } return res; }