GCC Code Coverage Report


Directory: cvmfs/
File: cvmfs/file_watcher.cc
Date: 2025-07-21 10:50:29
Exec Total Coverage
Lines: 46 51 90.2%
Branches: 25 44 56.8%

Line Branch Exec Source
1 /**
2 * This file is part of the CernVM File System.
3 */
4
5 #include "file_watcher.h"
6
7 #include <unistd.h>
8
9 #include <cassert>
10
11 #include "backoff.h"
12
13 #ifdef __APPLE__
14 #include "file_watcher_kqueue.h"
15 #else
16 #ifdef CVMFS_ENABLE_INOTIFY
17 #include "file_watcher_inotify.h"
18 #endif
19 #endif
20
21 #include "util/logging.h"
22 #include "util/posix.h"
23
24 namespace file_watcher {
25
26 60 EventHandler::EventHandler() { }
27
28 120 EventHandler::~EventHandler() { }
29
30 const unsigned FileWatcher::kInitialDelay = 1000;
31 const unsigned FileWatcher::kMaxDelay = 10000;
32 const unsigned FileWatcher::kResetDelay = 50000;
33
34 60 FileWatcher::FileWatcher()
35 60 : handler_map_()
36
2/2
✓ Branch 0 taken 120 times.
✓ Branch 1 taken 60 times.
180 , control_pipe_to_back_()
37
2/2
✓ Branch 0 taken 120 times.
✓ Branch 1 taken 60 times.
180 , control_pipe_to_front_()
38 60 , started_(false) { }
39
40 120 FileWatcher::~FileWatcher() { Stop(); }
41
42 60 FileWatcher *FileWatcher::Create() {
43 #ifdef __APPLE__
44 return new file_watcher::FileWatcherKqueue();
45 #else
46 #ifdef CVMFS_ENABLE_INOTIFY
47
1/2
✓ Branch 2 taken 60 times.
✗ Branch 3 not taken.
60 return new file_watcher::FileWatcherInotify();
48 #else
49 return NULL;
50 #endif
51 #endif
52 }
53
54 60 void FileWatcher::RegisterHandler(const std::string &file_path,
55 EventHandler *handler) {
56 60 handler_map_[file_path] = handler;
57 60 }
58
59 60 bool FileWatcher::Spawn() {
60
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 60 times.
60 if (started_) {
61 return false;
62 }
63
64
1/2
✓ Branch 1 taken 60 times.
✗ Branch 2 not taken.
60 MakePipe(control_pipe_to_back_);
65
1/2
✓ Branch 1 taken 60 times.
✗ Branch 2 not taken.
60 MakePipe(control_pipe_to_front_);
66
67
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 60 times.
60 assert(pthread_create(&thread_, NULL, &FileWatcher::BackgroundThread, this)
68 == 0);
69
70 // Before returning, wait for a start signal in the control pipe
71 // from the background thread.
72 char buffer[1];
73
1/2
✓ Branch 1 taken 60 times.
✗ Branch 2 not taken.
60 ReadHalfPipe(control_pipe_to_front_[0], buffer, 1);
74
75 60 started_ = true;
76 60 return true;
77 }
78
79 120 void FileWatcher::Stop() {
80
2/2
✓ Branch 0 taken 60 times.
✓ Branch 1 taken 60 times.
120 if (!started_) {
81 60 return;
82 }
83
84 60 WritePipe(control_pipe_to_back_[1], "q", 1);
85
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 60 times.
60 assert(pthread_join(thread_, NULL) == 0);
86
87 60 ClosePipe(control_pipe_to_front_);
88 60 ClosePipe(control_pipe_to_back_);
89
90
2/2
✓ Branch 3 taken 60 times.
✓ Branch 4 taken 60 times.
120 for (HandlerMap::iterator it = handler_map_.begin(); it != handler_map_.end();
91 60 ++it) {
92
1/2
✓ Branch 1 taken 60 times.
✗ Branch 2 not taken.
60 delete it->second;
93 }
94
95 60 started_ = false;
96 }
97
98 60 void *FileWatcher::BackgroundThread(void *d) {
99 60 FileWatcher *watcher = reinterpret_cast<FileWatcher *>(d);
100
101
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 60 times.
60 if (!watcher->RunEventLoop(watcher->handler_map_,
102 watcher->control_pipe_to_back_[0],
103 watcher->control_pipe_to_front_[1])) {
104 LogCvmfs(kLogCvmfs, kLogDebug, "Error running event loop.");
105 }
106
107 60 pthread_exit(NULL);
108 }
109
110 60 void FileWatcher::RegisterFilter(const std::string &file_path,
111 EventHandler *handler) {
112 60 bool done = false;
113
1/2
✓ Branch 1 taken 60 times.
✗ Branch 2 not taken.
60 BackoffThrottle throttle(kInitialDelay, kMaxDelay, kResetDelay);
114
2/2
✓ Branch 0 taken 60 times.
✓ Branch 1 taken 60 times.
120 while (!done) {
115
1/2
✓ Branch 1 taken 60 times.
✗ Branch 2 not taken.
60 const int wd = TryRegisterFilter(file_path);
116
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 60 times.
60 if (wd < 0) {
117 LogCvmfs(kLogCvmfs, kLogDebug,
118 "FileWatcher - Could not add watch for file %s. Retrying.",
119 file_path.c_str());
120 throttle.Throttle();
121 continue;
122 }
123
124
2/4
✓ Branch 1 taken 60 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 60 times.
✗ Branch 5 not taken.
60 watch_records_[wd] = WatchRecord(file_path, handler);
125
126 60 done = true;
127 }
128
1/2
✓ Branch 1 taken 60 times.
✗ Branch 2 not taken.
60 throttle.Reset();
129 60 }
130
131 } // namespace file_watcher
132