Skip to content

Commit

Permalink
move file watching to stdlib. closes #23715 (#24381)
Browse files Browse the repository at this point in the history
  • Loading branch information
JeffBezanson committed Oct 30, 2017
1 parent 4aa4652 commit 44a4a89
Show file tree
Hide file tree
Showing 14 changed files with 187 additions and 147 deletions.
8 changes: 6 additions & 2 deletions base/deprecated.jl
Original file line number Diff line number Diff line change
Expand Up @@ -473,8 +473,6 @@ end
@deprecate den denominator
@deprecate num numerator

Filesystem.stop_watching(stream::Filesystem._FDWatcher) = depwarn("stop_watching(::_FDWatcher) should not be used", :stop_watching)

# #19088
@deprecate takebuf_array take!
@deprecate takebuf_string(b) String(take!(b))
Expand Down Expand Up @@ -1361,6 +1359,12 @@ export conv, conv2, deconv, filt, filt!, xcorr
@deprecate_moved Base64EncodePipe "Base64" true true
@deprecate_moved Base64DecodePipe "Base64" true true

@deprecate_moved poll_fd "FileWatching" true true
@deprecate_moved poll_file "FileWatching" true true
@deprecate_moved PollingFileWatcher "FileWatching" true true
@deprecate_moved watch_file "FileWatching" true true
@deprecate_moved FileMonitor "FileWatching" true true

# PR #21709
@deprecate cov(x::AbstractVector, corrected::Bool) cov(x, corrected=corrected)
@deprecate cov(x::AbstractMatrix, vardim::Int, corrected::Bool) cov(x, vardim, corrected=corrected)
Expand Down
3 changes: 1 addition & 2 deletions base/event.jl
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,7 @@ Block the current task until some event occurs, depending on the type of the arg
can be used to determine success or failure.
* [`Task`](@ref): Wait for a `Task` to finish, returning its result value. If the task fails
with an exception, the exception is propagated (re-thrown in the task that called `wait`).
* `RawFD`: Wait for changes on a file descriptor (see [`poll_fd`](@ref) for keyword
arguments and return code)
* `RawFD`: Wait for changes on a file descriptor (see the `FileWatching` package).
If no argument is passed, the task blocks for an undefined period. A task can only be
restarted by an explicit call to [`schedule`](@ref) or [`yieldto`](@ref).
Expand Down
5 changes: 0 additions & 5 deletions base/exports.jl
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,6 @@ export
Enumerate,
ExponentialBackOff,
Factorization,
FileMonitor,
Hermitian,
UniformScaling,
IndexCartesian,
Expand All @@ -84,7 +83,6 @@ export
Pair,
PartialQuickSort,
PermutedDimsArray,
PollingFileWatcher,
QuickSort,
RangeIndex,
Rational,
Expand Down Expand Up @@ -1027,8 +1025,6 @@ export
pipeline,
Pipe,
PipeBuffer,
poll_fd,
poll_file,
position,
RawFD,
read,
Expand Down Expand Up @@ -1056,7 +1052,6 @@ export
take!,
truncate,
unmark,
watch_file,
write,
TCPSocket,
UDPSocket,
Expand Down
1 change: 0 additions & 1 deletion base/filesystem.jl
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,6 @@ end
include("path.jl")
include("stat.jl")
include("file.jl")
include("poll.jl")
include(string(length(Core.ARGS) >= 2 ? Core.ARGS[2] : "", "file_constants.jl")) # include($BUILDROOT/base/file_constants.jl)

## Operations with File (fd) objects ##
Expand Down
2 changes: 1 addition & 1 deletion base/io.jl
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ buffer_writes(x::IO, bufsize=SZ_UNBUFFERED_IO) = x
Determine whether an object - such as a stream, timer, or mmap -- is not yet closed. Once an
object is closed, it will never produce a new event. However, a closed stream may still have
data to read in its buffer, use [`eof`](@ref) to check for the ability to read data.
Use [`poll_fd`](@ref) to be notified when a stream might be writable or readable.
Use the `FileWatching` package to be notified when a stream might be writable or readable.
"""
function isopen end

Expand Down
9 changes: 7 additions & 2 deletions doc/make.jl
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,15 @@ if Sys.iswindows()
cp_q("../stdlib/SharedArrays/docs/src/index.md", "src/stdlib/sharedarrays.md")
cp_q("../stdlib/Profile/docs/src/index.md", "src/stdlib/profile.md")
cp_q("../stdlib/Base64/docs/src/index.md", "src/stdlib/base64.md")
cp_q("../stdlib/FileWatching/docs/src/index.md", "src/stdlib/filewatching.md")
else
symlink_q("../../../stdlib/DelimitedFiles/docs/src/index.md", "src/stdlib/delimitedfiles.md")
symlink_q("../../../stdlib/Test/docs/src/index.md", "src/stdlib/test.md")
symlink_q("../../../stdlib/Mmap/docs/src/index.md", "src/stdlib/mmap.md")
symlink_q("../../../stdlib/SharedArrays/docs/src/index.md", "src/stdlib/sharedarrays.md")
symlink_q("../../../stdlib/Profile/docs/src/index.md", "src/stdlib/profile.md")
symlink_q("../../../stdlib/Base64/docs/src/index.md", "src/stdlib/base64.md")
symlink_q("../../../stdlib/FileWatching/docs/src/index.md", "src/stdlib/filewatching.md")
end

const PAGES = [
Expand Down Expand Up @@ -104,6 +106,9 @@ const PAGES = [
"stdlib/stacktraces.md",
"stdlib/simd-types.md",
"stdlib/base64.md",
"stdlib/mmap.md",
"stdlib/sharedarrays.md",
"stdlib/filewatching.md",
],
"Developer Documentation" => [
"devdocs/reflection.md",
Expand Down Expand Up @@ -138,11 +143,11 @@ const PAGES = [
],
]

using DelimitedFiles, Test, Mmap, SharedArrays, Profile, Base64
using DelimitedFiles, Test, Mmap, SharedArrays, Profile, Base64, FileWatching

makedocs(
build = joinpath(pwd(), "_build/html/en"),
modules = [Base, Core, BuildSysImg, DelimitedFiles, Test, Mmap, SharedArrays, Profile, Base64],
modules = [Base, Core, BuildSysImg, DelimitedFiles, Test, Mmap, SharedArrays, Profile, Base64, FileWatching],
clean = false,
doctest = "doctest" in ARGS,
linkcheck = "linkcheck" in ARGS,
Expand Down
7 changes: 6 additions & 1 deletion doc/src/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
* [Linear Algebra](@ref)
* [Constants](@ref lib-constants)
* [Filesystem](@ref)
* [Delimited Files](@ref)
* [I/O and Network](@ref)
* [Punctuation](@ref)
* [Sorting and Related Functions](@ref)
Expand All @@ -63,9 +64,13 @@
* [C Interface](@ref)
* [C Standard Library](@ref)
* [Dynamic Linker](@ref)
* [Profiling](@ref lib-profiling)
* [StackTraces](@ref)
* [SIMD Support](@ref)
* [Profiling](@ref lib-profiling)
* [Memory-mapped I/O](@ref)
* [Shared Arrays](@ref)
* [Base64](@ref)
* [File Events](@ref lib-filewatching)

## Developer Documentation

Expand Down
5 changes: 4 additions & 1 deletion doc/src/stdlib/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,10 @@
* [LLVM Interface](@ref)
* [C Standard Library](@ref)
* [Dynamic Linker](@ref)
* [Profiling](@ref lib-profiling)
* [StackTraces](@ref)
* [SIMD Support](@ref)
* [Profiling](@ref lib-profiling)
* [Memory-mapped I/O](@ref)
* [Shared Arrays](@ref)
* [Base64](@ref)
* [File Events](@ref lib-filewatching)
3 changes: 0 additions & 3 deletions doc/src/stdlib/io-network.md
Original file line number Diff line number Diff line change
Expand Up @@ -149,9 +149,6 @@ Base.IPv6
Base.nb_available
Base.accept
Base.listenany
Base.Filesystem.poll_fd
Base.Filesystem.poll_file
Base.Filesystem.watch_file
Base.bind
Base.send
Base.recv
Expand Down
7 changes: 7 additions & 0 deletions stdlib/FileWatching/docs/src/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# [File Events](@id lib-filewatching)

```@docs
FileWatching.poll_fd
FileWatching.poll_file
FileWatching.watch_file
```
14 changes: 12 additions & 2 deletions base/poll.jl → stdlib/FileWatching/src/FileWatching.jl
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
# This file is a part of Julia. License is MIT: https://julialang.org/license

# filesystem operations
"""
Utilities for monitoring files and file descriptors for events.
"""
module FileWatching

export
watch_file,
Expand All @@ -11,8 +14,9 @@ export
FDWatcher

import Base: @handle_as, wait, close, uvfinalize, eventloop, notify_error, stream_wait,
_sizeof_uv_poll, _sizeof_uv_fs_poll, _sizeof_uv_fs_event, _uv_hook_close,
_sizeof_uv_poll, _sizeof_uv_fs_poll, _sizeof_uv_fs_event, _uv_hook_close, uv_error, UVError,
associate_julia_struct, disassociate_julia_struct, isreadable, iswritable, |
import Base.Filesystem.StatStruct
if Sys.iswindows()
import Base.WindowsRawSocket
end
Expand Down Expand Up @@ -563,3 +567,9 @@ function poll_file(s::AbstractString, interval_seconds::Real=5.007, timeout_s::R
close(pfw)
end
end

# deprecations

stop_watching(stream::_FDWatcher) = Base.depwarn("stop_watching(::_FDWatcher) should not be used", :stop_watching)

end
141 changes: 141 additions & 0 deletions test/pollfd.jl → stdlib/FileWatching/test/runtests.jl
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# This file is a part of Julia. License is MIT: https://julialang.org/license

using Test, FileWatching

# This script does the following
# Sets up n unix pipes
# For the odd pipes, a byte is written to the write end at intervals specified in intvls
Expand Down Expand Up @@ -145,3 +147,142 @@ let a = Ref(0)
gc()
@test a[] == 1
end

for f in (watch_file, poll_file)
local f
@test_throws ArgumentError f("adir\0bad")
end

#issue #12992
function test_12992()
pfw = PollingFileWatcher(@__FILE__, 0.01)
close(pfw)
pfw = PollingFileWatcher(@__FILE__, 0.01)
close(pfw)
pfw = PollingFileWatcher(@__FILE__, 0.01)
close(pfw)
gc()
gc()
end

# Make sure multiple close is fine
function test2_12992()
pfw = PollingFileWatcher(@__FILE__, 0.01)
close(pfw)
close(pfw)
pfw = PollingFileWatcher(@__FILE__, 0.01)
close(pfw)
close(pfw)
pfw = PollingFileWatcher(@__FILE__, 0.01)
close(pfw)
close(pfw)
gc()
gc()
end

test_12992()
test_12992()
test_12992()

test2_12992()
test2_12992()
test2_12992()

#######################################################################
# This section tests file watchers. #
#######################################################################
dir = mktempdir()
file = joinpath(dir, "afile.txt")
# like touch, but lets the operating system update the timestamp
# for greater precision on some platforms (windows)
@test close(open(file,"w")) === nothing

function test_file_poll(channel,interval,timeout_s)
rc = poll_file(file, interval, timeout_s)
put!(channel,rc)
end

function test_timeout(tval)
t_elapsed = @elapsed begin
channel = Channel(1)
@async test_file_poll(channel, 10, tval)
tr = take!(channel)
end
@test tr[1] === Base.Filesystem.StatStruct() && tr[2] === EOFError()
@test tval <= t_elapsed
end

function test_touch(slval)
tval = slval*1.1
channel = Channel(1)
@async test_file_poll(channel, tval/3, tval)
sleep(tval/3) # one poll period
f = open(file,"a")
write(f,"Hello World\n")
close(f)
tr = take!(channel)
@test ispath(tr[1]) && ispath(tr[2])
end

function test_watch_file_timeout(tval)
watch = @async watch_file(file, tval)
@test wait(watch) == FileWatching.FileEvent(false, false, true)
end

function test_watch_file_change(tval)
watch = @async watch_file(file, tval)
sleep(tval/3)
open(file, "a") do f
write(f, "small change\n")
end
@test wait(watch) == FileWatching.FileEvent(false, true, false)
end

function test_monitor_wait(tval)
fm = FileMonitor(file)
@async begin
sleep(tval)
f = open(file,"a")
write(f,"Hello World\n")
close(f)
end
fname, events = wait(fm)
close(fm)
if Sys.islinux() || Sys.iswindows() || Sys.isapple()
@test fname == basename(file)
else
@test fname == "" # platforms where F_GETPATH is not available
end
@test events.changed
end

function test_monitor_wait_poll()
pfw = PollingFileWatcher(file, 5.007)
@async begin
sleep(2.5)
f = open(file,"a")
write(f,"Hello World\n")
close(f)
end
(old, new) = wait(pfw)
close(pfw)
@test new.mtime - old.mtime > 2.5 - 1.5 # mtime may only have second-level accuracy (plus add some hysteresis)
end

test_timeout(0.1)
test_timeout(1)
test_touch(6)
test_monitor_wait(0.1)
test_monitor_wait(0.1)
test_monitor_wait_poll()
test_monitor_wait_poll()
test_watch_file_timeout(0.1)
test_watch_file_change(6)

@test_throws Base.UVError watch_file("____nonexistent_file", 10)
@test(@elapsed(
@test(poll_file("____nonexistent_file", 1, 3.1) ===
(Base.Filesystem.StatStruct(), EOFError()))) > 3)

rm(file)
rm(dir)
2 changes: 1 addition & 1 deletion test/choosetests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ function choosetests(choices = [])
"operators", "path", "ccall", "parse", "loading", "bigint",
"bigfloat", "sorting", "statistics", "spawn", "backtrace",
"file", "read", "version", "resolve",
"pollfd", "mpfr", "broadcast", "complex", "socket",
"mpfr", "broadcast", "complex", "socket",
"floatapprox", "stdlib", "reflection", "regex", "float16",
"combinatorics", "sysinfo", "env", "rounding", "ranges", "mod2pi",
"euler", "show", "lineedit", "replcompletions", "repl",
Expand Down
Loading

0 comments on commit 44a4a89

Please sign in to comment.