Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PollWatcher stops working when initialized from a function? #482

Closed
ShayBox opened this issue May 13, 2023 · 4 comments
Closed

PollWatcher stops working when initialized from a function? #482

ShayBox opened this issue May 13, 2023 · 4 comments

Comments

@ShayBox
Copy link

ShayBox commented May 13, 2023

System details

  • OS/Platform name and version: Windows 11 22H2 (22621.1702)
  • Rust version (if building from source): rustc 1.69.0 (84c898d65 2023-04-16)
  • Notify version (or commit hash if building from git): 5.1.0

What you did (as detailed as you can)

The example provided on docs.rs/notify doesn't include anything to keep the process running, this is a non-functional example.

use notify::{Watcher, RecommendedWatcher, RecursiveMode, Result};

fn main() -> Result<()> {
    // Automatically select the best implementation for your platform.
    let mut watcher = notify::recommended_watcher(|res| {
        match res {
           Ok(event) => println!("event: {:?}", event),
           Err(e) => println!("watch error: {:?}", e),
        }
    })?;

    // Add a path to be watched. All files and directories at that path and
    // below will be monitored for changes.
    watcher.watch(Path::new("."), RecursiveMode::Recursive)?;

    Ok(())
}

This is a working minimal example I was able to use to reproduce my issue
I added tokio, disabled default-features to use std instead of crossbeam
I switched to PollWatcher because my use-case doesn't emit events
I use tokio::time::sleep because you can't use std::thread::sleep
That took me forever to figure out from a random GitHub issue

use std::{path::Path, time::Duration};

use notify::{Config as NotifyConfig, PollWatcher, RecursiveMode, Watcher};

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    // Config should be named less generic to avoid collision
    // Fields shouldn't be private so they can be initialized
    let notify_config = NotifyConfig::default()
        .with_compare_contents(true)
        .with_poll_interval(Duration::from_secs(5));

    let mut watcher = PollWatcher::new(
        |res| match res {
            Ok(event) => println!("event: {:?}", event),
            Err(e) => println!("watch error: {:?}", e),
        },
        notify_config,
    )?;

    watcher.watch(Path::new("."), RecursiveMode::Recursive)?;

    loop {
        tokio::time::sleep(Duration::from_secs(1)).await;
    }
}

EDIT: Description of my program, not the example:
This works perfectly if the directory starts empty and grows into a large dataset (> 50 GiB), but if it's started with a large dataset it will work for a few seconds and then stop emitting events.

I can't share what my program does or what the dataset is, but it consists of many subdirectories (around 5 deep) with a hand full of files per directory, under 200 MiB each but in total upwards of 50 GiB.

Insight

I may have figured out what's causing it before submitting the issue: for some reason, if I move all the code into a new function and call it, it stops working...

use std::{path::Path, time::Duration};

use notify::{Config as NotifyConfig, PollWatcher, RecursiveMode, Watcher};

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    test()?;

    loop {
        tokio::time::sleep(Duration::from_secs(1)).await;
    }
}

fn test() -> anyhow::Result<()> {
    // Config should be named less generic to avoid collision
    // Fields shouldn't be private so they can be initialized
    let notify_config = NotifyConfig::default()
        .with_compare_contents(true)
        .with_poll_interval(Duration::from_secs(5));

    let mut watcher = PollWatcher::new(
        |res| match res {
            Ok(event) => println!("event: {:?}", event),
            Err(e) => println!("watch error: {:?}", e),
        },
        notify_config,
    )?;

    watcher.watch(Path::new("."), RecursiveMode::Recursive)?;
}
@ShayBox
Copy link
Author

ShayBox commented May 13, 2023

I don't know why the combination of my dataset and the code being in a function causes this, if the directory starts empty or the code is in the main function it works fine... someone please explain my brain hurts

@0xpr03
Copy link
Member

0xpr03 commented May 13, 2023

value of variable watcher is a guard, dropping (function end) it stops the watcher

@ShayBox
Copy link
Author

ShayBox commented May 13, 2023

That's good to know, I've never messed with guards before, quiet (not logged) actions when variables go out of scope are fun

@0xpr03
Copy link
Member

0xpr03 commented May 14, 2023

This for example would work, by just returning the watcher from the function:

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    let watcher = test()?; // keep the watcher

    loop {
        tokio::time::sleep(Duration::from_secs(1)).await;
    }
    // watcher dropped here, stopping it
}

fn test() -> anyhow::Result<PollWatcher> {
    // Config should be named less generic to avoid collision
    // Fields shouldn't be private so they can be initialized
    let notify_config = NotifyConfig::default()
        .with_compare_contents(true)
        .with_poll_interval(Duration::from_secs(5));

    let mut watcher = PollWatcher::new(
        |res| match res {
            Ok(event) => println!("event: {:?}", event),
            Err(e) => println!("watch error: {:?}", e),
        },
        notify_config,
    )?;

    watcher.watch(Path::new("."), RecursiveMode::Recursive)?;
    watcher
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants