diff --git a/include/uv-unix.h b/include/uv-unix.h index e918e4d47a..2420691cd9 100644 --- a/include/uv-unix.h +++ b/include/uv-unix.h @@ -39,6 +39,8 @@ typedef struct { size_t len; } uv_buf_t; +typedef int uv_file; + #define UV_REQ_BUFSML_SIZE (4) #define UV_REQ_PRIVATE_FIELDS /* empty */ @@ -155,4 +157,7 @@ typedef struct { #define UV_PROCESS_PRIVATE_FIELDS \ ev_child child_watcher; +#define UV_FS_PRIVATE_FIELDS \ +#define UV_WORK_PRIVATE_FIELDS \ + #endif /* UV_UNIX_H */ diff --git a/include/uv-win.h b/include/uv-win.h index 06966d5c20..438b18fce6 100644 --- a/include/uv-win.h +++ b/include/uv-win.h @@ -42,6 +42,8 @@ typedef struct uv_buf_t { char* base; } uv_buf_t; +typedef int uv_file; + #define UV_REQ_TYPE_PRIVATE \ /* TODO: remove the req suffix */ \ UV_ARES_EVENT_REQ, \ @@ -208,5 +210,22 @@ typedef struct uv_buf_t { HANDLE process_handle; \ HANDLE close_handle; +#define UV_FS_PRIVATE_FIELDS \ + int flags; \ + void* arg0; \ + union { \ + struct { \ + void* arg1; \ + void* arg2; \ + void* arg3; \ + }; \ + struct { \ + ssize_t arg4; \ + ssize_t arg5; \ + }; \ + }; + +#define UV_WORK_PRIVATE_FIELDS \ + int uv_utf16_to_utf8(const wchar_t* utf16Buffer, size_t utf16Size, char* utf8Buffer, size_t utf8Size); int uv_utf8_to_utf16(const char* utf8Buffer, wchar_t* utf16Buffer, size_t utf16Size); diff --git a/include/uv.h b/include/uv.h index 701599aa0e..2ad7c9ebb9 100644 --- a/include/uv.h +++ b/include/uv.h @@ -60,6 +60,8 @@ typedef struct uv_shutdown_s uv_shutdown_t; typedef struct uv_write_s uv_write_t; typedef struct uv_connect_s uv_connect_t; typedef struct uv_udp_send_s uv_udp_send_t; +typedef struct uv_fs_s uv_fs_t; +typedef struct uv_work_s uv_work_t; #if defined(__unix__) || defined(__POSIX__) || defined(__APPLE__) # include "uv-unix.h" @@ -123,6 +125,9 @@ typedef void (*uv_check_cb)(uv_check_t* handle, int status); typedef void (*uv_idle_cb)(uv_idle_t* handle, int status); typedef void (*uv_getaddrinfo_cb)(uv_getaddrinfo_t* handle, int status, struct addrinfo* res); typedef void (*uv_exit_cb)(uv_process_t*, int exit_status, int term_signal); +typedef void (*uv_fs_cb)(uv_fs_t* req); +typedef void (*uv_work_cb)(uv_work_t* req); +typedef void (*uv_after_work_cb)(uv_work_t* req); /* Expand this list if necessary. */ @@ -199,6 +204,8 @@ typedef enum { UV_SHUTDOWN, UV_WAKEUP, UV_UDP_SEND, + UV_FS, + UV_WORK, UV_REQ_TYPE_PRIVATE } uv_req_type; @@ -801,6 +808,89 @@ int uv_spawn(uv_process_t*, uv_process_options_t options); int uv_process_kill(uv_process_t*, int signum); +/* + * uv_work_t is a subclass of uv_req_t + */ +struct uv_work_s { + UV_REQ_FIELDS + uv_work_cb work_cb; + uv_after_work_cb after_work_cb; + UV_WORK_PRIVATE_FIELDS +}; + +/* Queues a work request to execute asynchronously on the thread pool. */ +int uv_queue_work(uv_work_t* req, uv_work_cb work_cb, uv_after_work_cb after_work_cb); + + +typedef enum { + UV_FS_UNKNOWN = -1, + UV_FS_CUSTOM, + UV_FS_OPEN, + UV_FS_CLOSE, + UV_FS_READ, + UV_FS_WRITE, + UV_FS_SENDFILE, + UV_FS_STAT, + UV_FS_LSTAT, + UV_FS_FSTAT, + UV_FS_FTRUNCATE, + UV_FS_UTIME, + UV_FS_FUTIME, + UV_FS_CHMOD, + UV_FS_FCHMOD, + UV_FS_FSYNC, + UV_FS_FDATASYNC, + UV_FS_UNLINK, + UV_FS_RMDIR, + UV_FS_MKDIR, + UV_FS_RENAME, + UV_FS_READDIR, + UV_FS_LINK, + UV_FS_SYMLINK, + UV_FS_READLINK, +} uv_fs_type; + +/* + * uv_fs_t is a subclass of uv_req_t + */ +struct uv_fs_s { + UV_REQ_FIELDS + uv_fs_type fs_type; + uv_fs_cb cb; + ssize_t result; + void* ptr; + int errorno; + UV_FS_PRIVATE_FIELDS +}; + +void uv_fs_req_cleanup(uv_fs_t* req); +int uv_fs_close(uv_fs_t* req, uv_file file, uv_fs_cb cb); +int uv_fs_open(uv_fs_t* req, const char* path, int flags, int mode, uv_fs_cb cb); +int uv_fs_read(uv_fs_t* req, uv_file file, void* buf, size_t length, off_t offset, uv_fs_cb cb); +int uv_fs_unlink(uv_fs_t* req, const char* path, uv_fs_cb cb); +int uv_fs_write(uv_fs_t* req, uv_file file, void* buf, size_t length, off_t offset, uv_fs_cb cb); +int uv_fs_mkdir(uv_fs_t* req, const char* path, int mode, uv_fs_cb cb); +int uv_fs_rmdir(uv_fs_t* req, const char* path, uv_fs_cb cb); +int uv_fs_readdir(uv_fs_t* req, const char* path, int flags, uv_fs_cb cb); +int uv_fs_stat(uv_fs_t* req, const char* path, uv_fs_cb cb); +int uv_fs_fstat(uv_fs_t* req, uv_file file, uv_fs_cb cb); +int uv_fs_rename(uv_fs_t* req, const char* path, const char* new_path, uv_fs_cb cb); +int uv_fs_fsync(uv_fs_t* req, uv_file file, uv_fs_cb cb); +int uv_fs_fdatasync(uv_fs_t* req, uv_file file, uv_fs_cb cb); +int uv_fs_ftruncate(uv_fs_t* req, uv_file file, off_t offset, uv_fs_cb cb); +int uv_fs_sendfile(uv_fs_t* req, uv_file out_fd, uv_file in_fd, off_t in_offset, size_t length, uv_fs_cb cb); +int uv_fs_chmod(uv_fs_t* req, const char* path, int mode, uv_fs_cb cb); +int uv_fs_utime(uv_fs_t* req, const char* path, double atime, double mtime, uv_fs_cb cb); +int uv_fs_futime(uv_fs_t* req, uv_file file, double atime, double mtime, uv_fs_cb cb); +int uv_fs_lstat(uv_fs_t* req, const char* path, uv_fs_cb cb); +int uv_fs_link(uv_fs_t* req, const char* path, const char* new_path, uv_fs_cb cb); +int uv_fs_symlink(uv_fs_t* req, const char* path, const char* new_path, uv_fs_cb cb); +int uv_fs_readlink(uv_fs_t* req, const char* path, uv_fs_cb cb); +int uv_fs_fchmod(uv_fs_t* req, uv_file file, int mode, uv_fs_cb cb); +int uv_fs_chown(uv_fs_t* req, const char* path, int uid, int gid, uv_fs_cb cb); +int uv_fs_fchown(uv_fs_t* req, uv_file file, int uid, int gid, uv_fs_cb cb); + + /* Utility */ /* Convert string ip addresses to binary structures */ @@ -843,6 +933,8 @@ union uv_any_req { uv_write_t write; uv_connect_t connect; uv_shutdown_t shutdown; + uv_fs_t fs_req; + uv_work_t work_req; }; @@ -876,6 +968,8 @@ uv_counters_t* uv_counters(); #undef UV_ASYNC_PRIVATE_FIELDS #undef UV_TIMER_PRIVATE_FIELDS #undef UV_GETADDRINFO_PRIVATE_FIELDS +#undef UV_FS_REQ_PRIVATE_FIELDS +#undef UV_WORK_PRIVATE_FIELDS #ifdef __cplusplus } diff --git a/src/uv-unix.c b/src/uv-unix.c index f45aa22760..708909e4c3 100644 --- a/src/uv-unix.c +++ b/src/uv-unix.c @@ -2961,3 +2961,158 @@ int uv_process_kill(uv_process_t* process, int signum) { return 0; } } + + +void uv_fs_req_cleanup(uv_fs_t* req) { + assert(0 && "implement me"); +} + + +int uv_fs_close(uv_fs_t* req, uv_file file, uv_fs_cb cb) { + assert(0 && "implement me"); + return -1; +} + + +int uv_fs_open(uv_fs_t* req, const char* path, int flags, int mode, uv_fs_cb cb) { + assert(0 && "implement me"); + return -1; +} + + +int uv_fs_read(uv_fs_t* req, uv_file file, void* buf, size_t length, off_t offset, uv_fs_cb cb) { + assert(0 && "implement me"); + return -1; +} + + +int uv_fs_unlink(uv_fs_t* req, const char* path, uv_fs_cb cb) { + assert(0 && "implement me"); + return -1; +} + + +int uv_fs_write(uv_fs_t* req, uv_file file, void* buf, size_t length, off_t offset, uv_fs_cb cb) { + assert(0 && "implement me"); + return -1; +} + + +int uv_fs_mkdir(uv_fs_t* req, const char* path, int mode, uv_fs_cb cb) { + assert(0 && "implement me"); + return -1; +} + + +int uv_fs_rmdir(uv_fs_t* req, const char* path, uv_fs_cb cb) { + assert(0 && "implement me"); + return -1; +} + + +int uv_fs_readdir(uv_fs_t* req, const char* path, int flags, uv_fs_cb cb) { + assert(0 && "implement me"); + return -1; +} + + +int uv_fs_stat(uv_fs_t* req, const char* path, uv_fs_cb cb) { + assert(0 && "implement me"); + return -1; +} + + +int uv_fs_fstat(uv_fs_t* req, uv_file file, uv_fs_cb cb) { + assert(0 && "implement me"); + return -1; +} + + +int uv_fs_rename(uv_fs_t* req, const char* path, const char* new_path, uv_fs_cb cb) { + assert(0 && "implement me"); + return -1; +} + + +int uv_fs_fsync(uv_fs_t* req, uv_file file, uv_fs_cb cb) { + assert(0 && "implement me"); + return -1; +} + + +int uv_fs_fdatasync(uv_fs_t* req, uv_file file, uv_fs_cb cb) { + assert(0 && "implement me"); + return -1; +} + + +int uv_fs_ftruncate(uv_fs_t* req, uv_file file, off_t offset, uv_fs_cb cb) { + assert(0 && "implement me"); + return -1; +} + + +int uv_fs_sendfile(uv_fs_t* req, uv_file out_fd, uv_file in_fd, off_t in_offset, size_t length, uv_fs_cb cb) { + assert(0 && "implement me"); + return -1; +} + + +int uv_fs_chmod(uv_fs_t* req, const char* path, int mode, uv_fs_cb cb) { + assert(0 && "implement me"); + return -1; +} + + +int uv_fs_utime(uv_fs_t* req, const char* path, double atime, double mtime, uv_fs_cb cb) { + assert(0 && "implement me"); + return -1; +} + + +int uv_fs_futime(uv_fs_t* req, uv_file file, double atime, double mtime, uv_fs_cb cb) { + assert(0 && "implement me"); + return -1; +} + + +int uv_fs_lstat(uv_fs_t* req, const char* path, uv_fs_cb cb) { + assert(0 && "implement me"); + return -1; +} + + +int uv_fs_link(uv_fs_t* req, const char* path, const char* new_path, uv_fs_cb cb) { + assert(0 && "implement me"); + return -1; +} + + +int uv_fs_symlink(uv_fs_t* req, const char* path, const char* new_path, uv_fs_cb cb) { + assert(0 && "implement me"); + return -1; +} + + +int uv_fs_readlink(uv_fs_t* req, const char* path, uv_fs_cb cb) { + assert(0 && "implement me"); + return -1; +} + + +int uv_fs_fchmod(uv_fs_t* req, uv_file file, int mode, uv_fs_cb cb) { + assert(0 && "implement me"); + return -1; +} + + +int uv_fs_chown(uv_fs_t* req, const char* path, int uid, int gid, uv_fs_cb cb) { + assert(0 && "implement me"); + return -1; +} + + +int uv_fs_fchown(uv_fs_t* req, uv_file file, int uid, int gid, uv_fs_cb cb) { + assert(0 && "implement me"); + return -1; +} diff --git a/src/win/core.c b/src/win/core.c index c85952b6ba..89e726fa86 100644 --- a/src/win/core.c +++ b/src/win/core.c @@ -73,6 +73,9 @@ void uv_init() { /* Fetch winapi function pointers */ uv_winapi_init(); + /* Initialize FS */ + uv_fs_init(); + /* Intialize event loop */ uv_loop_init(); } diff --git a/src/win/fs.c b/src/win/fs.c new file mode 100644 index 0000000000..a5186cd74e --- /dev/null +++ b/src/win/fs.c @@ -0,0 +1,732 @@ +/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "uv.h" +#include "internal.h" + +#define UV_FS_ASYNC 0x0001 +#define UV_FS_FREE_ARG0 0x0002 +#define UV_FS_FREE_ARG1 0x0004 +#define UV_FS_FREE_PTR 0x0008 + +#define SET_REQ_RESULT(req, result) \ + req->result = result; \ + if (result == -1) { \ + req->errorno = errno; \ + } + +#define STRDUP_ARG(req, i) \ + req->arg##i = (void*)strdup((const char*)req->arg##i); \ + if (!req->arg##i) { \ + uv_fatal_error(ERROR_OUTOFMEMORY, "malloc"); \ + } \ + req->flags |= UV_FS_FREE_ARG##i; + +#define WRAP_REQ_ARGS1(req, a0) \ + req->arg0 = (void*)a0; + +#define WRAP_REQ_ARGS2(req, a0, a1) \ + WRAP_REQ_ARGS1(req, a0) \ + req->arg1 = (void*)a1; + +#define WRAP_REQ_ARGS3(req, a0, a1, a2) \ + WRAP_REQ_ARGS2(req, a0, a1) \ + req->arg2 = (void*)a2; + +#define WRAP_REQ_ARGS4(req, a0, a1, a2, a3) \ + WRAP_REQ_ARGS3(req, a0, a1, a2) \ + req->arg3 = (void*)a3; + +#define QUEUE_FS_TP_JOB(req) \ + if (!QueueUserWorkItem(&uv_fs_thread_proc, req, WT_EXECUTELONGFUNCTION)) {\ + uv_set_sys_error(GetLastError()); \ + return -1; \ + } \ + uv_ref(); + + +void uv_fs_init() { + _fmode = _O_BINARY; +} + + +static void uv_fs_req_init_async(uv_fs_t* req, uv_fs_type fs_type, uv_fs_cb cb) { + uv_req_init((uv_req_t*) req); + req->type = UV_FS; + req->flags = UV_FS_ASYNC; + req->fs_type = fs_type; + req->cb = cb; + req->result = 0; + req->ptr = NULL; + req->errorno = 0; + memset(&req->overlapped, 0, sizeof(req->overlapped)); +} + + +static void uv_fs_req_init_sync(uv_fs_t* req, uv_fs_type fs_type) { + uv_req_init((uv_req_t*) req); + req->type = UV_FS; + req->flags = 0; + req->fs_type = fs_type; + req->result = 0; + req->ptr = NULL; + req->errorno = 0; +} + + +void fs__open(uv_fs_t* req, const char* path, int flags, int mode) { + int result = _open(path, flags, mode); + SET_REQ_RESULT(req, result); +} + + +void fs__close(uv_fs_t* req, uv_file file) { + int result = _close(file); + SET_REQ_RESULT(req, result); +} + + +void fs__read(uv_fs_t* req, uv_file file, void *buf, size_t length, off_t offset) { + int result = 0; + + if (offset != -1) { + result = _lseek(file, offset, SEEK_SET); + } + + if (result != -1) { + result = _read(file, buf, length); + } + + SET_REQ_RESULT(req, result); +} + + +void fs__write(uv_fs_t* req, uv_file file, void *buf, size_t length, off_t offset) { + int result = 0; + + if (offset != -1) { + result = _lseek(file, offset, SEEK_SET); + } + + if (result != -1) { + result = _write(file, buf, length); + } + + SET_REQ_RESULT(req, result); +} + + +void fs__unlink(uv_fs_t* req, const char* path) { + int result = _unlink(path); + SET_REQ_RESULT(req, result); +} + + +void fs__mkdir(uv_fs_t* req, const char* path, int mode) { + int result = _mkdir(path); + SET_REQ_RESULT(req, result); +} + + +void fs__rmdir(uv_fs_t* req, const char* path) { + int result = _rmdir(path); + SET_REQ_RESULT(req, result); +} + + +void fs__readdir(uv_fs_t* req, const char* path, int flags) { + int result; + char* buf, *ptr, *name; + HANDLE dir; + WIN32_FIND_DATAA ent = {0}; + size_t len = strlen(path); + size_t buf_size = 4096; + const char* fmt = !len ? "./*" + : (path[len - 1] == '/' || path[len - 1] == '\\') ? "%s*" + : "%s\\*"; + + char* path2 = (char*)malloc(len + 4); + if (!path2) { + uv_fatal_error(ERROR_OUTOFMEMORY, "malloc"); + } + + sprintf(path2, fmt, path); + dir = FindFirstFileA(path2, &ent); + free(path2); + + if(dir == INVALID_HANDLE_VALUE) { + result = -1; + goto done; + } + + buf = (char*)malloc(buf_size); + if (!buf) { + uv_fatal_error(ERROR_OUTOFMEMORY, "malloc"); + } + + ptr = buf; + result = 0; + + do { + name = ent.cFileName; + + if (name[0] != '.' || (name[1] && (name[1] != '.' || name[2]))) { + len = strlen(name); + + while ((ptr - buf) + len + 1 > buf_size) { + buf_size *= 2; + path2 = buf; + buf = (char*)realloc(buf, buf_size); + if (!buf) { + uv_fatal_error(ERROR_OUTOFMEMORY, "realloc"); + } + + ptr = buf + (ptr - path2); + } + + strcpy(ptr, name); + ptr += len + 1; + result++; + } + } while(FindNextFileA(dir, &ent)); + + FindClose(dir); + + req->ptr = buf; + req->flags |= UV_FS_FREE_PTR; + +done: + SET_REQ_RESULT(req, result); +} + + +void fs__stat(uv_fs_t* req, const char* path) { + int result; + + req->ptr = malloc(sizeof(struct _stat)); + if (!req->ptr) { + uv_fatal_error(ERROR_OUTOFMEMORY, "malloc"); + } + + result = _stat(path, (struct _stat*)req->ptr); + if (result == -1) { + free(req->ptr); + req->ptr = NULL; + } else { + req->flags |= UV_FS_FREE_PTR; + } + + SET_REQ_RESULT(req, result); +} + + +void fs__fstat(uv_fs_t* req, uv_file file) { + int result; + + req->ptr = malloc(sizeof(struct _stat)); + if (!req->ptr) { + uv_fatal_error(ERROR_OUTOFMEMORY, "malloc"); + } + + result = _fstat(file, (struct _stat*)req->ptr); + if (result == -1) { + free(req->ptr); + req->ptr = NULL; + } else { + req->flags |= UV_FS_FREE_PTR; + } + + SET_REQ_RESULT(req, result); +} + + +void fs__rename(uv_fs_t* req, const char* path, const char* new_path) { + int result = rename(path, new_path); + SET_REQ_RESULT(req, result); +} + + +void fs__fsync(uv_fs_t* req, uv_file file) { + int result = FlushFileBuffers((HANDLE)_get_osfhandle(file)) ? 0 : -1; + SET_REQ_RESULT(req, result); +} + + +void fs__ftruncate(uv_fs_t* req, uv_file file, off_t offset) { + int result = _chsize(file, offset); + SET_REQ_RESULT(req, result); +} + + +void fs__sendfile(uv_fs_t* req, uv_file out_file, uv_file in_file, off_t in_offset, size_t length) { + const size_t max_buf_size = 65536; + size_t buf_size = length < max_buf_size ? length : max_buf_size; + int n, result = 0; + char* buf = (char*)malloc(buf_size); + if (!buf) { + uv_fatal_error(ERROR_OUTOFMEMORY, "malloc"); + } + + if (in_offset != -1) { + result = _lseek(in_file, in_offset, SEEK_SET); + } + + if (result != -1) { + while (length > 0) { + n = _read(in_file, buf, length < buf_size ? length : buf_size); + if (n == 0) { + break; + } else if (n == -1) { + result = -1; + break; + } + + length -= n; + + n = _write(out_file, buf, n); + if (n == -1) { + result = -1; + break; + } + + result += n; + } + } + + SET_REQ_RESULT(req, result); +} + + +void fs__chmod(uv_fs_t* req, const char* path, int mode) { + int result = _chmod(path, mode); + SET_REQ_RESULT(req, result); +} + + +void fs__utime(uv_fs_t* req, const char* path, double atime, double mtime) { + int result; + struct _utimbuf b = {(time_t)atime, (time_t)mtime}; + result = _utime(path, &b); + SET_REQ_RESULT(req, result); +} + + +void fs__futime(uv_fs_t* req, uv_file file, double atime, double mtime) { + int result; + struct _utimbuf b = {(time_t)atime, (time_t)mtime}; + result = _futime(file, &b); + SET_REQ_RESULT(req, result); +} + + +static DWORD WINAPI uv_fs_thread_proc(void* parameter) { + uv_fs_t* req = (uv_fs_t*)parameter; + + assert(req != NULL); + assert(req->type == UV_FS); + + switch (req->fs_type) { + case UV_FS_OPEN: + fs__open(req, (const char*)req->arg0, (int)req->arg1, (int)req->arg2); + break; + case UV_FS_CLOSE: + fs__close(req, (uv_file)req->arg0); + break; + case UV_FS_READ: + fs__read(req, (uv_file)req->arg0, req->arg1, (size_t)req->arg2, (off_t)req->arg3); + break; + case UV_FS_WRITE: + fs__write(req, (uv_file)req->arg0, req->arg1, (size_t)req->arg2, (off_t)req->arg3); + break; + case UV_FS_UNLINK: + fs__unlink(req, (const char*)req->arg0); + break; + case UV_FS_MKDIR: + fs__mkdir(req, (const char*)req->arg0, (int)req->arg1); + break; + case UV_FS_RMDIR: + fs__rmdir(req, (const char*)req->arg0); + break; + case UV_FS_READDIR: + fs__readdir(req, (const char*)req->arg0, (int)req->arg1); + break; + case UV_FS_STAT: + fs__stat(req, (const char*)req->arg0); + break; + case UV_FS_FSTAT: + fs__fstat(req, (uv_file)req->arg0); + break; + case UV_FS_RENAME: + fs__rename(req, (const char*)req->arg0, (const char*)req->arg1); + break; + case UV_FS_FSYNC: + case UV_FS_FDATASYNC: + fs__fsync(req, (uv_file)req->arg0); + break; + case UV_FS_FTRUNCATE: + fs__ftruncate(req, (uv_file)req->arg0, (off_t)req->arg1); + break; + case UV_FS_SENDFILE: + fs__sendfile(req, (uv_file)req->arg0, (uv_file)req->arg1, (off_t)req->arg2, (size_t)req->arg3); + break; + case UV_FS_CHMOD: + fs__chmod(req, (const char*)req->arg0, (int)req->arg1); + break; + case UV_FS_UTIME: + fs__utime(req, (const char*)req->arg0, req->arg4, req->arg5); + break; + case UV_FS_FUTIME: + fs__futime(req, (uv_file)req->arg0, req->arg4, req->arg5); + break; + default: + assert(!"bad uv_fs_type"); + } + + /* Free stashed arguments; we no longer need them. */ + if (req->flags & UV_FS_FREE_ARG0) { + free(req->arg0); + req->arg0 = NULL; + } + + if (req->flags & UV_FS_FREE_ARG1) { + free(req->arg1); + req->arg1 = NULL; + } + + POST_COMPLETION_FOR_REQ(req); + + return 0; +} + + +int uv_fs_open(uv_fs_t* req, const char* path, int flags, int mode, uv_fs_cb cb) { + if (cb) { + uv_fs_req_init_async(req, UV_FS_OPEN, cb); + WRAP_REQ_ARGS3(req, path, flags, mode); + STRDUP_ARG(req, 0); + QUEUE_FS_TP_JOB(req); + } else { + uv_fs_req_init_sync(req, UV_FS_OPEN); + fs__open(req, path, flags, mode); + } + + return 0; +} + + +int uv_fs_close(uv_fs_t* req, uv_file file, uv_fs_cb cb) { + if (cb) { + uv_fs_req_init_async(req, UV_FS_CLOSE, cb); + WRAP_REQ_ARGS1(req, file); + QUEUE_FS_TP_JOB(req); + } else { + uv_fs_req_init_sync(req, UV_FS_CLOSE); + fs__close(req, file); + } + + return 0; +} + + +int uv_fs_read(uv_fs_t* req, uv_file file, void* buf, size_t length, off_t offset, uv_fs_cb cb) { + if (cb) { + uv_fs_req_init_async(req, UV_FS_READ, cb); + WRAP_REQ_ARGS4(req, file, buf, length, offset); + QUEUE_FS_TP_JOB(req); + } else { + uv_fs_req_init_sync(req, UV_FS_READ); + fs__read(req, file, buf, length, offset); + } + + return 0; +} + + +int uv_fs_write(uv_fs_t* req, uv_file file, void* buf, size_t length, off_t offset, uv_fs_cb cb) { + if (cb) { + uv_fs_req_init_async(req, UV_FS_WRITE, cb); + WRAP_REQ_ARGS4(req, file, buf, length, offset); + QUEUE_FS_TP_JOB(req); + } else { + uv_fs_req_init_sync(req, UV_FS_WRITE); + fs__write(req, file, buf, length, offset); + } + + return 0; +} + + +int uv_fs_unlink(uv_fs_t* req, const char* path, uv_fs_cb cb) { + if (cb) { + uv_fs_req_init_async(req, UV_FS_UNLINK, cb); + WRAP_REQ_ARGS1(req, path); + STRDUP_ARG(req, 0); + QUEUE_FS_TP_JOB(req); + } else { + uv_fs_req_init_sync(req, UV_FS_UNLINK); + fs__unlink(req, path); + } + + return 0; +} + + +int uv_fs_mkdir(uv_fs_t* req, const char* path, int mode, uv_fs_cb cb) { + if (cb) { + uv_fs_req_init_async(req, UV_FS_MKDIR, cb); + WRAP_REQ_ARGS2(req, path, mode); + STRDUP_ARG(req, 0); + QUEUE_FS_TP_JOB(req); + } else { + uv_fs_req_init_sync(req, UV_FS_MKDIR); + fs__mkdir(req, path, mode); + } + + return 0; +} + + +int uv_fs_rmdir(uv_fs_t* req, const char* path, uv_fs_cb cb) { + if (cb) { + uv_fs_req_init_async(req, UV_FS_RMDIR, cb); + WRAP_REQ_ARGS1(req, path); + STRDUP_ARG(req, 0); + QUEUE_FS_TP_JOB(req); + } else { + uv_fs_req_init_sync(req, UV_FS_RMDIR); + fs__rmdir(req, path); + } + + return 0; +} + + +int uv_fs_readdir(uv_fs_t* req, const char* path, int flags, uv_fs_cb cb) { + if (cb) { + uv_fs_req_init_async(req, UV_FS_READDIR, cb); + WRAP_REQ_ARGS2(req, path, flags); + STRDUP_ARG(req, 0); + QUEUE_FS_TP_JOB(req); + } else { + uv_fs_req_init_sync(req, UV_FS_READDIR); + fs__readdir(req, path, flags); + } + + return 0; +} + + +int uv_fs_stat(uv_fs_t* req, const char* path, uv_fs_cb cb) { + int len = strlen(path); + char* path2 = NULL; + int has_backslash = (path[len - 1] == '\\' || path[len - 1] == '/'); + + if (path[len - 1] == '\\' || path[len - 1] == '/') { + path2 = strdup(path); + if (!path2) { + uv_fatal_error(ERROR_OUTOFMEMORY, "malloc"); + } + + path2[len - 1] = '\0'; + } + + if (cb) { + uv_fs_req_init_async(req, UV_FS_STAT, cb); + if (path2) { + WRAP_REQ_ARGS1(req, path2); + req->flags |= UV_FS_FREE_ARG0; + } else { + WRAP_REQ_ARGS1(req, path); + STRDUP_ARG(req, 0); + } + + QUEUE_FS_TP_JOB(req); + } else { + uv_fs_req_init_sync(req, UV_FS_STAT); + fs__stat(req, path2 ? path2 : path); + if (path2) { + free(path2); + } + } + + return 0; +} + + +int uv_fs_fstat(uv_fs_t* req, uv_file file, uv_fs_cb cb) { + if (cb) { + uv_fs_req_init_async(req, UV_FS_FSTAT, cb); + WRAP_REQ_ARGS1(req, file); + QUEUE_FS_TP_JOB(req); + } else { + uv_fs_req_init_sync(req, UV_FS_FSTAT); + fs__fstat(req, file); + } + + return 0; +} + + +int uv_fs_rename(uv_fs_t* req, const char* path, const char* new_path, uv_fs_cb cb) { + if (cb) { + uv_fs_req_init_async(req, UV_FS_RENAME, cb); + WRAP_REQ_ARGS2(req, path, new_path); + STRDUP_ARG(req, 0); + STRDUP_ARG(req, 1); + QUEUE_FS_TP_JOB(req); + } else { + uv_fs_req_init_sync(req, UV_FS_RENAME); + fs__rename(req, path, new_path); + } + + return 0; +} + + +int uv_fs_fdatasync(uv_fs_t* req, uv_file file, uv_fs_cb cb) { + if (cb) { + uv_fs_req_init_async(req, UV_FS_FDATASYNC, cb); + WRAP_REQ_ARGS1(req, file); + QUEUE_FS_TP_JOB(req); + } else { + uv_fs_req_init_sync(req, UV_FS_FDATASYNC); + fs__fsync(req, file); + } + + return 0; +} + + +int uv_fs_fsync(uv_fs_t* req, uv_file file, uv_fs_cb cb) { + if (cb) { + uv_fs_req_init_async(req, UV_FS_FSYNC, cb); + WRAP_REQ_ARGS1(req, file); + QUEUE_FS_TP_JOB(req); + } else { + uv_fs_req_init_sync(req, UV_FS_FSYNC); + fs__fsync(req, file); + } + + return 0; +} + + +int uv_fs_ftruncate(uv_fs_t* req, uv_file file, off_t offset, uv_fs_cb cb) { + if (cb) { + uv_fs_req_init_async(req, UV_FS_FTRUNCATE, cb); + WRAP_REQ_ARGS2(req, file, offset); + QUEUE_FS_TP_JOB(req); + } else { + uv_fs_req_init_sync(req, UV_FS_FTRUNCATE); + fs__ftruncate(req, file, offset); + } + + return 0; +} + + +int uv_fs_sendfile(uv_fs_t* req, uv_file out_fd, uv_file in_fd, off_t in_offset, size_t length, uv_fs_cb cb) { + if (cb) { + uv_fs_req_init_async(req, UV_FS_SENDFILE, cb); + WRAP_REQ_ARGS4(req, out_fd, in_fd, in_offset, length); + QUEUE_FS_TP_JOB(req); + } else { + uv_fs_req_init_sync(req, UV_FS_SENDFILE); + fs__sendfile(req, out_fd, in_fd, in_offset, length); + } + + return 0; +} + + +int uv_fs_chmod(uv_fs_t* req, const char* path, int mode, uv_fs_cb cb) { + if (cb) { + uv_fs_req_init_async(req, UV_FS_CHMOD, cb); + WRAP_REQ_ARGS2(req, path, mode); + STRDUP_ARG(req, 0); + QUEUE_FS_TP_JOB(req); + } else { + uv_fs_req_init_sync(req, UV_FS_CHMOD); + fs__chmod(req, path, mode); + } + + return 0; +} + + +int uv_fs_utime(uv_fs_t* req, const char* path, double atime, double mtime, uv_fs_cb cb) { + if (cb) { + uv_fs_req_init_async(req, UV_FS_UTIME, cb); + WRAP_REQ_ARGS1(req, path); + STRDUP_ARG(req, 0); + req->arg4 = (ssize_t)atime; + req->arg5 = (ssize_t)mtime; + QUEUE_FS_TP_JOB(req); + } else { + uv_fs_req_init_sync(req, UV_FS_UTIME); + fs__utime(req, path, atime, mtime); + } + + return 0; +} + + +int uv_fs_futime(uv_fs_t* req, uv_file file, double atime, double mtime, uv_fs_cb cb) { + if (cb) { + uv_fs_req_init_async(req, UV_FS_FUTIME, cb); + WRAP_REQ_ARGS1(req, file); + req->arg4 = (ssize_t)atime; + req->arg5 = (ssize_t)mtime; + QUEUE_FS_TP_JOB(req); + } else { + uv_fs_req_init_sync(req, UV_FS_FUTIME); + fs__futime(req, file, atime, mtime); + } + + return 0; +} + + +void uv_process_fs_req(uv_fs_t* req) { + assert(req->cb); + req->cb(req); +} + + +void uv_fs_req_cleanup(uv_fs_t* req) { + if (req->flags & UV_FS_FREE_PTR) { + free(req->ptr); + req->ptr = NULL; + } + + if (req->flags & UV_FS_ASYNC) { + uv_unref(); + } +} diff --git a/src/win/internal.h b/src/win/internal.h index ab203caa09..50b9368f2d 100644 --- a/src/win/internal.h +++ b/src/win/internal.h @@ -251,6 +251,19 @@ void uv_process_ares_cleanup_req(uv_ares_task_t* handle, uv_req_t* req); void uv_process_getaddrinfo_req(uv_getaddrinfo_t* handle, uv_req_t* req); +/* + * FS + */ +void uv_fs_init(); +void uv_process_fs_req(uv_fs_t* req); + + +/* + * Threadpool + */ +void uv_process_work_req(uv_work_t* req); + + /* * Error handling */ diff --git a/src/win/req.c b/src/win/req.c index 5b88ff9b45..53430ec076 100644 --- a/src/win/req.c +++ b/src/win/req.c @@ -150,6 +150,14 @@ void uv_process_reqs() { uv_process_proc_close((uv_process_t*) req->data); break; + case UV_FS: + uv_process_fs_req((uv_fs_t*) req); + break; + + case UV_WORK: + uv_process_work_req((uv_work_t*) req); + break; + default: assert(0); } diff --git a/src/win/threadpool.c b/src/win/threadpool.c new file mode 100644 index 0000000000..5b4436e758 --- /dev/null +++ b/src/win/threadpool.c @@ -0,0 +1,69 @@ +/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include + +#include "uv.h" +#include "internal.h" + + +static void uv_work_req_init(uv_work_t* req, uv_work_cb work_cb, uv_after_work_cb after_work_cb) { + uv_req_init((uv_req_t*) req); + req->type = UV_WORK; + req->work_cb = work_cb; + req->after_work_cb = after_work_cb; + memset(&req->overlapped, 0, sizeof(req->overlapped)); +} + + +static DWORD WINAPI uv_work_thread_proc(void* parameter) { + uv_work_t* req = (uv_work_t*)parameter; + + assert(req != NULL); + assert(req->type == UV_WORK); + assert(req->work_cb); + + req->work_cb(req); + + POST_COMPLETION_FOR_REQ(req); + + return 0; +} + + +int uv_queue_work(uv_work_t* req, uv_work_cb work_cb, uv_after_work_cb after_work_cb) { + uv_work_req_init(req, work_cb, after_work_cb); + + if (!QueueUserWorkItem(&uv_work_thread_proc, req, WT_EXECUTELONGFUNCTION)) { + uv_set_sys_error(GetLastError()); + return -1; + } + + uv_ref(); + return 0; +} + + +void uv_process_work_req(uv_work_t* req) { + assert(req->after_work_cb); + req->after_work_cb(req); + uv_unref(); +} diff --git a/test/test-fs.c b/test/test-fs.c new file mode 100644 index 0000000000..3e38be3b0b --- /dev/null +++ b/test/test-fs.c @@ -0,0 +1,501 @@ +/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "uv.h" +#include "task.h" + +#include +#include +#include +#include + +static int close_cb_count; +static int create_cb_count; +static int open_cb_count; +static int read_cb_count; +static int write_cb_count; +static int unlink_cb_count; +static int mkdir_cb_count; +static int rmdir_cb_count; +static int readdir_cb_count; +static int stat_cb_count; +static int rename_cb_count; +static int fsync_cb_count; +static int fdatasync_cb_count; +static int ftruncate_cb_count; +static int sendfile_cb_count; + +static uv_fs_t open_req1; +static uv_fs_t open_req2; +static uv_fs_t read_req; +static uv_fs_t write_req; +static uv_fs_t unlink_req; +static uv_fs_t close_req; +static uv_fs_t mkdir_req; +static uv_fs_t rmdir_req; +static uv_fs_t readdir_req; +static uv_fs_t stat_req; +static uv_fs_t rename_req; +static uv_fs_t fsync_req; +static uv_fs_t fdatasync_req; +static uv_fs_t ftruncate_req; +static uv_fs_t sendfile_req; + +static char buf[32]; +static char test_buf[] = "test-buffer\n"; + + +static void unlink_cb(uv_fs_t* req) { + ASSERT(req == &unlink_req); + ASSERT(req->fs_type == UV_FS_UNLINK); + ASSERT(req->result != -1); + unlink_cb_count++; + uv_fs_req_cleanup(req); +} + + +static void close_cb(uv_fs_t* req) { + int r; + ASSERT(req == &close_req); + ASSERT(req->fs_type == UV_FS_CLOSE); + ASSERT(req->result != -1); + close_cb_count++; + uv_fs_req_cleanup(req); + if (close_cb_count == 3) { + r = uv_fs_unlink(&unlink_req, "test_file2", unlink_cb); + } +} + + +static void ftruncate_cb(uv_fs_t* req) { + int r; + ASSERT(req == &ftruncate_req); + ASSERT(req->fs_type == UV_FS_FTRUNCATE); + ASSERT(req->result != -1); + ftruncate_cb_count++; + uv_fs_req_cleanup(req); + r = uv_fs_close(&close_req, open_req1.result, close_cb); +} + + +static void read_cb(uv_fs_t* req) { + int r; + ASSERT(req == &read_req); + ASSERT(req->fs_type == UV_FS_READ); + ASSERT(req->result != -1); + read_cb_count++; + uv_fs_req_cleanup(req); + if (read_cb_count == 1) { + ASSERT(strcmp(buf, test_buf) == 0); + r = uv_fs_ftruncate(&ftruncate_req, open_req1.result, 7, ftruncate_cb); + } else { + ASSERT(strcmp(buf, "test-bu") == 0); + r = uv_fs_close(&close_req, open_req1.result, close_cb); + } +} + + +static void open_cb(uv_fs_t* req) { + int r; + ASSERT(req == &open_req1); + ASSERT(req->fs_type == UV_FS_OPEN); + ASSERT(req->result != -1); + open_cb_count++; + uv_fs_req_cleanup(req); + memset(buf, 0, sizeof(buf)); + r = uv_fs_read(&read_req, open_req1.result, buf, sizeof(buf), -1, read_cb); +} + + +static void fsync_cb(uv_fs_t* req) { + int r; + ASSERT(req == &fsync_req); + ASSERT(req->fs_type == UV_FS_FSYNC); + ASSERT(req->result != -1); + fsync_cb_count++; + uv_fs_req_cleanup(req); + r = uv_fs_close(&close_req, open_req1.result, close_cb); +} + + +static void fdatasync_cb(uv_fs_t* req) { + int r; + ASSERT(req == &fdatasync_req); + ASSERT(req->fs_type == UV_FS_FDATASYNC); + ASSERT(req->result != -1); + fdatasync_cb_count++; + uv_fs_req_cleanup(req); + r = uv_fs_fsync(&fsync_req, open_req1.result, fsync_cb); +} + + +static void write_cb(uv_fs_t* req) { + int r; + ASSERT(req == &write_req); + ASSERT(req->fs_type == UV_FS_WRITE); + ASSERT(req->result != -1); + write_cb_count++; + uv_fs_req_cleanup(req); + r = uv_fs_fdatasync(&fdatasync_req, open_req1.result, fdatasync_cb); +} + + +static void create_cb(uv_fs_t* req) { + int r; + ASSERT(req == &open_req1); + ASSERT(req->fs_type == UV_FS_OPEN); + ASSERT(req->result != -1); + create_cb_count++; + uv_fs_req_cleanup(req); + r = uv_fs_write(&write_req, req->result, test_buf, sizeof(test_buf), -1, write_cb); +} + + +static void rename_cb(uv_fs_t* req) { + ASSERT(req == &rename_req); + ASSERT(req->fs_type == UV_FS_RENAME); + ASSERT(req->result != -1); + rename_cb_count++; + uv_fs_req_cleanup(req); +} + + +static void mkdir_cb(uv_fs_t* req) { + ASSERT(req == &mkdir_req); + ASSERT(req->fs_type == UV_FS_MKDIR); + ASSERT(req->result != -1); + mkdir_cb_count++; + uv_fs_req_cleanup(req); +} + + +static void rmdir_cb(uv_fs_t* req) { + ASSERT(req == &rmdir_req); + ASSERT(req->fs_type == UV_FS_RMDIR); + ASSERT(req->result != -1); + rmdir_cb_count++; + uv_fs_req_cleanup(req); +} + + +static void readdir_cb(uv_fs_t* req) { + ASSERT(req == &readdir_req); + ASSERT(req->fs_type == UV_FS_READDIR); + ASSERT(req->result == 2); + ASSERT(req->ptr); + ASSERT(strcmp((const char*)req->ptr, "file1") == 0); + ASSERT(strcmp((char*)req->ptr + strlen((const char*)req->ptr) + 1, "file2") == 0); + readdir_cb_count++; + uv_fs_req_cleanup(req); + ASSERT(!req->ptr); +} + + +static void stat_cb(uv_fs_t* req) { + ASSERT(req == &stat_req); + ASSERT(req->fs_type == UV_FS_STAT); + ASSERT(req->result != -1); + ASSERT(req->ptr); + stat_cb_count++; + uv_fs_req_cleanup(req); + ASSERT(!req->ptr); +} + + +static void sendfile_cb(uv_fs_t* req) { + ASSERT(req == &sendfile_req); + ASSERT(req->fs_type == UV_FS_SENDFILE); + ASSERT(req->result == 65548); + sendfile_cb_count++; + uv_fs_req_cleanup(req); +} + + +TEST_IMPL(fs_file_async) { + int r; + + /* Setup. */ + _unlink("test_file"); + _unlink("test_file2"); + + uv_init(); + + r = uv_fs_open(&open_req1, "test_file", O_WRONLY | O_CREAT, S_IWRITE, create_cb); + ASSERT(r == 0); + uv_run(); + + ASSERT(create_cb_count == 1); + ASSERT(write_cb_count == 1); + ASSERT(fsync_cb_count == 1); + ASSERT(fdatasync_cb_count == 1); + ASSERT(close_cb_count == 1); + + r = uv_fs_rename(&rename_req, "test_file", "test_file2", rename_cb); + ASSERT(r == 0); + + uv_run(); + ASSERT(create_cb_count == 1); + ASSERT(write_cb_count == 1); + ASSERT(close_cb_count == 1); + ASSERT(rename_cb_count == 1); + + r = uv_fs_open(&open_req1, "test_file2", O_RDWR, 0, open_cb); + ASSERT(r == 0); + + uv_run(); + ASSERT(open_cb_count == 1); + ASSERT(read_cb_count == 1); + ASSERT(close_cb_count == 2); + ASSERT(rename_cb_count == 1); + ASSERT(create_cb_count == 1); + ASSERT(write_cb_count == 1); + ASSERT(ftruncate_cb_count == 1); + + r = uv_fs_open(&open_req1, "test_file2", O_RDONLY, 0, open_cb); + ASSERT(r == 0); + + uv_run(); + ASSERT(open_cb_count == 2); + ASSERT(read_cb_count == 2); + ASSERT(close_cb_count == 3); + ASSERT(rename_cb_count == 1); + ASSERT(unlink_cb_count == 1); + ASSERT(create_cb_count == 1); + ASSERT(write_cb_count == 1); + ASSERT(ftruncate_cb_count == 1); + + /* Cleanup. */ + _unlink("test_file"); + _unlink("test_file2"); + + return 0; +} + + +TEST_IMPL(fs_file_sync) { + int r; + + /* Setup. */ + _unlink("test_file"); + _unlink("test_file2"); + + uv_init(); + + r = uv_fs_open(&open_req1, "test_file", O_WRONLY | O_CREAT, S_IWRITE | S_IREAD, NULL); + ASSERT(r == 0); + ASSERT(open_req1.result != -1); + uv_fs_req_cleanup(&open_req1); + + r = uv_fs_write(&write_req, open_req1.result, test_buf, sizeof(test_buf), -1, NULL); + ASSERT(r == 0); + ASSERT(write_req.result != -1); + uv_fs_req_cleanup(&write_req); + + r = uv_fs_close(&close_req, open_req1.result, NULL); + ASSERT(r == 0); + ASSERT(close_req.result != -1); + uv_fs_req_cleanup(&close_req); + + r = uv_fs_open(&open_req1, "test_file", O_RDWR, 0, NULL); + ASSERT(r == 0); + ASSERT(open_req1.result != -1); + uv_fs_req_cleanup(&open_req1); + + r = uv_fs_read(&read_req, open_req1.result, buf, sizeof(buf), -1, NULL); + ASSERT(r == 0); + ASSERT(read_req.result != -1); + ASSERT(strcmp(buf, test_buf) == 0); + uv_fs_req_cleanup(&read_req); + + r = uv_fs_ftruncate(&ftruncate_req, open_req1.result, 7, NULL); + ASSERT(r == 0); + ASSERT(ftruncate_req.result != -1); + uv_fs_req_cleanup(&ftruncate_req); + + r = uv_fs_close(&close_req, open_req1.result, NULL); + ASSERT(r == 0); + ASSERT(close_req.result != -1); + uv_fs_req_cleanup(&close_req); + + r = uv_fs_rename(&rename_req, "test_file", "test_file2", NULL); + ASSERT(r == 0); + ASSERT(rename_req.result != -1); + uv_fs_req_cleanup(&rename_req); + + r = uv_fs_open(&open_req1, "test_file2", O_RDONLY, 0, NULL); + ASSERT(r == 0); + ASSERT(open_req1.result != -1); + uv_fs_req_cleanup(&open_req1); + + memset(buf, 0, sizeof(buf)); + r = uv_fs_read(&read_req, open_req1.result, buf, sizeof(buf), -1, NULL); + ASSERT(r == 0); + ASSERT(read_req.result != -1); + ASSERT(strcmp(buf, "test-bu") == 0); + uv_fs_req_cleanup(&read_req); + + r = uv_fs_close(&close_req, open_req1.result, NULL); + ASSERT(r == 0); + ASSERT(close_req.result != -1); + uv_fs_req_cleanup(&close_req); + + r = uv_fs_unlink(&unlink_req, "test_file2", NULL); + ASSERT(r == 0); + ASSERT(unlink_req.result != -1); + uv_fs_req_cleanup(&unlink_req); + + /* Cleanup */ + _unlink("test_file"); + _unlink("test_file2"); + + return 0; +} + + +TEST_IMPL(fs_async_dir) { + int r; + + /* Setup */ + _unlink("test_dir/file1"); + _unlink("test_dir/file2"); + _rmdir("test_dir"); + + uv_init(); + + r = uv_fs_mkdir(&mkdir_req, "test_dir", 0, mkdir_cb); + ASSERT(r == 0); + + uv_run(); + ASSERT(mkdir_cb_count == 1); + + /* Create 2 files synchronously. */ + r = uv_fs_open(&open_req1, "test_dir/file1", O_WRONLY | O_CREAT, S_IWRITE | S_IREAD, NULL); + ASSERT(r == 0); + uv_fs_req_cleanup(&open_req1); + r = uv_fs_close(&close_req, open_req1.result, NULL); + ASSERT(r == 0); + uv_fs_req_cleanup(&close_req); + + r = uv_fs_open(&open_req1, "test_dir/file2", O_WRONLY | O_CREAT, S_IWRITE | S_IREAD, NULL); + ASSERT(r == 0); + uv_fs_req_cleanup(&open_req1); + r = uv_fs_close(&close_req, open_req1.result, NULL); + ASSERT(r == 0); + uv_fs_req_cleanup(&close_req); + + r = uv_fs_readdir(&readdir_req, "test_dir", 0, readdir_cb); + ASSERT(r == 0); + + uv_run(); + ASSERT(readdir_cb_count == 1); + + r = uv_fs_stat(&stat_req, "test_dir", stat_cb); + ASSERT(r == 0); + uv_run(); + r = uv_fs_stat(&stat_req, "test_dir\\", stat_cb); + ASSERT(r == 0); + uv_run(); + + ASSERT(stat_cb_count == 2); + + r = uv_fs_unlink(&unlink_req, "test_dir/file1", unlink_cb); + ASSERT(r == 0); + uv_run(); + ASSERT(unlink_cb_count == 1); + + r = uv_fs_unlink(&unlink_req, "test_dir/file2", unlink_cb); + ASSERT(r == 0); + uv_run(); + ASSERT(unlink_cb_count == 2); + + r = uv_fs_rmdir(&rmdir_req, "test_dir", rmdir_cb); + ASSERT(r == 0); + uv_run(); + ASSERT(rmdir_cb_count == 1); + + /* Cleanup */ + _unlink("test_dir/file1"); + _unlink("test_dir/file2"); + _rmdir("test_dir"); + + return 0; +} + + +TEST_IMPL(fs_async_sendfile) { + int f, r; + struct _stat s1, s2; + + /* Setup. */ + _unlink("test_file"); + _unlink("test_file2"); + + f = _open("test_file", O_WRONLY | O_CREAT, S_IWRITE | S_IREAD); + ASSERT(f != -1); + + r = _write(f, "begin\n", 6); + ASSERT(r != -1); + + r = _lseek(f, 65536, SEEK_CUR); + ASSERT(r == 65543); + + r = _write(f, "end\n", 4); + ASSERT(r != -1); + + r = _close(f); + ASSERT(r == 0); + + /* Test starts here. */ + uv_init(); + + r = uv_fs_open(&open_req1, "test_file", O_RDWR, 0, NULL); + ASSERT(r == 0); + ASSERT(open_req1.result != -1); + uv_fs_req_cleanup(&open_req1); + + r = uv_fs_open(&open_req2, "test_file2", O_WRONLY | O_CREAT, S_IWRITE | S_IREAD, NULL); + ASSERT(r == 0); + ASSERT(open_req2.result != -1); + uv_fs_req_cleanup(&open_req2); + + r = uv_fs_sendfile(&sendfile_req, open_req2.result, open_req1.result, 0, 131072, sendfile_cb); + ASSERT(r == 0); + uv_run(); + + ASSERT(sendfile_cb_count == 1); + + r = uv_fs_close(&close_req, open_req1.result, NULL); + ASSERT(r == 0); + uv_fs_req_cleanup(&close_req); + r = uv_fs_close(&close_req, open_req2.result, NULL); + ASSERT(r == 0); + uv_fs_req_cleanup(&close_req); + + _stat("test_file", &s1); + _stat("test_file2", &s2); + ASSERT(65548 == s2.st_size && s1.st_size == s2.st_size); + + /* Cleanup. */ + _unlink("test_file"); + _unlink("test_file2"); + + return 0; +} diff --git a/test/test-list.h b/test/test-list.h index eeda7f40fb..a162551991 100644 --- a/test/test-list.h +++ b/test/test-list.h @@ -72,6 +72,11 @@ TEST_DECLARE (spawn_exit_code) TEST_DECLARE (spawn_stdout) TEST_DECLARE (spawn_stdin) TEST_DECLARE (spawn_and_kill) +TEST_DECLARE (fs_file_async) +TEST_DECLARE (fs_file_sync) +TEST_DECLARE (fs_async_dir) +TEST_DECLARE (fs_async_sendfile) +TEST_DECLARE (threadpool_queue_work_simple) #ifdef _WIN32 TEST_DECLARE (spawn_detect_pipe_name_collisions_on_windows) TEST_DECLARE (argument_escaping) @@ -170,6 +175,13 @@ TASK_LIST_START TEST_ENTRY (environment_creation) #endif + TEST_ENTRY (fs_file_async) + TEST_ENTRY (fs_file_sync) + TEST_ENTRY (fs_async_dir) + TEST_ENTRY (fs_async_sendfile) + + TEST_ENTRY (threadpool_queue_work_simple) + #if 0 /* These are for testing the test runner. */ TEST_ENTRY (fail_always) diff --git a/test/test-threadpool.c b/test/test-threadpool.c new file mode 100644 index 0000000000..19742f2384 --- /dev/null +++ b/test/test-threadpool.c @@ -0,0 +1,59 @@ +/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "uv.h" +#include "task.h" + +static int work_cb_count; +static int after_work_cb_count; +static uv_work_t work_req; +static char data; + + +static void work_cb(uv_work_t* req) { + ASSERT(req == &work_req); + ASSERT(req->data == &data); + work_cb_count++; +} + + +static void after_work_cb(uv_work_t* req) { + ASSERT(req == &work_req); + ASSERT(req->data == &data); + after_work_cb_count++; +} + + +TEST_IMPL(threadpool_queue_work_simple) { + int r; + + uv_init(); + + work_req.data = &data; + r = uv_queue_work(&work_req, work_cb, after_work_cb); + ASSERT(r == 0); + uv_run(); + + ASSERT(work_cb_count == 1); + ASSERT(after_work_cb_count == 1); + + return 0; +} diff --git a/uv.gyp b/uv.gyp index ca90b826c7..20c1ea37e2 100644 --- a/uv.gyp +++ b/uv.gyp @@ -98,6 +98,7 @@ 'src/win/cares.c', 'src/win/core.c', 'src/win/error.c', + 'src/win/fs.c', 'src/win/getaddrinfo.c', 'src/win/handle.c', 'src/win/internal.h', @@ -108,6 +109,7 @@ 'src/win/stdio.c', 'src/win/stream.c', 'src/win/tcp.c', + 'src/win/threadpool.c', 'src/win/timer.c', 'src/win/udp.c', 'src/win/util.c', @@ -214,6 +216,7 @@ 'test/test-connection-fail.c', 'test/test-delayed-accept.c', 'test/test-fail-always.c', + 'test/test-fs.c', 'test/test-get-currentexe.c', 'test/test-getaddrinfo.c', 'test/test-gethostbyname.c', @@ -231,6 +234,7 @@ 'test/test-tcp-bind-error.c', 'test/test-tcp-bind6-error.c', 'test/test-tcp-writealot.c', + 'test/test-threadpool.c', 'test/test-timer-again.c', 'test/test-timer.c', 'test/test-udp-dgram-too-big.c',