Code Monkey home page Code Monkey logo

notify's Introduction

Notify

» Crate » Docs » CI » Downloads » Conduct » Public Domain

Cross-platform filesystem notification library for Rust.

As used by: alacritty, cargo watch, cobalt, docket, mdBook, rust-analyzer, watchexec, xi-editor, watchfiles, and others.

(Looking for desktop notifications instead? Have a look at notify-rust or alert-after!)

Platforms

  • Linux / Android: inotify
  • macOS: FSEvents or kqueue, see features
  • Windows: ReadDirectoryChangesW
  • iOS / FreeBSD / NetBSD / OpenBSD / DragonflyBSD: kqueue
  • All platforms: polling

License

Notify is licensed under the CC Zero 1.0.
notify-debouncer-mini is licensed under the MIT or Apache-2.0 license.
notify-debouncer-full is licensed under the MIT or Apache-2.0 license.
file-id is licensed under the MIT or Apache-2.0 license.

Origins

Inspired by Go's fsnotify and Node.js's Chokidar, born out of need for cargo watch, and general frustration at the non-existence of C/Rust cross-platform notify libraries.

Originally created by Félix Saparelli and awesome contributors.

notify's People

Contributors

0xpr03 avatar a1phyr avatar andelf avatar blaenk avatar coswat avatar detegr avatar dfaust avatar dimbleby avatar erickt avatar fornwall avatar frangio avatar jasper-bekkers avatar jasta avatar jmquigs avatar johntitor avatar jorendorff avatar julienw avatar jwilm avatar maurizi avatar mitchmindtree avatar octplane avatar passcod avatar savovsa avatar sjoshid avatar tmfink avatar vandenoever avatar vemoo avatar visig9 avatar xanderio avatar yuhta avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

notify's Issues

Segfault when watcher is moved.

I have a program written using rsnotify 2.3.3 that segfaults when I touch a file that I'm watching.

Here's the backtrace from lldb

Process 60876 stopped
* thread #2: tid = 0x2b2995, 0x00000001000e36f8 stormtrooper`notify::sync::mpsc::oneshot::Packet<T>::sent(self=0x0000000000000010) + 8 at oneshot.rs:133, stop reason = EXC_BAD_ACCESS (code=1, address=0x60)
    frame #0: 0x00000001000e36f8 stormtrooper`notify::sync::mpsc::oneshot::Packet<T>::sent(self=0x0000000000000010) + 8 at oneshot.rs:133
(lldb) bt
error: need to add support for DW_TAG_base_type '()' encoded with DW_ATE = 0x7, bit_size = 0
* thread #2: tid = 0x2b2995, 0x00000001000e36f8 stormtrooper`notify::sync::mpsc::oneshot::Packet<T>::sent(self=0x0000000000000010) + 8 at oneshot.rs:133, stop reason = EXC_BAD_ACCESS (code=1, address=0x60)
  * frame #0: 0x00000001000e36f8 stormtrooper`notify::sync::mpsc::oneshot::Packet<T>::sent(self=0x0000000000000010) + 8 at oneshot.rs:133
    frame #1: 0x00000001000e2b95 stormtrooper`notify::sync::mpsc::Sender<T>::send(self=0x00007fff5fbff8e0, t=Event at 0x0000000101cfa618) + 517 at mod.rs:535
    frame #2: 0x00000001000e1c41 stormtrooper`notify::fsevent::callback(stream_ref=0x0000000101a002b0, info=0x00007fff5fbff8e0, num_events=1, event_paths=0x0000000101a00780, event_flags=0x00000001005f1000, event_ids=0x00000001005f0000) + 1649 at fsevent.rs:197
    frame #3: 0x00000001000dbf6d stormtrooper`fsevent::callback::hf1094fd0c5c09dbeeia + 61
    frame #4: 0x00007fff92498ba1 FSEvents`implementation_callback_rpc + 2123
    frame #5: 0x00007fff92496672 FSEvents`_Xcallback_rpc + 254
    frame #6: 0x00007fff92496746 FSEvents`FSEventsD2F_server + 54
    frame #7: 0x00007fff9249bf75 FSEvents`FSEventsClientProcessMessageCallback + 44
    frame #8: 0x00007fff88de8d87 CoreFoundation`__CFMachPortPerform + 247
    frame #9: 0x00007fff88de8c79 CoreFoundation`__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__ + 41
    frame #10: 0x00007fff88de8beb CoreFoundation`__CFRunLoopDoSource1 + 475
    frame #11: 0x00007fff88dda767 CoreFoundation`__CFRunLoopRun + 2375
    frame #12: 0x00007fff88dd9bd8 CoreFoundation`CFRunLoopRunSpecific + 296
    frame #13: 0x00007fff88e91671 CoreFoundation`CFRunLoopRun + 97
    frame #14: 0x00000001000de91a stormtrooper`fnfn + 410 at fsevent.rs:158
    frame #15: 0x00000001000de749 stormtrooper`notify::boxed::F.FnBox<A>::call_box(self=0x0000000101437030, args=<unavailable>) + 89 at boxed.rs:516
    frame #16: 0x00000001000de33c stormtrooper`notify::boxed::Box<FnBox<A, Output = R>+ Send + 'a>.FnOnce<A>::call_once(self=Box<FnBox<()>> at 0x0000000101d00bc0, args=<unavailable>) + 60 at boxed.rs:532
    frame #17: 0x00000001000ddf0e stormtrooper`fnfn + 78 at mod.rs:281
    frame #18: 0x00000001000ddeba stormtrooper`notify::sys_common::unwind::try::try_fn<closure>(opt_closure=0x0000000101d00cf0) + 58 at mod.rs:156
    frame #19: 0x00000001001a7e89 stormtrooper`__rust_try + 9
    frame #20: 0x00000001001a59cf stormtrooper`sys_common::unwind::try::inner_try::h9c69b1952218bc71tRr + 111
    frame #21: 0x00000001000dde24 stormtrooper`notify::sys_common::unwind::try<closure>(f=closure at 0x0000000101d00d08) + 100 at mod.rs:126
    frame #22: 0x00000001000ddc9c stormtrooper`fnfn + 412 at mod.rs:281
    frame #23: 0x00000001000de56d stormtrooper`notify::boxed::F.FnBox<A>::call_box(self=0x000000010143b000, args=<unavailable>) + 77 at boxed.rs:516
    frame #24: 0x00000001001a955e stormtrooper`sys::thread::_$LT$impl$GT$::new::thread_start::h08e832c9db3841f0Qcw + 142
    frame #25: 0x00007fff8aba805a libsystem_pthread.dylib`_pthread_body + 131
    frame #26: 0x00007fff8aba7fd7 libsystem_pthread.dylib`_pthread_start + 176
    frame #27: 0x00007fff8aba53ed libsystem_pthread.dylib`thread_start + 13

Polling: handle metadata and name changes

From the PollWatcher code:

  • Populate mtimes before loop, and then handle creation events (done in #88, released in v3.0.0)
  • Handle deletion events (done in #88, released in v3.0.0)
  • Handle chmod events
  • Handle renames

100% CPU usage on high (100k+) number of watches

I've tried the example on my $HOME directory with 125k folders.

fn watch() {
  // Create a channel to receive the events.
  let (tx, rx) = channel();

  // Automatically select the best implementation for your platform.
  // You can also access each implementation directly e.g. INotifyWatcher.
  let w: Result<RecommendedWatcher, Error> = Watcher::new(tx);

  match w {
    Ok(mut watcher) => {
      // Add a path to be watched. All files and directories at that path and
      // below will be monitored for changes.
      let mut r = watcher.watch("/home/oever");
      while let Ok(()) = r {
          // You'll probably want to do that in a loop. The type to match for is
          // notify::Event, look at src/lib.rs for details.
          match rx.recv() {
            Ok(notify::Event{path:Some(path),op:Ok(op)}) => {
                println!("{:?} {:?}", op, path);
            },
            Err(e) => println!("watch error {}", e),
            _ => ()
          }
          println!("next round");
          r = watcher.watch("/home/oever/src");
      }
      if let Err(err) = r {
          println!("Error?: {:?}", err);
      }
    },
    Err(err) => println!("Error!: {:?}", err)
  }
}

prints out

Error?: Io(Error { repr: Os { code: 28, message: "No space left on device" } })

The error occurred in the watch() loop. Inotify had plenty of handles left:

$ cat /proc/sys/fs/inotify/max_user_watches
524288

When I increase the number of allowed watches to 2.5M, then this error does not occur, but rsnotify does take up 100% cpu continuously.

I've compared with inotifywatch ~ -r. This program only needs only a watch per directory. It can set the watches very quickly. Only 3.2 seconds on my laptop with SSD. I've timed this with

time inotifywatch ~ -r -t 1

This sets watches on all directories in the home folder and continues to run for 1 second after it has done that.

Remake PollWatcher

For those not familiar with the background who want to help:

What was done:

  • Support was achieved for both Linux and OS X.
  • Windows native support (#4) may be worked on.
  • The polling backend requires the ability to get the mtime of a file, which cannot currently be done in stable Rust, so it was dropped.

What is needed:

  • To get a replacement for stat.modified().
  • To implement anew (or adapt from the old code) a polling backend.

INotifyWatcher recursively adds watches but does not recursively remove

Here's where the INotifyHandler gets requests to add/remove watches.

            EventLoopMsg::AddWatch(path, tx) => {
                let _ = tx.send(self.add_watch_recursively(path));
            }
            EventLoopMsg::RemoveWatch(path, tx) => {
                let _ = tx.send(self.remove_watch(path));
            }

Watches are definitely done recursively (that function does a WalkDir and add_watch for each entry). Remove watch only attempts to remove the given path.

    fn remove_watch(&mut self, path: PathBuf) -> Result<(), Error> {
        match self.watches.remove(&path) {
            None => Err(Error::WatchNotFound),
            Some(p) => {
                let w = p.0;
                match self.inotify.rm_watch(w) {
                    Err(e) => Err(Error::Io(e)),
                    Ok(_) => {
                        // Nothing depends on the value being gone
                        // from here now that inotify isn't watching.
                        (*self.paths).write().unwrap().remove(&w);
                        Ok(())
                    }
                }
            }
        }
    }

Just for completeness, here's the add_watch_recursively implementation.

    fn add_watch_recursively(&mut self, path: PathBuf) -> Result<(), Error> {
        match metadata(&path) {
            Err(e) => return Err(Error::Io(e)),
            Ok(m) => {
                if !m.is_dir() {
                    return self.add_watch(path);
                }
            }
        }

        for entry in WalkDir::new(path)
                         .follow_links(true)
                         .into_iter()
                         .filter_map(|e| e.ok()) {
            try!(self.add_watch(entry.path().to_path_buf()));
        }

        Ok(())
    }

does inotify leak when monitored objects are deleted?

I'm wondering what happens if a monitored object is deleted. I'm wondering if it would remain/leak/linger in the watches map. Perhaps we can watch for the IN_DELETE_SELF event for each watched path, and if triggered, we remove the entries? Not sure if that'd work.

           IN_DELETE_SELF
                  Watched file/directory was itself deleted.  (This event
                  also occurs if an object is moved to another filesystem,
                  since mv(1) in effect copies the file to the other
                  filesystem and then deletes it from the original
                  filesystem.)  In addition, an IN_IGNORED event will
                  subsequently be generated for the watch descriptor.

Also from TLPI:

An IN_IGNORED event is generated when a watch is removed. This can occur for two reasons: the application used an inotify_rm_watch() call to explicitly remove the watch, or the watch was implicitly removed by the kernel because the monitored object was deleted or the file system where it resides was unmounted.

So I think monitoring for IN_IGNORED should be enough. When we receive it, we remove the entries from our own data structures.

consider using walkdir crate

We might want to switch over to @BurntSushi's walkdir crate which may end up being more popular and therefore (hopefully) better maintained than the one @passcod extracted into walker.

We can evaluate this further after some time to see how things pan out, but the stuff from @BurntSushi is solid.

inotify watcher thread never shuts down

The inotify watcher loop keeps going no matter what. Apparently the blocking call to wait_for_events does not get interrupted when the watcher is dropped/close is called. Given that, I don't know how to fix this. Should this be reported upstream?

Error "No space left on device"

Hi, I'm having trouble getting the example monitor_debounced to run without error on linux (kernel 3.16 debian amd64) compiled with rustc 1.13.0.

It seems to run fine on most directories on my filesystem (ZFS from zfsonlinux.org), but there are at least two directories where I get a "No space left on device" error. The distinguishing feature of these directories is that there are many files and subdirectories (e.g. hundreds of thousands of files and tens of thousands of subdirectories).

The thing is, I don't know what device should have no space left. The filesystem in question has 45 TB free and the root filesystem (with /tmp) has 97 GB free. Also, diving into the code I don't see any temporary file allocations. Any hints on what's behind this and how to debug it?

# ./monitor_debounced plots
watching plots
error: Io(Error { repr: Os { code: 28, message: "No space left on device" } })

Additional maintainers + Vision

I'm going on a 5-week holiday soon, and will probably not be available to do much on this during that time. Even apart from that, I'm generally quite busy from work and my contributions to Notify have been reduced to merging changes and shaping discussions for a little while now.

I'm looking for maintainers to help with this. You'll get commit and crates.io access, a nice badge on your posts in issue threads, and eternal thanks.

My vision for Notify can be summarised thus:

  • A single library for file watching in Rust, à la fs.watch in node, Listen in Ruby, fsnotify in Go.
  • I'd love for it to be embeddable / usable in other languages. C is the obvious target here, but there has been some recent forays in Python-to-Rust-and-back, for example.
  • Since #22, Notify should be able to watch both directories and single files.
  • A single interface for file watching. Yes, different system interfaces have different options, and one may want to take advantage of some advanced feature of one, but not here. They can use a binding directly. Notify should remain the same across all platforms and implementations.
  • Abstract away as much as possible from the backends. See this comment on #22. Afaik, fanotify on Linux is the only backend that could provide recursive directory watches natively. Notify should allow fanotify to handle that, as it will do so far more efficiently than us, but it should also take care of recursive watching for the other backends.
  • A more advanced vision of the above is to compose backends if necessary. On Windows, the native call can only watch directories, not files. We could implement single file watching by watching the directory above and filtering events, or we could use the PollWatcher to watch that file. The behaviour could be chosen dynamically, e.g. a large amount of sibling files in that directory may generate a large amount of events, and in that case it would be preferable to switch to a PollWatcher.
  • Speaking of, the current PollWatcher is lacking terribly, see #20.

Pinging those of the current contributors who aren't watching this repo: @blaenk @octplane @andelf @retep998 and I'll also post this on Reddit for visibility.

OSX - Watching 3 or more directories fails

Hi,
I'm running OSX 10.10.4 and was trying to use the cargo watch tool, however it wasn't working. It seemed whenever the "benches" watch was added, cargo watch wouldn't watch anything...

Digging further, it seems something goes awry when adding the third watch. For the prior two watches, this condition is met (fsevent.rs line 62):

    if !self.is_running() {
      return;
    }

On the third try however, that condition isn't met, and it makes its way all the way to this match (fsevent.rs line 78):

      match context_info.done.recv() {
        Ok(()) => (),
        Err(_) => panic!("the runloop may not be finished!"),
      }

And never makes it past there...

Let me know if you need more information.

Also, if I try running cargo test, it fails:

~/rsnotify git:master ❯❯❯ cargo test                                                                                                                                                                                            ⏎
     Running target/debug/notify-a00f19333567126a

running 1 test
test fsevent::test_fsevent_watcher_drop ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured

     Running target/debug/notify-b65501780e7463a9

running 7 tests
test new_null ... ok
test new_fsevent ... ok
test new_recommended ... ok
test new_poll ... ok
test watch_dir_recommended ... FAILED
test watch_single_file_poll ... ok
test watch_single_file_recommended ... FAILED

failures:

---- watch_dir_recommended stdout ----
    thread 'watch_dir_recommended' panicked at 'assertion failed: `(left == right)` (left: `"/private/var/folders/b5/yh22bw792yj4n0q68vckrbjw0000gn/T/dir.KTsLmVgBAqyo"`, right: `"/var/folders/b5/yh22bw792yj4n0q68vckrbjw0000gn/T/dir.KTsLmVgBAqyo/dir1.W4y90yVhB8oP/dir11.EPrh1Cq6JFaE/.9ZIU5fW5tHKo"`)', tests/notify.rs:16


---- watch_single_file_recommended stdout ----
    thread 'watch_single_file_recommended' panicked at 'assertion failed: `(left == right)` (left: `"/private/var/folders/b5/yh22bw792yj4n0q68vckrbjw0000gn/T/.D36zoPQgBQr0"`, right: `"/var/folders/b5/yh22bw792yj4n0q68vckrbjw0000gn/T/.D36zoPQgBQr0"`)', tests/notify.rs:16



failures:
    watch_dir_recommended
    watch_single_file_recommended

test result: FAILED. 5 passed; 2 failed; 0 ignored; 0 measured

thread '<main>' panicked at 'Some tests failed', /Users/rustbuild/src/rust-buildbot/slave/stable-dist-rustc-mac/build/src/libtest/lib.rs:255

Errors when watcher backend is given broken symlinks

I'm on Linux kernel 4.1.0, rsnotify 2.1.0 and Emacs 24.5.

My code is as simple as:

fn _watch (p: &Path) -> Result<(), Error>{
    let (tx, rx) = channel();
    let mut watcher: RecommendedWatcher = try!(Watcher::new(tx));
    println!("watcher init");
    try!(watcher.watch(p));
    println!("watching");
    let _ = rx.recv();
    Ok(())
}

Every time I modified files in Emacs, watcher.watch(p) returns error:

Io(Error { repr: Os { code: 2, message: "No such file or directory" } })

If I ls -al the watched directory, I'm getting:

 $ ls -al examples/templates  
drwxr-xr-x 2 nsun nsun 4096 Jul  6 21:04 .
drwxr-xr-x 3 nsun nsun 4096 Jul  4 10:37 ..
lrwxrwxrwx 1 nsun nsun   31 Jul  6 21:04 .#index.hbs -> [email protected]:1435199339
-rw-r--r-- 1 nsun nsun  279 Jul  6 20:57 index.hbs

The .#index.hbs symbol link, as the SO question said, is Emacs lockfile.

I'm not quite sure if this is an rsnotify issue, anything suggestion or workaround are welcomed.

MacOSX' RecommendedWatcher (FsEventWatcher) doesn't implement Send

This is misleading because other watchers are Send and so can be moved between threads.

Here is an example of an error I get in MacOS X only:

src/lib.rs:643:9: 643:22 error: the trait bound `*mut libc::c_void: std::marker::Send` is not satisfied [E0277]
src/lib.rs:643         thread::spawn(move || {
                                                                                                          ^~~~~~~~~~~~~
src/lib.rs:643:9: 643:22 help: run `rustc --explain E0277` to see a detailed explanation
src/lib.rs:643:9: 643:22 note: `*mut libc::c_void` cannot be sent between threads safely
src/lib.rs:643:9: 643:22 note: required because it appears within the type `notify::FsEventWatcher`
src/lib.rs:643:9: 643:22 note: required because it appears within the type `FsWatcher`

Unable to watch files on Ubuntu

On OSX I'm able to run a notifier for a specific file. When I run on Ubuntu/Linux I get an OS error telling me the path is not a directory (ENOTDIR - Err(Io(Error { repr: Os(20) }))).

Here's some code that demonstrates the problem:

extern crate notify;

use notify::{RecommendedWatcher, Error, Watcher};
use std::sync::mpsc::channel;
use std::path::Path;

fn main() {
           println!("Hello, world!");

           let path = "/tmp/foo.db";

          let (tx, rx) = channel();

          // Automatically select the best implementation for your platform.
          // You can also access each implementation directly e.g. INotifyWatcher.
          let w: Result<RecommendedWatcher, Error> = Watcher::new(tx);

          match w {
              Ok(mut watcher) => {

                  println!("created watcher!");

                  // Add a path to be watched. All files and directories at that path and
                  // below will be monitored for changes.
                  let x = watcher.watch(&Path::new(&path));

                  println!("watch result: {:?}", x);

                  // You'll probably want to do that in a loop. The type to match for is
                  // notify::Event, look at src/lib.rs for details.
                  loop {
                      match rx.recv() {
                          _ => {
                              println!("received and event!!!");
                          }
                      }
                  }
              },
              Err(e) => println!("failed to create watcher, error {:?}", e)
          }

}

Make backends slim and move common concerns to notify core

(Originally from #60.)

Generally, I think there backends should be a lot more low-level, and rsnotify should do a lot more itself than just selecting a backend and delegating everything to that.

  • Backends should implement a small, well-defined interface:
    • ::new or ::from, see #67
    • ::watch(&Path) -> Result
    • ::unwatch(&Path) -> Result
  • Notify itself should manage everything else about watches, as discussed above, to the general goal of:
    • Recursive watches implemented generically and unwatching properly
    • Optimising the watches
    • Marking paths/watches wanted by the user, so those aren't ::unwatched when we unwatch parent dirs (and vice versa)

bump inotify version constraint

inotify is now 0.1.11, so the current constraint continues to use an old version which no longer builds on the latest nightly.

Release 3.0.1

@passcod I think now would be a good time to make a new release. My last commit should take care of any misbehavior from fsevents.

Update to inotify 0.2

With 0.1, we build. With 0.2, we don't:

   Compiling notify v2.5.4 (file:///home/code/rust/notify)
src/inotify/mod.rs:76:17: 76:21 error: cannot move out of borrowed content [E0507]
src/inotify/mod.rs:76         let _ = self.inotify.close();
                                      ^~~~
error: aborting due to previous error
Could not compile `notify`.

fsevent support

Hi,

I'm the maintainer of a minimal but working fsevent binding for OSX. I guess we could try to integrate the two together, if this is possible :)

I think I should have a look at the inotify watcher in order to have something that blends in your library correctly. Let me know if there are some pitfalls I should be prepared to face... If you are interested in this, I can start digging :)

windows implementation

I know you already have this in your todo list, but I thought I would bring it to your attention that someone has provided the signatures for what I believe to be the necessary types and functions here, in case you ever get around to this.

Generic event delivery mechanism

I would like to propose a more flexible event delivery mechanism than the current mpsc::channel implementation.

Motivation

Currently, the only supported way to receive notify events is to use an mpsc::channel.

let (tx, rx) = mpsc::channel();
let watcher = RecommendedWatcher::new(tx);
match rx.recv() { /* ... /* }

If a user wants to deliver events with a different mechanism (eg mio::Sender<T>) or even use a different message type with mpsc::Sender<T>, an intermediate thread would be needed to proxy the events. This is an unnecessary level of overhead. The following solution should enable delivering events via any mechanism while being equivalent performance-wise with the current implementation.

Implementation

Public API

There are two changes to the public API. First, a Notify (name bike-shedding needed) trait is added.

/// Any type capable of receiving notify events
pub trait Notify : Send + Sync {
    /// An event has been generated, and this function is called to handle it.
    fn notify(&self, notify::Event);
}

Second, the Watcher trait must be modified to accept a Notify instead of an mpsc::Sender<Event>.

pub trait Watcher<N> {
    /// Create a new Watcher
    fn new(N) -> Result<Watcher<N>, Error>;
    /* other methods same as before */
}

For convenience (and ease upgrading from the previous version), a Notify implementation based on mpsc channels could be provided.

pub struct MpscChannelNotifier(pub mpsc::Sender<Event>);

impl Notify for MpscChannelNotifier {
    #[inline]
    fn notify(&self) {
        let _ = self.0.send(event);
    }
}

Upgrading from a previous version with MpscChannelNotifier would look like this:

let (tx, rx) = mpsc::channel();
// Just adds MpscChannelNotifier
let watcher = RecommendedWatcher::new(MpscChannelNotifier(tx));

Backends

Support for this feature varies by backend

FSEvent

With the current implementation, FsEventWatcher would need to hold the Notify in an Arc. The mpsc::Sender it holds currently is cloned on each call to watch when a new StreamContextInfo is created. Alternatively, StreamContextInfo could made Sync so that a reference could be held both by the watcher and the context info.

Windows

Similar story as FSEvent. The channel is cloned occasionally.

inotify

The INotifyHandler type would hold an Notify instead of the mpsc::channel. The couple of locations where self.tx.send is called would be replaced with self.notifier.notify

INotifyWatcher would need a PhantomData to satisfy the type param of Watcher.

Polling

The poll watcher currently clones the mpsc::Sender at startup, but it doesn't have a use for the sender on the calling thread. This could easily be converted to not use clone and be statically dispatched with a Notify. The type parameter need not be exposed on the Watcher type either.

Unanswered Questions

  • Should notify return a status? It may occur that a Notify becomes unusable (eg MpscChannelNotifier has the recv end hang up), and there would be no point in continuing the watch in that case.
  • Should Notify require Clone, or should it be wrapped in an Arc where needed?
  • Should Watcher actually have the type parameter for Notify? The Notify type could be boxed instead where needed, and cases like Poll could be statically dispatched from another thread. The type parameter would only be necessary for the Watcher::new method.

need Self: Sized (RFC 1214)

There is a warning (which will be eventually be an error) when compiling on nightly:

src/lib.rs:53:3: 53:48 warning: the trait `core::marker::Sized` is not implemented for the type `Self` [E0277]
src/lib.rs:53   fn new(Sender<Event>) -> Result<Self, Error>;
                ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/lib.rs:53:3: 53:48 help: run `rustc --explain E0277` to see a detailed explanation
src/lib.rs:53:3: 53:48 note: `Self` does not have a constant size known at compile-time
src/lib.rs:53   fn new(Sender<Event>) -> Result<Self, Error>;
                ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/lib.rs:53:3: 53:48 note: this warning results from recent bug fixes and clarifications; it will become a HARD ERROR in the next release. See RFC 1214 for details.
src/lib.rs:53   fn new(Sender<Event>) -> Result<Self, Error>;
                ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/lib.rs:53:3: 53:48 note: required by `core::result::Result`
src/lib.rs:53   fn new(Sender<Event>) -> Result<Self, Error>;
                ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

INotifyWatcher::watch returns an error if any subdirectories contain a broken link.

This came up on my project: maurizi/retag.rs#23

From what I can tell, because follow_links(true) is called here, WalkDir returns the target path of symlinks, even if they are broken links. Then when we pass the path over to inotify, it correctly returns an error.

@passcod I can think of a couple of ways to fix this.

  • Each item returned by WalkDir could be tested to see that it exists before calling add_watch
  • Ignore some errors (IO errors like file does not exist) returned by add_watch when calling it from add_watch_recursively
  • Ignore all errors returned by add_watch when calling it from add_watch_recursively

What do you think?

FsEventsWatcher doesn't seem to work on OS X 10.10.5

I tried the example in the README.md file on current Rust (1.6.0) on my Mac (OS X version 10.10.5) and it doesn't seem to work. I tried using both the RecommendedWatcher and FsEventWatcher to watch /var/log/system.log. I tailed the file at the same time. I could see writes coming in on the tail, but I wasn't receiving any events from my watcher.

Edit: PollWatcher is working.

Newly created folders are not watched recursively on Ubuntu

When I run an example code from the doc, rsnotify detects creation of a new subfolder of the watched main folder, however when a file is added to/removed from a newly created subfolder, rsnotify does not generate any events.

I am running this on Ubuntu 16.04 VM inside VMware Workstation. It is ext4 file system.

Unreachable error with git repository (chmod event)

Trying some events with git actions, I found some strange behaviour.

I am on OSX Sierra beta 10.12.2 (16C41b)

To reproduce this:

$> # listen to this directory
$> RUST_BACKTRACE=1 ./target/release/examples/monitor_debounced /tmp/hello
$>
$> mkdir /tmp/hello
$> cd /tmp/hello
$> touch hello
$> git init
$> RUST_BACKTRACE=1 ./target/release/examples/monitor_debounced /tmp/hello
watching /tmp/hello
Create("/private/tmp/hello/hello")
Some(CREATE) # that's my println debug at line 117
thread '<unnamed>' panicked at 'internal error: entered unreachable code', src/debounce/mod.rs:118
stack backtrace:
   1:        0x1003baada - std::sys::imp::backtrace::tracing::imp::write::he3d1bfbdbf113480
   2:        0x1003bde6f - std::panicking::default_hook::{{closure}}::h575f1b40d2e88f07
   3:        0x1003bcb5f - std::panicking::default_hook::h3d5dccce8125d0cf
   4:        0x1003bd196 - std::panicking::rust_panic_with_hook::h00b81bb3dcbd51f2
   5:        0x10034bb63 - std::panicking::begin_panic::h70ee11343cda1bc2
   6:        0x1003a94d8 - notify::debounce::Debounce::check_partial_rename::hcbf233e36f356a5c
   7:        0x1003a9e62 - notify::debounce::Debounce::event::h1601b8a062e67e75
   8:        0x1003a8172 - notify::debounce::EventTx::send::h9136acde8e818b2e
   9:        0x1003a33d1 - notify::fsevent::send_pending_rename_event::h8813d6188d6441e8
  10:        0x1003a5169 - notify::fsevent::callback::h843bd243c277573c
  11:     0x7fffa703db02 - implementation_callback_rpc
  12:     0x7fffa703b70d - _Xcallback_rpc
  13:     0x7fffa703b7cb - FSEventsD2F_server
  14:     0x7fffa7040fec - FSEventsClientProcessMessageCallback
  15:     0x7fffa5e517dc - __CFMachPortPerform
  16:     0x7fffa5e516c8 - __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__
  17:     0x7fffa5e51640 - __CFRunLoopDoSource1
  18:     0x7fffa5e49524 - __CFRunLoopRun
  19:     0x7fffa5e48973 - CFRunLoopRunSpecific
  20:     0x7fffa5e884f0 - CFRunLoopRun
  21:        0x1003af6c8 - notify::fsevent::FsEventWatcher::run::{{closure}}::hf9485ba872fe490f
  22:        0x1003a1cba - <std::panic::AssertUnwindSafe<F> as core::ops::FnOnce<()>>::call_once::h0986629d799f6b84
  23:        0x10034c4c3 - std::panicking::try::do_call::h075cdc0f1c38c144
  24:        0x1003be42a - __rust_maybe_catch_panic
  25:        0x10034c0f2 - std::panicking::try::h83b082fadfa7802d
  26:        0x10034a16e - std::panic::catch_unwind::h4efcd890cc4b3a3b
  27:        0x1003aeea2 - std::thread::Builder::spawn::{{closure}}::he27652165ddf436f
  28:        0x10037c783 - <F as alloc::boxed::FnBox<A>>::call_box::h95de920981a48031
  29:        0x1003bc5a4 - std::sys::imp::thread::Thread::new::thread_start::h990fb082eb5abe34
  30:     0x7fffbb7c6aaa - _pthread_body
  31:     0x7fffbb7c69f6 - _pthread_start

Licensing

IANAL, but I think some countries don't recognize "public domain". Have you considered dual licensing under a standard open source license such as Apache v2 or MIT?

try_recv'ing panics

I'm trying to receive events from the watcher using try_recv but it panics.

thread '<unnamed>' panicked at 'Failed to convert C string into Rust string: invalid utf-8: invalid byte near index 1', /home/travis/.cargo/registry/src/github.com-0a35038f75765ae4/inotify-0.1.12/src/wrapper.rs:168

I'm using try_recv because I don't want to block my main thread if the channel is empty.

Am I doing concurrency wrong? Any viable workarounds until a fix comes?

Support watching single files

Currently the FSEvents backend supports watching single files, the inotify backend doesn't.

Go's fsnotify, Ruby's Listen, Facebook's Watchman and others also do not support watching single files.

Phase 0 of this feature is research:

  • What is the reason behind lack of support in others?
  • What are libraries that do have support?
  • What are challenges for an implementation?
  • Is this doable in a backend-independent manner?

If that last point can be answered by yes, another question is whether this behaviour would be better done in a separate package.

4 events on file change

I get 4 times the same event on a file change.

Event: Event { path: Some("/home/azerupi/Programming/Rust/mdBook/book-example/src/SUMMARY.md"), op: Ok(WRITE) }
File changed: "/home/azerupi/Programming/Rust/mdBook/book-example/src/SUMMARY.md"

Event: Event { path: Some("/home/azerupi/Programming/Rust/mdBook/book-example/src/SUMMARY.md"), op: Ok(WRITE) }
File changed: "/home/azerupi/Programming/Rust/mdBook/book-example/src/SUMMARY.md"

Event: Event { path: Some("/home/azerupi/Programming/Rust/mdBook/book-example/src/SUMMARY.md"), op: Ok(WRITE) }
File changed: "/home/azerupi/Programming/Rust/mdBook/book-example/src/SUMMARY.md"

Event: Event { path: Some("/home/azerupi/Programming/Rust/mdBook/book-example/src/SUMMARY.md"), op: Ok(WRITE) }
File changed: "/home/azerupi/Programming/Rust/mdBook/book-example/src/SUMMARY.md"

I am on Arch Linux, using v2.3.2 of notify

EDIT: At the moment, the workaround i found was to return and call the watching function recursively

fn watch(args: &ArgMatches) -> Result<(), Box<Error>> {
    // Create a channel to receive the events.
     let (tx, rx) = channel();
     let w: Result<notify::RecommendedWatcher, notify::Error> = notify::Watcher::new(tx);

     match w {
         Ok(mut watcher) => {
             watcher.watch("path").unwrap();

             loop {
                 match rx.recv() {
                     Ok(event) => {
                         if let Some(path) = event.path {
                             println!("File changed: {:?}\n", path);
                             // Do things...
                             // Hack to prevent receiving the event 4 times
                             return watch(args); // <---------------------------------
                         } else {
                             continue;
                         }
                     },
                     Err(e) => { println!("An error occured: {:?}", e); }
                 }
             }
         },
         Err(e) => { ::std::process::exit(0); }
     }
    Ok(())
}

Strange paths in events on windows

I'm presently relying on the version of rsnotify direct from github, since the version on crates.io doesn't seem to have any way of tracking renames, or if it does, it isn't documented.

However, when watching a directory (in my case C:\Users\Daniel\dir1), the create notification (and maybe others too, but it crashes before I can check them) has a path with a whole bunch of leading slashes: \\?\C:\Users\Daniel\dir1\New Text Document.txt I imagine this has something to do with partitions or volumes or some such, but it makes it pretty tricky to access the file.

i686-apple-darwin not supported?

I noticed that i686-apple-darwin it's missing the fsevent{,-sys} dependencies in the Cargo.toml. Is this target not supported for technical reasons or simply not tested? If the problem is testing I can help to extend the test matrix to cover all the tier-1 unixy platforms like rust-everywhere does.

std::error::Error implementation for notify::Error

Consider implementing std::error::Error trait for notify::Error because it helps with interoperability with other code. Currently I'm unable to use the error with {} pattern when formatting a string, for example.

Filenames without path not handled on Windows

Supplying watch() with just a filename and no path will result in a "parent directory could not be opened" error.

This is because Path.parent() doesn't return the actual path to the parent directory, but rather the original path minus the last entry, resulting in an empty string when given just a filename.

Proposed changes to event translation

I have collected a list of all events I found relevant for rsnotify on Linux, Windows and OS X. The list contains the native events and the rsnotify events they get translated to. As you can see some differ between platforms. So I propose some changes:

  • On OS X ITEM_CHANGE_OWNER is fired when changing permissions, rsnotify should watch for it and report it as CHMOD (like on Linux)
  • When a file is moved within a monitored directory, the inotify back-end currently reports a RENAME and a CREATE event. Windows and OS X report two RENAME events. Although that could be changed on Windows, it would be hard to do that on OS X. So I propose to change the CREATE event on linux to a RENAME event.
  • It is currently not possible to properly detect the old and the new file name of a rename. Inotify provides a cookie and I think that cookie should be sent to the user. On Windows a rsnotify could create a cookie if two rename events are successive and in the same event group. There should be no confusion with only getting parts of rename events like on Linux because Windows reports FILE_ACTION_REMOVED when files are moved out of the monitored directory and FILE_ACTION_ADDED when they are moved in. So moving a file out and another in can't be mistaken as one rename. With FSEvent this is not as simple, more below.
  • If a file is moved out of the monitored directory on Linux, currently a RENAME event is reported. This should be changed to a REMOVE event, like on Windows.
  • Currently the Inotify back-end reports IGNORE events. These don't seem to fit the design goals of rsnotify and there are no comparable events for Windows and OS X. So I propose to remove them.
  • Inotify and FSEvent can report problems via IN_Q_OVERFLOW and MUST_SCAN_SUBDIRS. rsnotify should forward them so the API user knows they have to rescan the watched directories. Edit: Windows can report a ERROR_NOTIFY_ENUM_DIR error when it's buffer overflows.

Remaining problems:

  • FSEvent only emits ITEM_RENAMED whenever a file gets renamed without further information. It also lumps events together so using some smart logic to reconstruct what happened is hard (or impossible?). Eg. for a file that is created and then renamed two times FSEvent will emit 3 events (create | rename, rename, rename). In order to get the old and the new file name of a rename, it is possible to check if the event ids are consecutive. In the example above this would work on the second rename, but not on the first because the event ids are not consecutive. Simply using two successive rename events doesn't work either because a file could be moved out of the monitored directory and another one in. So this is something that should be documented and be left to the API user.
  • Changing file attributes (like setting readonly) on Windows fires a FILE_ACTION_MODIFIED event. This cannot be distinguished from writing to a file. This should be documented.
  • Renaming a watched directory creates a RENAME event on Linux and OS X, but not on Windows. This should be documented.
  • Removing a watched directory creates a REMOVE event on Linux and OS X, but not on Windows. This should be documented. On windows an empty event is generated and if the monitored directory is moved to the trash, the monitor example gets into an infinite loop, reporting empty events. At least on my virtual machine.
  • Linux and Windows monitor "inodes", OS X monitors "paths". So the behavior when renaming the watched file or directory differs. This should be documented.
event platform native event current proposed comment
create linux IN_CREATE create create
windows FILE_ACTION_ADDED create create
osx ITEM_CREATED create create
remove linux IN_DELETE remove remove
windows FILE_ACTION_REMOVED remove remove
osx ITEM_REMOVED remove remove
write linux IN_MODIFY write write
windows FILE_ACTION_MODIFIED write write
osx ITEM_MODIFIED write write
chmod linux IN_ATTRIB chmod chmod
windows FILE_ACTION_MODIFIED write write eg. readonly flag
osx ITEM_CHANGE_OWNER - chmod
xattr linux IN_ATTRIB chmod chmod
windows - - -
osx ITEM_XATTR_MOD chmod chmod
rename from linux IN_MOVED_FROM rename rename has cookie
windows ..._RENAMED_OLD_NAME rename rename add cookie
osx ITEM_RENAMED rename rename add cookie when possible
rename to linux IN_MOVED_TO create rename has cookie
windows ..._RENAMED_NEW_NAME rename rename add cookie
osx ITEM_RENAMED rename rename add cookie when possible
rename in linux IN_MOVED_TO create create
windows FILE_ACTION_ADDED create create
osx ITEM_RENAMED rename rename must be handled by API user
rename out linux IN_MOVED_FROM rename remove
windows FILE_ACTION_REMOVED remove remove
osx ITEM_RENAMED rename rename must be handled by API user
remove self directory linux IN_DELETE_SELF remove remove
windows ? infinite loop on move-to-trash
osx ITEM_REMOVED remove remove
remove self file linux IN_DELETE_SELF remove remove
windows FILE_ACTION_REMOVED remove remove
osx ITEM_REMOVED remove remove
rename self directory linux IN_MOVE_SELF rename rename continues monitoring directory
windows - - - continues monitoring directory
osx ITEM_RENAMED rename rename stops monitoring directory, but continues monitoring path
rename self file linux IN_MOVE_SELF rename rename continues monitoring file
windows ..._RENAMED_OLD_NAME rename rename stops monitoring file, but continues monitoring path
osx ITEM_RENAMED rename rename stops monitoring file, but continues monitoring path
ignore linux IN_IGNORED ignore -
windows - - -
osx - - -
overflow linux IN_Q_OVERFLOW - overflow
windows ERROR_NOTIFY_ENUM_DIR - overflow
osx MUST_SCAN_SUBDIRS - overflow

Please let me know what you think.

Editing a file twice in VIM on OSX causes a panic

On OSX, When watching a Directory using the standard code in the readme, if you edit a file in VIM twice, you will receive a panic:

Create("/Users/cetra/Desktop/test/a_file")
NoticeRemove("/Users/cetra/Desktop/test/4913")
Remove("/Users/cetra/Desktop/test/4913")
Create("/Users/cetra/Desktop/test/a_file")
thread '<unnamed>' panicked at 'rename_path is set but not present in operations_buffer 1', ../src/libcore/option.rs:705
stack backtrace:
   1:        0x10192bc08 - std::sys::backtrace::tracing::imp::write::h6f1d53a70916b90d
   2:        0x10192eb0f - std::panicking::default_hook::{{closure}}::h137e876f7d3b5850
   3:        0x10192da85 - std::panicking::default_hook::h0ac3811ec7cee78c
   4:        0x10192e096 - std::panicking::rust_panic_with_hook::hc303199e04562edf
   5:        0x10192df34 - std::panicking::begin_panic::h6ed03353807cf54d
   6:        0x10192de52 - std::panicking::begin_panic_fmt::hc321cece241bb2f5
   7:        0x10192ddb7 - rust_begin_unwind
   8:        0x101959620 - core::panicking::panic_fmt::h27224b181f9f037f
   9:        0x10195968d - core::option::expect_failed::h8606bc228cd3f504
  10:        0x10174ad03 - <core::option::Option<T>>::expect::hf5b09477b4671fcd
  11:        0x1017b3e2e - notify::debounce::Debounce::check_partial_rename::h21ee7ede26b20766
  12:        0x1017b49e2 - notify::debounce::Debounce::event::h2419ccbe62b5ab92
  13:        0x1017b2e86 - notify::debounce::EventTx::send::h152c2dceaf42a036
  14:        0x1017afe83 - notify::fsevent::callback::he2e1ef2d83315289
  15:     0x7fff90726b0a - implementation_callback_rpc
  16:     0x7fff907246c9 - _Xcallback_rpc
  17:     0x7fff9072479d - FSEventsD2F_server
  18:     0x7fff90729fd8 - FSEventsClientProcessMessageCallback
  19:     0x7fff946bb12b - __CFMachPortPerform
  20:     0x7fff946bb018 - __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__
  21:     0x7fff946baf88 - __CFRunLoopDoSource1
  22:     0x7fff946b29ba - __CFRunLoopRun
  23:     0x7fff946b1ed7 - CFRunLoopRunSpecific
  24:     0x7fff946f39b0 - CFRunLoopRun
  25:        0x1017b99a8 - notify::fsevent::FsEventWatcher::run::{{closure}}::h4f46b41cc2f19b31
  26:        0x1017aca8a - <std::panic::AssertUnwindSafe<F> as core::ops::FnOnce<()>>::call_once::hcf84c8244c5eae82
  27:        0x101756d23 - std::panicking::try::do_call::h48d766d41966c5a9
  28:        0x10192f0ca - __rust_maybe_catch_panic
  29:        0x101756952 - std::panicking::try::hbe278a19a1197288
  30:        0x1017548be - std::panic::catch_unwind::h03c775351675d8db
  31:        0x1017b900a - std::thread::Builder::spawn::{{closure}}::ha08cc323ee8b26c3
  32:        0x101786d53 - <F as alloc::boxed::FnBox<A>>::call_box::hf5e4e39f9a88e223
  33:        0x10192d254 - std::sys::thread::Thread::new::thread_start::h759e10bc4abc7e72
  34:     0x7fff9d95399c - _pthread_body
  35:     0x7fff9d953919 - _pthread_start

Tests hang if no events are received

I was doing some debugging of the tests trying to figure out why a CREATE event was delivered even though a file should have existed before the watch. My debugging uncovered a deadlock in watch when a sleep_ms is performed before it. Something about the CFRunLoop code I imagine but I couldn't track it down.

This causes the deadlock:

diff --git a/tests/notify.rs b/tests/notify.rs
index 7fb5bb9..73fb23f 100644
--- a/tests/notify.rs
+++ b/tests/notify.rs
@@ -25,6 +25,7 @@ fn validate_watch_single_file<F, W>(ctor: F) where
   let mut file = NamedTempFile::new().unwrap();
   let (tx, rx) = channel();
   let mut w = ctor(tx).unwrap();
+  thread::sleep_ms(1000);
   w.watch(file.path()).unwrap();
   thread::sleep_ms(1000);
   file.write_all(b"foo").unwrap();

internal error: entered unreachable code

Hello there, I'm using OSX El Captain and some times when files are created and removed really fast the code reaches this unreachable!() . It doean't happens all the time but an easy way is to monitor a Node.js project while rm -rf ./node_modules && npm install, you will easly receive +30K events and the error almost always happens.

This is my environment:

cargo 0.15.0-nightly (a9c23dd 2016-11-15)
rustc 1.15.0-nightly (43006fcea 2016-11-15)

The stack trace is this:

thread '<unnamed>' panicked at 'internal error: entered unreachable code', /Users/alanhoff/.cargo/git/checkouts/notify-04e06d4ee73f48be/6f58cce7be12c9b5f92404765241541424c6e741/src/debounce/mod.rs:151
stack backtrace:
   1:        0x10b1f17aa - std::sys::imp::backtrace::tracing::imp::write::h944c02ac40aee2d7
   2:        0x10b1f3b6f - std::panicking::default_hook::{{closure}}::h6875a2976258b020
   3:        0x10b1f3817 - std::panicking::default_hook::h88ffbc5922643264
   4:        0x10b1f4036 - std::panicking::rust_panic_with_hook::ha5aed1dfc0e220e3
   5:        0x10b1841b3 - std::panicking::begin_panic::h5f0a1fd8f4de57ec
   6:        0x10b1e2f84 - notify::debounce::Debounce::check_partial_rename::h5471efb435c2dda1
   7:        0x10b1e3742 - notify::debounce::Debounce::event::h5ac2f781e05a9a46
   8:        0x10b1e1c12 - notify::debounce::EventTx::send::h1850375a0ba61046
   9:        0x10b1de360 - notify::fsevent::callback::h8dc799b35ccea274
  10:     0x7fff870b2b0a - implementation_callback_rpc
  11:     0x7fff870b06c9 - _Xcallback_rpc
  12:     0x7fff870b079d - FSEventsD2F_server
  13:     0x7fff870b5fd8 - FSEventsClientProcessMessageCallback
  14:     0x7fff86cca12b - __CFMachPortPerform
  15:     0x7fff86cca018 - __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__
  16:     0x7fff86cc9f88 - __CFRunLoopDoSource1
  17:     0x7fff86cc19ba - __CFRunLoopRun
  18:     0x7fff86cc0ed7 - CFRunLoopRunSpecific
  19:     0x7fff86d029b0 - CFRunLoopRun
  20:        0x10b1dd688 - notify::fsevent::FsEventWatcher::run::{{closure}}::h1f26a1146180fe54
  21:        0x10b1db4da - <std::panic::AssertUnwindSafe<F> as core::ops::FnOnce<()>>::call_once::h2a906c890c327ae8
  22:        0x10b184d03 - std::panicking::try::do_call::he6db71e4883187fb
  23:        0x10b1f4e8a - __rust_maybe_catch_panic
  24:        0x10b184332 - std::panicking::try::h5e80a8d4bba11476
  25:        0x10b18203e - std::panic::catch_unwind::hd00cde07760ce1f8
  26:        0x10b183ee2 - std::thread::Builder::spawn::{{closure}}::ha40d83fe6940f3de
  27:        0x10b1b5513 - <F as alloc::boxed::FnBox<A>>::call_box::h7dc0418dee0ac1eb
  28:        0x10b1f3264 - std::sys::imp::thread::Thread::new::thread_start::h8084b1107992ae5b
  29:     0x7fff909d999c - _pthread_body
  30:     0x7fff909d9919 - _pthread_start

The code that I used is this one:

extern crate notify;

use std::env;
use notify::{RecommendedWatcher, Watcher, RecursiveMode};
use std::sync::mpsc::{channel};
use std::time::Duration;
use std::thread;

fn watch() -> notify::Result<()> {
    let (tx, rx) = channel();
    let mut watcher: RecommendedWatcher = try!(Watcher::new(tx, Duration::from_secs(2)));
    let current_dir = env::current_dir().unwrap();

    try!(watcher.watch(current_dir.clone(), RecursiveMode::Recursive));

    loop {
        match rx.try_recv() {
            Ok(event) => {
                println!("{:?}", event);
            },
            _ => thread::sleep(Duration::from_millis(10))
        };
    }
}

fn main() {
    if let Err(e) = watch() {
        println!("error: {:?}", e)
    }
}

And here is my Cargo.lock

[root]
name = "rust-notify"
version = "0.1.0"
dependencies = [
 "notify 3.0.1 (git+https://github.com/passcod/notify)",
]

[[package]]
name = "bitflags"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"

[[package]]
name = "bitflags"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"

[[package]]
name = "bytes"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"

[[package]]
name = "cfg-if"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"

[[package]]
name = "filetime"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
 "libc 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)",
]

[[package]]
name = "fsevent"
version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
 "bitflags 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
 "fsevent-sys 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
 "libc 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)",
]

[[package]]
name = "fsevent-sys"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
 "libc 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)",
]

[[package]]
name = "inotify"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
 "libc 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)",
]

[[package]]
name = "kernel32-sys"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
 "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
 "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
]

[[package]]
name = "libc"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"

[[package]]
name = "log"
version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"

[[package]]
name = "mio"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
 "bytes 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
 "libc 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)",
 "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
 "miow 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
 "net2 0.2.26 (registry+https://github.com/rust-lang/crates.io-index)",
 "nix 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
 "slab 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
 "time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)",
 "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
]

[[package]]
name = "miow"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
 "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
 "net2 0.2.26 (registry+https://github.com/rust-lang/crates.io-index)",
 "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
 "ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
]

[[package]]
name = "net2"
version = "0.2.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
 "cfg-if 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
 "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
 "libc 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)",
 "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
 "ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
]

[[package]]
name = "nix"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
 "bitflags 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
 "libc 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)",
]

[[package]]
name = "notify"
version = "3.0.1"
source = "git+https://github.com/passcod/notify#6f58cce7be12c9b5f92404765241541424c6e741"
dependencies = [
 "bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
 "filetime 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
 "fsevent 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)",
 "fsevent-sys 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
 "inotify 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
 "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
 "libc 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)",
 "mio 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
 "time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)",
 "walkdir 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
 "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
]

[[package]]
name = "slab"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"

[[package]]
name = "time"
version = "0.1.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
 "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
 "libc 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)",
 "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
]

[[package]]
name = "walkdir"
version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
 "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
 "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
]

[[package]]
name = "winapi"
version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"

[[package]]
name = "winapi-build"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"

[[package]]
name = "ws2_32-sys"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
 "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
 "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
]

[metadata]
"checksum bitflags 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8dead7461c1127cf637931a1e50934eb6eee8bff2f74433ac7909e9afcee04a3"
"checksum bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "aad18937a628ec6abcd26d1489012cc0e18c21798210f491af69ded9b881106d"
"checksum bytes 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c129aff112dcc562970abb69e2508b40850dd24c274761bb50fb8a0067ba6c27"
"checksum cfg-if 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "de1e760d7b6535af4241fca8bd8adf68e2e7edacc6b29f5d399050c5e48cf88c"
"checksum filetime 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "5363ab8e4139b8568a6237db5248646e5a8a2f89bd5ccb02092182b11fd3e922"
"checksum fsevent 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)" = "740a52ca589381d87dd0d9960555de3320aa6d408326659e3bae88be9f71a125"
"checksum fsevent-sys 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "72e33a926306442d961595c3a325864326ca4287795e106dae8993afe484ede6"
"checksum inotify 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "e8458c07bdbdaf309c80e2c3304d14c3db64e7465d4f07cf589ccb83fd0ff31a"
"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
"checksum libc 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)" = "044d1360593a78f5c8e5e710beccdc24ab71d1f01bc19a29bcacdba22e8475d8"
"checksum log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "ab83497bf8bf4ed2a74259c1c802351fcd67a65baa86394b6ba73c36f4838054"
"checksum mio 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a637d1ca14eacae06296a008fa7ad955347e34efcb5891cfd8ba05491a37907e"
"checksum miow 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "d5bfc6782530ac8ace97af10a540054a37126b63b0702ddaaa243b73b5745b9a"
"checksum net2 0.2.26 (registry+https://github.com/rust-lang/crates.io-index)" = "5edf9cb6be97212423aed9413dd4729d62b370b5e1c571750e882cebbbc1e3e2"
"checksum nix 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "bfb3ddedaa14746434a02041940495bf11325c22f6d36125d3bdd56090d50a79"
"checksum notify 3.0.1 (git+https://github.com/passcod/notify)" = "<none>"
"checksum slab 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "d807fd58c4181bbabed77cb3b891ba9748241a552bcc5be698faaebefc54f46e"
"checksum time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)" = "3c7ec6d62a20df54e07ab3b78b9a3932972f4b7981de295563686849eb3989af"
"checksum walkdir 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "c66c0b9792f0a765345452775f3adbd28dde9d33f30d13e5dcc5ae17cf6f3780"
"checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a"
"checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc"
"checksum ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e"

Breaks: minimize public API

There's a few things that probably shouldn't be marked pub:

  • Internal module layout. The watcher types (eg FSEventWatcher) are already reexported, and there's nothing else in the modules which a consumer cares about (or should have access to, really). Specifically, these modules should have pub removed, and the reexports should be favored:
    • fsevent
    • null
    • op
    • poll
  • fsevent::FsEventWatcher::run
  • fsevent::FsEventWatcher::stop
  • fsevent::FsEventWatcher::is_running
  • fsevent::callback

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.