NIF除了自身提供的功能外,还封装了一系列driver的功能,这些功能与操作系统平台紧密相关,主要包括:
系统信息, 操作系统线程及线程私有资源 ,条件变量、信号量、读写锁等,这些功能本身与erlang的进程体系无关,本身也是线程安全的,因此可以直接复用到NIF中,统一NIF的接口。
此处简单的分析一个操作系统线程创建的接口,其余的类似。
int enif_thread_create(char *name, ErlNifTid *tid, void* (*func)(void *),
void *args, ErlNifThreadOpts *opts) {
return erl_drv_thread_create(name,tid,func,args,(ErlDrvThreadOpts*)opts);
}
enif_thread_create直接调用了对应的driver接口erl_drv_thread_create,其它复用driver功能的接口也类似。
int
erl_drv_thread_create(char *name,
ErlDrvTid *tid,
void* (*func)(void*),
void* arg,
ErlDrvThreadOpts *opts)
{
#ifdef USE_THREADS
int res;
struct ErlDrvTid_ *dtid;
ethr_thr_opts ethr_opts;
ethr_thr_opts *use_opts;
if (!opts)
use_opts = NULL;
else {
sys_memcpy((void *) ðr_opts,
(void *) &def_ethr_opts,
sizeof(ethr_thr_opts));
ethr_opts.suggested_stack_size = opts->suggested_stack_size;
use_opts = ðr_opts;
}
dtid = erts_alloc_fnf(ERTS_ALC_T_DRV_TID,
(sizeof(struct ErlDrvTid_)
+ (name ? sys_strlen(name) + 1 : 0)));
/* 分配一个ErlDrvTid_结构,保存线程描述符 */
/*
struct ErlDrvTid_ {
ethr_tid tid;
void* (*func)(void*);
void* arg;
int drv_thr;
Uint tsd_len;
void **tsd;
char *name;
};
*/
if (!dtid)
return ENOMEM;
dtid->drv_thr = 1;
dtid->func = func;
dtid->arg = arg;
dtid->tsd = NULL;
dtid->tsd_len = 0;
/* 填充线程描述符,func为线程执行入口点,arg为其参数,tsd为线程私有数据结构,可以保存线程私有数据,与线程紧密相关 */
if (!name)
dtid->name = no_name;
else {
dtid->name = ((char *) dtid) + sizeof(struct ErlDrvTid_);
sys_strcpy(dtid->name, name);
}
res = ethr_thr_create(&dtid->tid, erl_drv_thread_wrapper, dtid, use_opts);
if (res != 0) {
erts_free(ERTS_ALC_T_DRV_TID, dtid);
return res;
}
*tid = (ErlDrvTid) dtid;
/* 返回创建的线程描述符 */
return 0;
#else
return ENOTSUP;
#endif
}
int ethr_thr_create(ethr_tid *tid, void * (*func)(void *), void *arg, ethr_thr_opts *opts)
{
ethr_thr_wrap_data__ twd;
pthread_attr_t attr;
int res, dres;
int use_stack_size = (opts && opts->suggested_stack_size >= 0
? opts->suggested_stack_size
: -1 /* Use system default */);
#ifdef ETHR_MODIFIED_DEFAULT_STACK_SIZE
if (use_stack_size < 0)
use_stack_size = ETHR_MODIFIED_DEFAULT_STACK_SIZE;
#endif
#if ETHR_XCHK
if (ethr_not_completely_inited__) {
ETHR_ASSERT(0);
return EACCES;
}
if (!tid || !func) {
ETHR_ASSERT(0);
return EINVAL;
}
#endif
ethr_atomic32_init(&twd.result, (ethr_sint32_t) -1);
twd.tse = ethr_get_ts_event();
twd.thr_func = func;
twd.arg = arg;
res = pthread_attr_init(&attr);
if (res != 0)
return res;
/* Error cleanup needed after this point */
/* Schedule child thread in system scope (if possible) ... */
res = pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM);
if (res != 0 && res != ENOTSUP)
goto error;
if (use_stack_size >= 0) {
size_t suggested_stack_size = (size_t) use_stack_size;
size_t stack_size;
#ifdef ETHR_DEBUG
suggested_stack_size /= 2; /* Make sure we got margin */
#endif
#ifdef ETHR_STACK_GUARD_SIZE
/* The guard is at least on some platforms included in the stack size
passed when creating threads */
suggested_stack_size += ETHR_B2KW(ETHR_STACK_GUARD_SIZE);
#endif
if (suggested_stack_size < ethr_min_stack_size__)
stack_size = ETHR_KW2B(ethr_min_stack_size__);
else if (suggested_stack_size > ethr_max_stack_size__)
stack_size = ETHR_KW2B(ethr_max_stack_size__);
else
stack_size = ETHR_PAGE_ALIGN(ETHR_KW2B(suggested_stack_size));
(void) pthread_attr_setstacksize(&attr, stack_size);
}
#ifdef ETHR_STACK_GUARD_SIZE
(void) pthread_attr_setguardsize(&attr, ETHR_STACK_GUARD_SIZE);
#endif
/* Detached or joinable... */
res = pthread_attr_setdetachstate(&attr,
(opts && opts->detached
? PTHREAD_CREATE_DETACHED
: PTHREAD_CREATE_JOINABLE));
if (res != 0)
goto error;
/* Call prepare func if it exist */
if (ethr_thr_prepare_func__)
twd.prep_func_res = ethr_thr_prepare_func__();
else
twd.prep_func_res = NULL;
res = pthread_create((pthread_t *) tid, &attr, thr_wrapper, (void*) &twd);
if (res == 0) {
int spin_count = child_wait_spin_count;
/* Wait for child to initialize... */
while (1) {
ethr_sint32_t result;
ethr_event_reset(&twd.tse->event);
result = ethr_atomic32_read(&twd.result);
if (result == 0)
break;
if (result > 0) {
res = (int) result;
goto error;
}
res = ethr_event_swait(&twd.tse->event, spin_count);
if (res != 0 && res != EINTR)
goto error;
spin_count = 0;
}
}
/* Cleanup... */
error:
dres = pthread_attr_destroy(&attr);
if (res == 0)
res = dres;
if (ethr_thr_parent_func__)
ethr_thr_parent_func__(twd.prep_func_res);
return res;
}
int
ethr_tsd_set(ethr_tsd_key key, void *value)
{
#if ETHR_XCHK
if (ethr_not_inited__) {
ETHR_ASSERT(0);
return EACCES;
}
#endif
return pthread_setspecific((pthread_key_t) key, value);
}
外覆函数通过pthread_setspecific为线程设置kv私有数据结构,这个数据结构是线程的erlang线程描述符ErlDrvTid,然后以用户给出的参数调用用户的函数。
进入NIF世界后,就可以借助很多erlang虚拟机的功能来探索erlang世界了,此时既可以通过erlang来实现功能,也可以通过c来实现功能,可以将各式各样的c系统移植如erlang虚拟机,扩充和丰富erlang的功能,同时减少开发的难度,性能也将得到很大的提高。