Toolbox snapshot
The Reactive C++ Toolbox
Loading...
Searching...
No Matches
Inotify.cpp
Go to the documentation of this file.
1// The Reactive C++ Toolbox.
2// Copyright (C) 2013-2019 Swirly Cloud Limited
3// Copyright (C) 2024 Reactive Markets Limited
4//
5// Licensed under the Apache License, Version 2.0 (the "License");
6// you may not use this file except in compliance with the License.
7// You may obtain a copy of the License at
8//
9// http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing, software
12// distributed under the License is distributed on an "AS IS" BASIS,
13// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14// See the License for the specific language governing permissions and
15// limitations under the License.
16
17#include "Inotify.hpp"
18
19namespace toolbox {
20inline namespace io {
21
23: inotify_{&inotify}
24, sub_{r.subscribe(inotify.fd(), EpollIn, toolbox::bind<&FileWatcher::on_inotify>(this))}
25{
26}
27
28void FileWatcher::watch(const Path& path, Slot slot, std::uint32_t mask)
29{
30 auto new_wh{inotify_->add_watch(path.c_str(), mask)};
31 auto& watch{path_index_[path]};
32 if (watch.wh == new_wh) {
33 // Update entries for existing watch descriptor.
34 watch.slot = slot;
35 return;
36 }
37 wd_index_[new_wh.get().wd] = &watch;
38 // Update watch.
39 // N.B. in the unlikely event that an exception is thrown, the dangling entry in wd_index_
40 // would be cleaned-up by the garbage collection logic in on_inotify function below.
41 watch = Watch{.path = path, .slot = slot, .wh = std::move(new_wh)};
42}
43
44void FileWatcher::on_inotify(CyclTime /*now*/, int fd, unsigned events)
45{
46 if (events & (EpollIn | EpollHup)) {
47 char buf[1024 * (sizeof(struct inotify_event) + NAME_MAX + 1)];
48 const auto size{os::read(fd, buf, sizeof(buf))};
49 if (size == 0) {
50 // FIXME: a zero return normally indicates end of file.
51 // Can this happen on an inotify descriptor?
52 // And if so, how should the application behave?
53 wd_index_.clear();
54 path_index_.clear();
55 sub_.reset();
56 return;
57 }
58 for (std::size_t i{0}; i < size;) {
59 inotify_event* event{reinterpret_cast<inotify_event*>(&buf[i])};
60 const auto it{wd_index_.find(event->wd)};
61 if (it != wd_index_.end()) {
62 it->second->slot(it->second->path, event->wd, event->mask);
63 }
64 i += sizeof(struct inotify_event) + event->len;
65 }
66 // Perform garbage collection on dangling entries in wd_index_. These entries refer to
67 // watch handles in path_index_ that are now associated with different watch descriptors.
68 // This situation typically arises when a watch is updated, causing the path to point to
69 // a new inode.
70 //
71 // Note: This garbage collection occurs after all inotify events have been dispatched.
72 // This ensures that events for old watch descriptors are still dispatched, even if the
73 // path was rebound during the iteration.
74 //
75 for (auto it{wd_index_.cbegin()}; it != wd_index_.cend();) {
76 if (it->first != it->second->wh.get().wd) {
77 it = wd_index_.erase(it);
78 } else {
79 ++it;
80 }
81 }
82 }
83}
84
85} // namespace io
86} // namespace toolbox
FileWatcher watches for changes to files.
Definition Inotify.hpp:137
void watch(const Path &path, Slot slot, std::uint32_t mask=IN_ALL_EVENTS)
Definition Inotify.cpp:28
std::filesystem::path Path
Definition Inotify.hpp:139
FileWatcher(Reactor &r, Inotify &inotify)
Definition Inotify.cpp:22
Inotify provides a simplified interface to an inotify instance.
Definition Inotify.hpp:102
WatchFileHandle add_watch(const char *path, std::uint32_t mask, std::error_code &ec) noexcept
Add a watch to an initialised inotify instance.
Definition Inotify.hpp:121
void reset(std::nullptr_t=nullptr) noexcept
Definition Reactor.hpp:84
@ EpollIn
The associated file is available for read(2) operations.
Definition Epoll.hpp:115
ssize_t read(int fd, void *buf, std::size_t len, std::error_code &ec) noexcept
Read from a file descriptor.
Definition File.hpp:146
constexpr std::size_t size(const detail::Struct< detail::Member< TagsT, ValuesT >... > &s)
Definition Struct.hpp:98
constexpr auto bind() noexcept
Definition Slot.hpp:92