Skip to content
This repository has been archived by the owner on May 4, 2018. It is now read-only.

Commit

Permalink
unix, windows: nanosecond resolution for uv_fs_[fl]stat
Browse files Browse the repository at this point in the history
Closes #739.
  • Loading branch information
tjfontaine authored and piscisaureus committed Mar 19, 2013
1 parent ab935a2 commit 499c797
Show file tree
Hide file tree
Showing 8 changed files with 177 additions and 77 deletions.
1 change: 0 additions & 1 deletion include/uv-private/uv-unix.h
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,6 @@ typedef struct {

typedef int uv_file;
typedef int uv_os_sock_t;
typedef struct stat uv_statbuf_t;

#define UV_ONCE_INIT PTHREAD_ONCE_INIT

Expand Down
2 changes: 0 additions & 2 deletions include/uv-private/uv-win.h
Original file line number Diff line number Diff line change
Expand Up @@ -207,8 +207,6 @@ typedef struct uv_buf_t {

typedef int uv_file;

typedef struct _stati64 uv_statbuf_t;

typedef SOCKET uv_os_sock_t;

typedef HANDLE uv_thread_t;
Expand Down
31 changes: 27 additions & 4 deletions include/uv.h
Original file line number Diff line number Diff line change
Expand Up @@ -350,6 +350,29 @@ typedef void (*uv_getaddrinfo_cb)(uv_getaddrinfo_t* req,
int status,
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.
* If uv_fs_event_t was initialized with a directory the filename parameter
Expand All @@ -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,
int status,
const uv_statbuf_t* prev,
const uv_statbuf_t* curr);
const uv_stat_t* prev,
const uv_stat_t* curr);

typedef void (*uv_signal_cb)(uv_signal_t* handle, int signum);

Expand Down Expand Up @@ -1519,7 +1542,7 @@ struct uv_fs_s {
void* ptr;
const char* path;
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
};

Expand Down Expand Up @@ -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).
*
* 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!
*
* For maximum portability, use multi-second intervals. Sub-second intervals
Expand Down
49 changes: 9 additions & 40 deletions src/fs-poll.c
Original file line number Diff line number Diff line change
Expand Up @@ -35,16 +35,16 @@ struct poll_ctx {
uv_fs_poll_cb poll_cb;
uv_timer_t timer_handle;
uv_fs_t fs_req; /* TODO(bnoordhuis) mark fs_req internal */
uv_statbuf_t statbuf;
uv_stat_t statbuf;
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 timer_cb(uv_timer_t* timer, int status);
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) {
Expand Down Expand Up @@ -137,7 +137,7 @@ static void timer_cb(uv_timer_t* timer, int status) {


static void poll_cb(uv_fs_t* req) {
uv_statbuf_t* statbuf;
uv_stat_t* statbuf;
struct poll_ctx* ctx;
uint64_t interval;

Expand Down Expand Up @@ -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) {
#if defined(_WIN32)
return a->st_mtime == b->st_mtime
&& a->st_size == b->st_size
&& a->st_mode == b->st_mode;
#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
static int statbuf_eq(const uv_stat_t* a, const uv_stat_t* b) {
return a->st_ctim.tv_nsec == b->st_ctim.tv_nsec
&& a->st_mtim.tv_nsec == b->st_mtim.tv_nsec
&& a->st_ctim.tv_sec == b->st_ctim.tv_sec
&& a->st_mtim.tv_sec == b->st_mtim.tv_sec
&& a->st_size == b->st_size
&& a->st_mode == b->st_mode
&& a->st_uid == b->st_uid
&& a->st_gid == b->st_gid
&& a->st_ino == b->st_ino
&& a->st_dev == b->st_dev;
#endif
}


Expand Down
69 changes: 66 additions & 3 deletions src/unix/fs.c
Original file line number Diff line number Diff line change
Expand Up @@ -497,6 +497,69 @@ static ssize_t uv__fs_write(uv_fs_t* req) {
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) {
int retry_on_eintr;
Expand All @@ -521,11 +584,11 @@ static void uv__fs_work(struct uv__work* w) {
X(FCHMOD, fchmod(req->file, req->mode));
X(FCHOWN, fchown(req->file, req->uid, req->gid));
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(FTRUNCATE, ftruncate(req->file, req->off));
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(MKDIR, mkdir(req->path, req->mode));
X(OPEN, open(req->path, req->flags, req->mode));
Expand All @@ -535,7 +598,7 @@ static void uv__fs_work(struct uv__work* w) {
X(RENAME, rename(req->path, req->new_path));
X(RMDIR, rmdir(req->path));
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(UNLINK, unlink(req->path));
X(UTIME, uv__fs_utime(req));
Expand Down
26 changes: 20 additions & 6 deletions src/win/fs.c
Original file line number Diff line number Diff line change
Expand Up @@ -77,16 +77,27 @@
return; \
}

#define FILETIME_TO_UINT(filetime) \
(*((uint64_t*) &(filetime)) - 116444736000000000ULL)

#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) \
do { \
*(uint64_t*) (filetime_ptr) = ((int64_t) (time) * 10000000LL) + \
116444736000000000ULL; \
} while(0)


#define IS_SLASH(c) ((c) == L'\\' || (c) == L'/')
#define IS_LETTER(c) (((c) >= L'a' && (c) <= L'z') || \
((c) >= L'A' && (c) <= L'Z'))
Expand Down Expand Up @@ -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;

if (!GetFileInformationByHandle(handle, &info)) {
Expand All @@ -825,6 +836,9 @@ INLINE static int fs__stat_handle(HANDLE handle, uv_statbuf_t* statbuf) {

statbuf->st_mode = 0;

statbuf->st_blksize = 0;
statbuf->st_blocks = 0;

if (info.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) {
if (fs__readlink_handle(handle, NULL, &statbuf->st_size) != 0) {
return -1;
Expand All @@ -846,9 +860,9 @@ INLINE static int fs__stat_handle(HANDLE handle, uv_statbuf_t* statbuf) {
((_S_IREAD|_S_IWRITE) >> 6));
}

statbuf->st_mtime = FILETIME_TO_TIME_T(info.ftLastWriteTime);
statbuf->st_atime = FILETIME_TO_TIME_T(info.ftLastAccessTime);
statbuf->st_ctime = FILETIME_TO_TIME_T(info.ftCreationTime);
FILETIME_TO_TIMESPEC(statbuf->st_mtim, info.ftLastWriteTime);
FILETIME_TO_TIMESPEC(statbuf->st_atim, info.ftLastAccessTime);
FILETIME_TO_TIMESPEC(statbuf->st_ctim, info.ftCreationTime);

statbuf->st_nlink = (info.nNumberOfLinks <= SHRT_MAX) ?
(short) info.nNumberOfLinks : SHRT_MAX;
Expand Down
10 changes: 5 additions & 5 deletions test/test-fs-poll.c
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@ static void timer_cb(uv_timer_t* handle, int status);
static void close_cb(uv_handle_t* handle);
static void poll_cb(uv_fs_poll_t* handle,
int status,
const uv_statbuf_t* prev,
const uv_statbuf_t* curr);
const uv_stat_t* prev,
const uv_stat_t* curr);

static uv_fs_poll_t poll_handle;
static uv_timer_t timer_handle;
Expand Down Expand Up @@ -74,9 +74,9 @@ static void timer_cb(uv_timer_t* handle, int status) {

static void poll_cb(uv_fs_poll_t* handle,
int status,
const uv_statbuf_t* prev,
const uv_statbuf_t* curr) {
uv_statbuf_t zero_statbuf;
const uv_stat_t* prev,
const uv_stat_t* curr) {
uv_stat_t zero_statbuf;

memset(&zero_statbuf, 0, sizeof(zero_statbuf));

Expand Down
Loading

0 comments on commit 499c797

Please sign in to comment.