in erts/emulator/beam/erl_bif_ddll.c [175:448]
BIF_RETTYPE erl_ddll_try_load_3(BIF_ALIST_3)
{
Eterm path_term = BIF_ARG_1;
Eterm name_term = BIF_ARG_2;
Eterm options = BIF_ARG_3;
char *path = NULL;
Sint path_len;
char *name = NULL;
DE_Handle *dh;
erts_driver_t *drv;
int res;
Eterm soft_error_term = NIL;
Eterm ok_term = NIL;
Eterm *hp;
Eterm t;
int monitor = 0;
int reload = 0;
Eterm l;
Uint flags = 0;
int kill_ports = 0;
int do_build_load_error = 0;
int build_this_load_error = 0;
int encoding;
for(l = options; is_list(l); l = CDR(list_val(l))) {
Eterm opt = CAR(list_val(l));
Eterm *tp;
if (is_not_tuple(opt)) {
goto error;
}
tp = tuple_val(opt);
if (*tp != make_arityval(2) || is_not_atom(tp[1])) {
goto error;
}
switch (tp[1]) {
case am_driver_options:
{
Eterm ll;
for(ll = tp[2]; is_list(ll); ll = CDR(list_val(ll))) {
Eterm dopt = CAR(list_val(ll));
if (dopt == am_kill_ports) {
flags |= ERL_DE_FL_KILL_PORTS;
} else {
goto error;
}
}
if (is_not_nil(ll)) {
goto error;
}
}
break;
case am_monitor:
if (tp[2] == am_pending_driver) {
monitor = 1;
} else if (tp[2] == am_pending ) {
monitor = 2;
} else {
goto error;
}
break;
case am_reload:
if (tp[2] == am_pending_driver) {
reload = 1;
} else if (tp[2] == am_pending ) {
reload = 2;
} else {
goto error;
}
break;
default:
goto error;
}
}
if (is_not_nil(l)) {
goto error;
}
if ((name = pick_list_or_atom(name_term)) == NULL) {
goto error;
}
encoding = erts_get_native_filename_encoding();
if (encoding == ERL_FILENAME_WIN_WCHAR) {
/* Do not convert the lib name to utf-16le yet, do that in win32 specific code */
/* since lib_name is used in error messages */
encoding = ERL_FILENAME_UTF8;
}
path = erts_convert_filename_to_encoding(path_term, NULL, 0,
ERTS_ALC_T_DDLL_TMP_BUF, 1, 0,
encoding, &path_len,
sys_strlen(name) + 2); /* might need path separator */
if (!path) {
goto error;
}
ASSERT(path_len > 0 && path[path_len-1] == 0);
while (--path_len > 0 && (path[path_len-1] == '\\' || path[path_len-1] == '/'))
;
path[path_len++] = '/';
sys_strcpy(path+path_len,name);
erts_proc_unlock(BIF_P, ERTS_PROC_LOCK_MAIN);
lock_drv_list();
if ((drv = lookup_driver(name)) != NULL) {
if (drv->handle == NULL) {
/* static_driver */
soft_error_term = am_linked_in_driver;
goto soft_error;
} else {
dh = drv->handle;
if (dh->status == ERL_DE_OK) {
int is_last = is_last_user(dh, BIF_P);
if (reload == 1 && !is_last) {
/*Want reload if no other users,
but there are others...*/
soft_error_term = am_pending_process;
goto soft_error;
}
if (reload != 0) {
DE_ProcEntry *old;
if ((dh->flags & ERL_FL_CONSISTENT_MASK) !=
(flags & ERL_FL_CONSISTENT_MASK)) {
soft_error_term = am_inconsistent;
goto soft_error;
}
if ((old = find_proc_entry(dh, BIF_P,
ERL_DE_PROC_LOADED)) ==
NULL) {
soft_error_term = am_not_loaded_by_this_process;
goto soft_error;
} else {
remove_proc_entry(dh, old);
erts_ddll_dereference_driver(dh);
erts_free(ERTS_ALC_T_DDLL_PROCESS, old);
}
/* Reload requested and granted */
dereference_all_processes(dh);
set_driver_reloading(dh, BIF_P, path, name, flags);
if (dh->flags & ERL_DE_FL_KILL_PORTS) {
kill_ports = 1;
}
ok_term = (reload == 1) ? am_pending_driver :
am_pending_process;
} else {
/* Already loaded and healthy (might be by me) */
if (sys_strcmp(dh->full_path, path) ||
(dh->flags & ERL_FL_CONSISTENT_MASK) !=
(flags & ERL_FL_CONSISTENT_MASK)) {
soft_error_term = am_inconsistent;
goto soft_error;
}
add_proc_loaded(dh, BIF_P);
erts_ddll_reference_driver(dh);
monitor = 0;
ok_term = mkatom("already_loaded");
}
} else if (dh->status == ERL_DE_UNLOAD ||
dh->status == ERL_DE_FORCE_UNLOAD) {
/* pending driver */
if (reload != 0) {
soft_error_term = am_not_loaded_by_this_process;
goto soft_error;
}
if (sys_strcmp(dh->full_path, path) ||
(dh->flags & ERL_FL_CONSISTENT_MASK) !=
(flags & ERL_FL_CONSISTENT_MASK)) {
soft_error_term = am_inconsistent;
goto soft_error;
}
dh->status = ERL_DE_OK;
notify_all(dh, drv->name,
ERL_DE_PROC_AWAIT_UNLOAD, am_UP,
am_unload_cancelled);
add_proc_loaded(dh, BIF_P);
erts_ddll_reference_driver(dh);
monitor = 0;
ok_term = mkatom("already_loaded");
} else if (dh->status == ERL_DE_RELOAD ||
dh->status == ERL_DE_FORCE_RELOAD) {
if (reload != 0) {
soft_error_term = am_pending_reload;
goto soft_error;
}
if (sys_strcmp(dh->reload_full_path, path) ||
(dh->reload_flags & ERL_FL_CONSISTENT_MASK) !=
(flags & ERL_FL_CONSISTENT_MASK)) {
soft_error_term = am_inconsistent;
goto soft_error;
}
/* Load of granted unload... */
/* Don't reference, will happen after reload */
add_proc_loaded_deref(dh, BIF_P);
++monitor;
ok_term = am_pending_driver;
} else { /* ERL_DE_PERMANENT */
soft_error_term = am_permanent;
goto soft_error;
}
}
} else { /* driver non-existing */
if (reload != 0) {
soft_error_term = am_not_loaded;
goto soft_error;
}
if ((res = load_driver_entry(&dh, path, name)) != ERL_DE_NO_ERROR) {
build_this_load_error = res;
do_build_load_error = 1;
soft_error_term = am_undefined;
goto soft_error;
} else {
dh->flags = flags;
add_proc_loaded(dh, BIF_P);
first_ddll_reference(dh);
monitor = 0;
ok_term = mkatom("loaded");
}
}
assert_drv_list_rwlocked();
if (kill_ports) {
/* Avoid closing the driver by referencing it */
erts_ddll_reference_driver(dh);
ASSERT(dh->status == ERL_DE_RELOAD);
dh->status = ERL_DE_FORCE_RELOAD;
unlock_drv_list();
kill_ports_driver_unloaded(dh);
/* Dereference, eventually causing driver destruction */
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);
BIF_P->flags |= F_USING_DDLL;
if (monitor) {
Eterm mref = add_monitor(BIF_P, dh, ERL_DE_PROC_AWAIT_LOAD);
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);
}
unlock_drv_list();
erts_free(ERTS_ALC_T_DDLL_TMP_BUF, (void *) path);
erts_free(ERTS_ALC_T_DDLL_TMP_BUF, (void *) name);
ERTS_LC_ASSERT(ERTS_PROC_LOCK_MAIN & erts_proc_lc_my_proc_locks(BIF_P));
BIF_RET(t);
soft_error:
unlock_drv_list();
erts_proc_lock(BIF_P, ERTS_PROC_LOCK_MAIN);
if (do_build_load_error) {
soft_error_term = build_load_error(BIF_P, build_this_load_error);
}
hp = HAlloc(BIF_P, 3);
t = TUPLE2(hp, am_error, soft_error_term);
erts_free(ERTS_ALC_T_DDLL_TMP_BUF, (void *) path);
erts_free(ERTS_ALC_T_DDLL_TMP_BUF, (void *) name);
ERTS_LC_ASSERT(ERTS_PROC_LOCK_MAIN & erts_proc_lc_my_proc_locks(BIF_P));
BIF_RET(t);
error:
assert_drv_list_not_locked();
ERTS_LC_ASSERT(ERTS_PROC_LOCK_MAIN & erts_proc_lc_my_proc_locks(BIF_P));
if (path != NULL) {
erts_free(ERTS_ALC_T_DDLL_TMP_BUF, (void *) path);
}
if (name != NULL) {
erts_free(ERTS_ALC_T_DDLL_TMP_BUF, (void *) name);
}
BIF_ERROR(BIF_P, BADARG);
}