`
wqtn22
  • 浏览: 99627 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

erlang NIF部分接口实现(五)复用driver功能的接口

    博客分类:
  • erts
 
阅读更多

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 *) &ethr_opts,

  (void *) &def_ethr_opts,

  sizeof(ethr_thr_opts));

ethr_opts.suggested_stack_size = opts->suggested_stack_size;

use_opts = &ethr_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的功能,同时减少开发的难度,性能也将得到很大的提高。

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics