Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #include <string>
- #include <cstdio>
- #include <unordered_map>
- #include <utility>
- #if __has_include(<optional>)
- # include <optional>
- #else
- # include <experimental/optional>
- namespace std {
- using std::experimental::optional;
- }
- #endif
- #include <stdexcept>
- #include <boost/coroutine2/all.hpp>
- #include <uv.h>
- struct raii_fs_t: uv_fs_t {
- ~raii_fs_t() {
- uv_fs_req_cleanup(this);
- }
- };
- struct async {
- using coro_t = boost::coroutines2::coroutine<void>;
- template <typename UVT, typename Func, typename... Args>
- UVT await(std::nothrow_t, Func* func, uv_loop_t* loop, Args... args) noexcept {
- UVT req;
- req.data = this;
- func(loop, &req, args..., (uv_fs_cb)[] (auto *handle) {
- const auto that = (async *)handle->data;
- (*that->pull)();
- });
- (*this->push)();
- return req;
- }
- template <typename UVT, typename Func, typename... Args>
- UVT await(Func* func, uv_loop_t* loop, Args... args) {
- UVT req = await<UVT>(std::nothrow, func, loop, args...);
- if (req.result < 0) {
- throw std::runtime_error(uv_strerror((int)req.result));
- }
- return req;
- }
- // The callback should be noexcept
- template <typename Callback>
- static void run(Callback&& cb) {
- // We cannot use std::make_unique here or we must make the constructor public
- auto runner = std::unique_ptr<async>(new async(std::move(cb)));
- runner->instanceHolder = std::move(runner);
- }
- private:
- async(std::function<void (async&)>&& _cb): callback(std::move(_cb)) {
- // Go into the subroutine here
- this->pull = coro_t::pull_type([&](coro_t::push_type& _push) {
- this->push = coro_t::push_type(std::move(_push));
- // All async code runs in the subroutine
- this->callback(*this);
- // async instance is destructed here
- this->instanceHolder = nullptr;
- });
- }
- async() = delete;
- async(const async&) = delete;
- private:
- std::optional<coro_t::push_type> push;
- std::optional<coro_t::pull_type> pull;
- private:
- // callback holder, to prevent lambda object from being destructed unexpectly
- std::function<void (async&)> callback;
- // instance holder, to prevent async instance itself from being destructed unexpectly
- std::unique_ptr<async> instanceHolder;
- };
- template <typename Callback>
- inline int
- wrap_fs_event_start(uv_fs_event_t* handle,
- Callback&& cb,
- const char* path,
- unsigned int flags) noexcept {
- handle->data = &cb;
- return uv_fs_event_start(handle, [](uv_fs_event_t* handle, const char* filename, int events, int status) {
- Callback& cb = *(Callback *)handle->data;
- cb(handle, filename, events, status);
- }, path, flags);
- }
- int main(int argc, char *argv[]) {
- // if (argc != 2) {
- // fputs("Usage: ./main FOLDER_TO_LISTEN\n", stderr);
- // return 1;
- // }
- // std::string path2listen = argv[1];
- std::string path2listen = "/Users/Carter/test";
- std::unordered_map<std::string, size_t> progresses;
- auto loop = uv_default_loop();
- uv_fs_event_t fsEventHandle;
- uv_fs_event_init(loop, &fsEventHandle);
- wrap_fs_event_start(&fsEventHandle, [&](uv_fs_event_t* /*handle*/, const char* file_name, int events, int /*status*/) {
- async::run([&, events, filename = std::string(file_name)](async& runner) noexcept {
- // We should carefully hold other "closure variables" because the variables outside may have been destructed.
- // Local variables are safe to use
- std::printf("[Detected changes] %s: %s\n", filename.c_str(), (events == UV_RENAME ? "RENAME" : "CHANGE"));
- uv_file file = 0;
- try {
- const std::string fullPath = path2listen + "/" + filename;
- file = (uv_file)runner.await<raii_fs_t>(uv_fs_open, loop, fullPath.c_str(), UV_FS_O_RDONLY, S_IRUSR).result;
- const auto fileSize = runner.await<raii_fs_t>(uv_fs_fstat, loop, file).statbuf.st_size;
- const auto iter = progresses.insert({ filename, 0 }).first;
- const auto progress = std::exchange(iter->second, fileSize);
- const auto appendedSize = static_cast<unsigned int>(fileSize - progress);
- if (appendedSize) { // The changed contents may have been handled by other event.
- std::string fileContent(appendedSize, '\0');
- auto buf = uv_buf_init(&fileContent[0], (unsigned int)fileContent.size());
- runner.await<raii_fs_t>(uv_fs_read, loop, file, &buf, 1, progress);
- std::printf("[Contents appended]: %s\n", fileContent.c_str());
- }
- runner.await<raii_fs_t>(uv_fs_close, loop, file);
- } catch (std::runtime_error& e) {
- std::fprintf(stderr, "Error happens: %s\n", e.what());
- progresses.erase(filename);
- if (file) {
- raii_fs_t req;
- uv_fs_close(loop, &req, file, nullptr);
- }
- }
- });
- }, path2listen.c_str(), 0);
- uv_run(loop, UV_RUN_DEFAULT);
- }
Advertisement
Add Comment
Please, Sign In to add comment