libuv学习笔记(18)
uv_fs_poll_t数据结构与相关函数
数据结构
typedef struct uv_fs_poll_s uv_fs_poll_t;
struct uv_fs_poll_s {
UV_HANDLE_FIELDS//uv_handle_t成员
/* Private, don't touch. */
void* poll_ctx;
};
//内部数据结构
struct poll_ctx {
uv_fs_poll_t* parent_handle; /* NULL if parent has been stopped or closed */
int busy_polling;
unsigned int interval;
uint64_t start_time;
uv_loop_t* loop;
uv_fs_poll_cb poll_cb;
uv_timer_t timer_handle;//定时器
uv_fs_t fs_req; /* TODO(bnoordhuis) mark fs_req internal */
uv_stat_t statbuf;
char path[1]; /* variable length */
};
相关函数
初始化
int uv_fs_poll_init(uv_loop_t* loop, uv_fs_poll_t* handle) {
//简单的初始化handle
uv__handle_init(loop, (uv_handle_t*)handle, UV_FS_POLL);
return 0;
}
开始监听
int uv_fs_poll_start(uv_fs_poll_t* handle,
uv_fs_poll_cb cb,
const char* path,
unsigned int interval) {
struct poll_ctx* ctx;
uv_loop_t* loop;
size_t len;
int err;
if (uv__is_active(handle))
return 0;
loop = handle->loop;
len = strlen(path);
ctx = uv__calloc(1, sizeof(*ctx) + len);//分配内存,大小为结构体大小加上字符串长度
if (ctx == NULL)
return UV_ENOMEM;
ctx->loop = loop;
ctx->poll_cb = cb;
ctx->interval = interval ? interval : 1;//设置间隔
ctx->start_time = uv_now(loop);//记录起始时间
ctx->parent_handle = handle;//与uv_fs_poll_t联系起来
memcpy(ctx->path, path, len + 1);//路径
//初始化定时器
err = uv_timer_init(loop, &ctx->timer_handle);
if (err < 0)
goto error;
ctx->timer_handle.flags |= UV__HANDLE_INTERNAL;//内部使用handle
uv__handle_unref(&ctx->timer_handle);
err = uv_fs_stat(loop, &ctx->fs_req, ctx->path, poll_cb);
if (err < 0)
goto error;
handle->poll_ctx = ctx;
uv__handle_start(handle);
return 0;
error:
uv__free(ctx);
return err;
}
int uv_fs_stat(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb) {
int err;
//初始化请求
uv_fs_req_init(loop, req, UV_FS_STAT, cb);
err = fs__capture_path(req, path, NULL, cb != NULL);
if (err) {
return uv_translate_sys_error(err);
}
if (cb) {
//QUEUE_FS_TP_JOB(loop, req);展开如下:
do {
uv__req_register(loop, req);
//将uv__fs_work添加到线程池,uv__fs_work中会查询文件状态,uv__fs_done会调用poll_cb,其中会比较上一次与本次的文件状态,若状态改变会调用start的回调函数。poll_cb最终会再次开启定时器
uv__work_submit((loop), &(req)->work_req, uv__fs_work, uv__fs_done);
} while (0)
return 0;
} else {
fs__stat(req);
return req->result;
}
}
停止监听
int uv_fs_poll_stop(uv_fs_poll_t* handle) {
struct poll_ctx* ctx;
if (!uv__is_active(handle))
return 0;
ctx = handle->poll_ctx;
assert(ctx != NULL);
assert(ctx->parent_handle != NULL);
ctx->parent_handle = NULL;
handle->poll_ctx = NULL;
//如果定时器为激活状态,那么关闭它
if (uv__is_active(&ctx->timer_handle))
uv_close((uv_handle_t*)&ctx->timer_handle, timer_close_cb);
uv__handle_stop(handle);
return 0;
}
整个文件状态轮询是通过线程池与定时器实现的
1.首先向线程池注册一个任务uv__fs_work,在这个任务中会查询对应文件的状态,结果保存在uv_fs_t的statbuf中。
2.uv__fs_work完成之后,会通过loop的wq_async向loop发送异步唤醒请求,此处通过uv_mutex_lock来达到线程同步
3.在loop所在线程中,处理唤醒请求,调用uv__fs_done
4.uv__fs_done最终调用poll_cb,在poll_cb中会判断上一次的文件状态(存在poll_ctx的statbuf中)与本次文件状态是否有区别,如果有区别,就会调用用户的回调函数,并将statbuf赋值给poll_ctx的statbuf。poll_cb最终会再次激活定时器
5.定时器的回调函数timer_cb会调用uv_fs_stat再次重复上面的流程
uv_fs_poll_t所用的定时器是没有设置重复间隔的,所以只会执行一次,每次执行完之后,会再次激活。
只有uv__fs_work函数也就是查询文件状态的函数会在线程池中执行,其他的回调函数都在loop所在的线程池中执行,以此来达到线程池安全。