dreamxyp 发表于 2013-10-21 19:21:31

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

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的功能,同时减少开发的难度,性能也将得到很大的提高。
页: [1]
查看完整版本: erlang NIF部分接口实现(五)复用driver功能的接口