mm_camera_poll_thread

  mm_camera_poll_thread

  先看下提供的外部接口:

/*
    mm_camera_poll_thread_commit_updates 
    功能:同步之前挂起的所有异步事件

    mm_camera_poll_thread_add_poll_fd
    功能:添加一个新的文件句柄到 polling thread,polling thread 负责监听该文件句柄的IO事件

    mm_camera_poll_thread_del_poll_fd
    功能:将指定文件句柄从 polling thread 中删除,polling thread 不在负责监听该文件句柄的IO事件

    mm_camera_poll_thread_launch
    功能:初始化并启动 polling thread

    mm_camera_poll_thread_release
    功能:释放 polling thread
*/

  从上述API中,大体可以猜测出,polling thread 的核心功能:新建一个线程,负责 polling 用户指定的 文件句柄。

 

  那么下面看看具体实现:

/* mm_camera.h */

typedef struct {
    mm_camera_poll_thread_type_t poll_type;
    /* array to store poll fd and cb info
     * for MM_CAMERA_POLL_TYPE_EVT, only index 0 is valid;
     * for MM_CAMERA_POLL_TYPE_DATA, depends on valid stream fd */
    mm_camera_poll_entry_t poll_entries[MAX_STREAM_NUM_IN_BUNDLE];
    int32_t pfds[2];
    pthread_t pid;
    int32_t state;
    int timeoutms;
    uint32_t cmd;
    struct pollfd poll_fds[MAX_STREAM_NUM_IN_BUNDLE + 1];
    uint8_t num_fds;
    pthread_mutex_t mutex;
    pthread_cond_t cond_v;
    int32_t status;
    char threadName[THREAD_NAME_SIZE];
    //void *my_obj;
} mm_camera_poll_thread_t;

typedef enum {
    MM_CAMERA_POLL_TYPE_EVT,
    MM_CAMERA_POLL_TYPE_DATA,
    MM_CAMERA_POLL_TYPE_MAX
} mm_camera_poll_thread_type_t;


/* function ptr defined for poll notify CB,
 * registered at poll thread with poll fd */
typedef void (*mm_camera_poll_notify_t)(void *user_data);

typedef struct {
    int32_t fd;
    mm_camera_poll_notify_t notify_cb;
    uint32_t handler;
    void* user_data;
} mm_camera_poll_entry_t;

 

 

int32_t mm_camera_poll_thread_launch(mm_camera_poll_thread_t * poll_cb,
                                     mm_camera_poll_thread_type_t poll_type)  // poll_type 指定 polling thread 监听的事件类型,是普通事件或者是数据事件
{
    int32_t rc = 0;
    size_t i = 0, cnt = 0;
    poll_cb->poll_type = poll_type;

    //Initialize poll_fds
    cnt = sizeof(poll_cb->poll_fds) / sizeof(poll_cb->poll_fds[0]);
    for (i = 0; i < cnt; i++) {
        poll_cb->poll_fds[i].fd = -1;
    }
    //Initialize poll_entries
    cnt = sizeof(poll_cb->poll_entries) / sizeof(poll_cb->poll_entries[0]);
    for (i = 0; i < cnt; i++) {
        poll_cb->poll_entries[i].fd = -1;
    }
    //Initialize pipe fds
    poll_cb->pfds[0] = -1;
    poll_cb->pfds[1] = -1;
    rc = pipe(poll_cb->pfds);  // 这里很关键,这里创建了一个管道,这个管道负责什么事情呢?
    if(rc < 0) {
        CDBG_ERROR("%s: pipe open rc=%d\n", __func__, rc);
        return -1;
    }

    poll_cb->timeoutms = -1;  /* Infinite seconds */

    CDBG("%s: poll_type = %d, read fd = %d, write fd = %d timeout = %d",
        __func__, poll_cb->poll_type,
        poll_cb->pfds[0], poll_cb->pfds[1],poll_cb->timeoutms);

    pthread_mutex_init(&poll_cb->mutex, NULL);
    pthread_cond_init(&poll_cb->cond_v, NULL);

    /* launch the thread */
    pthread_mutex_lock(&poll_cb->mutex);
    poll_cb->status = 0;
    pthread_create(&poll_cb->pid, NULL, mm_camera_poll_thread, (void *)poll_cb);  // 新建线程,负责 polling ,线程主体是: mm_camera_poll_thread
    if(!poll_cb->status) {
        pthread_cond_wait(&poll_cb->cond_v, &poll_cb->mutex);  // 创建线程的方式以阻塞方式,这里通过检测 poll_cb_status 来确保线程创建成功并以成功调度
    }

    pthread_mutex_unlock(&poll_cb->mutex);
    CDBG("%s: End",__func__);
    return rc;
}

 

int32_t mm_camera_poll_thread_release(mm_camera_poll_thread_t *poll_cb)
{
    int32_t rc = 0;
    if(MM_CAMERA_POLL_TASK_STATE_STOPPED == poll_cb->state) {
        CDBG_ERROR("%s: err, poll thread is not running.\n", __func__);
        return rc;
    }

    /* send exit signal to poll thread */
    mm_camera_poll_sig(poll_cb, MM_CAMERA_PIPE_CMD_EXIT);  // 发送 退出 命令到 polling thread,具体实现下文分析
    /* wait until poll thread exits */
    if (pthread_join(poll_cb->pid, NULL) != 0) {           // 等待 polling thread 成功退出
        CDBG_ERROR("%s: pthread dead already\n", __func__);
    }

    /* close pipe */
    if(poll_cb->pfds[0] >= 0) {               // 关闭管道的读写端
        close(poll_cb->pfds[0]);
    }
    if(poll_cb->pfds[1] >= 0) {
        close(poll_cb->pfds[1]);
    }

    pthread_mutex_destroy(&poll_cb->mutex);
    pthread_cond_destroy(&poll_cb->cond_v);
    memset(poll_cb, 0, sizeof(mm_camera_poll_thread_t));
    poll_cb->pfds[0] = -1;
    poll_cb->pfds[1] = -1;
    return rc;
}

 

   接下来,来看最重要的 polling thread 的线程主体:

static void *mm_camera_poll_thread(void *data)
{
    mm_camera_poll_thread_t *poll_cb = (mm_camera_poll_thread_t *)data;

    mm_camera_cmd_thread_name(poll_cb->threadName);   // 设置线程的名字,当然,这个名字是由用户决定的
    /* add pipe read fd into poll first */
    poll_cb->poll_fds[poll_cb->num_fds++].fd = poll_cb->pfds[0];  // poll_cb->pfds 是管道的读写端的的文件句柄,而 poll_cb->pfds[0] 是管道读端文件句柄。

    mm_camera_poll_sig_done(poll_cb);  // 设置 poll_cb->status 为 TRUE
    mm_camera_poll_set_state(poll_cb, MM_CAMERA_POLL_TASK_STATE_POLL);  // 将 poll_cb->state 设置为 MM_CAMERA_POLL_TASK_STATE_POLL.
    return mm_camera_poll_fn(poll_cb);  // 核心逻辑
}

 

static void mm_camera_poll_sig_done(mm_camera_poll_thread_t *poll_cb)
{
    pthread_mutex_lock(&poll_cb->mutex);
    poll_cb->status = TRUE;                      // 设置 poll_cb->status 为 TRUE。在创建 polling thread 的父线程中,会检测该状态,故需要 加锁。
    pthread_cond_signal(&poll_cb->cond_v);
    CDBG("%s: done, in mutex", __func__);
    pthread_mutex_unlock(&poll_cb->mutex);
}

 

static void *mm_camera_poll_fn(mm_camera_poll_thread_t *poll_cb)
{
    int rc = 0, i;

    if (NULL == poll_cb) {
        CDBG_ERROR("%s: poll_cb is NULL!\n", __func__);
        return NULL;
    }
    CDBG("%s: poll type = %d, num_fd = %d poll_cb = %p\n",
         __func__, poll_cb->poll_type, poll_cb->num_fds,poll_cb);
    do {
         for(i = 0; i < poll_cb->num_fds; i++) {
            poll_cb->poll_fds[i].events = POLLIN|POLLRDNORM|POLLPRI;   // 将所有的 pollfd 要监听的事件设置为: POLLIN|POLLRDNORM|POLLPRI , POLLIN: 有数据可读; POLLRDNORM:普通数据可读; POLLPRI:紧迫数据可读
         }

         rc = poll(poll_cb->poll_fds, poll_cb->num_fds, poll_cb->timeoutms);
         if(rc > 0) {  // 存在就绪的 fd ,rc为就绪的 fd 的个数
            if ((poll_cb->poll_fds[0].revents & POLLIN) &&
                (poll_cb->poll_fds[0].revents & POLLRDNORM)) {   // poll_cb->poll_fds[0]为读就绪状态。poll_fds[0] 始终是 pipe ,即在 launch 中创建的 pipe,该 pipe 用来接收用户命令。
                /* if we have data on pipe, we only process pipe in this iteration */
                CDBG("%s: cmd received on pipe\n", __func__);
                mm_camera_poll_proc_pipe(poll_cb);   // 处理该事件
            } else {                                            // 否则,就是监听的用户文件句柄为读就绪状态
                for(i=1; i<poll_cb->num_fds; i++) {             // poll_fds[1] 开始就是用户文件句柄
                    /* Checking for ctrl events */
                    if ((poll_cb->poll_type == MM_CAMERA_POLL_TYPE_EVT) &&
                        (poll_cb->poll_fds[i].revents & POLLPRI)) {        // 如果 polling thread type 为 MM_CAMERA_POLL_TYPE_EVT,并且实际检测到的时间类型为 POLLPRI
                        CDBG("%s: mm_camera_evt_notify\n", __func__);
                        if (NULL != poll_cb->poll_entries[i-1].notify_cb) {
                            poll_cb->poll_entries[i-1].notify_cb(poll_cb->poll_entries[i-1].user_data);   // 回调用户注册的 回调函数
                        }
                    }

                    if ((MM_CAMERA_POLL_TYPE_DATA == poll_cb->poll_type) &&
                        (poll_cb->poll_fds[i].revents & POLLIN) &&
                        (poll_cb->poll_fds[i].revents & POLLRDNORM)) {     // polling thread type 为MM_CAMERA_POLL_TYPE_DATA
                        CDBG("%s: mm_stream_data_notify\n", __func__);
                        if (NULL != poll_cb->poll_entries[i-1].notify_cb) {
                            poll_cb->poll_entries[i-1].notify_cb(poll_cb->poll_entries[i-1].user_data);
                        }
                    }
                }
            }
        } else {
            /* in error case sleep 10 us and then continue. hard coded here */
            usleep(10);
            continue;
        }
    } while ((poll_cb != NULL) && (poll_cb->state == MM_CAMERA_POLL_TASK_STATE_POLL));
    return NULL;
}

   mm_camera_poll_proc_pipe 是处理管道发送的命令,那么一定是不断的读管道,然后进行处理。在分析此函数之前,我们先分析写管道。

int32_t mm_camera_poll_thread_add_poll_fd(mm_camera_poll_thread_t * poll_cb,
                                          uint32_t handler,
                                          int32_t fd,
                                          mm_camera_poll_notify_t notify_cb,
                                          void* userdata,
                                          mm_camera_call_type_t call_type)
{
    int32_t rc = -1;
    uint8_t idx = 0;

    if (MM_CAMERA_POLL_TYPE_DATA == poll_cb->poll_type) {      // 如果是 监听数据,首先获取 stream index.所以可以猜测,每个 stream 都会通过 poll 监听
        /* get stream idx from handler if CH type */
        idx = mm_camera_util_get_index_by_handler(handler);
    } else {
        /* for EVT type, only idx=0 is valid */
        idx = 0;
    }

    if (MAX_STREAM_NUM_IN_BUNDLE > idx) {   // poll_cb->poll_entries 存放用户注册的信息
        poll_cb->poll_entries[idx].fd = fd;
        poll_cb->poll_entries[idx].handler = handler;
        poll_cb->poll_entries[idx].notify_cb = notify_cb;
        poll_cb->poll_entries[idx].user_data = userdata;
        /* send poll entries updated signal to poll thread */
        if (call_type == mm_camera_sync_call ) {  // 同步调用
            rc = mm_camera_poll_sig(poll_cb, MM_CAMERA_PIPE_CMD_POLL_ENTRIES_UPDATED);
        } else {  // 异步调用
            rc = mm_camera_poll_sig_async(poll_cb, MM_CAMERA_PIPE_CMD_POLL_ENTRIES_UPDATED_ASYNC );
        }
    } else {
        CDBG_ERROR("%s: invalid handler %d (%d)",
                   __func__, handler, idx);
    }
    return rc;
}

 

 

static int32_t mm_camera_poll_sig(mm_camera_poll_thread_t *poll_cb,
                                  uint32_t cmd)
{
    /* send through pipe */
    /* get the mutex */
    mm_camera_sig_evt_t cmd_evt;

    CDBG("%s: E cmd = %d", __func__,cmd);
    memset(&cmd_evt, 0, sizeof(cmd_evt));
    cmd_evt.cmd = cmd;
    pthread_mutex_lock(&poll_cb->mutex);
    /* reset the statue to false */
    poll_cb->status = FALSE;
    /* send cmd to worker */

    ssize_t len = write(poll_cb->pfds[1], &cmd_evt, sizeof(cmd_evt));
    if(len < 1) {
        CDBG_ERROR("%s: len = %lld, errno = %d", __func__,
                (long long int)len, errno);
        /* Avoid waiting for the signal */
        pthread_mutex_unlock(&poll_cb->mutex);
        return 0;
    }
    CDBG("%s: begin IN mutex write done, len = %lld", __func__,
            (long long int)len);
    /* wait till worker task gives positive signal */
    if (FALSE == poll_cb->status) {   // 同步调用,等待 polling thread 成功接收到该命令
        CDBG("%s: wait", __func__);
        pthread_cond_wait(&poll_cb->cond_v, &poll_cb->mutex);
    }
    /* done */
    pthread_mutex_unlock(&poll_cb->mutex);
    CDBG("%s: X", __func__);
    return 0;
}

 

 

static int32_t mm_camera_poll_sig_async(mm_camera_poll_thread_t *poll_cb,
                                  uint32_t cmd)
{
    /* send through pipe */
    /* get the mutex */
    mm_camera_sig_evt_t cmd_evt;

    CDBG("%s: E cmd = %d", __func__,cmd);
    memset(&cmd_evt, 0, sizeof(cmd_evt));
    cmd_evt.cmd = cmd;
    pthread_mutex_lock(&poll_cb->mutex);
    /* reset the statue to false */
    poll_cb->status = FALSE;

    /* send cmd to worker */
    ssize_t len = write(poll_cb->pfds[1], &cmd_evt, sizeof(cmd_evt));  // 异步调用,只是写管道
    if (len < 1) {
        CDBG_ERROR("%s: len = %lld, errno = %d", __func__,
                (long long int)len, errno);
        /* Avoid waiting for the signal */
        pthread_mutex_unlock(&poll_cb->mutex);
        return 0;
    }
    CDBG("%s: begin IN mutex write done, len = %lld", __func__,
            (long long int)len);
    pthread_mutex_unlock(&poll_cb->mutex);
    CDBG("%s: X", __func__);
    return 0;
}

 

   看完如何通过管道发送命令,继续看 polling thread 如何处理管道命令?

static void mm_camera_poll_proc_pipe(mm_camera_poll_thread_t *poll_cb)
{
    ssize_t read_len;
    int i;
    mm_camera_sig_evt_t cmd_evt;
    read_len = read(poll_cb->pfds[0], &cmd_evt, sizeof(cmd_evt));    // 首先读管道,即读取发送的命令
    CDBG("%s: read_fd = %d, read_len = %d, expect_len = %d cmd = %d",
         __func__, poll_cb->pfds[0], (int)read_len, (int)sizeof(cmd_evt), cmd_evt.cmd);
    switch (cmd_evt.cmd) {
    case MM_CAMERA_PIPE_CMD_POLL_ENTRIES_UPDATED:
    case MM_CAMERA_PIPE_CMD_POLL_ENTRIES_UPDATED_ASYNC:
        /* we always have index 0 for pipe read */  /* 重要:总是将 poll_cb->poll_fds[0]作为管道读的文件句柄 */
        poll_cb->num_fds = 0;
        poll_cb->poll_fds[poll_cb->num_fds].fd = poll_cb->pfds[0];
        poll_cb->poll_fds[poll_cb->num_fds].events = POLLIN|POLLRDNORM|POLLPRI;
        poll_cb->num_fds++;

        if (MM_CAMERA_POLL_TYPE_EVT == poll_cb->poll_type &&
                poll_cb->num_fds < MAX_STREAM_NUM_IN_BUNDLE) {
            if (poll_cb->poll_entries[0].fd >= 0) {
                /* fd is valid, we update poll_fds */
                poll_cb->poll_fds[poll_cb->num_fds].fd = poll_cb->poll_entries[0].fd;
                poll_cb->poll_fds[poll_cb->num_fds].events = POLLIN|POLLRDNORM|POLLPRI;
                poll_cb->num_fds++;
            }
        } else if (MM_CAMERA_POLL_TYPE_DATA == poll_cb->poll_type &&
                poll_cb->num_fds <= MAX_STREAM_NUM_IN_BUNDLE) {
            for(i = 0; i < MAX_STREAM_NUM_IN_BUNDLE; i++) {
                if(poll_cb->poll_entries[i].fd >= 0) {
                    /* fd is valid, we update poll_fds to this fd */
                    poll_cb->poll_fds[poll_cb->num_fds].fd = poll_cb->poll_entries[i].fd;
                    poll_cb->poll_fds[poll_cb->num_fds].events = POLLIN|POLLRDNORM|POLLPRI;
                    poll_cb->num_fds++;
                } else {
                    /* fd is invalid, we set the entry to -1 to prevent polling.
                     * According to spec, polling will not poll on entry with fd=-1.
                     * If this is not the case, we need to skip these invalid fds
                     * when updating this array.
                     * We still keep fd=-1 in this array because this makes easier to
                     * map cb associated with this fd once incoming data avail by directly
                     * using the index-1(0 is reserved for pipe read, so need to reduce index by 1) */
                    poll_cb->poll_fds[poll_cb->num_fds].fd = -1;
                    poll_cb->poll_fds[poll_cb->num_fds].events = 0;
                    poll_cb->num_fds++;
                }
            }
        }
        if (cmd_evt.cmd != MM_CAMERA_PIPE_CMD_POLL_ENTRIES_UPDATED_ASYNC)  // 如果不是异步调用,就需要将 poll_cb->status 设置为 TRUE,表示已经接收并处理过命令,通知父线程
            mm_camera_poll_sig_done(poll_cb);
        break;

    case MM_CAMERA_PIPE_CMD_COMMIT:
        mm_camera_poll_sig_done(poll_cb);
        break;
    case MM_CAMERA_PIPE_CMD_EXIT:
    default:
        mm_camera_poll_set_state(poll_cb, MM_CAMERA_POLL_TASK_STATE_STOPPED);
        mm_camera_poll_sig_done(poll_cb);
        break;
    }
}

 

  对于异步调用,必须提供一个接口来确保 polling thread 来处理异步命令。

int32_t mm_camera_poll_thread_commit_updates(mm_camera_poll_thread_t * poll_cb)
{
    return mm_camera_poll_sig(poll_cb, MM_CAMERA_PIPE_CMD_COMMIT);  // 要想确保,还是得通过一个同步命令调用
}

 

  最后,来看看 mm_camera_poll_thread_del_poll_fd、

int32_t mm_camera_poll_thread_del_poll_fd(mm_camera_poll_thread_t * poll_cb,
                                          uint32_t handler,
                                          mm_camera_call_type_t call_type)
{
    int32_t rc = -1;
    uint8_t idx = 0;

    if (MM_CAMERA_POLL_TYPE_DATA == poll_cb->poll_type) {
        /* get stream idx from handler if CH type */
        idx = mm_camera_util_get_index_by_handler(handler);
    } else {
        /* for EVT type, only idx=0 is valid */
        idx = 0;
    }

    if ((MAX_STREAM_NUM_IN_BUNDLE > idx) &&
        (handler == poll_cb->poll_entries[idx].handler)) {
        /* reset poll entry */
        poll_cb->poll_entries[idx].fd = -1; /* set fd to invalid */
        poll_cb->poll_entries[idx].handler = 0;
        poll_cb->poll_entries[idx].notify_cb = NULL;

        /* send poll entries updated signal to poll thread */
        if (call_type == mm_camera_sync_call ) {
            rc = mm_camera_poll_sig(poll_cb, MM_CAMERA_PIPE_CMD_POLL_ENTRIES_UPDATED);
        } else {
            rc = mm_camera_poll_sig_async(poll_cb, MM_CAMERA_PIPE_CMD_POLL_ENTRIES_UPDATED_ASYNC );
        }
    } else {
        CDBG_ERROR("%s: invalid handler %d (%d)",
                   __func__, handler, idx);
        return -1;
    }

    return rc;
}

 

  非常明确的 一个函数。 

posted @ 2018-01-19 10:19  rain-dot  阅读(549)  评论(0)    收藏  举报