Caffe2 - C++ API
A deep learning, cross platform ML framework
file_store_handler.cc
1 #include "file_store_handler_op.h"
2 
3 #include <errno.h>
4 #include <fcntl.h>
5 #include <limits.h>
6 #include <stdio.h>
7 #include <stdlib.h>
8 #include <sys/stat.h>
9 
10 #include <array>
11 #include <chrono>
12 #include <iostream>
13 #include <thread>
14 
15 #if defined(_MSC_VER)
16 #include <direct.h> // for _mkdir
17 #endif
18 
19 #include "caffe2/utils/murmur_hash3.h"
20 
21 namespace caffe2 {
22 
23 static std::string encodeName(const std::string& name) {
24  std::array<uint64_t, 2> out;
25  MurmurHash3_x64_128(name.data(), name.size(), 0xcafef00d, out.data());
26 
27  // Size is 33 to have space for final NUL
28  std::array<char, 33> buf;
29  for (int i = 0; i < 16; i++) {
30  snprintf(&buf[i * 2], buf.size() - (i * 2), "%02x", ((char*)out.data())[i]);
31  }
32 
33  // Return everything but the final NUL
34  return std::string(buf.data(), buf.size() - 1);
35 }
36 
37 FileStoreHandler::FileStoreHandler(
38  const std::string& path,
39  const std::string& prefix) {
40  basePath_ = realPath(path);
41  if (!prefix.empty()) {
42  basePath_ = basePath_ + "/" + encodeName(prefix);
43  }
44 #if defined(_MSC_VER)
45  auto ret = _mkdir(basePath_.c_str());
46 #else
47  auto ret = mkdir(basePath_.c_str(), 0777);
48 #endif // defined(_MSC_VER)
49  if (ret == -1) {
50  CHECK_EQ(errno, EEXIST) << "mkdir: " << strerror(errno);
51  }
52 }
53 
54 FileStoreHandler::~FileStoreHandler() {}
55 
56 std::string FileStoreHandler::realPath(const std::string& path) {
57 #if defined(_MSC_VER)
58  std::array<char, _MAX_PATH> buf;
59  auto ret = _fullpath(buf.data(), path.c_str(), buf.size());
60 #else
61  std::array<char, PATH_MAX> buf;
62  auto ret = realpath(path.c_str(), buf.data());
63 #endif
64  CHECK_EQ(buf.data(), ret) << "realpath: " << strerror(errno);
65  return std::string(buf.data());
66 }
67 
68 std::string FileStoreHandler::tmpPath(const std::string& name) {
69  return basePath_ + "/." + encodeName(name);
70 }
71 
72 std::string FileStoreHandler::objectPath(const std::string& name) {
73  return basePath_ + "/" + encodeName(name);
74 }
75 
76 void FileStoreHandler::set(const std::string& name, const std::string& data) {
77  auto tmp = tmpPath(name);
78  auto path = objectPath(name);
79 
80  {
81  std::ofstream ofs(tmp.c_str(), std::ios::out | std::ios::trunc);
82  if (!ofs.is_open()) {
83  CAFFE_ENFORCE(
84  false, "File cannot be created: ", tmp, " (", ofs.rdstate(), ")");
85  }
86  ofs << data;
87  }
88 
89  // Atomically movve result to final location
90  auto rv = rename(tmp.c_str(), path.c_str());
91  CAFFE_ENFORCE_EQ(rv, 0, "rename: ", strerror(errno));
92 }
93 
94 std::string FileStoreHandler::get(const std::string& name) {
95  auto path = objectPath(name);
96  std::string result;
97 
98  // Block until key is set
99  wait({name});
100 
101  std::ifstream ifs(path.c_str(), std::ios::in);
102  if (!ifs) {
103  CAFFE_ENFORCE(
104  false, "File cannot be opened: ", path, " (", ifs.rdstate(), ")");
105  }
106  ifs.seekg(0, std::ios::end);
107  size_t n = ifs.tellg();
108  result.resize(n);
109  ifs.seekg(0);
110  ifs.read(&result[0], n);
111  return result;
112 }
113 
114 int64_t FileStoreHandler::add(
115  const std::string& /* unused */,
116  int64_t /* unused */) {
117  CHECK(false) << "add not implemented for FileStoreHandler";
118  return 0;
119 }
120 
121 bool FileStoreHandler::check(const std::vector<std::string>& names) {
122  std::vector<std::string> paths;
123  for (const auto& name : names) {
124  paths.push_back(objectPath(name));
125  }
126 
127  for (const auto& path : paths) {
128  int fd = open(path.c_str(), O_RDONLY);
129  if (fd == -1) {
130  // Only deal with files that don't exist.
131  // Anything else is a problem.
132  CHECK_EQ(errno, ENOENT);
133 
134  // One of the paths doesn't exist; return early
135  return false;
136  }
137 
138  close(fd);
139  }
140 
141  return true;
142 }
143 
144 void FileStoreHandler::wait(
145  const std::vector<std::string>& names,
146  const std::chrono::milliseconds& timeout) {
147  // Not using inotify because it doesn't work on many
148  // shared filesystems (such as NFS).
149  const auto start = std::chrono::steady_clock::now();
150  while (!check(names)) {
151  const auto elapsed = std::chrono::duration_cast<std::chrono::seconds>(
152  std::chrono::steady_clock::now() - start);
153  if (timeout != kNoTimeout && elapsed > timeout) {
154  STORE_HANDLER_TIMEOUT("Wait timeout for name(s): ", Join(" ", names));
155  }
156  /* sleep override */
157  std::this_thread::sleep_for(std::chrono::milliseconds(10));
158  }
159 }
160 }
A global dictionary that holds information about what Caffe2 modules have been loaded in the current ...