proxygen
MultiFilePoller.cpp
Go to the documentation of this file.
1 /*
2  * Copyright 2004-present Facebook, Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
18 
19 #include <algorithm>
20 #include <folly/FileUtil.h>
21 #include <folly/String.h>
22 
23 using namespace folly;
24 
25 namespace wangle {
26 
27 MultiFilePoller::MultiFilePoller(std::chrono::milliseconds pollInterval)
28  : poller_(pollInterval) {}
29 
31  size_t ret = lastCallbackId_;
32  // Order of callback cancellation is arbitrary, so the next available
33  // callbackId might not be lastCallbackId_+1.
34  while (++ret != lastCallbackId_) {
35  if (idsToCallbacks_.find(ret) == idsToCallbacks_.end()) {
36  lastCallbackId_ = ret; // Assume write lock is acquired.
37  return ret;
38  }
39  }
40  throw std::runtime_error("Run out of callback ID.");
41 }
42 
44  std::string path,
45  Callback cb) {
46  return registerFiles({std::move(path)}, std::move(cb));
47 }
48 
50  const std::vector<std::string>& paths,
51  Callback cb) {
52  VLOG(4) << "registerFiles({" << join(", ", paths) << "}, cb=" << &cb << ")";
53  if (paths.empty()) {
54  throw std::invalid_argument("Argument paths must be non-empty.");
55  }
56  StringReferences cbPaths;
58  auto cbId = getNextCallbackId();
59  // Create the bi-directional relation between path and callback.
60  for (const auto& path : paths) {
61  pathsToCallbackIds_[path].push_back(cbId);
62  // Use reference to key of pathsToCallbackIds_ map to avoid duplicates.
63  const auto& key = pathsToCallbackIds_.find(path)->first;
64  cbPaths.push_back(key);
65  poller_.addFileToTrack(key, [=] { onFileUpdated(key); });
66  }
67  idsToCallbacks_.emplace(cbId,
68  CallbackDetail(std::move(cbPaths), std::move(cb)));
69  return MultiFilePoller::CallbackId(cbId);
70 }
71 
73  std::vector<std::string> pathsToErase;
75 
76  auto pos = idsToCallbacks_.find(cbId.id_);
77  if (pos == idsToCallbacks_.end()) {
78  throw std::out_of_range(
79  to<std::string>("Callback ", cbId.id_, " not found"));
80  }
81 
82  // Remove the callback ID from its registered paths.
83  for (const auto& path : pos->second.files_) {
84  auto& callbackIds = pathsToCallbackIds_[path];
85  callbackIds.erase(
86  std::remove(callbackIds.begin(), callbackIds.end(), cbId.id_));
87  // If the path has no more callbacks, erase it from map.
88  if (callbackIds.empty()) {
90  pathsToErase.emplace_back(path);
91  }
92  }
93  // Remove the callback.
94  idsToCallbacks_.erase(cbId.id_);
95  // Remove callback-less paths from pathsToCallbackIds_, if any, at last.
96  for (const auto& path : pathsToErase) {
97  pathsToCallbackIds_.erase(path);
98  }
99 }
100 
101 void MultiFilePoller::onFileUpdated(const std::string& triggeredPath) {
102  VLOG(4) << "onFileUpdated(" << triggeredPath << ").";
103 
104  // A temporary read cache. Not worth it making it permanent because
105  // files do not change frequently.
106  std::unordered_map<std::string, std::string> filePathsToFileContents;
108 
109  const auto& callbacks = pathsToCallbackIds_.find(triggeredPath);
110  if (callbacks == pathsToCallbackIds_.end()) {
111  return;
112  }
113 
114  for (const auto& cbId : callbacks->second) {
115  const auto& cbEnt = idsToCallbacks_.find(cbId);
116  // Lazily read all files needed by the callback.
117  for (const auto& path : cbEnt->second.files_) {
118  if (filePathsToFileContents.find(path) == filePathsToFileContents.end()) {
120  if (readFile(path.get().c_str(), data)) {
121  filePathsToFileContents.emplace(path, std::move(data));
122  } else {
123  VLOG(4) << "Failed to read file " << path.get();
124  }
125  }
126  }
127  cbEnt->second.cb_(filePathsToFileContents);
128  }
129 }
130 
131 } // namespace wangle
bool readFile(int fd, Container &out, size_t num_bytes=std::numeric_limits< size_t >::max())
Definition: FileUtil.h:125
std::unordered_map< std::string, std::vector< size_t > > pathsToCallbackIds_
constexpr detail::Map< Move > move
Definition: Base-inl.h:2567
void onFileUpdated(const std::string &triggeredPath)
—— Concurrent Priority Queue Implementation ——
Definition: AtomicBitSet.h:29
void addFileToTrack(const std::string &fileName, Cob yCob, Cob nCob=nullptr, Condition condition=fileTouchedCondInternal)
Definition: FilePoller.cpp:115
std::vector< std::reference_wrapper< const std::string >> StringReferences
wangle::FilePoller poller_
void removeFileToTrack(const std::string &fileName)
Definition: FilePoller.cpp:133
std::unordered_map< size_t, CallbackDetail > idsToCallbacks_
void cancelCallback(const CallbackId &cbId)
folly::SharedMutex rwlock_
CallbackId registerFile(std::string path, Callback cb)
const char * string
Definition: Conv.cpp:212
#define join
static constexpr uint64_t data[1]
Definition: Fingerprint.cpp:43
CallbackId registerFiles(const std::vector< std::string > &paths, Callback cb)