From ffab0509a65f6f32851e64d66a5f7eeb453ab29f Mon Sep 17 00:00:00 2001 From: Mikko Rasa Date: Sat, 21 Sep 2019 18:25:59 +0300 Subject: [PATCH 1/1] Add a class for monitoring changes in files --- source/fs/filemonitor.cpp | 51 ++++++++++ source/fs/filemonitor.h | 55 +++++++++++ source/fs/unix/filemonitor.cpp | 131 ++++++++++++++++++++++++++ source/fs/unix/filemonitor_platform.h | 45 +++++++++ 4 files changed, 282 insertions(+) create mode 100644 source/fs/filemonitor.cpp create mode 100644 source/fs/filemonitor.h create mode 100644 source/fs/unix/filemonitor.cpp create mode 100644 source/fs/unix/filemonitor_platform.h diff --git a/source/fs/filemonitor.cpp b/source/fs/filemonitor.cpp new file mode 100644 index 0000000..5cc990d --- /dev/null +++ b/source/fs/filemonitor.cpp @@ -0,0 +1,51 @@ +#include "filemonitor.h" +#include "filemonitor_platform.h" + +using namespace std; + +namespace Msp { +namespace FS { + +FileMonitor::FileMonitor(): + priv(new Private(*this)), + event_disp(0) +{ } + +FileMonitor::~FileMonitor() +{ + delete priv; +} + +void FileMonitor::use_event_dispatcher(IO::EventDispatcher &ed) +{ + if(event_disp) + throw logic_error("event_disp!=0"); + + event_disp = &ed; + platform_use_event_dispatcher(); +} + +void FileMonitor::add_file(const FS::Path &path) +{ + MonitoredFile file; + file.path = path; + file.modified = false; + prepare_file(file); + files.push_back(file); +} + +void FileMonitor::remove_file(const FS::Path &path) +{ + for(vector::iterator i=files.begin(); i!=files.end(); ++i) + if(i->path==path) + { + cleanup_file(*i); + if(&*i!=&files.back()) + *i = files.back(); + files.pop_back(); + break; + } +} + +} // namespace FS +} // namespace Msp diff --git a/source/fs/filemonitor.h b/source/fs/filemonitor.h new file mode 100644 index 0000000..e96aff6 --- /dev/null +++ b/source/fs/filemonitor.h @@ -0,0 +1,55 @@ +#ifndef FILEMONITOR_H_ +#define FILEMONITOR_H_ + +#include +#include +#include +#include + +namespace Msp { +namespace FS { + +class FileMonitor: NonCopyable +{ +private: + struct Private; + + struct MonitoredFile + { + FS::Path path; + bool modified; + int tag; + }; + +public: + sigc::signal signal_file_modified; + +private: + Private *priv; + IO::EventDispatcher *event_disp; + std::vector files; + +public: + FileMonitor(); + ~FileMonitor(); + + void add_file(const FS::Path &); + void remove_file(const FS::Path &); +private: + void prepare_file(MonitoredFile &); + void cleanup_file(MonitoredFile &); + +public: + void use_event_dispatcher(IO::EventDispatcher &); +private: + void platform_use_event_dispatcher(); + +public: + void tick(); + void tick(const Msp::Time::TimeDelta &); +}; + +} // namespace FS +} // namespace Msp + +#endif diff --git a/source/fs/unix/filemonitor.cpp b/source/fs/unix/filemonitor.cpp new file mode 100644 index 0000000..70cfc4e --- /dev/null +++ b/source/fs/unix/filemonitor.cpp @@ -0,0 +1,131 @@ +#include +#include +#include +#include "filemonitor.h" +#include "filemonitor_platform.h" + +using namespace std; + +namespace Msp { +namespace FS { + +INotify::INotify() +{ + *fd = inotify_init(); + mode = IO::M_READ; + set_events(IO::P_INPUT); +} + +INotify::~INotify() +{ + IO::sys_close(fd); +} + +int INotify::add_watch(const FS::Path &path, int ev) +{ + int ret = inotify_add_watch(*fd, path.c_str(), ev); + if(ret==-1) + throw system_error("inotify_add_watch"); + return ret; +} + +void INotify::remove_watch(int wd) +{ + int ret = inotify_rm_watch(*fd, wd); + if(ret==-1) + throw system_error("inotify_rm_watch"); +} + +unsigned INotify::do_write(const char *, unsigned) +{ + check_access(IO::M_WRITE); + return 0; +} + +unsigned INotify::do_read(char *buf, unsigned size) +{ + return IO::sys_read(fd, buf, size); +} + + +void FileMonitor::platform_use_event_dispatcher() +{ + event_disp->add(priv->inotify); +} + +void FileMonitor::prepare_file(MonitoredFile &file) +{ + file.tag = priv->inotify.add_watch(file.path, IN_MODIFY|IN_CLOSE_WRITE|IN_DELETE_SELF); +} + +void FileMonitor::cleanup_file(MonitoredFile &file) +{ + if(file.tag!=-1) + priv->inotify.remove_watch(file.tag); +} + +void FileMonitor::tick() +{ + bool first = true; + while(1) + { + if(!first && !IO::poll(priv->inotify, IO::P_INPUT, Time::zero)) + break; + + first = false; + priv->events_available(); + } +} + +void FileMonitor::tick(const Time::TimeDelta &timeout) +{ + if(IO::poll(priv->inotify, IO::P_INPUT, timeout)) + tick(); +} + + +FileMonitor::Private::Private(FileMonitor &m): + monitor(m) +{ + inotify.signal_data_available.connect(sigc::mem_fun(this, &Private::events_available)); +} + +void FileMonitor::Private::events_available() +{ + vector changed_files; + char event_buf[sizeof(struct inotify_event)+NAME_MAX+1]; + unsigned len = inotify.read(event_buf, sizeof(event_buf)); + for(unsigned i=0; i(event_buf+i); + for(vector::iterator j=monitor.files.begin(); j!=monitor.files.end(); ++j) + if(j->tag==event->wd) + { + if(event->mask&IN_MODIFY) + j->modified = true; + if(((event->mask&IN_CLOSE_WRITE) && j->modified) || (event->mask&IN_DELETE_SELF)) + { + j->modified = false; + changed_files.push_back(j->path); + } + if(event->mask&IN_IGNORED) + j->tag = -1; + break; + } + i += sizeof(struct inotify_event)+event->len; + } + + for(vector::const_iterator i=changed_files.begin(); i!=changed_files.end(); ++i) + monitor.signal_file_modified.emit(*i); + + for(vector::iterator j=monitor.files.begin(); j!=monitor.files.end(); ) + { + if(j->tag==-1) + monitor.files.erase(j++); + else + ++j; + } +} + +} // namespace FS +} // namespace Msp diff --git a/source/fs/unix/filemonitor_platform.h b/source/fs/unix/filemonitor_platform.h new file mode 100644 index 0000000..31b728f --- /dev/null +++ b/source/fs/unix/filemonitor_platform.h @@ -0,0 +1,45 @@ +#ifndef MSP_FS_FILEMONITOR_PLATFORM_H_ +#define MSP_FS_FILEMONITOR_PLATFORM_H_ + +namespace Msp { +namespace FS { + +class INotify: public IO::EventObject +{ +private: + IO::Handle fd; + +public: + INotify(); + ~INotify(); + + int add_watch(const FS::Path &, int); + void remove_watch(int); + + virtual void set_block(bool) { } + virtual void set_inherit(bool) { } + +protected: + virtual unsigned do_write(const char *, unsigned); + virtual unsigned do_read(char *, unsigned); + +public: + virtual const IO::Handle &get_handle(IO::Mode) { return fd; } + virtual const IO::Handle &get_event_handle() { return fd; } +}; + + +struct FileMonitor::Private +{ + FileMonitor &monitor; + INotify inotify; + + Private(FileMonitor &); + + void events_available(); +}; + +} // namespace FS +} // namespace Msp + +#endif -- 2.43.0