libuv学习笔记(17)
uv_fs_event_t数据结构与相关函数
数据结构
typedef struct uv_fs_event_s uv_fs_event_t;
struct uv_fs_event_s {
UV_HANDLE_FIELDS//uv_handle_t的成员
/* private */
char* path;//路径,utf8编码,由libuv申请、释放
//UV_FS_EVENT_PRIVATE_FIELDS展开如下:
struct uv_fs_event_req_s {
UV_REQ_FIELDS
} req; //请求
HANDLE dir_handle;//文件夹句柄,通过CreateFileW获取
int req_pending;//表计量,判断是否开始监听文件
uv_fs_event_cb cb;//回调函数
WCHAR* filew;/utf16文件名,由libuv申请、释放
WCHAR* short_filew;//utf16编码的短路径文件名,由libuv申请、释放
WCHAR* dirw;//utf16编码的文件夹路径,由libuv申请、释放
char* buffer;//存放监控返回的信息,由libuv申请、释放
};
相关函数
初始化
int uv_fs_event_init(uv_loop_t* loop, uv_fs_event_t* handle) {
uv__handle_init(loop, (uv_handle_t*) handle, UV_FS_EVENT);//初始化handle
handle->dir_handle = INVALID_HANDLE_VALUE;
handle->buffer = NULL;
handle->req_pending = 0;
handle->filew = NULL;
handle->short_filew = NULL;
handle->dirw = NULL;
uv_req_init(loop, (uv_req_t*)&handle->req);//初始化请求
handle->req.type = UV_FS_EVENT_REQ;//请求类型
handle->req.data = handle;
return 0;
}
开始event
int uv_fs_event_start(uv_fs_event_t* handle,
uv_fs_event_cb cb,
const char* path,
unsigned int flags) {
int name_size, is_path_dir;
DWORD attr, last_error;
WCHAR* dir = NULL, *dir_to_watch, *pathw = NULL;
WCHAR short_path[MAX_PATH];
if (uv__is_active(handle))//活动状态,返回
return UV_EINVAL;
handle->cb = cb;
handle->path = uv__strdup(path);//复制路径
if (!handle->path) {
uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc");
}
uv__handle_start(handle);//设为活动状态
//utf8转utf16 path ------》 pathw
......
//判断路径是文件还是文件夹
attr = GetFileAttributesW(pathw);
if (attr == INVALID_FILE_ATTRIBUTES) {
last_error = GetLastError();
goto error;
}
is_path_dir = (attr & FILE_ATTRIBUTE_DIRECTORY) ? 1 : 0;
if (is_path_dir) {
//路径是文件夹
handle->dirw = pathw;
dir_to_watch = pathw;
} else {
//路径是文件,获取文件名与路径
//转换为短路径
if (!GetShortPathNameW(pathw, short_path, ARRAY_SIZE(short_path))) {
last_error = GetLastError();
goto error;
}
//分割路径,获取路径与文件夹
if (uv_split_path(pathw, &dir, &handle->filew) != 0) {
last_error = GetLastError();
goto error;
}
if (uv_split_path(short_path, NULL, &handle->short_filew) != 0) {
last_error = GetLastError();
goto error;
}
dir_to_watch = dir;
uv__free(pathw);//释放
pathw = NULL;
}
//获取文件夹句柄
handle->dir_handle = CreateFileW(dir_to_watch,
FILE_LIST_DIRECTORY,
FILE_SHARE_READ | FILE_SHARE_DELETE |
FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS |
FILE_FLAG_OVERLAPPED,
NULL);
if (dir) {//在路径为文件的情况下释放文件夹路径
uv__free(dir);
dir = NULL;
}
//未能获取句柄,返回错误
if (handle->dir_handle == INVALID_HANDLE_VALUE) {
last_error = GetLastError();
goto error;
}
//与iocp端口联系起来
if (CreateIoCompletionPort(handle->dir_handle,
handle->loop->iocp,
(ULONG_PTR)handle,
0) == NULL) {
last_error = GetLastError();
goto error;
}
//分配handle->buffer内存
if (!handle->buffer) {
handle->buffer = (char*)uv__malloc(uv_directory_watcher_buffer_size);
}
if (!handle->buffer) {
uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc");
}
memset(&(handle->req.u.io.overlapped), 0,
sizeof(handle->req.u.io.overlapped));
//异步监控文件夹
if (!ReadDirectoryChangesW(handle->dir_handle,
handle->buffer,
uv_directory_watcher_buffer_size,
(flags & UV_FS_EVENT_RECURSIVE) ? TRUE : FALSE,
FILE_NOTIFY_CHANGE_FILE_NAME |
FILE_NOTIFY_CHANGE_DIR_NAME |
FILE_NOTIFY_CHANGE_ATTRIBUTES |
FILE_NOTIFY_CHANGE_SIZE |
FILE_NOTIFY_CHANGE_LAST_WRITE |
FILE_NOTIFY_CHANGE_LAST_ACCESS |
FILE_NOTIFY_CHANGE_CREATION |
FILE_NOTIFY_CHANGE_SECURITY,
NULL,
&handle->req.u.io.overlapped,
NULL)) {
last_error = GetLastError();
goto error;
}
handle->req_pending = 1;
return 0;
error:
if (handle->path) {
uv__free(handle->path);
handle->path = NULL;
}
if (handle->filew) {
uv__free(handle->filew);
handle->filew = NULL;
}
if (handle->short_filew) {
uv__free(handle->short_filew);
handle->short_filew = NULL;
}
uv__free(pathw);
if (handle->dir_handle != INVALID_HANDLE_VALUE) {
CloseHandle(handle->dir_handle);
handle->dir_handle = INVALID_HANDLE_VALUE;
}
if (handle->buffer) {
uv__free(handle->buffer);
handle->buffer = NULL;
}
return uv_translate_sys_error(last_error);
}
在uv_process_reqs中对于监控请求的处理会调用uv_process_fs_event_req
void uv_process_fs_event_req(uv_loop_t* loop, uv_req_t* req,
uv_fs_event_t* handle) {
FILE_NOTIFY_INFORMATION* file_info;
int err, sizew, size;
char* filename = NULL;
WCHAR* filenamew = NULL;
WCHAR* long_filenamew = NULL;
DWORD offset = 0;
assert(req->type == UV_FS_EVENT_REQ);
assert(handle->req_pending);
handle->req_pending = 0;//标记量改为0,表示一个当前监听已被处理
//非激活状态下,直接返回
//closing状态下,调用uv_want_endgame,返回
if (!uv__is_active(handle)) {
if (handle->flags & UV__HANDLE_CLOSING) {
uv_want_endgame(loop, (uv_handle_t*) handle);
}
return;
}
//获取文件变动信息
file_info = (FILE_NOTIFY_INFORMATION*)(handle->buffer + offset);
if (REQ_SUCCESS(req)) {//成功
if (req->u.io.overlapped.InternalHigh > 0) {
do {//循环对每一个监听到的改变调用回调函数
file_info = (FILE_NOTIFY_INFORMATION*)((char*)file_info + offset);
assert(!filename);
assert(!filenamew);
assert(!long_filenamew);
//如果监听的是文件夹,或者改变的文件名与监听的文件名匹配,那么调用回调
if (handle->dirw ||//监听的是文件夹
_wcsnicmp(handle->filew, file_info->FileName,
file_info->FileNameLength / sizeof(WCHAR)) == 0 ||
_wcsnicmp(handle->short_filew, file_info->FileName,
file_info->FileNameLength / sizeof(WCHAR)) == 0) {
if (handle->dirw) {
//监听的是文件夹
if (file_info->Action != FILE_ACTION_REMOVED &&
file_info->Action != FILE_ACTION_RENAMED_OLD_NAME) {
//构建文件的全路径
size = wcslen(handle->dirw) +
file_info->FileNameLength / sizeof(WCHAR) + 2;
filenamew = (WCHAR*)uv__malloc(size * sizeof(WCHAR));
if (!filenamew) {
uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc");
}
_snwprintf(filenamew, size, L"%s\\%.*s", handle->dirw,
file_info->FileNameLength / sizeof(WCHAR),
file_info->FileName);
filenamew[size - 1] = L'\0';
//转换为长路径
size = GetLongPathNameW(filenamew, NULL, 0);
if (size) {
long_filenamew = (WCHAR*)uv__malloc(size * sizeof(WCHAR));
if (!long_filenamew) {
uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc");
}
size = GetLongPathNameW(filenamew, long_filenamew, size);
if (size) {
long_filenamew[size] = '\0';
} else {
uv__free(long_filenamew);
long_filenamew = NULL;
}
}
uv__free(filenamew);
if (long_filenamew) {
/* Get the file name out of the long path. */
uv_relative_path(long_filenamew,
handle->dirw,
&filenamew);
uv__free(long_filenamew);
long_filenamew = filenamew;
sizew = -1;
} else {
//无法获取长路径
filenamew = file_info->FileName;
sizew = file_info->FileNameLength / sizeof(WCHAR);
}
} else {
filenamew = file_info->FileName;
sizew = file_info->FileNameLength / sizeof(WCHAR);
}
} else {
//监控的是文件,直接使用监控的文件名
filenamew = handle->filew;
sizew = -1;
}
//将文件名转为utf8
uv__convert_utf16_to_utf8(filenamew, sizew, &filename);
switch (file_info->Action) {
case FILE_ACTION_ADDED:
case FILE_ACTION_REMOVED:
case FILE_ACTION_RENAMED_OLD_NAME:
case FILE_ACTION_RENAMED_NEW_NAME:
handle->cb(handle, filename, UV_RENAME, 0);
break;
case FILE_ACTION_MODIFIED:
handle->cb(handle, filename, UV_CHANGE, 0);
break;
}
uv__free(filename);
filename = NULL;
uv__free(long_filenamew);
long_filenamew = NULL;
filenamew = NULL;
}
offset = file_info->NextEntryOffset;
} while (offset && !(handle->flags & UV__HANDLE_CLOSING));
} else {
handle->cb(handle, NULL, UV_CHANGE, 0);
}
} else {
err = GET_REQ_ERROR(req);
handle->cb(handle, NULL, 0, uv_translate_sys_error(err));
}
if (!(handle->flags & UV__HANDLE_CLOSING)) {
//不在关闭状态,继续下一次监听,也就是调用ReadDirectoryChangesW
uv_fs_event_queue_readdirchanges(loop, handle);
} else {
uv_want_endgame(loop, (uv_handle_t*)handle);
}
}
停止监听
int uv_fs_event_stop(uv_fs_event_t* handle) {
if (!uv__is_active(handle))
return 0;
//关闭句柄
if (handle->dir_handle != INVALID_HANDLE_VALUE) {
CloseHandle(handle->dir_handle);
handle->dir_handle = INVALID_HANDLE_VALUE;
}
//停止handle
uv__handle_stop(handle);
//释放字符串
if (handle->filew) {
uv__free(handle->filew);
handle->filew = NULL;
}
if (handle->short_filew) {
uv__free(handle->short_filew);
handle->short_filew = NULL;
}
if (handle->path) {
uv__free(handle->path);
handle->path = NULL;
}
if (handle->dirw) {
uv__free(handle->dirw);
handle->dirw = NULL;
}
return 0;
}
uv_fs_event_t提供了对于文件的监控(文件修改、重命名等),可以看出,实际上是监视的文件所在的文件夹。整个流程相对于其他的异步事件要简单一些,特别是停止监视的流程。