unix, windows: nanosecond resolution for uv_fs_[fl]stat

Closes #739.
This commit is contained in:
Timothy J Fontaine 2013-03-12 10:09:52 -07:00 committed by Bert Belder
parent ab935a25a5
commit 499c7976c6
8 changed files with 177 additions and 77 deletions

View File

@ -121,7 +121,6 @@ typedef struct {
typedef int uv_file; typedef int uv_file;
typedef int uv_os_sock_t; typedef int uv_os_sock_t;
typedef struct stat uv_statbuf_t;
#define UV_ONCE_INIT PTHREAD_ONCE_INIT #define UV_ONCE_INIT PTHREAD_ONCE_INIT

View File

@ -207,8 +207,6 @@ typedef struct uv_buf_t {
typedef int uv_file; typedef int uv_file;
typedef struct _stati64 uv_statbuf_t;
typedef SOCKET uv_os_sock_t; typedef SOCKET uv_os_sock_t;
typedef HANDLE uv_thread_t; typedef HANDLE uv_thread_t;

View File

@ -350,6 +350,29 @@ typedef void (*uv_getaddrinfo_cb)(uv_getaddrinfo_t* req,
int status, int status,
struct addrinfo* res); struct addrinfo* res);
typedef struct {
long tv_sec;
long tv_nsec;
} uv_timespec_t;
typedef struct {
uint64_t st_dev;
uint64_t st_mode;
uint64_t st_nlink;
uint64_t st_uid;
uint64_t st_gid;
uint64_t st_rdev;
uint64_t st_ino;
uint64_t st_size;
uint64_t st_blksize;
uint64_t st_blocks;
uv_timespec_t st_atim;
uv_timespec_t st_mtim;
uv_timespec_t st_ctim;
} uv_stat_t;
/* /*
* This will be called repeatedly after the uv_fs_event_t is initialized. * This will be called repeatedly after the uv_fs_event_t is initialized.
* If uv_fs_event_t was initialized with a directory the filename parameter * If uv_fs_event_t was initialized with a directory the filename parameter
@ -361,8 +384,8 @@ typedef void (*uv_fs_event_cb)(uv_fs_event_t* handle, const char* filename,
typedef void (*uv_fs_poll_cb)(uv_fs_poll_t* handle, typedef void (*uv_fs_poll_cb)(uv_fs_poll_t* handle,
int status, int status,
const uv_statbuf_t* prev, const uv_stat_t* prev,
const uv_statbuf_t* curr); const uv_stat_t* curr);
typedef void (*uv_signal_cb)(uv_signal_t* handle, int signum); typedef void (*uv_signal_cb)(uv_signal_t* handle, int signum);
@ -1519,7 +1542,7 @@ struct uv_fs_s {
void* ptr; void* ptr;
const char* path; const char* path;
uv_err_code errorno; uv_err_code errorno;
uv_statbuf_t statbuf; /* Stores the result of uv_fs_stat and uv_fs_fstat. */ uv_stat_t statbuf; /* Stores the result of uv_fs_stat and uv_fs_fstat. */
UV_FS_PRIVATE_FIELDS UV_FS_PRIVATE_FIELDS
}; };
@ -1646,7 +1669,7 @@ UV_EXTERN int uv_fs_poll_init(uv_loop_t* loop, uv_fs_poll_t* handle);
* or the error reason changes). * or the error reason changes).
* *
* When `status == 0`, your callback receives pointers to the old and new * When `status == 0`, your callback receives pointers to the old and new
* `uv_statbuf_t` structs. They are valid for the duration of the callback * `uv_stat_t` structs. They are valid for the duration of the callback
* only! * only!
* *
* For maximum portability, use multi-second intervals. Sub-second intervals * For maximum portability, use multi-second intervals. Sub-second intervals

View File

@ -35,16 +35,16 @@ struct poll_ctx {
uv_fs_poll_cb poll_cb; uv_fs_poll_cb poll_cb;
uv_timer_t timer_handle; uv_timer_t timer_handle;
uv_fs_t fs_req; /* TODO(bnoordhuis) mark fs_req internal */ uv_fs_t fs_req; /* TODO(bnoordhuis) mark fs_req internal */
uv_statbuf_t statbuf; uv_stat_t statbuf;
char path[1]; /* variable length */ char path[1]; /* variable length */
}; };
static int statbuf_eq(const uv_statbuf_t* a, const uv_statbuf_t* b); static int statbuf_eq(const uv_stat_t* a, const uv_stat_t* b);
static void poll_cb(uv_fs_t* req); static void poll_cb(uv_fs_t* req);
static void timer_cb(uv_timer_t* timer, int status); static void timer_cb(uv_timer_t* timer, int status);
static void timer_close_cb(uv_handle_t* handle); static void timer_close_cb(uv_handle_t* handle);
static uv_statbuf_t zero_statbuf; static uv_stat_t zero_statbuf;
int uv_fs_poll_init(uv_loop_t* loop, uv_fs_poll_t* handle) { int uv_fs_poll_init(uv_loop_t* loop, uv_fs_poll_t* handle) {
@ -137,7 +137,7 @@ static void timer_cb(uv_timer_t* timer, int status) {
static void poll_cb(uv_fs_t* req) { static void poll_cb(uv_fs_t* req) {
uv_statbuf_t* statbuf; uv_stat_t* statbuf;
struct poll_ctx* ctx; struct poll_ctx* ctx;
uint64_t interval; uint64_t interval;
@ -189,48 +189,17 @@ static void timer_close_cb(uv_handle_t* handle) {
} }
static int statbuf_eq(const uv_statbuf_t* a, const uv_statbuf_t* b) { static int statbuf_eq(const uv_stat_t* a, const uv_stat_t* b) {
#if defined(_WIN32) return a->st_ctim.tv_nsec == b->st_ctim.tv_nsec
return a->st_mtime == b->st_mtime && a->st_mtim.tv_nsec == b->st_mtim.tv_nsec
&& a->st_size == b->st_size && a->st_ctim.tv_sec == b->st_ctim.tv_sec
&& a->st_mode == b->st_mode; && a->st_mtim.tv_sec == b->st_mtim.tv_sec
#else
/* Jump through a few hoops to get sub-second granularity on Linux. */
# if defined(__linux__)
# if defined(__USE_MISC) /* _BSD_SOURCE || _SVID_SOURCE */
if (a->st_ctim.tv_nsec != b->st_ctim.tv_nsec) return 0;
if (a->st_mtim.tv_nsec != b->st_mtim.tv_nsec) return 0;
# else
if (a->st_ctimensec != b->st_ctimensec) return 0;
if (a->st_mtimensec != b->st_mtimensec) return 0;
# endif
# endif
/* Jump through different hoops on OS X. */
# if defined(__APPLE__)
# if !defined(_POSIX_C_SOURCE) || defined(_DARWIN_C_SOURCE)
if (a->st_ctimespec.tv_nsec != b->st_ctimespec.tv_nsec) return 0;
if (a->st_mtimespec.tv_nsec != b->st_mtimespec.tv_nsec) return 0;
# else
if (a->st_ctimensec != b->st_ctimensec) return 0;
if (a->st_mtimensec != b->st_mtimensec) return 0;
# endif
# endif
/* TODO(bnoordhuis) Other Unices have st_ctim and friends too, provided
* the stars and compiler flags are right...
*/
return a->st_ctime == b->st_ctime
&& a->st_mtime == b->st_mtime
&& a->st_size == b->st_size && a->st_size == b->st_size
&& a->st_mode == b->st_mode && a->st_mode == b->st_mode
&& a->st_uid == b->st_uid && a->st_uid == b->st_uid
&& a->st_gid == b->st_gid && a->st_gid == b->st_gid
&& a->st_ino == b->st_ino && a->st_ino == b->st_ino
&& a->st_dev == b->st_dev; && a->st_dev == b->st_dev;
#endif
} }

View File

@ -497,6 +497,69 @@ static ssize_t uv__fs_write(uv_fs_t* req) {
return r; return r;
} }
static inline void uv__to_stat(struct stat* src, uv_stat_t* dst) {
dst->st_dev = src->st_dev;
dst->st_mode = src->st_mode;
dst->st_nlink = src->st_nlink;
dst->st_uid = src->st_uid;
dst->st_gid = src->st_gid;
dst->st_rdev = src->st_rdev;
dst->st_ino = src->st_ino;
dst->st_size = src->st_size;
dst->st_blksize = src->st_blksize;
dst->st_blocks = src->st_blocks;
#if defined(__APPLE__)
dst->st_atim.tv_sec = src->st_atimespec.tv_sec;
dst->st_atim.tv_nsec = src->st_atimespec.tv_nsec;
dst->st_mtim.tv_sec = src->st_mtimespec.tv_sec;
dst->st_mtim.tv_nsec = src->st_mtimespec.tv_nsec;
dst->st_ctim.tv_sec = src->st_ctimespec.tv_sec;
dst->st_ctim.tv_nsec = src->st_ctimespec.tv_nsec;
#elif defined(_BSD_SOURCE) || defined(_SVID_SOURCE) || defined(_XOPEN_SOURCE)
dst->st_atim.tv_sec = src->st_atim.tv_sec;
dst->st_atim.tv_nsec = src->st_atim.tv_nsec;
dst->st_mtim.tv_sec = src->st_mtim.tv_sec;
dst->st_mtim.tv_nsec = src->st_mtim.tv_nsec;
dst->st_ctim.tv_sec = src->st_ctim.tv_sec;
dst->st_ctim.tv_nsec = src->st_ctim.tv_nsec;
#else
dst->st_atim.tv_sec = src->st_atime;
dst->st_atim.tv_nsec = 0;
dst->st_mtim.tv_sec = src->st_mtime;
dst->st_mtim.tv_nsec = 0;
dst->st_ctim.tv_sec = src->st_ctime;
dst->st_ctim.tv_nsec = 0;
#endif
}
static int uv__fs_stat(const char *path, uv_stat_t *buf) {
struct stat pbuf;
int ret;
ret = stat(path, &pbuf);
uv__to_stat(&pbuf, buf);
return ret;
}
static int uv__fs_lstat(const char *path, uv_stat_t *buf) {
struct stat pbuf;
int ret;
ret = lstat(path, &pbuf);
uv__to_stat(&pbuf, buf);
return ret;
}
static int uv__fs_fstat(int fd, uv_stat_t *buf) {
struct stat pbuf;
int ret;
ret = fstat(fd, &pbuf);
uv__to_stat(&pbuf, buf);
return ret;
}
static void uv__fs_work(struct uv__work* w) { static void uv__fs_work(struct uv__work* w) {
int retry_on_eintr; int retry_on_eintr;
@ -521,11 +584,11 @@ static void uv__fs_work(struct uv__work* w) {
X(FCHMOD, fchmod(req->file, req->mode)); X(FCHMOD, fchmod(req->file, req->mode));
X(FCHOWN, fchown(req->file, req->uid, req->gid)); X(FCHOWN, fchown(req->file, req->uid, req->gid));
X(FDATASYNC, uv__fs_fdatasync(req)); X(FDATASYNC, uv__fs_fdatasync(req));
X(FSTAT, fstat(req->file, &req->statbuf)); X(FSTAT, uv__fs_fstat(req->file, &req->statbuf));
X(FSYNC, fsync(req->file)); X(FSYNC, fsync(req->file));
X(FTRUNCATE, ftruncate(req->file, req->off)); X(FTRUNCATE, ftruncate(req->file, req->off));
X(FUTIME, uv__fs_futime(req)); X(FUTIME, uv__fs_futime(req));
X(LSTAT, lstat(req->path, &req->statbuf)); X(LSTAT, uv__fs_lstat(req->path, &req->statbuf));
X(LINK, link(req->path, req->new_path)); X(LINK, link(req->path, req->new_path));
X(MKDIR, mkdir(req->path, req->mode)); X(MKDIR, mkdir(req->path, req->mode));
X(OPEN, open(req->path, req->flags, req->mode)); X(OPEN, open(req->path, req->flags, req->mode));
@ -535,7 +598,7 @@ static void uv__fs_work(struct uv__work* w) {
X(RENAME, rename(req->path, req->new_path)); X(RENAME, rename(req->path, req->new_path));
X(RMDIR, rmdir(req->path)); X(RMDIR, rmdir(req->path));
X(SENDFILE, uv__fs_sendfile(req)); X(SENDFILE, uv__fs_sendfile(req));
X(STAT, stat(req->path, &req->statbuf)); X(STAT, uv__fs_stat(req->path, &req->statbuf));
X(SYMLINK, symlink(req->path, req->new_path)); X(SYMLINK, symlink(req->path, req->new_path));
X(UNLINK, unlink(req->path)); X(UNLINK, unlink(req->path));
X(UTIME, uv__fs_utime(req)); X(UTIME, uv__fs_utime(req));

View File

@ -77,8 +77,20 @@
return; \ return; \
} }
#define FILETIME_TO_UINT(filetime) \
(*((uint64_t*) &(filetime)) - 116444736000000000ULL)
#define FILETIME_TO_TIME_T(filetime) \ #define FILETIME_TO_TIME_T(filetime) \
((*((uint64_t*) &(filetime)) - 116444736000000000ULL) / 10000000ULL); (FILETIME_TO_UINT(filetime) / 10000000ULL);
#define FILETIME_TO_TIME_NS(filetime, secs) \
((FILETIME_TO_UINT(filetime) - (secs * 10000000ULL)) * 100);
#define FILETIME_TO_TIMESPEC(ts, filetime) \
do { \
(ts).tv_sec = FILETIME_TO_TIME_T(filetime); \
(ts).tv_nsec = FILETIME_TO_TIME_NS(filetime, (ts).tv_sec); \
} while(0)
#define TIME_T_TO_FILETIME(time, filetime_ptr) \ #define TIME_T_TO_FILETIME(time, filetime_ptr) \
do { \ do { \
@ -86,7 +98,6 @@
116444736000000000ULL; \ 116444736000000000ULL; \
} while(0) } while(0)
#define IS_SLASH(c) ((c) == L'\\' || (c) == L'/') #define IS_SLASH(c) ((c) == L'\\' || (c) == L'/')
#define IS_LETTER(c) (((c) >= L'a' && (c) <= L'z') || \ #define IS_LETTER(c) (((c) >= L'a' && (c) <= L'z') || \
((c) >= L'A' && (c) <= L'Z')) ((c) >= L'A' && (c) <= L'Z'))
@ -808,7 +819,7 @@ void fs__readdir(uv_fs_t* req) {
} }
INLINE static int fs__stat_handle(HANDLE handle, uv_statbuf_t* statbuf) { INLINE static int fs__stat_handle(HANDLE handle, uv_stat_t* statbuf) {
BY_HANDLE_FILE_INFORMATION info; BY_HANDLE_FILE_INFORMATION info;
if (!GetFileInformationByHandle(handle, &info)) { if (!GetFileInformationByHandle(handle, &info)) {
@ -825,6 +836,9 @@ INLINE static int fs__stat_handle(HANDLE handle, uv_statbuf_t* statbuf) {
statbuf->st_mode = 0; statbuf->st_mode = 0;
statbuf->st_blksize = 0;
statbuf->st_blocks = 0;
if (info.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) { if (info.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) {
if (fs__readlink_handle(handle, NULL, &statbuf->st_size) != 0) { if (fs__readlink_handle(handle, NULL, &statbuf->st_size) != 0) {
return -1; return -1;
@ -846,9 +860,9 @@ INLINE static int fs__stat_handle(HANDLE handle, uv_statbuf_t* statbuf) {
((_S_IREAD|_S_IWRITE) >> 6)); ((_S_IREAD|_S_IWRITE) >> 6));
} }
statbuf->st_mtime = FILETIME_TO_TIME_T(info.ftLastWriteTime); FILETIME_TO_TIMESPEC(statbuf->st_mtim, info.ftLastWriteTime);
statbuf->st_atime = FILETIME_TO_TIME_T(info.ftLastAccessTime); FILETIME_TO_TIMESPEC(statbuf->st_atim, info.ftLastAccessTime);
statbuf->st_ctime = FILETIME_TO_TIME_T(info.ftCreationTime); FILETIME_TO_TIMESPEC(statbuf->st_ctim, info.ftCreationTime);
statbuf->st_nlink = (info.nNumberOfLinks <= SHRT_MAX) ? statbuf->st_nlink = (info.nNumberOfLinks <= SHRT_MAX) ?
(short) info.nNumberOfLinks : SHRT_MAX; (short) info.nNumberOfLinks : SHRT_MAX;

View File

@ -30,8 +30,8 @@ static void timer_cb(uv_timer_t* handle, int status);
static void close_cb(uv_handle_t* handle); static void close_cb(uv_handle_t* handle);
static void poll_cb(uv_fs_poll_t* handle, static void poll_cb(uv_fs_poll_t* handle,
int status, int status,
const uv_statbuf_t* prev, const uv_stat_t* prev,
const uv_statbuf_t* curr); const uv_stat_t* curr);
static uv_fs_poll_t poll_handle; static uv_fs_poll_t poll_handle;
static uv_timer_t timer_handle; static uv_timer_t timer_handle;
@ -74,9 +74,9 @@ static void timer_cb(uv_timer_t* handle, int status) {
static void poll_cb(uv_fs_poll_t* handle, static void poll_cb(uv_fs_poll_t* handle,
int status, int status,
const uv_statbuf_t* prev, const uv_stat_t* prev,
const uv_statbuf_t* curr) { const uv_stat_t* curr) {
uv_statbuf_t zero_statbuf; uv_stat_t zero_statbuf;
memset(&zero_statbuf, 0, sizeof(zero_statbuf)); memset(&zero_statbuf, 0, sizeof(zero_statbuf));

View File

@ -107,7 +107,7 @@ static char test_buf[] = "test-buffer\n";
static void check_permission(const char* filename, int mode) { static void check_permission(const char* filename, int mode) {
int r; int r;
uv_fs_t req; uv_fs_t req;
uv_statbuf_t* s; uv_stat_t* s;
r = uv_fs_stat(uv_default_loop(), &req, filename, NULL); r = uv_fs_stat(uv_default_loop(), &req, filename, NULL);
ASSERT(r == 0); ASSERT(r == 0);
@ -213,7 +213,7 @@ static void unlink_cb(uv_fs_t* req) {
} }
static void fstat_cb(uv_fs_t* req) { static void fstat_cb(uv_fs_t* req) {
struct stat* s = req->ptr; uv_stat_t* s = req->ptr;
ASSERT(req->fs_type == UV_FS_FSTAT); ASSERT(req->fs_type == UV_FS_FSTAT);
ASSERT(req->result == 0); ASSERT(req->result == 0);
ASSERT(s->st_size == sizeof(test_buf)); ASSERT(s->st_size == sizeof(test_buf));
@ -543,7 +543,7 @@ TEST_IMPL(fs_file_loop) {
} }
static void check_utime(const char* path, double atime, double mtime) { static void check_utime(const char* path, double atime, double mtime) {
uv_statbuf_t* s; uv_stat_t* s;
uv_fs_t req; uv_fs_t req;
int r; int r;
@ -553,16 +553,8 @@ static void check_utime(const char* path, double atime, double mtime) {
ASSERT(req.result == 0); ASSERT(req.result == 0);
s = &req.statbuf; s = &req.statbuf;
#if defined(_WIN32) || defined(_AIX)
ASSERT(s->st_atime == atime);
ASSERT(s->st_mtime == mtime);
#elif !defined(_POSIX_C_SOURCE) || defined(_DARWIN_C_SOURCE)
ASSERT(s->st_atimespec.tv_sec == atime);
ASSERT(s->st_mtimespec.tv_sec == mtime);
#else
ASSERT(s->st_atim.tv_sec == atime); ASSERT(s->st_atim.tv_sec == atime);
ASSERT(s->st_mtim.tv_sec == mtime); ASSERT(s->st_mtim.tv_sec == mtime);
#endif
uv_fs_req_cleanup(&req); uv_fs_req_cleanup(&req);
} }
@ -906,7 +898,10 @@ TEST_IMPL(fs_fstat) {
int r; int r;
uv_fs_t req; uv_fs_t req;
uv_file file; uv_file file;
struct stat* s; uv_stat_t* s;
#ifndef _WIN32
struct stat t;
#endif
/* Setup. */ /* Setup. */
unlink("test_file"); unlink("test_file");
@ -930,6 +925,45 @@ TEST_IMPL(fs_fstat) {
ASSERT(req.result == 0); ASSERT(req.result == 0);
s = req.ptr; s = req.ptr;
ASSERT(s->st_size == sizeof(test_buf)); ASSERT(s->st_size == sizeof(test_buf));
#ifndef _WIN32
r = fstat(file, &t);
ASSERT(r == 0);
ASSERT(s->st_dev == t.st_dev);
ASSERT(s->st_mode == t.st_mode);
ASSERT(s->st_nlink == t.st_nlink);
ASSERT(s->st_uid == t.st_uid);
ASSERT(s->st_gid == t.st_gid);
ASSERT(s->st_rdev == t.st_rdev);
ASSERT(s->st_ino == t.st_ino);
ASSERT(s->st_size == t.st_size);
ASSERT(s->st_blksize == t.st_blksize);
ASSERT(s->st_blocks == t.st_blocks);
#if defined(__APPLE__)
ASSERT(s->st_atim.tv_sec == t.st_atimespec.tv_sec);
ASSERT(s->st_atim.tv_nsec == t.st_atimespec.tv_nsec);
ASSERT(s->st_mtim.tv_sec == t.st_mtimespec.tv_sec);
ASSERT(s->st_mtim.tv_nsec == t.st_mtimespec.tv_nsec);
ASSERT(s->st_ctim.tv_sec == t.st_ctimespec.tv_sec);
ASSERT(s->st_ctim.tv_nsec == t.st_ctimespec.tv_nsec);
#elif defined(_BSD_SOURCE) || defined(_SVID_SOURCE) || defined(_XOPEN_SOURCE)
ASSERT(s->st_atim.tv_sec == t.st_atim.tv_sec);
ASSERT(s->st_atim.tv_nsec == t.st_atim.tv_nsec);
ASSERT(s->st_mtim.tv_sec == t.st_mtim.tv_sec);
ASSERT(s->st_mtim.tv_nsec == t.st_mtim.tv_nsec);
ASSERT(s->st_ctim.tv_sec == t.st_ctim.tv_sec);
ASSERT(s->st_ctim.tv_nsec == t.st_ctim.tv_nsec);
#else
ASSERT(s->st_atim.tv_sec == t.st_atime);
ASSERT(s->st_atim.tv_nsec == 0);
ASSERT(s->st_mtim.tv_sec == t.st_mtime);
ASSERT(s->st_mtim.tv_nsec == 0);
ASSERT(s->st_ctim.tv_sec == t.st_ctime);
ASSERT(s->st_ctim.tv_nsec == 0);
#endif
#endif
uv_fs_req_cleanup(&req); uv_fs_req_cleanup(&req);
/* Now do the uv_fs_fstat call asynchronously */ /* Now do the uv_fs_fstat call asynchronously */
@ -1382,16 +1416,16 @@ TEST_IMPL(fs_symlink_dir) {
r = uv_fs_stat(loop, &req, "test_dir_symlink", NULL); r = uv_fs_stat(loop, &req, "test_dir_symlink", NULL);
ASSERT(r == 0); ASSERT(r == 0);
ASSERT(((struct stat*)req.ptr)->st_mode & S_IFDIR); ASSERT(((uv_stat_t*)req.ptr)->st_mode & S_IFDIR);
uv_fs_req_cleanup(&req); uv_fs_req_cleanup(&req);
r = uv_fs_lstat(loop, &req, "test_dir_symlink", NULL); r = uv_fs_lstat(loop, &req, "test_dir_symlink", NULL);
ASSERT(r == 0); ASSERT(r == 0);
ASSERT(((struct stat*)req.ptr)->st_mode & S_IFLNK); ASSERT(((uv_stat_t*)req.ptr)->st_mode & S_IFLNK);
#ifdef _WIN32 #ifdef _WIN32
ASSERT(((struct stat*)req.ptr)->st_size == strlen(test_dir + 4)); ASSERT(((uv_stat_t*)req.ptr)->st_size == strlen(test_dir + 4));
#else #else
ASSERT(((struct stat*)req.ptr)->st_size == strlen(test_dir)); ASSERT(((uv_stat_t*)req.ptr)->st_size == strlen(test_dir));
#endif #endif
uv_fs_req_cleanup(&req); uv_fs_req_cleanup(&req);