/Users/deen/code/yugabyte-db/src/yb/rocksdb/util/mock_env.cc
Line | Count | Source (jump to first uncovered line) |
1 | | // Copyright (c) 2011-present, Facebook, Inc. All rights reserved. |
2 | | // This source code is licensed under the BSD-style license found in the |
3 | | // LICENSE file in the root directory of this source tree. An additional grant |
4 | | // of patent rights can be found in the PATENTS file in the same directory. |
5 | | // |
6 | | // The following only applies to changes made to this file as part of YugaByte development. |
7 | | // |
8 | | // Portions Copyright (c) YugaByte, Inc. |
9 | | // |
10 | | // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except |
11 | | // in compliance with the License. You may obtain a copy of the License at |
12 | | // |
13 | | // http://www.apache.org/licenses/LICENSE-2.0 |
14 | | // |
15 | | // Unless required by applicable law or agreed to in writing, software distributed under the License |
16 | | // is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express |
17 | | // or implied. See the License for the specific language governing permissions and limitations |
18 | | // under the License. |
19 | | // |
20 | | // Copyright (c) 2011 The LevelDB Authors. All rights reserved. |
21 | | // Use of this source code is governed by a BSD-style license that can be |
22 | | // found in the LICENSE file. See the AUTHORS file for names of contributors. |
23 | | |
24 | | #include "yb/rocksdb/util/mock_env.h" |
25 | | |
26 | | #include <algorithm> |
27 | | #include <chrono> |
28 | | |
29 | | #include "yb/rocksdb/port/sys_time.h" |
30 | | #include "yb/rocksdb/rate_limiter.h" |
31 | | #include "yb/rocksdb/util/murmurhash.h" |
32 | | #include "yb/rocksdb/util/mutexlock.h" |
33 | | #include "yb/rocksdb/util/random.h" |
34 | | |
35 | | #include "yb/util/result.h" |
36 | | #include "yb/util/status_log.h" |
37 | | |
38 | | namespace rocksdb { |
39 | | |
40 | | class MemFile { |
41 | | public: |
42 | | explicit MemFile(Env* env, const std::string& fn, bool _is_lock_file = false) |
43 | | : env_(env), |
44 | | fn_(fn), |
45 | | refs_(0), |
46 | | is_lock_file_(_is_lock_file), |
47 | | locked_(false), |
48 | | size_(0), |
49 | | modified_time_(Now()), |
50 | | rnd_(static_cast<uint32_t>( |
51 | | MurmurHash(fn.data(), static_cast<int>(fn.size()), 0))), |
52 | 10.3k | fsynced_bytes_(0) {} |
53 | | |
54 | 30.8k | void Ref() { |
55 | 30.8k | MutexLock lock(&mutex_); |
56 | 30.8k | ++refs_; |
57 | 30.8k | } |
58 | | |
59 | 10.2k | bool is_lock_file() const { return is_lock_file_; } |
60 | | |
61 | 13 | bool Lock() { |
62 | 13 | assert(is_lock_file_); |
63 | 0 | MutexLock lock(&mutex_); |
64 | 13 | if (locked_) { |
65 | 0 | return false; |
66 | 13 | } else { |
67 | 13 | locked_ = true; |
68 | 13 | return true; |
69 | 13 | } |
70 | 13 | } |
71 | | |
72 | 13 | void Unlock() { |
73 | 13 | assert(is_lock_file_); |
74 | 0 | MutexLock lock(&mutex_); |
75 | 13 | locked_ = false; |
76 | 13 | } |
77 | | |
78 | 30.8k | void Unref() { |
79 | 30.8k | bool do_delete = false; |
80 | 30.8k | { |
81 | 30.8k | MutexLock lock(&mutex_); |
82 | 30.8k | --refs_; |
83 | 30.8k | assert(refs_ >= 0); |
84 | 30.8k | if (refs_ <= 0) { |
85 | 10.3k | do_delete = true; |
86 | 10.3k | } |
87 | 30.8k | } |
88 | | |
89 | 30.8k | if (do_delete) { |
90 | 10.3k | delete this; |
91 | 10.3k | } |
92 | 30.8k | } |
93 | | |
94 | 58.2k | uint64_t Size() const { |
95 | 58.2k | return size_; |
96 | 58.2k | } |
97 | | |
98 | 0 | void Truncate(size_t size) { |
99 | 0 | MutexLock lock(&mutex_); |
100 | 0 | if (size < size_) { |
101 | 0 | data_.resize(size); |
102 | 0 | size_ = size; |
103 | 0 | } |
104 | 0 | } |
105 | | |
106 | 2 | void CorruptBuffer() { |
107 | 2 | if (fsynced_bytes_ >= size_) { |
108 | 1 | return; |
109 | 1 | } |
110 | 1 | uint64_t buffered_bytes = size_ - fsynced_bytes_; |
111 | 1 | uint64_t start = |
112 | 1 | fsynced_bytes_ + rnd_.Uniform(static_cast<int>(buffered_bytes)); |
113 | 1 | uint64_t end = std::min(start + 512, size_.load()); |
114 | 1 | MutexLock lock(&mutex_); |
115 | 17 | for (uint64_t pos = start; pos < end; ++pos16 ) { |
116 | 16 | data_[pos] = static_cast<char>(rnd_.Uniform(256)); |
117 | 16 | } |
118 | 1 | } |
119 | | |
120 | 27.6k | Status Read(uint64_t offset, size_t n, Slice* result, uint8_t* scratch) const { |
121 | 27.6k | MutexLock lock(&mutex_); |
122 | 27.6k | if (offset > Size()) { |
123 | 1 | return STATUS(IOError, "Offset greater than file size."); |
124 | 1 | } |
125 | 27.6k | const uint64_t available = Size() - offset; |
126 | 27.6k | if (n > available) { |
127 | 55 | n = available; |
128 | 55 | } |
129 | 27.6k | if (n == 0) { |
130 | 17 | *result = Slice(); |
131 | 17 | return Status::OK(); |
132 | 17 | } |
133 | 27.6k | if (scratch) { |
134 | 27.6k | memcpy(scratch, &(data_[offset]), n); |
135 | 27.6k | *result = Slice(scratch, n); |
136 | 27.6k | } else { |
137 | 0 | *result = Slice(&(data_[offset]), n); |
138 | 0 | } |
139 | 27.6k | return Status::OK(); |
140 | 27.6k | } |
141 | | |
142 | 270k | Status Append(const Slice& data) { |
143 | 270k | MutexLock lock(&mutex_); |
144 | 270k | data_.append(data.cdata(), data.size()); |
145 | 270k | size_ = data_.size(); |
146 | 270k | modified_time_ = Now(); |
147 | 270k | return Status::OK(); |
148 | 270k | } |
149 | | |
150 | 21.7k | Status Fsync() { |
151 | 21.7k | fsynced_bytes_ = size_.load(); |
152 | 21.7k | return Status::OK(); |
153 | 21.7k | } |
154 | | |
155 | 21 | uint64_t ModifiedTime() const { |
156 | 21 | return modified_time_; |
157 | 21 | } |
158 | | |
159 | | private: |
160 | 280k | uint64_t Now() { |
161 | 280k | int64_t unix_time; |
162 | 280k | auto s = env_->GetCurrentTime(&unix_time); |
163 | 280k | assert(s.ok()); |
164 | 0 | return static_cast<uint64_t>(unix_time); |
165 | 280k | } |
166 | | |
167 | | // Private since only Unref() should be used to delete it. |
168 | 10.3k | ~MemFile() { |
169 | 10.3k | assert(refs_ == 0); |
170 | 10.3k | } |
171 | | |
172 | | // No copying allowed. |
173 | | MemFile(const MemFile&); |
174 | | void operator=(const MemFile&); |
175 | | |
176 | | Env* env_; |
177 | | const std::string fn_; |
178 | | mutable port::Mutex mutex_; |
179 | | int refs_; |
180 | | bool is_lock_file_; |
181 | | bool locked_; |
182 | | |
183 | | // Data written into this file, all bytes before fsynced_bytes are |
184 | | // persistent. |
185 | | std::string data_; |
186 | | std::atomic<uint64_t> size_; |
187 | | std::atomic<uint64_t> modified_time_; |
188 | | |
189 | | Random rnd_; |
190 | | std::atomic<uint64_t> fsynced_bytes_; |
191 | | }; |
192 | | |
193 | | namespace { |
194 | | |
195 | | class MockSequentialFile : public SequentialFile { |
196 | | public: |
197 | 62 | explicit MockSequentialFile(MemFile* file) : file_(file), pos_(0) { |
198 | 62 | file_->Ref(); |
199 | 62 | } |
200 | | |
201 | 62 | ~MockSequentialFile() { |
202 | 62 | file_->Unref(); |
203 | 62 | } |
204 | | |
205 | 94 | Status Read(size_t n, Slice* result, uint8_t* scratch) override { |
206 | 94 | Status s = file_->Read(pos_, n, result, scratch); |
207 | 94 | if (s.ok()) { |
208 | 94 | pos_ += result->size(); |
209 | 94 | } |
210 | 94 | return s; |
211 | 94 | } |
212 | | |
213 | 2 | Status Skip(uint64_t n) override { |
214 | 2 | if (pos_ > file_->Size()) { |
215 | 0 | return STATUS(IOError, "pos_ > file_->Size()"); |
216 | 0 | } |
217 | 2 | const size_t available = file_->Size() - pos_; |
218 | 2 | if (n > available) { |
219 | 1 | n = available; |
220 | 1 | } |
221 | 2 | pos_ += n; |
222 | 2 | return Status::OK(); |
223 | 2 | } |
224 | | |
225 | 0 | const std::string& filename() const override { |
226 | 0 | static const std::string kFilename = "MockSequentialFile"; |
227 | 0 | return kFilename; |
228 | 0 | } |
229 | | |
230 | | private: |
231 | | MemFile* file_; |
232 | | size_t pos_; |
233 | | }; |
234 | | |
235 | | class MockRandomAccessFile : public RandomAccessFile { |
236 | | public: |
237 | 10.1k | explicit MockRandomAccessFile(MemFile* file) : file_(file) { |
238 | 10.1k | file_->Ref(); |
239 | 10.1k | } |
240 | | |
241 | 10.1k | ~MockRandomAccessFile() { |
242 | 10.1k | file_->Unref(); |
243 | 10.1k | } |
244 | | |
245 | 27.5k | Status Read(uint64_t offset, size_t n, Slice* result, uint8_t* scratch) const override { |
246 | 27.5k | return file_->Read(offset, n, result, scratch); |
247 | 27.5k | } |
248 | | |
249 | 0 | yb::Result<uint64_t> Size() const override { return file_->Size(); } |
250 | | |
251 | 0 | yb::Result<uint64_t> INode() const override { return STATUS(NotSupported, "Not supported"); }; |
252 | | |
253 | 0 | const std::string& filename() const override { return filename_; } |
254 | | |
255 | 0 | size_t memory_footprint() const override { return 0; } |
256 | | |
257 | | private: |
258 | | std::string filename_ = "MockRandomAccessFile"; |
259 | | MemFile* file_; |
260 | | }; |
261 | | |
262 | | class MockWritableFile : public WritableFile { |
263 | | public: |
264 | | MockWritableFile(MemFile* file, RateLimiter* rate_limiter) |
265 | | : file_(file), |
266 | 10.3k | rate_limiter_(rate_limiter) { |
267 | 10.3k | file_->Ref(); |
268 | 10.3k | } |
269 | | |
270 | 10.3k | ~MockWritableFile() { |
271 | 10.3k | file_->Unref(); |
272 | 10.3k | } |
273 | | |
274 | 270k | Status Append(const Slice& data) override { |
275 | 270k | uint64_t bytes_written = 0; |
276 | 540k | while (bytes_written < data.size()) { |
277 | 270k | auto bytes = RequestToken(data.size() - bytes_written); |
278 | 270k | Status s = file_->Append(Slice(data.data() + bytes_written, bytes)); |
279 | 270k | if (!s.ok()) { |
280 | 0 | return s; |
281 | 0 | } |
282 | 270k | bytes_written += bytes; |
283 | 270k | } |
284 | 270k | return Status::OK(); |
285 | 270k | } |
286 | 10.2k | Status Truncate(uint64_t size) override { |
287 | 10.2k | return Status::OK(); |
288 | 10.2k | } |
289 | 10.2k | Status Close() override { return file_->Fsync(); } |
290 | | |
291 | 252k | Status Flush() override { return Status::OK(); } |
292 | | |
293 | 11.4k | Status Sync() override { return file_->Fsync(); } |
294 | | |
295 | 2 | uint64_t GetFileSize() override { return file_->Size(); } |
296 | | |
297 | | private: |
298 | 270k | inline size_t RequestToken(size_t bytes) { |
299 | 270k | if (rate_limiter_ && io_priority_ < Env::IO_TOTAL0 ) { |
300 | 0 | bytes = std::min(bytes, |
301 | 0 | static_cast<size_t>(rate_limiter_->GetSingleBurstBytes())); |
302 | 0 | rate_limiter_->Request(bytes, io_priority_); |
303 | 0 | } |
304 | 270k | return bytes; |
305 | 270k | } |
306 | | |
307 | | MemFile* file_; |
308 | | RateLimiter* rate_limiter_; |
309 | | }; |
310 | | |
311 | | class MockEnvDirectory : public Directory { |
312 | | public: |
313 | 2.04k | Status Fsync() override { return Status::OK(); } |
314 | | }; |
315 | | |
316 | | class MockEnvFileLock : public FileLock { |
317 | | public: |
318 | | explicit MockEnvFileLock(const std::string& fname) |
319 | 13 | : fname_(fname) {} |
320 | | |
321 | 13 | std::string FileName() const { |
322 | 13 | return fname_; |
323 | 13 | } |
324 | | |
325 | | private: |
326 | | const std::string fname_; |
327 | | }; |
328 | | |
329 | | class TestMemLogger : public Logger { |
330 | | private: |
331 | | std::unique_ptr<WritableFile> file_; |
332 | | std::atomic_size_t log_size_; |
333 | | static const uint64_t flush_every_seconds_ = 5; |
334 | | std::atomic_uint_fast64_t last_flush_micros_; |
335 | | Env* env_; |
336 | | std::atomic_bool flush_pending_; |
337 | | |
338 | | public: |
339 | | TestMemLogger(std::unique_ptr<WritableFile> f, Env* env, |
340 | | const InfoLogLevel log_level = InfoLogLevel::ERROR_LEVEL) |
341 | | : Logger(log_level), |
342 | | file_(std::move(f)), |
343 | | log_size_(0), |
344 | | last_flush_micros_(0), |
345 | | env_(env), |
346 | 12 | flush_pending_(false) {} |
347 | 12 | virtual ~TestMemLogger() { |
348 | 12 | } |
349 | | |
350 | 10.0k | void Flush() override { |
351 | 10.0k | if (flush_pending_) { |
352 | 9.74k | flush_pending_ = false; |
353 | 9.74k | } |
354 | 10.0k | last_flush_micros_ = env_->NowMicros(); |
355 | 10.0k | } |
356 | | |
357 | | using Logger::Logv; |
358 | 34.3k | void Logv(const char* format, va_list ap) override { |
359 | | // We try twice: the first time with a fixed-size stack allocated buffer, |
360 | | // and the second time with a much larger dynamically allocated buffer. |
361 | 34.3k | char buffer[500]; |
362 | 39.0k | for (int iter = 0; iter < 2; iter++4.67k ) { |
363 | 39.0k | char* base; |
364 | 39.0k | int bufsize; |
365 | 39.0k | if (iter == 0) { |
366 | 34.3k | bufsize = sizeof(buffer); |
367 | 34.3k | base = buffer; |
368 | 34.3k | } else { |
369 | 4.67k | bufsize = 30000; |
370 | 4.67k | base = new char[bufsize]; |
371 | 4.67k | } |
372 | 39.0k | char* p = base; |
373 | 39.0k | char* limit = base + bufsize; |
374 | | |
375 | 39.0k | struct timeval now_tv; |
376 | 39.0k | gettimeofday(&now_tv, nullptr); |
377 | 39.0k | const time_t seconds = now_tv.tv_sec; |
378 | 39.0k | struct tm t; |
379 | 39.0k | localtime_r(&seconds, &t); |
380 | 39.0k | p += snprintf(p, limit - p, |
381 | 39.0k | "%04d/%02d/%02d-%02d:%02d:%02d.%06d ", |
382 | 39.0k | t.tm_year + 1900, |
383 | 39.0k | t.tm_mon + 1, |
384 | 39.0k | t.tm_mday, |
385 | 39.0k | t.tm_hour, |
386 | 39.0k | t.tm_min, |
387 | 39.0k | t.tm_sec, |
388 | 39.0k | static_cast<int>(now_tv.tv_usec)); |
389 | | |
390 | | // Print the message |
391 | 39.0k | if (p < limit) { |
392 | 39.0k | va_list backup_ap; |
393 | 39.0k | va_copy(backup_ap, ap); |
394 | 39.0k | p += vsnprintf(p, limit - p, format, backup_ap); |
395 | 39.0k | va_end(backup_ap); |
396 | 39.0k | } |
397 | | |
398 | | // Truncate to available space if necessary |
399 | 39.0k | if (p >= limit) { |
400 | 4.67k | if (iter == 0) { |
401 | 4.67k | continue; // Try again with larger buffer |
402 | 4.67k | } else { |
403 | 0 | p = limit - 1; |
404 | 0 | } |
405 | 4.67k | } |
406 | | |
407 | | // Add newline if necessary |
408 | 34.3k | if (p == base || p[-1] != '\n'34.3k ) { |
409 | 24.6k | *p++ = '\n'; |
410 | 24.6k | } |
411 | | |
412 | 34.3k | assert(p <= limit); |
413 | 0 | const size_t write_size = p - base; |
414 | | |
415 | 34.3k | CHECK_OK(file_->Append(Slice(base, write_size))); |
416 | 34.3k | flush_pending_ = true; |
417 | 34.3k | log_size_ += write_size; |
418 | 34.3k | uint64_t now_micros = static_cast<uint64_t>(now_tv.tv_sec) * 1000000 + |
419 | 34.3k | now_tv.tv_usec; |
420 | 34.3k | if (now_micros - last_flush_micros_ >= flush_every_seconds_ * 1000000) { |
421 | 57 | flush_pending_ = false; |
422 | 57 | last_flush_micros_ = now_micros; |
423 | 57 | } |
424 | 34.3k | if (base != buffer) { |
425 | 4.67k | delete[] base; |
426 | 4.67k | } |
427 | 34.3k | break; |
428 | 39.0k | } |
429 | 34.3k | } |
430 | 0 | size_t GetLogFileSize() const override { return log_size_; } |
431 | | }; |
432 | | |
433 | | } // Anonymous namespace |
434 | | |
435 | 16 | MockEnv::MockEnv(Env* base_env) : EnvWrapper(base_env), fake_sleep_micros_(0) {} |
436 | | |
437 | 16 | MockEnv::~MockEnv() { |
438 | 212 | for (FileSystem::iterator i = file_map_.begin(); i != file_map_.end(); ++i196 ) { |
439 | 196 | i->second->Unref(); |
440 | 196 | } |
441 | 16 | } |
442 | | |
443 | | // Partial implementation of the Env interface. |
444 | | Status MockEnv::NewSequentialFile(const std::string& fname, |
445 | | unique_ptr<SequentialFile>* result, |
446 | 63 | const EnvOptions& soptions) { |
447 | 63 | auto fn = NormalizePath(fname); |
448 | 63 | MutexLock lock(&mutex_); |
449 | 63 | if (file_map_.find(fn) == file_map_.end()) { |
450 | 1 | *result = NULL; |
451 | 1 | return STATUS(IOError, fn, "File not found"); |
452 | 1 | } |
453 | 62 | auto* f = file_map_[fn]; |
454 | 62 | if (f->is_lock_file()) { |
455 | 0 | return STATUS(InvalidArgument, fn, "Cannot open a lock file."); |
456 | 0 | } |
457 | 62 | result->reset(new MockSequentialFile(f)); |
458 | 62 | return Status::OK(); |
459 | 62 | } |
460 | | |
461 | | Status MockEnv::NewRandomAccessFile(const std::string& fname, |
462 | | unique_ptr<RandomAccessFile>* result, |
463 | 10.1k | const EnvOptions& soptions) { |
464 | 10.1k | auto fn = NormalizePath(fname); |
465 | 10.1k | MutexLock lock(&mutex_); |
466 | 10.1k | if (file_map_.find(fn) == file_map_.end()) { |
467 | 1 | *result = NULL; |
468 | 1 | return STATUS(IOError, fn, "File not found"); |
469 | 1 | } |
470 | 10.1k | auto* f = file_map_[fn]; |
471 | 10.1k | if (f->is_lock_file()) { |
472 | 0 | return STATUS(InvalidArgument, fn, "Cannot open a lock file."); |
473 | 0 | } |
474 | 10.1k | result->reset(new MockRandomAccessFile(f)); |
475 | 10.1k | return Status::OK(); |
476 | 10.1k | } |
477 | | |
478 | | Status MockEnv::NewWritableFile(const std::string& fname, |
479 | | unique_ptr<WritableFile>* result, |
480 | 10.3k | const EnvOptions& env_options) { |
481 | 10.3k | auto fn = NormalizePath(fname); |
482 | 10.3k | MutexLock lock(&mutex_); |
483 | 10.3k | if (file_map_.find(fn) != file_map_.end()) { |
484 | 1 | DeleteFileInternal(fn); |
485 | 1 | } |
486 | 10.3k | MemFile* file = new MemFile(this, fn, false); |
487 | 10.3k | file->Ref(); |
488 | 10.3k | file_map_[fn] = file; |
489 | | |
490 | 10.3k | result->reset(new MockWritableFile(file, env_options.rate_limiter)); |
491 | 10.3k | return Status::OK(); |
492 | 10.3k | } |
493 | | |
494 | | Status MockEnv::NewDirectory(const std::string& name, |
495 | 9 | unique_ptr<Directory>* result) { |
496 | 9 | result->reset(new MockEnvDirectory()); |
497 | 9 | return Status::OK(); |
498 | 9 | } |
499 | | |
500 | 4.36k | Status MockEnv::FileExists(const std::string& fname) { |
501 | 4.36k | auto fn = NormalizePath(fname); |
502 | 4.36k | MutexLock lock(&mutex_); |
503 | 4.36k | if (file_map_.find(fn) != file_map_.end()) { |
504 | | // File exists |
505 | 4.33k | return Status::OK(); |
506 | 4.33k | } |
507 | | // Now also check if fn exists as a dir |
508 | 56 | for (const auto& iter : file_map_)34 { |
509 | 56 | const std::string& filename = iter.first; |
510 | 56 | if (filename.size() >= fn.size() + 1 && |
511 | 56 | filename[fn.size()] == '/'23 && |
512 | 56 | Slice(filename).starts_with(Slice(fn))2 ) { |
513 | 2 | return Status::OK(); |
514 | 2 | } |
515 | 56 | } |
516 | 32 | return STATUS(NotFound, ""); |
517 | 34 | } |
518 | | |
519 | | Status MockEnv::GetChildren(const std::string& dir, |
520 | 55 | std::vector<std::string>* result) { |
521 | 55 | auto d = NormalizePath(dir); |
522 | 55 | { |
523 | 55 | MutexLock lock(&mutex_); |
524 | 55 | result->clear(); |
525 | 4.60k | for (const auto& iter : file_map_) { |
526 | 4.60k | const std::string& filename = iter.first; |
527 | | |
528 | 4.60k | if (filename.size() >= d.size() + 1 && filename[d.size()] == '/' && |
529 | 4.60k | Slice(filename).starts_with(Slice(d))) { |
530 | 4.60k | size_t next_slash = filename.find('/', d.size() + 1); |
531 | 4.60k | if (next_slash != std::string::npos) { |
532 | 4 | result->push_back(filename.substr( |
533 | 4 | d.size() + 1, next_slash - d.size() - 1)); |
534 | 4.60k | } else { |
535 | 4.60k | result->push_back(filename.substr(d.size() + 1)); |
536 | 4.60k | } |
537 | 4.60k | } |
538 | 4.60k | } |
539 | 55 | } |
540 | 55 | result->erase(std::unique(result->begin(), result->end()), result->end()); |
541 | 55 | return Status::OK(); |
542 | 55 | } |
543 | | |
544 | 10.1k | void MockEnv::DeleteFileInternal(const std::string& fname) { |
545 | 10.1k | assert(fname == NormalizePath(fname)); |
546 | 0 | const auto& pair = file_map_.find(fname); |
547 | 10.1k | if (pair != file_map_.end()) { |
548 | 10.1k | pair->second->Unref(); |
549 | 10.1k | file_map_.erase(fname); |
550 | 10.1k | } |
551 | 10.1k | } |
552 | | |
553 | 10.1k | Status MockEnv::DeleteFile(const std::string& fname) { |
554 | 10.1k | auto fn = NormalizePath(fname); |
555 | 10.1k | MutexLock lock(&mutex_); |
556 | 10.1k | if (file_map_.find(fn) == file_map_.end()) { |
557 | 1 | return STATUS(IOError, fn, "File not found"); |
558 | 1 | } |
559 | | |
560 | 10.1k | DeleteFileInternal(fn); |
561 | 10.1k | return Status::OK(); |
562 | 10.1k | } |
563 | | |
564 | 15 | Status MockEnv::CreateDir(const std::string& dirname) { |
565 | 15 | return Status::OK(); |
566 | 15 | } |
567 | | |
568 | 37 | Status MockEnv::CreateDirIfMissing(const std::string& dirname) { |
569 | 37 | return Status::OK(); |
570 | 37 | } |
571 | | |
572 | 1 | Status MockEnv::DeleteDir(const std::string& dirname) { |
573 | 1 | return Status::OK(); |
574 | 1 | } |
575 | | |
576 | 2.90k | Status MockEnv::GetFileSize(const std::string& fname, uint64_t* file_size) { |
577 | 2.90k | auto fn = NormalizePath(fname); |
578 | 2.90k | MutexLock lock(&mutex_); |
579 | 2.90k | auto iter = file_map_.find(fn); |
580 | 2.90k | if (iter == file_map_.end()) { |
581 | 1 | return STATUS(IOError, fn, "File not found"); |
582 | 1 | } |
583 | | |
584 | 2.90k | *file_size = iter->second->Size(); |
585 | 2.90k | return Status::OK(); |
586 | 2.90k | } |
587 | | |
588 | | Status MockEnv::GetFileModificationTime(const std::string& fname, |
589 | 21 | uint64_t* time) { |
590 | 21 | auto fn = NormalizePath(fname); |
591 | 21 | MutexLock lock(&mutex_); |
592 | 21 | auto iter = file_map_.find(fn); |
593 | 21 | if (iter == file_map_.end()) { |
594 | 0 | return STATUS(IOError, fn, "File not found"); |
595 | 0 | } |
596 | 21 | *time = iter->second->ModifiedTime(); |
597 | 21 | return Status::OK(); |
598 | 21 | } |
599 | | |
600 | 37 | Status MockEnv::RenameFile(const std::string& src, const std::string& dest) { |
601 | 37 | auto s = NormalizePath(src); |
602 | 37 | auto t = NormalizePath(dest); |
603 | 37 | MutexLock lock(&mutex_); |
604 | 37 | if (file_map_.find(s) == file_map_.end()) { |
605 | 1 | return STATUS(IOError, s, "File not found"); |
606 | 1 | } |
607 | | |
608 | 36 | DeleteFileInternal(t); |
609 | 36 | file_map_[t] = file_map_[s]; |
610 | 36 | file_map_.erase(s); |
611 | 36 | return Status::OK(); |
612 | 37 | } |
613 | | |
614 | 0 | Status MockEnv::LinkFile(const std::string& src, const std::string& dest) { |
615 | 0 | auto s = NormalizePath(src); |
616 | 0 | auto t = NormalizePath(dest); |
617 | 0 | MutexLock lock(&mutex_); |
618 | 0 | if (file_map_.find(s) == file_map_.end()) { |
619 | 0 | return STATUS(IOError, s, "File not found"); |
620 | 0 | } |
621 | | |
622 | 0 | DeleteFileInternal(t); |
623 | 0 | file_map_[t] = file_map_[s]; |
624 | 0 | return Status::OK(); |
625 | 0 | } |
626 | | |
627 | | Status MockEnv::NewLogger(const std::string& fname, |
628 | 12 | shared_ptr<Logger>* result) { |
629 | 12 | auto fn = NormalizePath(fname); |
630 | 12 | MutexLock lock(&mutex_); |
631 | 12 | auto iter = file_map_.find(fn); |
632 | 12 | MemFile* file = nullptr; |
633 | 12 | if (iter == file_map_.end()) { |
634 | 12 | file = new MemFile(this, fn, false); |
635 | 12 | file->Ref(); |
636 | 12 | file_map_[fn] = file; |
637 | 12 | } else { |
638 | 0 | file = iter->second; |
639 | 0 | } |
640 | 12 | std::unique_ptr<WritableFile> f(new MockWritableFile(file, nullptr)); |
641 | 12 | result->reset(new TestMemLogger(std::move(f), this)); |
642 | 12 | return Status::OK(); |
643 | 12 | } |
644 | | |
645 | 13 | Status MockEnv::LockFile(const std::string& fname, FileLock** flock) { |
646 | 13 | auto fn = NormalizePath(fname); |
647 | 13 | { |
648 | 13 | MutexLock lock(&mutex_); |
649 | 13 | if (file_map_.find(fn) != file_map_.end()) { |
650 | 7 | if (!file_map_[fn]->is_lock_file()) { |
651 | 0 | return STATUS(InvalidArgument, fname, "Not a lock file."); |
652 | 0 | } |
653 | 7 | if (!file_map_[fn]->Lock()) { |
654 | 0 | return STATUS(IOError, fn, "Lock is already held."); |
655 | 0 | } |
656 | 7 | } else { |
657 | 6 | auto* file = new MemFile(this, fn, true); |
658 | 6 | file->Ref(); |
659 | 6 | file->Lock(); |
660 | 6 | file_map_[fn] = file; |
661 | 6 | } |
662 | 13 | } |
663 | 13 | *flock = new MockEnvFileLock(fn); |
664 | 13 | return Status::OK(); |
665 | 13 | } |
666 | | |
667 | 13 | Status MockEnv::UnlockFile(FileLock* flock) { |
668 | 13 | std::string fn = dynamic_cast<MockEnvFileLock*>(flock)->FileName(); |
669 | 13 | { |
670 | 13 | MutexLock lock(&mutex_); |
671 | 13 | if (file_map_.find(fn) != file_map_.end()) { |
672 | 13 | if (!file_map_[fn]->is_lock_file()) { |
673 | 0 | return STATUS(InvalidArgument, fn, "Not a lock file."); |
674 | 0 | } |
675 | 13 | file_map_[fn]->Unlock(); |
676 | 13 | } |
677 | 13 | } |
678 | 13 | delete flock; |
679 | 13 | return Status::OK(); |
680 | 13 | } |
681 | | |
682 | 1 | Status MockEnv::GetTestDirectory(std::string* path) { |
683 | 1 | *path = "/test"; |
684 | 1 | return Status::OK(); |
685 | 1 | } |
686 | | |
687 | 280k | Status MockEnv::GetCurrentTime(int64_t* unix_time) { |
688 | 280k | auto s = EnvWrapper::GetCurrentTime(unix_time); |
689 | 280k | *unix_time += fake_sleep_micros_.load() / (1000 * 1000); |
690 | 280k | return s; |
691 | 280k | } |
692 | | |
693 | 16.2k | uint64_t MockEnv::NowMicros() { |
694 | 16.2k | return EnvWrapper::NowMicros() + fake_sleep_micros_.load(); |
695 | 16.2k | } |
696 | | |
697 | 5 | uint64_t MockEnv::NowNanos() { |
698 | 5 | return EnvWrapper::NowNanos() + fake_sleep_micros_.load() * 1000; |
699 | 5 | } |
700 | | |
701 | | // Non-virtual functions, specific to MockEnv |
702 | 0 | Status MockEnv::Truncate(const std::string& fname, size_t size) { |
703 | 0 | auto fn = NormalizePath(fname); |
704 | 0 | MutexLock lock(&mutex_); |
705 | 0 | auto iter = file_map_.find(fn); |
706 | 0 | if (iter == file_map_.end()) { |
707 | 0 | return STATUS(IOError, fn, "File not found"); |
708 | 0 | } |
709 | 0 | iter->second->Truncate(size); |
710 | 0 | return Status::OK(); |
711 | 0 | } |
712 | | |
713 | 2 | Status MockEnv::CorruptBuffer(const std::string& fname) { |
714 | 2 | auto fn = NormalizePath(fname); |
715 | 2 | MutexLock lock(&mutex_); |
716 | 2 | auto iter = file_map_.find(fn); |
717 | 2 | if (iter == file_map_.end()) { |
718 | 0 | return STATUS(IOError, fn, "File not found"); |
719 | 0 | } |
720 | 2 | iter->second->CorruptBuffer(); |
721 | 2 | return Status::OK(); |
722 | 2 | } |
723 | | |
724 | 48.2k | std::string MockEnv::NormalizePath(const std::string path) { |
725 | 48.2k | std::string dst; |
726 | 3.87M | for (auto c : path) { |
727 | 3.87M | if (!dst.empty() && c == '/'3.83M && dst.back() == '/'144k ) { |
728 | 32 | continue; |
729 | 32 | } |
730 | 3.87M | dst.push_back(c); |
731 | 3.87M | } |
732 | 48.2k | return dst; |
733 | 48.2k | } |
734 | | |
735 | 3 | void MockEnv::FakeSleepForMicroseconds(int64_t micros) { |
736 | 3 | fake_sleep_micros_.fetch_add(micros); |
737 | 3 | } |
738 | | |
739 | | } // namespace rocksdb |