Unravel Engine C++ Reference
Loading...
Searching...
No Matches
watcher_fallback.cpp
Go to the documentation of this file.
1#include "watcher_fallback.h"
2#include <algorithm>
3#include <chrono>
4#include <filesystem>
5#include <iostream>
6#include <mutex>
7#include <set>
8#include <unordered_map>
9#include <unordered_set>
10#include <utility>
12#include <hpp/event.hpp>
13#define POOLSTL_STD_SUPPLEMENT 1
14#include <poolstl/poolstl.hpp>
15
16namespace fs
17{
18using namespace std::literals;
19
20namespace
21{
22
23void log_path(const fs::path& /*unused*/)
24{
25}
26
27} // namespace
28
30{
31public:
32
34 {
35 std::vector<watcher::entry> entries;
36
37 std::vector<size_t> created;
38 std::vector<size_t> modified;
39
40 void append(const observed_changes& rhs)
41 {
42
43 for(const auto& e : rhs.entries)
44 {
45 entries.emplace_back(e);
46 }
47
48 auto created_sz_before = created.size();
49 for(auto idx : rhs.created)
50 {
51 created.emplace_back(created_sz_before + idx);
52 }
53
54 auto modified_sz_before = modified.size();
55 for(auto idx : rhs.modified)
56 {
57 modified.emplace_back(modified_sz_before + idx);
58 }
59 }
60
62 {
63
64 for(auto& e : rhs.entries)
65 {
66 entries.emplace_back(std::move(e));
67 }
68
69 auto created_sz_before = created.size();
70 for(auto idx : rhs.created)
71 {
72 created.emplace_back(created_sz_before + idx);
73 }
74
75 auto modified_sz_before = modified.size();
76 for(auto idx : rhs.modified)
77 {
78 modified.emplace_back(modified_sz_before + idx);
79 }
80
81 rhs = {};
82 }
83 };
84 //-----------------------------------------------------------------------------
85 // Name : impl ()
91 //-----------------------------------------------------------------------------
92 directory_listener(const fs::path& path,
93 bool recursive,
94 watcher::clock_t::duration poll_interval)
95 : root_(path)
96 , poll_interval_(poll_interval)
97 , recursive_(recursive)
98 , init_time_timestamp_(std::chrono::system_clock::now())
99 {
100 observed_changes changes;
101 if(recursive_)
102 {
103 fs::error_code err;
104 for(auto& entry : fs::recursive_directory_iterator(root_, err))
105 {
106 poll_entry(entry, changes);
107 }
108 }
109 else
110 {
111 fs::error_code err;
112 for(auto& entry : fs::directory_iterator(root_, err))
113 {
114 poll_entry(entry, changes);
115 }
116 }
117 }
118
119 void pause()
120 {
121 paused_ = true;
122 }
123
124 void resume()
125 {
126 paused_ = false;
127 }
128
130 {
131 last_poll_ = watcher::clock_t::time_point{};
132 }
133
134 //-----------------------------------------------------------------------------
135 // Name : watch ()
141 //-----------------------------------------------------------------------------
142 void watch()
143 {
144 // const auto started = std::chrono::steady_clock::now();
145
146 observed_changes changes;
147 seen_keys_this_scan_.clear();
148 seen_keys_this_scan_.reserve(entries_.size());
149 if(!paused_.load())
150 {
151 if(!buffered_changes_.entries.empty())
152 {
153 std::swap(changes, buffered_changes_);
154 }
155 }
156 if(recursive_)
157 {
158 fs::error_code err;
159 for(auto& entry : fs::recursive_directory_iterator(root_, err))
160 {
161 poll_entry(entry, changes);
162 }
163 }
164 else
165 {
166 fs::error_code err;
167 for(auto& entry : fs::directory_iterator(root_, err))
168 {
169 poll_entry(entry, changes);
170 }
171 }
172 process_modifications(entries_, changes, seen_keys_this_scan_, root_);
173
174
175 // const auto elapsed = std::chrono::steady_clock::now() - started;
176 // const auto elapsed_ms = std::chrono::duration_cast<std::chrono::milliseconds>(elapsed).count();
177 // std::cout << "process_modifications: " << elapsed_ms << " ms"
178 // << " (" << changes.created.size() << " created, "
179 // << entries_.size() << " cached, "
180 // << changes.entries.size() << " events)"
181 // << " root=" << root_.string() << std::endl;
182
183 if(paused_.load())
184 {
185 if(!changes.entries.empty())
186 {
187 buffered_changes_.append(std::move(changes));
188 }
189 }
190 else
191 {
192 if(!changes.entries.empty())
193 {
194 on_changes.emit(changes.entries);
195 }
196 }
197 }
198
199 static auto get_original_path(const fs::path& old_path, const fs::path& renamed_path, const fs::path& new_path) -> fs::path
200 {
201 fs::path relative_path = fs::relative(new_path, renamed_path);
202 fs::path original_path = old_path / relative_path;
203 return original_path;
204 }
205
206 static auto check_if_same_extension(const fs::path& p1, const fs::path& p2) -> bool
207 {
208 bool same_extensions = true;
209
210 auto ep = p1;
211 auto fp = p2;
212
213 while(ep.has_extension() || fp.has_extension())
214 {
215 same_extensions &= ep.extension() == fp.extension();
216 ep = ep.stem();
217 fp = fp.stem();
218 }
219
220 return same_extensions;
221 };
222
223 static auto check_if_parent_dir_was_renamed(const std::vector<size_t>& renamed_dirs, const std::vector<watcher::entry>& entries, watcher::entry& e) -> bool
224 {
225 //check if parent_dir was renamed
226 for(const auto& renamed_idx : renamed_dirs)
227 {
228 const auto& renamed_e = entries[renamed_idx];
229
230 if(fs::is_any_parent_path(renamed_e.path, e.path))
231 {
233 e.last_path = get_original_path(renamed_e.last_path, renamed_e.path, e.path);
234 e.event_time = std::chrono::system_clock::now();
235
236 return true;
237 }
238 }
239 return false;
240 };
241
242
243 static void remove_missing_candidate(const std::string& key,
244 uintmax_t size,
245 std::unordered_map<uintmax_t, std::vector<std::string>>& missing_by_size)
246 {
247 auto size_it = missing_by_size.find(size);
248 if(size_it == missing_by_size.end())
249 {
250 return;
251 }
252 auto& candidates = size_it->second;
253 for(std::size_t i = 0; i < candidates.size(); ++i)
254 {
255 if(candidates[i] != key)
256 {
257 continue;
258 }
259 candidates[i] = candidates.back();
260 candidates.pop_back();
261 if(candidates.empty())
262 {
263 missing_by_size.erase(size_it);
264 }
265 return;
266 }
267 }
268
269 template<typename Container>
270 static void collect_missing_entries(const Container& old_entries,
271 const std::unordered_set<std::string>& seen_keys,
272 std::unordered_map<uintmax_t, std::vector<std::string>>& missing_by_size)
273 {
274 for(const auto& kvp : old_entries)
275 {
276 if(seen_keys.find(kvp.first) == seen_keys.end())
277 {
278 missing_by_size[kvp.second.size].push_back(kvp.first);
279 }
280 }
281 }
282
283 template<typename Container>
285 Container& old_entries,
286 std::unordered_map<uintmax_t, std::vector<std::string>>& missing_by_size) -> bool
287 {
288 auto size_it = missing_by_size.find(e.size);
289 if(size_it == missing_by_size.end())
290 {
291 return false;
292 }
293
294 auto& candidates = size_it->second;
295 for(std::size_t i = 0; i < candidates.size(); ++i)
296 {
297 const auto& key = candidates[i];
298 auto entry_it = old_entries.find(key);
299 if(entry_it == old_entries.end())
300 {
301 continue;
302 }
303
304 const auto& fi = entry_it->second;
305 auto diff = (e.last_mod_time - fi.last_mod_time);
306 auto d = std::chrono::duration_cast<std::chrono::milliseconds>(diff);
307 if(d > std::chrono::milliseconds(0))
308 {
309 continue;
310 }
311 if(!check_if_same_extension(e.path, fi.path))
312 {
313 continue;
314 }
315
317 e.last_path = fi.path;
318 e.event_time = std::chrono::system_clock::now();
319 old_entries.erase(entry_it);
320
321 candidates[i] = candidates.back();
322 candidates.pop_back();
323 if(candidates.empty())
324 {
325 missing_by_size.erase(size_it);
326 }
327 return true;
328 }
329
330 return false;
331 }
332
333 template<typename Container>
334 static void emit_remaining_removed(std::vector<watcher::entry>& entries,
335 Container& old_entries,
336 std::unordered_map<uintmax_t, std::vector<std::string>>& missing_by_size)
337 {
338 for(auto& size_group : missing_by_size)
339 {
340 for(const auto& key : size_group.second)
341 {
342 auto it = old_entries.find(key);
343 if(it == old_entries.end())
344 {
345 continue;
346 }
347 auto fi = it->second;
349 fi.event_time = std::chrono::system_clock::now();
350 entries.push_back(std::move(fi));
351 old_entries.erase(it);
352 }
353 }
354 }
355
356 template<typename Container>
357 static void process_modifications(Container& old_entries,
358 observed_changes& changes,
359 const std::unordered_set<std::string>& seen_keys,
360 const fs::path& listener_root)
361 {
362
363 std::vector<size_t> renamed_dirs;
364 std::unordered_map<uintmax_t, std::vector<std::string>> missing_by_size;
365 collect_missing_entries(old_entries, seen_keys, missing_by_size);
366
367 for(auto idx : changes.created)
368 {
369 auto& e = changes.entries[idx];
370
371 if(check_if_parent_dir_was_renamed(renamed_dirs, changes.entries, e))
372 {
373 const auto key = e.last_path.string();
374 auto old_it = old_entries.find(key);
375 if(old_it != old_entries.end())
376 {
377 remove_missing_candidate(key, old_it->second.size, missing_by_size);
378 old_entries.erase(old_it);
379 }
380 continue;
381 }
382
383 if(!missing_by_size.empty() && try_match_rename(e, old_entries, missing_by_size))
384 {
385 if(e.type == fs::file_type::directory)
386 {
387 renamed_dirs.emplace_back(idx);
388 }
389 }
390 }
391
392 emit_remaining_removed(changes.entries, old_entries, missing_by_size);
393 }
394
395 //-----------------------------------------------------------------------------
396 // Name : poll_entry ()
402 //-----------------------------------------------------------------------------
403 void poll_entry(const fs::directory_entry& entry,
404 observed_changes& changes)
405 {
406 // get the last modification time
407 fs::error_code err;
408 auto time = entry.last_write_time( err);
409 auto size = entry.file_size( err);
410 fs::file_status status = entry.status( err);
411 std::string key = entry.path().string();
412 seen_keys_this_scan_.insert(key);
413 auto it = entries_.find(key);
414 if(it != entries_.end())
415 {
416 auto& fi = it->second;
417
418 if(fi.last_mod_time != time || fi.size != size || fi.type != status.type())
419 {
420 fi.size = size;
421 fi.last_mod_time = time;
423 fi.type = status.type();
424
425 // on modify set the event time to the last modification time.
426 // since modifications are always observed while watching, we can use the last modification time.
427 auto last_mod_time = fs::filetime_to_system_clock(time);
428 fi.event_time = last_mod_time;
429
430 changes.entries.push_back(fi);
431 changes.modified.push_back(changes.entries.size() - 1);
432 }
433 else
434 {
436 fi.type = status.type();
437 }
438 }
439 else
440 {
441 // or compare with an older one
442 auto& fi = entries_[key];
443 fi.path = entry.path();
444 fi.last_path = entry.path();
445 fi.last_mod_time = time;
447 fi.size = size;
448 fi.type = status.type();
449
450 // on create set the event time to the current time
451 fi.event_time = std::chrono::system_clock::now();
452 changes.entries.push_back(fi);
453 changes.created.push_back(changes.entries.size() - 1);
454 }
455 }
456
458 hpp::event<void(const std::vector<watcher::entry>&)> on_changes;
459
460 auto get_path() const -> const fs::path&
461 {
462 return root_;
463 }
464
465 auto get_recursive() const -> bool
466 {
467 return recursive_;
468 }
469
470private:
471 friend class watcher_fallback;
472
474 fs::path root_;
476 std::map<std::string, watcher::entry> entries_;
477
478 std::chrono::system_clock::time_point init_time_timestamp_;
480 watcher::clock_t::duration poll_interval_ = 500ms;
481
482 watcher::clock_t::time_point last_poll_ = watcher::clock_t::now();
484 bool recursive_ = false;
485
486 std::atomic<bool> paused_ = {false};
487
488 std::unordered_set<std::string> seen_keys_this_scan_;
489
490 observed_changes buffered_changes_;
491};
492
494{
495public:
496 impl(const fs::path& path,
497 const pattern_filter& filter,
498 bool recursive,
499 bool initial_list,
500 watcher::clock_t::duration poll_interval,
502 std::shared_ptr<directory_listener> listener,
503 const std::string& watcher_name)
504 : path_(path)
505 , filter_(filter)
506 , recursive_(recursive)
507 , callback_(std::move(callback))
508 , listener_(std::move(listener))
509 , init_time_timestamp_(std::chrono::system_clock::now())
510 , watcher_name_(watcher_name)
511 {
512 // Initialize entries cache and optionally emit initial list
513 initialize_entries(initial_list);
514
515 // Connect to the listener's changes
516 slot_key_ = listener_->on_changes.connect([this](const std::vector<watcher::entry>& changes) -> void
517 {
518 handle_changes(changes);
519 });
520 }
521
523 {
524 if(listener_)
525 {
526 listener_->on_changes.disconnect(slot_key_);
527 }
528 }
529
530 void pause()
531 {
532 paused_ = true;
533 }
534
535 void resume()
536 {
537 paused_ = false;
538 }
539
540 auto get_path() const -> const fs::path&
541 {
542 return path_;
543 }
544
545 auto get_listener() const -> std::shared_ptr<directory_listener>
546 {
547 return listener_;
548 }
549
550private:
551 void poll_entry(const fs::directory_entry& entry, std::vector<watcher::entry>& initial_entries, bool emit_initial_list)
552 {
553 bool filter_passed = filter_.should_include(entry.path());
554 fs::error_code err2;
555 fs::file_status file_status = entry.status(err2);
556
557 auto file_type = file_status.type();
558 if(filter_passed || (file_type == fs::file_type::directory))
559 {
561 e.path = entry.path();
562 e.last_path = entry.path();
564
565 fs::error_code err3;
566 e.last_mod_time = entry.last_write_time(err3);
567 e.size = entry.file_size(err3);
568 e.type = file_type;
569
570 // on create set the event time to the current time
571 e.event_time = std::chrono::system_clock::now();
572
573 // Add to cache
574 std::string key = e.path.string();
575
576 if(emit_initial_list && filter_passed)
577 {
578 initial_entries.push_back(e);
579 }
580 }
581
582 }
583
584 void initialize_entries(bool emit_initial_list)
585 {
586 // Iterate through the directory and populate entries_ cache
587 fs::error_code err;
588 std::vector<watcher::entry> initial_entries;
589
590 if(recursive_)
591 {
592 for(auto& entry : fs::recursive_directory_iterator(path_, err))
593 {
594 poll_entry(entry, initial_entries, emit_initial_list);
595 }
596 }
597 else
598 {
599 for(auto& entry : fs::directory_iterator(path_, err))
600 {
601 poll_entry(entry, initial_entries, emit_initial_list);
602 }
603 }
604
605 // Emit initial list if requested
606 if(emit_initial_list && !initial_entries.empty() && callback_)
607 {
608 callback_(initial_entries, true);
609 }
610 }
611
612 auto get_system_timestamp(const watcher::entry& entry) -> std::chrono::system_clock::time_point
613 {
614 // if(entry.status == watcher::entry_status::renamed || entry.status == watcher::entry_status::removed)
615 {
616 return entry.event_time;
617 }
618 // return fs::filetime_to_system_clock(entry.last_mod_time);
619
620 }
621
622 void handle_changes(const std::vector<watcher::entry>& changes)
623 {
624 // Filter changes according to this impl's filter and path
625 std::vector<watcher::entry> filtered_changes;
626
627 for(const auto& entry : changes)
628 {
629 // Check if event is under our watched path (for parent listener reuse)
630 if(!is_path_under_watch(entry.path))
631 {
632 continue;
633 }
634
635 // Check filter
636 if(!filter_.should_include(entry.path))
637 {
638 continue;
639 }
640
641 // Check timestamp
642 auto system_timestamp = get_system_timestamp(entry);
643
644 if(system_timestamp < init_time_timestamp_)
645 {
646 continue;
647 }
648
649 filtered_changes.push_back(entry);
650 }
651
652 if(filtered_changes.empty())
653 {
654 return;
655 }
656
657 // Check if paused
658 if(paused_)
659 {
660 // Buffer changes when paused
661 buffered_changes_.insert(buffered_changes_.end(), filtered_changes.begin(), filtered_changes.end());
662 return;
663 }
664
665 // Call callback
666 if(callback_)
667 {
668 callback_(filtered_changes, false);
669 }
670 }
671
672 auto is_path_under_watch(const fs::path& event_path) const -> bool
673 {
674 fs::error_code ec;
675 fs::path watch_path = fs::weakly_canonical(path_, ec);
676 if(ec)
677 {
678 watch_path = fs::absolute(path_, ec);
679 if(ec)
680 {
681 watch_path = path_;
682 }
683 watch_path = watch_path.lexically_normal();
684 }
685 fs::path resolved_event_path = fs::weakly_canonical(event_path, ec);
686 if(ec)
687 {
688 resolved_event_path = fs::absolute(event_path, ec);
689 if(ec)
690 {
691 resolved_event_path = event_path;
692 }
693 resolved_event_path = resolved_event_path.lexically_normal();
694 }
695 if(resolved_event_path == watch_path)
696 {
697 return true;
698 }
699 return fs::is_any_parent_path(watch_path, resolved_event_path);
700 }
701
702 fs::path path_;
703 pattern_filter filter_;
704 bool recursive_;
705 watcher::notify_callback callback_;
706 std::shared_ptr<directory_listener> listener_;
707
708 std::chrono::system_clock::time_point init_time_timestamp_;
709 uint64_t slot_key_ = 0;
710 std::atomic<bool> paused_ = false;
711 std::vector<watcher::entry> buffered_changes_;
712 std::string watcher_name_;
713};
714
719
721{
722 std::lock_guard<std::mutex> lock(mutex_);
723 globally_paused_ = true;
724 for(auto& kvp : directory_listeners_)
725 {
726 kvp.second->pause();
727 }
728 for(auto& kvp : watchers_)
729 {
730 kvp.second->pause();
731 }
732}
733
735{
736 {
737 std::lock_guard<std::mutex> lock(mutex_);
738 globally_paused_ = false;
739 for(auto& kvp : directory_listeners_)
740 {
741 kvp.second->resume();
742 kvp.second->request_immediate_poll();
743 }
744 for(auto& kvp : watchers_)
745 {
746 kvp.second->resume();
747 }
748 }
749 cv_.notify_all();
750}
751
752void watcher_fallback::wait_all(watcher::clock_t::duration duration)
753{
754 cv_.notify_all();
755 std::this_thread::sleep_for(duration);
756}
758{
759 // stop the thread
760 watching_ = false;
761 // remove all watchers
763
764 if(thread_.joinable())
765 {
766 thread_.join();
767 }
768}
769
771{
772 watching_ = true;
773 thread_ = std::thread(
774 [this]() -> void
775 {
776 platform::set_thread_name("fs::watcher");
777 // keep watching for modifications every ms milliseconds
778 using namespace std::literals;
779 while(watching_)
780 {
781 if(globally_paused_.load())
782 {
783 std::unique_lock<std::mutex> lock(mutex_);
784 cv_.wait_for(lock, 500ms);
785 continue;
786 }
787
788 watcher::clock_t::duration sleep_time = 99999h;
789
790 // iterate through each directory listener and check for modification
791 std::map<fs::path, std::shared_ptr<directory_listener>> listeners;
792 {
793 std::unique_lock<std::mutex> lock(mutex_);
795 listeners = directory_listeners_;
796 }
797
798 for(auto& pair : listeners)
799 {
800 auto listener = pair.second;
801
802 auto now = watcher::clock_t::now();
803
804 auto diff = (listener->last_poll_ + listener->poll_interval_) - now;
805 if(diff <= watcher::clock_t::duration(0))
806 {
807 listener->watch();
808 listener->last_poll_ = now;
809
810 sleep_time = std::min(sleep_time, listener->poll_interval_);
811 }
812 else
813 {
814 sleep_time = std::min(sleep_time, diff);
815 }
816 }
817
818 std::unique_lock<std::mutex> lock(mutex_);
819 cv_.wait_for(lock, sleep_time);
820 }
821 });
822}
823
824auto watcher_fallback::watch_impl(const fs::path& path,
825 const pattern_filter& filter,
826 bool recursive,
827 bool initial_list,
828 watcher::clock_t::duration poll_interval,
830 const std::string& watcher_name
831 ) -> std::uint64_t
832{
833 if(!callback)
834 {
835 return 0;
836 }
837
838 std::shared_ptr<directory_listener> listener;
839 fs::error_code err;
840 fs::path abs_path = fs::absolute(path, err);
841 if(!err)
842 {
843 abs_path = abs_path.lexically_normal();
844 }
845 bool is_new_listener = false;
846
847 {
848 std::lock_guard<std::mutex> lock(mutex_);
849
850 auto it = directory_listeners_.find(abs_path);
851 if(it != directory_listeners_.end())
852 {
853 listener = it->second;
854 }
855 else
856 {
857 for(auto& [watched_path, existing_listener] : directory_listeners_)
858 {
859 if(existing_listener->get_recursive() && fs::is_any_parent_path(watched_path, abs_path))
860 {
861 listener = existing_listener;
862
863 break;
864 }
865 }
866 if(!listener)
867 {
868 listener = std::make_shared<directory_listener>(abs_path, recursive, poll_interval);
869
870 is_new_listener = true;
871 }
872 }
873 }
874 static std::atomic<std::uint64_t> free_id = {1};
875 auto key = free_id++;
876 auto impl = std::make_shared<watcher_fallback::impl>(path, filter, recursive, initial_list, poll_interval, std::move(callback), listener, watcher_name);
877 {
878 std::lock_guard<std::mutex> lock(mutex_);
879 watchers_[key] = impl;
880 if(is_new_listener)
881 {
882 directory_listeners_[abs_path] = listener;
883 }
884 }
885 if(globally_paused_)
886 {
887 listener->pause();
888 impl->pause();
889 }
890 cv_.notify_all();
891 return key;
892}
893
895{
896 for(auto it = directory_listeners_.begin(); it != directory_listeners_.end();)
897 {
898 const auto& listener = it->second;
899 const bool referenced = std::any_of(watchers_.begin(),
900 watchers_.end(),
901 [&listener](const auto& kvp) -> bool
902 {
903 return kvp.second && kvp.second->get_listener() == listener;
904 });
905 if(!referenced)
906 {
907 it = directory_listeners_.erase(it);
908 }
909 else
910 {
911 ++it;
912 }
913 }
914}
915
916void watcher_fallback::unwatch_impl(std::uint64_t key)
917{
918 {
919 std::lock_guard<std::mutex> lock(mutex_);
920 watchers_.erase(key);
922 }
923 cv_.notify_all();
924}
925
927{
928 {
929 std::lock_guard<std::mutex> lock(mutex_);
930 watchers_.clear();
931 directory_listeners_.clear();
932 }
933 cv_.notify_all();
934}
935
936} // namespace fs
A filter that combines include and exclude patterns for file/directory filtering.
auto should_include(const fs::path &path) const -> bool
Tests if a path should be included based on the filter rules Logic: (matches any include pattern OR n...
static void emit_remaining_removed(std::vector< watcher::entry > &entries, Container &old_entries, std::unordered_map< uintmax_t, std::vector< std::string > > &missing_by_size)
auto get_path() const -> const fs::path &
directory_listener(const fs::path &path, bool recursive, watcher::clock_t::duration poll_interval)
static auto try_match_rename(watcher::entry &e, Container &old_entries, std::unordered_map< uintmax_t, std::vector< std::string > > &missing_by_size) -> bool
static void remove_missing_candidate(const std::string &key, uintmax_t size, std::unordered_map< uintmax_t, std::vector< std::string > > &missing_by_size)
static void collect_missing_entries(const Container &old_entries, const std::unordered_set< std::string > &seen_keys, std::unordered_map< uintmax_t, std::vector< std::string > > &missing_by_size)
static auto get_original_path(const fs::path &old_path, const fs::path &renamed_path, const fs::path &new_path) -> fs::path
static void process_modifications(Container &old_entries, observed_changes &changes, const std::unordered_set< std::string > &seen_keys, const fs::path &listener_root)
hpp::event< void(const std::vector< watcher::entry > &)> on_changes
Event that emits changes to all connected impls.
void poll_entry(const fs::directory_entry &entry, observed_changes &changes)
static auto check_if_parent_dir_was_renamed(const std::vector< size_t > &renamed_dirs, const std::vector< watcher::entry > &entries, watcher::entry &e) -> bool
static auto check_if_same_extension(const fs::path &p1, const fs::path &p2) -> bool
impl(const fs::path &path, const pattern_filter &filter, bool recursive, bool initial_list, watcher::clock_t::duration poll_interval, watcher::notify_callback callback, std::shared_ptr< directory_listener > listener, const std::string &watcher_name)
auto get_listener() const -> std::shared_ptr< directory_listener >
auto get_path() const -> const fs::path &
std::thread thread_
Thread that polls for changes.
auto watch_impl(const fs::path &path, const pattern_filter &filter, bool recursive, bool initial_list, watcher::clock_t::duration poll_interval, watcher::notify_callback callback, const std::string &watcher_name) -> std::uint64_t
std::atomic< bool > globally_paused_
std::atomic< bool > watching_
Atomic bool sync.
std::map< fs::path, std::shared_ptr< directory_listener > > directory_listeners_
std::condition_variable cv_
std::map< std::uint64_t, std::shared_ptr< impl > > watchers_
std::mutex mutex_
Mutex for the file watchers.
void unwatch_impl(std::uint64_t key)
void wait_all(watcher::clock_t::duration duration)
std::function< void(const std::vector< entry > &, bool)> notify_callback
Definition watcher.h:41
std::vector< render_pass_entry > entries
Definition cache.hpp:11
auto filetime_to_system_clock(fs::file_time_type ft) -> std::chrono::system_clock::time_point
Definition watcher.h:106
bool is_any_parent_path(const path &parent, const path &child)
void set_thread_name(const char *threadName)
Definition thread.hpp:160
Hash specialization for batch_key to enable use in std::unordered_map.
float size