Skip to content

Commit

Permalink
Resource modification watch
Browse files Browse the repository at this point in the history
  • Loading branch information
Nercury committed Dec 2, 2018
1 parent 6515954 commit 3c5bffa
Show file tree
Hide file tree
Showing 6 changed files with 354 additions and 193 deletions.
1 change: 0 additions & 1 deletion lesson-25-x-terrain/core/shaders/quad.frag
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,5 @@ out vec4 Color;

void main()
{

Color = IN.Color;
}
6 changes: 3 additions & 3 deletions lesson-25-x-terrain/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ fn run() -> Result<(), failure::Error> {
"core",
0,
FileSystem::from_rel_path(env!("CARGO_MANIFEST_DIR"), "core")
.with_write(),
.with_write()
.with_watch(),
);

let res = resources.resource("shaders/quad.frag");
Expand All @@ -31,8 +32,7 @@ fn run() -> Result<(), failure::Error> {
resources.notify_changes_synced(p);
}

let mut v = String::new();
::std::io::stdin().read_line(&mut v).unwrap();
::std::thread::sleep_ms(500);
}

Ok(())
Expand Down
113 changes: 101 additions & 12 deletions lib/resources/src/backend/filesystem.rs
Original file line number Diff line number Diff line change
@@ -1,48 +1,129 @@
use crate::backend::{Backend, BackendSyncPoint};
use crate::backend::{Backend, BackendSyncPoint, Modification};
use std::path::{Path, PathBuf};
use std::{fs, io};
use std::collections::VecDeque;
use crate::{Error, ResourcePath};
use std::sync::Mutex;

#[cfg(feature = "backend_filesystem_watch")]
mod watch_impl {
use std::collections::VecDeque;
use std::path::{Path, PathBuf};
use std::sync::mpsc::{channel, Receiver};
use std::time::Duration;
use std::sync::mpsc::{channel, Receiver, TryRecvError};
use std::time::{Duration, Instant};
use notify::{RecommendedWatcher, Watcher as NotifyWatcher, RecursiveMode, DebouncedEvent};
use crate::backend::{BackendSyncPoint, Modification};
use crate::{ResourcePathBuf};

pub struct Watcher {
root_path: PathBuf,
watcher: RecommendedWatcher,
_watcher: RecommendedWatcher,
receiver: Receiver<DebouncedEvent>,
outdated_at: Option<Instant>,
}

impl Watcher {
pub fn new(root_path: &Path) -> Option<Watcher> {
let (tx, rx) = channel();

let mut watcher: RecommendedWatcher = NotifyWatcher::new(tx, Duration::from_secs(2))
.map_err(|e| error!("faled to create watcher for {:?}", root_path))
let mut watcher: RecommendedWatcher = NotifyWatcher::new(tx, Duration::from_millis(50))
.map_err(|e| error!("failed to create watcher for {:?}, {:?}", root_path, e))
.ok()?;
watcher.watch(root_path, RecursiveMode::Recursive).ok()?;

Some(Watcher {
root_path: root_path.into(),
watcher,
_watcher: watcher,
receiver: rx,
outdated_at: None,
})
}

pub fn notify_changes_synced(&mut self, point: BackendSyncPoint) {
if let Some(last_outdated) = self.outdated_at {
if point.instant == last_outdated {
self.outdated_at = None;
}
}
}

pub fn new_changes(&mut self, queue: &mut VecDeque<Modification>) -> Option<BackendSyncPoint> {
let mut something_outdated = false;

loop {
match self.receiver.try_recv() {
Ok(event) => {
match event {
DebouncedEvent::Create(path) => {
if let Some(resource_path) = ResourcePathBuf::from_filesystem_path(&self.root_path, &path) {
queue.push_back(Modification::Create(resource_path));
something_outdated = true;
} else {
warn!("unrecognised resource path {:?} for {} event", path, "Create")
}
},
DebouncedEvent::Write(path) => {
if let Some(resource_path) = ResourcePathBuf::from_filesystem_path(&self.root_path, &path) {
queue.push_back(Modification::Write(resource_path));
something_outdated = true;
} else {
warn!("unrecognised resource path {:?} for {} event", path, "Write")
}
},
DebouncedEvent::Remove(path) => {
if let Some(resource_path) = ResourcePathBuf::from_filesystem_path(&self.root_path, &path) {
queue.push_back(Modification::Remove(resource_path));
something_outdated = true;
} else {
warn!("unrecognised resource path {:?} for {} event", path, "Remove")
}
},
DebouncedEvent::Rename(from_path, to_path) => {
match (ResourcePathBuf::from_filesystem_path(&self.root_path, &from_path), ResourcePathBuf::from_filesystem_path(&self.root_path, &to_path)) {
(Some(from), Some(to)) => {
queue.push_back(Modification::Rename { from, to });
something_outdated = true;
},
(None, Some(_)) => warn!("unrecognised resource path {:?} for {} event", from_path, "Rename"),
(Some(_), None) => warn!("unrecognised resource path {:?} for {} event", to_path, "Rename"),
(None, None) => warn!("unrecognised resource paths {:?} and {:?} for Rename event", from_path, to_path),
}
},
_ => (),
}
},
Err(TryRecvError::Empty) => break,
Err(TryRecvError::Disconnected) => {
error!("filesystem watcher disconnected");
break;
},
}
}

if something_outdated {
let outdated_at = Instant::now();

self.outdated_at = Some(outdated_at);

Some(BackendSyncPoint { instant: outdated_at })
} else {
None
}
}
}
}

#[cfg(not(feature = "backend_filesystem_watch"))]
mod watch_impl {
use std::path::{Path};
use std::collections::VecDeque;
use crate::backend::{BackendSyncPoint, Modification};

pub struct Watcher {}

impl Watcher {
pub fn new(_root_path: &Path) -> Option<Watcher> {
pub fn notify_changes_synced(&mut self, _point: BackendSyncPoint) {}

pub fn new_changes(&mut self, _queue: &mut VecDeque<Modification>) -> Option<BackendSyncPoint> {
None
}
}
Expand Down Expand Up @@ -91,10 +172,18 @@ impl Backend for FileSystem {
resource_name_to_path(&self.root_path, path).exists()
}

fn notify_changes_synced(&mut self, _point: BackendSyncPoint) {}
fn notify_changes_synced(&mut self, point: BackendSyncPoint) {
if let Some(ref mut watch) = self.watch {
watch.lock().unwrap().notify_changes_synced(point);
}
}

fn new_changes(&mut self) -> Option<BackendSyncPoint> {
None
fn new_changes(&mut self, queue: &mut VecDeque<Modification>) -> Option<BackendSyncPoint> {
if let Some(ref mut watch) = self.watch {
watch.lock().unwrap().new_changes(queue)
} else {
None
}
}

fn read_into(&mut self, path: &ResourcePath, mut output: &mut io::Write) -> Result<(), Error> {
Expand Down
15 changes: 12 additions & 3 deletions lib/resources/src/backend/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use crate::path::ResourcePath;
use crate::path::{ResourcePath, ResourcePathBuf};
use std::collections::VecDeque;
use std::io;
use std::time::Instant;
use crate::Error;
Expand All @@ -20,7 +21,15 @@ pub use self::filesystem::FileSystem;

#[derive(Eq, PartialEq, Copy, Clone, Debug)]
pub struct BackendSyncPoint {
instant: Instant,
pub (crate) instant: Instant,
}

#[derive(Eq, PartialEq,Clone, Debug)]
pub enum Modification {
Create(ResourcePathBuf),
Write(ResourcePathBuf),
Remove(ResourcePathBuf),
Rename { from: ResourcePathBuf, to: ResourcePathBuf },
}

impl BackendSyncPoint {
Expand All @@ -36,7 +45,7 @@ pub trait Backend: Send + Sync {
fn exists(&self, path: &ResourcePath) -> bool;

fn notify_changes_synced(&mut self, point: BackendSyncPoint);
fn new_changes(&mut self) -> Option<BackendSyncPoint>;
fn new_changes(&mut self, queue: &mut VecDeque<Modification>) -> Option<BackendSyncPoint>;

fn read_into(&mut self, path: &ResourcePath, output: &mut io::Write) -> Result<(), Error>;
fn read_vec(&mut self, path: &ResourcePath) -> Result<Vec<u8>, Error> {
Expand Down
Loading

0 comments on commit 3c5bffa

Please sign in to comment.