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);
- /* 创建一个操作系统线程,该线程并不直接以用户提供的函数为入口点,而是提供了一层外覆函数, 外覆 函数为erl_drv_thread_wrapper */
-
- 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;
- }
复制代码
可以看出,线程创建过程是一个标准的pthread线程创建框架。
- static void *erl_drv_thread_wrapper(void *vdtid)
- {
- int res;
- struct ErlDrvTid_ *dtid = (struct ErlDrvTid_ *) vdtid;
- res = ethr_tsd_set(tid_key, vdtid);
- if (res != 0)
- fatal_error(res, "erl_drv_thread_wrapper()");
- return (*dtid->func)(dtid->arg);
- }
- 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的功能,同时减少开发的难度,性能也将得到很大的提高。
|