Use higher time resolution when available in os.nim (#7709)

This commit is contained in:
Oscar Nihlgård
2018-06-04 14:56:56 +02:00
committed by Andreas Rumpf
parent 440212a154
commit 05b447374b
4 changed files with 55 additions and 19 deletions

View File

@@ -82,6 +82,14 @@ const
# Special types
type Sighandler = proc (a: cint) {.noconv.}
const StatHasNanoseconds* = defined(linux) or defined(freebsd) or
defined(openbsd) or defined(dragonfly) ## \
## Boolean flag that indicates if the system supports nanosecond time
## resolution in the fields of ``Stat``. Note that the nanosecond based fields
## (``Stat.st_atim``, ``Stat.st_mtim`` and ``Stat.st_ctim``) can be accessed
## without checking this flag, because this module defines fallback procs
## when they are not available.
# Platform specific stuff
when defined(linux) and defined(amd64):
@@ -92,9 +100,9 @@ else:
# There used to be this name in posix.nim a long time ago, not sure why!
{.deprecated: [cSIG_HOLD: SIG_HOLD].}
when not defined(macosx) and not defined(android):
when StatHasNanoseconds:
proc st_atime*(s: Stat): Time {.inline.} =
## Second-granularity time of last access
## Second-granularity time of last access.
result = s.st_atim.tv_sec
proc st_mtime*(s: Stat): Time {.inline.} =
## Second-granularity time of last data modification.
@@ -102,6 +110,16 @@ when not defined(macosx) and not defined(android):
proc st_ctime*(s: Stat): Time {.inline.} =
## Second-granularity time of last status change.
result = s.st_ctim.tv_sec
else:
proc st_atim*(s: Stat): TimeSpec {.inline.} =
## Nanosecond-granularity time of last access.
result.tv_sec = s.st_atime
proc st_mtim*(s: Stat): TimeSpec {.inline.} =
## Nanosecond-granularity time of last data modification.
result.tv_sec = s.st_mtime
proc st_ctim*(s: Stat): TimeSpec {.inline.} =
## Nanosecond-granularity time of last data modification.
result.tv_sec = s.st_ctime
when hasAioH:
proc aio_cancel*(a1: cint, a2: ptr Taiocb): cint {.importc, header: "<aio.h>".}

View File

@@ -215,14 +215,14 @@ type
## For a typed memory object, the length in bytes.
## For other file types, the use of this field is
## unspecified.
when defined(macosx) or defined(android):
st_atime*: Time ## Time of last access.
st_mtime*: Time ## Time of last data modification.
st_ctime*: Time ## Time of last status change.
else:
when StatHasNanoseconds:
st_atim*: Timespec ## Time of last access.
st_mtim*: Timespec ## Time of last data modification.
st_ctim*: Timespec ## Time of last status change.
else:
st_atime*: Time ## Time of last access.
st_mtime*: Time ## Time of last data modification.
st_ctime*: Time ## Time of last status change.
st_blksize*: Blksize ## A file system-specific preferred I/O block size
## for this object. In some file system types, this
## may vary from file to file.

View File

@@ -23,6 +23,10 @@ when defined(windows):
import winlean
elif defined(posix):
import posix
proc toTime(ts: Timespec): times.Time {.inline.} =
result = initTime(ts.tv_sec.int64, ts.tv_nsec.int)
else:
{.error: "OS module not ported to your operating system!".}
@@ -186,7 +190,7 @@ proc getLastModificationTime*(file: string): times.Time {.rtl, extern: "nos$1".}
when defined(posix):
var res: Stat
if stat(file, res) < 0'i32: raiseOSError(osLastError())
return fromUnix(res.st_mtime.int64)
result = res.st_mtim.toTime
else:
var f: WIN32_FIND_DATA
var h = findFirstFile(file, f)
@@ -199,7 +203,7 @@ proc getLastAccessTime*(file: string): times.Time {.rtl, extern: "nos$1".} =
when defined(posix):
var res: Stat
if stat(file, res) < 0'i32: raiseOSError(osLastError())
return fromUnix(res.st_atime.int64)
result = res.st_atim.toTime
else:
var f: WIN32_FIND_DATA
var h = findFirstFile(file, f)
@@ -216,7 +220,7 @@ proc getCreationTime*(file: string): times.Time {.rtl, extern: "nos$1".} =
when defined(posix):
var res: Stat
if stat(file, res) < 0'i32: raiseOSError(osLastError())
return fromUnix(res.st_ctime.int64)
result = res.st_ctim.toTime
else:
var f: WIN32_FIND_DATA
var h = findFirstFile(file, f)
@@ -228,10 +232,13 @@ proc fileNewer*(a, b: string): bool {.rtl, extern: "nos$1".} =
## Returns true if the file `a` is newer than file `b`, i.e. if `a`'s
## modification time is later than `b`'s.
when defined(posix):
result = getLastModificationTime(a) - getLastModificationTime(b) >= DurationZero
# Posix's resolution sucks so, we use '>=' for posix.
# If we don't have access to nanosecond resolution, use '>='
when not StatHasNanoseconds:
result = getLastModificationTime(a) >= getLastModificationTime(b)
else:
result = getLastModificationTime(a) > getLastModificationTime(b)
else:
result = getLastModificationTime(a) - getLastModificationTime(b) > DurationZero
result = getLastModificationTime(a) > getLastModificationTime(b)
proc getCurrentDir*(): string {.rtl, extern: "nos$1", tags: [].} =
## Returns the `current working directory`:idx:.
@@ -1494,7 +1501,7 @@ type
template rawToFormalFileInfo(rawInfo, path, formalInfo): untyped =
## Transforms the native file info structure into the one nim uses.
## 'rawInfo' is either a 'TBY_HANDLE_FILE_INFORMATION' structure on Windows,
## 'rawInfo' is either a 'BY_HANDLE_FILE_INFORMATION' structure on Windows,
## or a 'Stat' structure on posix
when defined(Windows):
template merge(a, b): untyped = a or (b shl 32)
@@ -1520,7 +1527,6 @@ template rawToFormalFileInfo(rawInfo, path, formalInfo): untyped =
if (rawInfo.dwFileAttributes and FILE_ATTRIBUTE_REPARSE_POINT) != 0'i32:
formalInfo.kind = succ(result.kind)
else:
template checkAndIncludeMode(rawMode, formalMode: untyped) =
if (rawInfo.st_mode and rawMode) != 0'i32:
@@ -1528,9 +1534,9 @@ template rawToFormalFileInfo(rawInfo, path, formalInfo): untyped =
formalInfo.id = (rawInfo.st_dev, rawInfo.st_ino)
formalInfo.size = rawInfo.st_size
formalInfo.linkCount = rawInfo.st_Nlink.BiggestInt
formalInfo.lastAccessTime = fromUnix(rawInfo.st_atime.int64)
formalInfo.lastWriteTime = fromUnix(rawInfo.st_mtime.int64)
formalInfo.creationTime = fromUnix(rawInfo.st_ctime.int64)
formalInfo.lastAccessTime = rawInfo.st_atim.toTime
formalInfo.lastWriteTime = rawInfo.st_mtim.toTime
formalInfo.creationTime = rawInfo.st_ctim.toTime
result.permissions = {}
checkAndIncludeMode(S_IRUSR, fpUserRead)
@@ -1644,7 +1650,9 @@ proc setLastModificationTime*(file: string, t: times.Time) =
## an error.
when defined(posix):
let unixt = posix.Time(t.toUnix)
var timevals = [Timeval(tv_sec: unixt), Timeval(tv_sec: unixt)] # [last access, last modification]
let micro = convert(Nanoseconds, Microseconds, t.nanosecond)
var timevals = [Timeval(tv_sec: unixt, tv_usec: micro),
Timeval(tv_sec: unixt, tv_usec: micro)] # [last access, last modification]
if utimes(file, timevals.addr) != 0: raiseOSError(osLastError())
else:
let h = openHandle(path = file, writeAccess = true)

View File

@@ -42,6 +42,7 @@ Raises
true
true
true
true
'''
"""
# test os path creation, iteration, and deletion
@@ -129,3 +130,12 @@ echo fileExists("../dest/a/b/file.txt")
echo fileExists("../dest/a/b/c/fileC.txt")
removeDir("../dest")
# Test get/set modification times
# Should support at least microsecond resolution
import times
let tm = fromUnix(0) + 100.microseconds
writeFile("a", "")
setLastModificationTime("a", tm)
echo getLastModificationTime("a") == tm
removeFile("a")