]>
Commit | Line | Data |
---|---|---|
7c673cae | 1 | // Copyright (c) 2011-present, Facebook, Inc. All rights reserved. |
11fdf7f2 TL |
2 | // This source code is licensed under both the GPLv2 (found in the |
3 | // COPYING file in the root directory) and Apache 2.0 License | |
4 | // (found in the LICENSE.Apache file in the root directory). | |
7c673cae FG |
5 | // |
6 | // Copyright (c) 2011 The LevelDB Authors. All rights reserved. | |
7 | // Use of this source code is governed by a BSD-style license that can be | |
8 | // found in the LICENSE file. See the AUTHORS file for names of contributors. | |
9 | ||
10 | #include "env/mock_env.h" | |
20effc67 | 11 | |
7c673cae FG |
12 | #include <algorithm> |
13 | #include <chrono> | |
20effc67 TL |
14 | |
15 | #include "file/filename.h" | |
7c673cae | 16 | #include "port/sys_time.h" |
20effc67 | 17 | #include "rocksdb/file_system.h" |
11fdf7f2 | 18 | #include "util/cast_util.h" |
20effc67 | 19 | #include "util/hash.h" |
7c673cae FG |
20 | #include "util/random.h" |
21 | #include "util/rate_limiter.h" | |
22 | ||
f67539c2 | 23 | namespace ROCKSDB_NAMESPACE { |
7c673cae FG |
24 | |
25 | class MemFile { | |
26 | public: | |
27 | explicit MemFile(Env* env, const std::string& fn, bool _is_lock_file = false) | |
28 | : env_(env), | |
29 | fn_(fn), | |
30 | refs_(0), | |
31 | is_lock_file_(_is_lock_file), | |
32 | locked_(false), | |
33 | size_(0), | |
34 | modified_time_(Now()), | |
20effc67 | 35 | rnd_(Lower32of64(GetSliceNPHash64(fn))), |
7c673cae | 36 | fsynced_bytes_(0) {} |
f67539c2 TL |
37 | // No copying allowed. |
38 | MemFile(const MemFile&) = delete; | |
39 | void operator=(const MemFile&) = delete; | |
7c673cae FG |
40 | |
41 | void Ref() { | |
42 | MutexLock lock(&mutex_); | |
43 | ++refs_; | |
44 | } | |
45 | ||
46 | bool is_lock_file() const { return is_lock_file_; } | |
47 | ||
48 | bool Lock() { | |
49 | assert(is_lock_file_); | |
50 | MutexLock lock(&mutex_); | |
51 | if (locked_) { | |
52 | return false; | |
53 | } else { | |
54 | locked_ = true; | |
55 | return true; | |
56 | } | |
57 | } | |
58 | ||
59 | void Unlock() { | |
60 | assert(is_lock_file_); | |
61 | MutexLock lock(&mutex_); | |
62 | locked_ = false; | |
63 | } | |
64 | ||
65 | void Unref() { | |
66 | bool do_delete = false; | |
67 | { | |
68 | MutexLock lock(&mutex_); | |
69 | --refs_; | |
70 | assert(refs_ >= 0); | |
71 | if (refs_ <= 0) { | |
72 | do_delete = true; | |
73 | } | |
74 | } | |
75 | ||
76 | if (do_delete) { | |
77 | delete this; | |
78 | } | |
79 | } | |
80 | ||
11fdf7f2 | 81 | uint64_t Size() const { return size_; } |
7c673cae | 82 | |
20effc67 TL |
83 | void Truncate(size_t size, const IOOptions& /*options*/, |
84 | IODebugContext* /*dbg*/) { | |
7c673cae FG |
85 | MutexLock lock(&mutex_); |
86 | if (size < size_) { | |
87 | data_.resize(size); | |
88 | size_ = size; | |
89 | } | |
90 | } | |
91 | ||
92 | void CorruptBuffer() { | |
93 | if (fsynced_bytes_ >= size_) { | |
94 | return; | |
95 | } | |
96 | uint64_t buffered_bytes = size_ - fsynced_bytes_; | |
97 | uint64_t start = | |
98 | fsynced_bytes_ + rnd_.Uniform(static_cast<int>(buffered_bytes)); | |
99 | uint64_t end = std::min(start + 512, size_.load()); | |
100 | MutexLock lock(&mutex_); | |
101 | for (uint64_t pos = start; pos < end; ++pos) { | |
11fdf7f2 | 102 | data_[static_cast<size_t>(pos)] = static_cast<char>(rnd_.Uniform(256)); |
7c673cae FG |
103 | } |
104 | } | |
105 | ||
20effc67 TL |
106 | IOStatus Read(uint64_t offset, size_t n, const IOOptions& /*options*/, |
107 | Slice* result, char* scratch, IODebugContext* /*dbg*/) const { | |
7c673cae FG |
108 | MutexLock lock(&mutex_); |
109 | const uint64_t available = Size() - std::min(Size(), offset); | |
11fdf7f2 | 110 | size_t offset_ = static_cast<size_t>(offset); |
7c673cae | 111 | if (n > available) { |
11fdf7f2 | 112 | n = static_cast<size_t>(available); |
7c673cae FG |
113 | } |
114 | if (n == 0) { | |
115 | *result = Slice(); | |
20effc67 | 116 | return IOStatus::OK(); |
7c673cae FG |
117 | } |
118 | if (scratch) { | |
11fdf7f2 | 119 | memcpy(scratch, &(data_[offset_]), n); |
7c673cae FG |
120 | *result = Slice(scratch, n); |
121 | } else { | |
11fdf7f2 | 122 | *result = Slice(&(data_[offset_]), n); |
7c673cae | 123 | } |
20effc67 | 124 | return IOStatus::OK(); |
7c673cae FG |
125 | } |
126 | ||
20effc67 TL |
127 | IOStatus Write(uint64_t offset, const Slice& data, |
128 | const IOOptions& /*options*/, IODebugContext* /*dbg*/) { | |
7c673cae | 129 | MutexLock lock(&mutex_); |
11fdf7f2 | 130 | size_t offset_ = static_cast<size_t>(offset); |
7c673cae | 131 | if (offset + data.size() > data_.size()) { |
11fdf7f2 | 132 | data_.resize(offset_ + data.size()); |
7c673cae | 133 | } |
11fdf7f2 | 134 | data_.replace(offset_, data.size(), data.data(), data.size()); |
7c673cae FG |
135 | size_ = data_.size(); |
136 | modified_time_ = Now(); | |
20effc67 | 137 | return IOStatus::OK(); |
7c673cae FG |
138 | } |
139 | ||
20effc67 TL |
140 | IOStatus Append(const Slice& data, const IOOptions& /*options*/, |
141 | IODebugContext* /*dbg*/) { | |
7c673cae FG |
142 | MutexLock lock(&mutex_); |
143 | data_.append(data.data(), data.size()); | |
144 | size_ = data_.size(); | |
145 | modified_time_ = Now(); | |
20effc67 | 146 | return IOStatus::OK(); |
7c673cae FG |
147 | } |
148 | ||
20effc67 | 149 | IOStatus Fsync(const IOOptions& /*options*/, IODebugContext* /*dbg*/) { |
7c673cae | 150 | fsynced_bytes_ = size_.load(); |
20effc67 | 151 | return IOStatus::OK(); |
7c673cae FG |
152 | } |
153 | ||
11fdf7f2 | 154 | uint64_t ModifiedTime() const { return modified_time_; } |
7c673cae FG |
155 | |
156 | private: | |
157 | uint64_t Now() { | |
11fdf7f2 | 158 | int64_t unix_time = 0; |
7c673cae FG |
159 | auto s = env_->GetCurrentTime(&unix_time); |
160 | assert(s.ok()); | |
161 | return static_cast<uint64_t>(unix_time); | |
162 | } | |
163 | ||
164 | // Private since only Unref() should be used to delete it. | |
11fdf7f2 | 165 | ~MemFile() { assert(refs_ == 0); } |
7c673cae | 166 | |
7c673cae FG |
167 | Env* env_; |
168 | const std::string fn_; | |
169 | mutable port::Mutex mutex_; | |
170 | int refs_; | |
171 | bool is_lock_file_; | |
172 | bool locked_; | |
173 | ||
174 | // Data written into this file, all bytes before fsynced_bytes are | |
175 | // persistent. | |
176 | std::string data_; | |
177 | std::atomic<uint64_t> size_; | |
178 | std::atomic<uint64_t> modified_time_; | |
179 | ||
180 | Random rnd_; | |
181 | std::atomic<uint64_t> fsynced_bytes_; | |
182 | }; | |
183 | ||
184 | namespace { | |
185 | ||
20effc67 | 186 | class MockSequentialFile : public FSSequentialFile { |
7c673cae | 187 | public: |
20effc67 TL |
188 | explicit MockSequentialFile(MemFile* file, const FileOptions& opts) |
189 | : file_(file), | |
190 | use_direct_io_(opts.use_direct_reads), | |
191 | use_mmap_read_(opts.use_mmap_reads), | |
192 | pos_(0) { | |
7c673cae FG |
193 | file_->Ref(); |
194 | } | |
195 | ||
494da23a | 196 | ~MockSequentialFile() override { file_->Unref(); } |
7c673cae | 197 | |
20effc67 TL |
198 | IOStatus Read(size_t n, const IOOptions& options, Slice* result, |
199 | char* scratch, IODebugContext* dbg) override { | |
200 | IOStatus s = file_->Read(pos_, n, options, result, | |
201 | (use_mmap_read_) ? nullptr : scratch, dbg); | |
7c673cae FG |
202 | if (s.ok()) { |
203 | pos_ += result->size(); | |
204 | } | |
205 | return s; | |
206 | } | |
207 | ||
20effc67 TL |
208 | bool use_direct_io() const override { return use_direct_io_; } |
209 | IOStatus Skip(uint64_t n) override { | |
7c673cae | 210 | if (pos_ > file_->Size()) { |
20effc67 | 211 | return IOStatus::IOError("pos_ > file_->Size()"); |
7c673cae | 212 | } |
11fdf7f2 | 213 | const uint64_t available = file_->Size() - pos_; |
7c673cae FG |
214 | if (n > available) { |
215 | n = available; | |
216 | } | |
11fdf7f2 | 217 | pos_ += static_cast<size_t>(n); |
20effc67 | 218 | return IOStatus::OK(); |
7c673cae FG |
219 | } |
220 | ||
221 | private: | |
222 | MemFile* file_; | |
20effc67 TL |
223 | bool use_direct_io_; |
224 | bool use_mmap_read_; | |
7c673cae FG |
225 | size_t pos_; |
226 | }; | |
227 | ||
20effc67 | 228 | class MockRandomAccessFile : public FSRandomAccessFile { |
7c673cae | 229 | public: |
20effc67 TL |
230 | explicit MockRandomAccessFile(MemFile* file, const FileOptions& opts) |
231 | : file_(file), | |
232 | use_direct_io_(opts.use_direct_reads), | |
233 | use_mmap_read_(opts.use_mmap_reads) { | |
234 | file_->Ref(); | |
235 | } | |
7c673cae | 236 | |
494da23a | 237 | ~MockRandomAccessFile() override { file_->Unref(); } |
7c673cae | 238 | |
20effc67 TL |
239 | bool use_direct_io() const override { return use_direct_io_; } |
240 | ||
241 | IOStatus Prefetch(uint64_t /*offset*/, size_t /*n*/, | |
242 | const IOOptions& /*options*/, | |
243 | IODebugContext* /*dbg*/) override { | |
244 | return IOStatus::OK(); | |
245 | } | |
246 | ||
247 | IOStatus Read(uint64_t offset, size_t n, const IOOptions& options, | |
248 | Slice* result, char* scratch, | |
249 | IODebugContext* dbg) const override { | |
250 | if (use_mmap_read_) { | |
251 | return file_->Read(offset, n, options, result, nullptr, dbg); | |
252 | } else { | |
253 | return file_->Read(offset, n, options, result, scratch, dbg); | |
254 | } | |
7c673cae FG |
255 | } |
256 | ||
257 | private: | |
258 | MemFile* file_; | |
20effc67 TL |
259 | bool use_direct_io_; |
260 | bool use_mmap_read_; | |
7c673cae FG |
261 | }; |
262 | ||
20effc67 | 263 | class MockRandomRWFile : public FSRandomRWFile { |
7c673cae FG |
264 | public: |
265 | explicit MockRandomRWFile(MemFile* file) : file_(file) { file_->Ref(); } | |
266 | ||
494da23a | 267 | ~MockRandomRWFile() override { file_->Unref(); } |
7c673cae | 268 | |
20effc67 TL |
269 | IOStatus Write(uint64_t offset, const Slice& data, const IOOptions& options, |
270 | IODebugContext* dbg) override { | |
271 | return file_->Write(offset, data, options, dbg); | |
7c673cae FG |
272 | } |
273 | ||
20effc67 TL |
274 | IOStatus Read(uint64_t offset, size_t n, const IOOptions& options, |
275 | Slice* result, char* scratch, | |
276 | IODebugContext* dbg) const override { | |
277 | return file_->Read(offset, n, options, result, scratch, dbg); | |
7c673cae FG |
278 | } |
279 | ||
20effc67 TL |
280 | IOStatus Close(const IOOptions& options, IODebugContext* dbg) override { |
281 | return file_->Fsync(options, dbg); | |
282 | } | |
7c673cae | 283 | |
20effc67 TL |
284 | IOStatus Flush(const IOOptions& /*options*/, |
285 | IODebugContext* /*dbg*/) override { | |
286 | return IOStatus::OK(); | |
287 | } | |
7c673cae | 288 | |
20effc67 TL |
289 | IOStatus Sync(const IOOptions& options, IODebugContext* dbg) override { |
290 | return file_->Fsync(options, dbg); | |
291 | } | |
7c673cae FG |
292 | |
293 | private: | |
294 | MemFile* file_; | |
295 | }; | |
296 | ||
20effc67 | 297 | class MockWritableFile : public FSWritableFile { |
7c673cae | 298 | public: |
20effc67 TL |
299 | MockWritableFile(MemFile* file, const FileOptions& opts) |
300 | : file_(file), | |
301 | use_direct_io_(opts.use_direct_writes), | |
302 | rate_limiter_(opts.rate_limiter) { | |
7c673cae FG |
303 | file_->Ref(); |
304 | } | |
305 | ||
494da23a | 306 | ~MockWritableFile() override { file_->Unref(); } |
7c673cae | 307 | |
20effc67 TL |
308 | bool use_direct_io() const override { return false && use_direct_io_; } |
309 | ||
310 | using FSWritableFile::Append; | |
311 | IOStatus Append(const Slice& data, const IOOptions& options, | |
312 | IODebugContext* dbg) override { | |
11fdf7f2 | 313 | size_t bytes_written = 0; |
7c673cae FG |
314 | while (bytes_written < data.size()) { |
315 | auto bytes = RequestToken(data.size() - bytes_written); | |
20effc67 TL |
316 | IOStatus s = file_->Append(Slice(data.data() + bytes_written, bytes), |
317 | options, dbg); | |
7c673cae FG |
318 | if (!s.ok()) { |
319 | return s; | |
320 | } | |
321 | bytes_written += bytes; | |
322 | } | |
20effc67 TL |
323 | return IOStatus::OK(); |
324 | } | |
325 | ||
326 | using FSWritableFile::PositionedAppend; | |
327 | IOStatus PositionedAppend(const Slice& data, uint64_t /*offset*/, | |
328 | const IOOptions& options, | |
329 | IODebugContext* dbg) override { | |
330 | assert(use_direct_io_); | |
331 | return Append(data, options, dbg); | |
332 | } | |
333 | ||
334 | IOStatus Truncate(uint64_t size, const IOOptions& options, | |
335 | IODebugContext* dbg) override { | |
336 | file_->Truncate(static_cast<size_t>(size), options, dbg); | |
337 | return IOStatus::OK(); | |
7c673cae | 338 | } |
20effc67 TL |
339 | IOStatus Close(const IOOptions& options, IODebugContext* dbg) override { |
340 | return file_->Fsync(options, dbg); | |
7c673cae | 341 | } |
7c673cae | 342 | |
20effc67 TL |
343 | IOStatus Flush(const IOOptions& /*options*/, |
344 | IODebugContext* /*dbg*/) override { | |
345 | return IOStatus::OK(); | |
346 | } | |
7c673cae | 347 | |
20effc67 TL |
348 | IOStatus Sync(const IOOptions& options, IODebugContext* dbg) override { |
349 | return file_->Fsync(options, dbg); | |
350 | } | |
7c673cae | 351 | |
20effc67 TL |
352 | uint64_t GetFileSize(const IOOptions& /*options*/, |
353 | IODebugContext* /*dbg*/) override { | |
354 | return file_->Size(); | |
355 | } | |
7c673cae FG |
356 | |
357 | private: | |
358 | inline size_t RequestToken(size_t bytes) { | |
359 | if (rate_limiter_ && io_priority_ < Env::IO_TOTAL) { | |
11fdf7f2 TL |
360 | bytes = std::min( |
361 | bytes, static_cast<size_t>(rate_limiter_->GetSingleBurstBytes())); | |
7c673cae FG |
362 | rate_limiter_->Request(bytes, io_priority_); |
363 | } | |
364 | return bytes; | |
365 | } | |
366 | ||
367 | MemFile* file_; | |
20effc67 | 368 | bool use_direct_io_; |
7c673cae FG |
369 | RateLimiter* rate_limiter_; |
370 | }; | |
371 | ||
20effc67 | 372 | class MockEnvDirectory : public FSDirectory { |
7c673cae | 373 | public: |
20effc67 TL |
374 | IOStatus Fsync(const IOOptions& /*options*/, |
375 | IODebugContext* /*dbg*/) override { | |
376 | return IOStatus::OK(); | |
377 | } | |
7c673cae FG |
378 | }; |
379 | ||
380 | class MockEnvFileLock : public FileLock { | |
381 | public: | |
11fdf7f2 | 382 | explicit MockEnvFileLock(const std::string& fname) : fname_(fname) {} |
7c673cae | 383 | |
11fdf7f2 | 384 | std::string FileName() const { return fname_; } |
7c673cae FG |
385 | |
386 | private: | |
387 | const std::string fname_; | |
388 | }; | |
389 | ||
390 | class TestMemLogger : public Logger { | |
391 | private: | |
20effc67 | 392 | std::unique_ptr<FSWritableFile> file_; |
7c673cae FG |
393 | std::atomic_size_t log_size_; |
394 | static const uint64_t flush_every_seconds_ = 5; | |
395 | std::atomic_uint_fast64_t last_flush_micros_; | |
396 | Env* env_; | |
20effc67 TL |
397 | IOOptions options_; |
398 | IODebugContext* dbg_; | |
494da23a | 399 | std::atomic<bool> flush_pending_; |
7c673cae FG |
400 | |
401 | public: | |
20effc67 TL |
402 | TestMemLogger(std::unique_ptr<FSWritableFile> f, Env* env, |
403 | const IOOptions& options, IODebugContext* dbg, | |
7c673cae FG |
404 | const InfoLogLevel log_level = InfoLogLevel::ERROR_LEVEL) |
405 | : Logger(log_level), | |
406 | file_(std::move(f)), | |
407 | log_size_(0), | |
408 | last_flush_micros_(0), | |
409 | env_(env), | |
20effc67 TL |
410 | options_(options), |
411 | dbg_(dbg), | |
7c673cae | 412 | flush_pending_(false) {} |
494da23a | 413 | ~TestMemLogger() override {} |
7c673cae | 414 | |
494da23a | 415 | void Flush() override { |
7c673cae FG |
416 | if (flush_pending_) { |
417 | flush_pending_ = false; | |
418 | } | |
419 | last_flush_micros_ = env_->NowMicros(); | |
420 | } | |
421 | ||
422 | using Logger::Logv; | |
494da23a | 423 | void Logv(const char* format, va_list ap) override { |
7c673cae FG |
424 | // We try twice: the first time with a fixed-size stack allocated buffer, |
425 | // and the second time with a much larger dynamically allocated buffer. | |
426 | char buffer[500]; | |
427 | for (int iter = 0; iter < 2; iter++) { | |
428 | char* base; | |
429 | int bufsize; | |
430 | if (iter == 0) { | |
431 | bufsize = sizeof(buffer); | |
432 | base = buffer; | |
433 | } else { | |
434 | bufsize = 30000; | |
435 | base = new char[bufsize]; | |
436 | } | |
437 | char* p = base; | |
438 | char* limit = base + bufsize; | |
439 | ||
440 | struct timeval now_tv; | |
441 | gettimeofday(&now_tv, nullptr); | |
442 | const time_t seconds = now_tv.tv_sec; | |
443 | struct tm t; | |
11fdf7f2 TL |
444 | memset(&t, 0, sizeof(t)); |
445 | struct tm* ret __attribute__((__unused__)); | |
446 | ret = localtime_r(&seconds, &t); | |
447 | assert(ret); | |
448 | p += snprintf(p, limit - p, "%04d/%02d/%02d-%02d:%02d:%02d.%06d ", | |
449 | t.tm_year + 1900, t.tm_mon + 1, t.tm_mday, t.tm_hour, | |
450 | t.tm_min, t.tm_sec, static_cast<int>(now_tv.tv_usec)); | |
7c673cae FG |
451 | |
452 | // Print the message | |
453 | if (p < limit) { | |
454 | va_list backup_ap; | |
455 | va_copy(backup_ap, ap); | |
456 | p += vsnprintf(p, limit - p, format, backup_ap); | |
457 | va_end(backup_ap); | |
458 | } | |
459 | ||
460 | // Truncate to available space if necessary | |
461 | if (p >= limit) { | |
462 | if (iter == 0) { | |
11fdf7f2 | 463 | continue; // Try again with larger buffer |
7c673cae FG |
464 | } else { |
465 | p = limit - 1; | |
466 | } | |
467 | } | |
468 | ||
469 | // Add newline if necessary | |
470 | if (p == base || p[-1] != '\n') { | |
471 | *p++ = '\n'; | |
472 | } | |
473 | ||
474 | assert(p <= limit); | |
475 | const size_t write_size = p - base; | |
476 | ||
20effc67 TL |
477 | Status s = file_->Append(Slice(base, write_size), options_, dbg_); |
478 | if (s.ok()) { | |
479 | flush_pending_ = true; | |
480 | log_size_ += write_size; | |
481 | } | |
11fdf7f2 TL |
482 | uint64_t now_micros = |
483 | static_cast<uint64_t>(now_tv.tv_sec) * 1000000 + now_tv.tv_usec; | |
7c673cae FG |
484 | if (now_micros - last_flush_micros_ >= flush_every_seconds_ * 1000000) { |
485 | flush_pending_ = false; | |
486 | last_flush_micros_ = now_micros; | |
487 | } | |
488 | if (base != buffer) { | |
489 | delete[] base; | |
490 | } | |
491 | break; | |
492 | } | |
493 | } | |
494 | size_t GetLogFileSize() const override { return log_size_; } | |
495 | }; | |
496 | ||
20effc67 TL |
497 | class MockFileSystem : public FileSystem { |
498 | public: | |
499 | explicit MockFileSystem(Env* env, bool supports_direct_io = true) | |
500 | : env_(env), supports_direct_io_(supports_direct_io) {} | |
7c673cae | 501 | |
20effc67 TL |
502 | ~MockFileSystem() override { |
503 | for (auto i = file_map_.begin(); i != file_map_.end(); ++i) { | |
504 | i->second->Unref(); | |
505 | } | |
506 | } | |
7c673cae | 507 | |
20effc67 TL |
508 | const char* Name() const override { return "Memory"; } |
509 | IOStatus NewSequentialFile(const std::string& f, const FileOptions& file_opts, | |
510 | std::unique_ptr<FSSequentialFile>* r, | |
511 | IODebugContext* dbg) override; | |
512 | IOStatus NewRandomAccessFile(const std::string& f, | |
513 | const FileOptions& file_opts, | |
514 | std::unique_ptr<FSRandomAccessFile>* r, | |
515 | IODebugContext* dbg) override; | |
516 | ||
517 | IOStatus NewRandomRWFile(const std::string& fname, | |
518 | const FileOptions& file_opts, | |
519 | std::unique_ptr<FSRandomRWFile>* result, | |
520 | IODebugContext* dbg) override; | |
521 | IOStatus ReuseWritableFile(const std::string& fname, | |
522 | const std::string& old_fname, | |
523 | const FileOptions& file_opts, | |
524 | std::unique_ptr<FSWritableFile>* result, | |
525 | IODebugContext* dbg) override; | |
526 | IOStatus NewWritableFile(const std::string& fname, | |
527 | const FileOptions& file_opts, | |
528 | std::unique_ptr<FSWritableFile>* result, | |
529 | IODebugContext* dbg) override; | |
530 | IOStatus ReopenWritableFile(const std::string& fname, | |
531 | const FileOptions& options, | |
532 | std::unique_ptr<FSWritableFile>* result, | |
533 | IODebugContext* dbg) override; | |
534 | IOStatus NewDirectory(const std::string& /*name*/, const IOOptions& io_opts, | |
535 | std::unique_ptr<FSDirectory>* result, | |
536 | IODebugContext* dbg) override; | |
537 | IOStatus FileExists(const std::string& fname, const IOOptions& /*io_opts*/, | |
538 | IODebugContext* /*dbg*/) override; | |
539 | IOStatus GetChildren(const std::string& dir, const IOOptions& options, | |
540 | std::vector<std::string>* result, | |
541 | IODebugContext* dbg) override; | |
542 | IOStatus DeleteFile(const std::string& fname, const IOOptions& options, | |
543 | IODebugContext* dbg) override; | |
544 | IOStatus Truncate(const std::string& fname, size_t size, | |
545 | const IOOptions& options, IODebugContext* dbg) override; | |
546 | IOStatus CreateDir(const std::string& dirname, const IOOptions& options, | |
547 | IODebugContext* dbg) override; | |
548 | IOStatus CreateDirIfMissing(const std::string& dirname, | |
549 | const IOOptions& options, | |
550 | IODebugContext* dbg) override; | |
551 | IOStatus DeleteDir(const std::string& dirname, const IOOptions& options, | |
552 | IODebugContext* dbg) override; | |
553 | ||
554 | IOStatus GetFileSize(const std::string& fname, const IOOptions& options, | |
555 | uint64_t* file_size, IODebugContext* dbg) override; | |
556 | ||
557 | IOStatus GetFileModificationTime(const std::string& fname, | |
558 | const IOOptions& options, | |
559 | uint64_t* file_mtime, | |
560 | IODebugContext* dbg) override; | |
561 | IOStatus RenameFile(const std::string& src, const std::string& target, | |
562 | const IOOptions& options, IODebugContext* dbg) override; | |
563 | IOStatus LinkFile(const std::string& /*src*/, const std::string& /*target*/, | |
564 | const IOOptions& /*options*/, | |
565 | IODebugContext* /*dbg*/) override; | |
566 | IOStatus LockFile(const std::string& fname, const IOOptions& options, | |
567 | FileLock** lock, IODebugContext* dbg) override; | |
568 | IOStatus UnlockFile(FileLock* lock, const IOOptions& options, | |
569 | IODebugContext* dbg) override; | |
570 | IOStatus GetTestDirectory(const IOOptions& options, std::string* path, | |
571 | IODebugContext* dbg) override; | |
572 | IOStatus NewLogger(const std::string& fname, const IOOptions& io_opts, | |
573 | std::shared_ptr<Logger>* result, | |
574 | IODebugContext* dbg) override; | |
575 | // Get full directory name for this db. | |
576 | IOStatus GetAbsolutePath(const std::string& db_path, | |
577 | const IOOptions& /*options*/, | |
578 | std::string* output_path, | |
579 | IODebugContext* /*dbg*/) override { | |
580 | *output_path = NormalizeMockPath(db_path); | |
581 | if (output_path->at(0) != '/') { | |
582 | return IOStatus::NotSupported("GetAbsolutePath"); | |
583 | } else { | |
584 | return IOStatus::OK(); | |
585 | } | |
7c673cae | 586 | } |
20effc67 TL |
587 | IOStatus IsDirectory(const std::string& /*path*/, |
588 | const IOOptions& /*options*/, bool* /*is_dir*/, | |
589 | IODebugContext* /*dgb*/) override { | |
590 | return IOStatus::NotSupported("IsDirectory"); | |
591 | } | |
592 | ||
593 | Status CorruptBuffer(const std::string& fname); | |
594 | ||
595 | private: | |
596 | bool RenameFileInternal(const std::string& src, const std::string& dest); | |
597 | void DeleteFileInternal(const std::string& fname); | |
598 | bool GetChildrenInternal(const std::string& fname, | |
599 | std::vector<std::string>* results); | |
600 | ||
601 | std::string NormalizeMockPath(const std::string& path) { | |
602 | std::string p = NormalizePath(path); | |
603 | if (p.back() == kFilePathSeparator && p.size() > 1) { | |
604 | p.pop_back(); | |
605 | } | |
606 | return p; | |
607 | } | |
608 | ||
609 | private: | |
610 | // Map from filenames to MemFile objects, representing a simple file system. | |
611 | port::Mutex mutex_; | |
612 | std::map<std::string, MemFile*> file_map_; // Protected by mutex_. | |
613 | Env* env_; | |
614 | bool supports_direct_io_; | |
615 | }; | |
7c673cae | 616 | |
20effc67 | 617 | } // Anonymous namespace |
11fdf7f2 | 618 | // Partial implementation of the Env interface. |
20effc67 TL |
619 | IOStatus MockFileSystem::NewSequentialFile( |
620 | const std::string& fname, const FileOptions& file_opts, | |
621 | std::unique_ptr<FSSequentialFile>* result, IODebugContext* /*dbg*/) { | |
622 | auto fn = NormalizeMockPath(fname); | |
623 | ||
7c673cae FG |
624 | MutexLock lock(&mutex_); |
625 | if (file_map_.find(fn) == file_map_.end()) { | |
11fdf7f2 | 626 | *result = nullptr; |
20effc67 | 627 | return IOStatus::PathNotFound(fn); |
7c673cae FG |
628 | } |
629 | auto* f = file_map_[fn]; | |
630 | if (f->is_lock_file()) { | |
20effc67 TL |
631 | return IOStatus::InvalidArgument(fn, "Cannot open a lock file."); |
632 | } else if (file_opts.use_direct_reads && !supports_direct_io_) { | |
633 | return IOStatus::NotSupported("Direct I/O Not Supported"); | |
634 | } else { | |
635 | result->reset(new MockSequentialFile(f, file_opts)); | |
636 | return IOStatus::OK(); | |
7c673cae | 637 | } |
7c673cae FG |
638 | } |
639 | ||
20effc67 TL |
640 | IOStatus MockFileSystem::NewRandomAccessFile( |
641 | const std::string& fname, const FileOptions& file_opts, | |
642 | std::unique_ptr<FSRandomAccessFile>* result, IODebugContext* /*dbg*/) { | |
643 | auto fn = NormalizeMockPath(fname); | |
7c673cae FG |
644 | MutexLock lock(&mutex_); |
645 | if (file_map_.find(fn) == file_map_.end()) { | |
11fdf7f2 | 646 | *result = nullptr; |
20effc67 | 647 | return IOStatus::PathNotFound(fn); |
7c673cae FG |
648 | } |
649 | auto* f = file_map_[fn]; | |
650 | if (f->is_lock_file()) { | |
20effc67 TL |
651 | return IOStatus::InvalidArgument(fn, "Cannot open a lock file."); |
652 | } else if (file_opts.use_direct_reads && !supports_direct_io_) { | |
653 | return IOStatus::NotSupported("Direct I/O Not Supported"); | |
654 | } else { | |
655 | result->reset(new MockRandomAccessFile(f, file_opts)); | |
656 | return IOStatus::OK(); | |
7c673cae | 657 | } |
7c673cae FG |
658 | } |
659 | ||
20effc67 TL |
660 | IOStatus MockFileSystem::NewRandomRWFile( |
661 | const std::string& fname, const FileOptions& /*file_opts*/, | |
662 | std::unique_ptr<FSRandomRWFile>* result, IODebugContext* /*dbg*/) { | |
663 | auto fn = NormalizeMockPath(fname); | |
7c673cae FG |
664 | MutexLock lock(&mutex_); |
665 | if (file_map_.find(fn) == file_map_.end()) { | |
11fdf7f2 | 666 | *result = nullptr; |
20effc67 | 667 | return IOStatus::PathNotFound(fn); |
7c673cae FG |
668 | } |
669 | auto* f = file_map_[fn]; | |
670 | if (f->is_lock_file()) { | |
20effc67 | 671 | return IOStatus::InvalidArgument(fn, "Cannot open a lock file."); |
7c673cae FG |
672 | } |
673 | result->reset(new MockRandomRWFile(f)); | |
20effc67 | 674 | return IOStatus::OK(); |
7c673cae FG |
675 | } |
676 | ||
20effc67 TL |
677 | IOStatus MockFileSystem::ReuseWritableFile( |
678 | const std::string& fname, const std::string& old_fname, | |
679 | const FileOptions& options, std::unique_ptr<FSWritableFile>* result, | |
680 | IODebugContext* dbg) { | |
681 | auto s = RenameFile(old_fname, fname, IOOptions(), dbg); | |
7c673cae FG |
682 | if (!s.ok()) { |
683 | return s; | |
20effc67 TL |
684 | } else { |
685 | result->reset(); | |
686 | return NewWritableFile(fname, options, result, dbg); | |
7c673cae | 687 | } |
7c673cae FG |
688 | } |
689 | ||
20effc67 TL |
690 | IOStatus MockFileSystem::NewWritableFile( |
691 | const std::string& fname, const FileOptions& file_opts, | |
692 | std::unique_ptr<FSWritableFile>* result, IODebugContext* /*dbg*/) { | |
693 | auto fn = NormalizeMockPath(fname); | |
7c673cae FG |
694 | MutexLock lock(&mutex_); |
695 | if (file_map_.find(fn) != file_map_.end()) { | |
696 | DeleteFileInternal(fn); | |
697 | } | |
20effc67 | 698 | MemFile* file = new MemFile(env_, fn, false); |
7c673cae FG |
699 | file->Ref(); |
700 | file_map_[fn] = file; | |
20effc67 TL |
701 | if (file_opts.use_direct_writes && !supports_direct_io_) { |
702 | return IOStatus::NotSupported("Direct I/O Not Supported"); | |
703 | } else { | |
704 | result->reset(new MockWritableFile(file, file_opts)); | |
705 | return IOStatus::OK(); | |
706 | } | |
707 | } | |
7c673cae | 708 | |
20effc67 TL |
709 | IOStatus MockFileSystem::ReopenWritableFile( |
710 | const std::string& fname, const FileOptions& file_opts, | |
711 | std::unique_ptr<FSWritableFile>* result, IODebugContext* /*dbg*/) { | |
712 | auto fn = NormalizeMockPath(fname); | |
713 | MutexLock lock(&mutex_); | |
714 | MemFile* file = nullptr; | |
715 | if (file_map_.find(fn) == file_map_.end()) { | |
716 | file = new MemFile(env_, fn, false); | |
717 | file_map_[fn] = file; | |
718 | } else { | |
719 | file = file_map_[fn]; | |
720 | } | |
721 | file->Ref(); | |
722 | if (file_opts.use_direct_writes && !supports_direct_io_) { | |
723 | return IOStatus::NotSupported("Direct I/O Not Supported"); | |
724 | } else { | |
725 | result->reset(new MockWritableFile(file, file_opts)); | |
726 | return IOStatus::OK(); | |
727 | } | |
7c673cae FG |
728 | } |
729 | ||
20effc67 TL |
730 | IOStatus MockFileSystem::NewDirectory(const std::string& /*name*/, |
731 | const IOOptions& /*io_opts*/, | |
732 | std::unique_ptr<FSDirectory>* result, | |
733 | IODebugContext* /*dbg*/) { | |
7c673cae | 734 | result->reset(new MockEnvDirectory()); |
20effc67 | 735 | return IOStatus::OK(); |
7c673cae FG |
736 | } |
737 | ||
20effc67 TL |
738 | IOStatus MockFileSystem::FileExists(const std::string& fname, |
739 | const IOOptions& /*io_opts*/, | |
740 | IODebugContext* /*dbg*/) { | |
741 | auto fn = NormalizeMockPath(fname); | |
7c673cae FG |
742 | MutexLock lock(&mutex_); |
743 | if (file_map_.find(fn) != file_map_.end()) { | |
744 | // File exists | |
20effc67 | 745 | return IOStatus::OK(); |
7c673cae FG |
746 | } |
747 | // Now also check if fn exists as a dir | |
748 | for (const auto& iter : file_map_) { | |
749 | const std::string& filename = iter.first; | |
11fdf7f2 | 750 | if (filename.size() >= fn.size() + 1 && filename[fn.size()] == '/' && |
7c673cae | 751 | Slice(filename).starts_with(Slice(fn))) { |
20effc67 | 752 | return IOStatus::OK(); |
7c673cae FG |
753 | } |
754 | } | |
20effc67 | 755 | return IOStatus::NotFound(); |
7c673cae FG |
756 | } |
757 | ||
20effc67 TL |
758 | bool MockFileSystem::GetChildrenInternal(const std::string& dir, |
759 | std::vector<std::string>* result) { | |
760 | auto d = NormalizeMockPath(dir); | |
7c673cae | 761 | bool found_dir = false; |
20effc67 TL |
762 | result->clear(); |
763 | for (const auto& iter : file_map_) { | |
764 | const std::string& filename = iter.first; | |
765 | ||
766 | if (filename == d) { | |
767 | found_dir = true; | |
768 | } else if (filename.size() >= d.size() + 1 && filename[d.size()] == '/' && | |
769 | Slice(filename).starts_with(Slice(d))) { | |
770 | found_dir = true; | |
771 | size_t next_slash = filename.find('/', d.size() + 1); | |
772 | if (next_slash != std::string::npos) { | |
773 | result->push_back( | |
774 | filename.substr(d.size() + 1, next_slash - d.size() - 1)); | |
775 | } else { | |
776 | result->push_back(filename.substr(d.size() + 1)); | |
7c673cae FG |
777 | } |
778 | } | |
779 | } | |
780 | result->erase(std::unique(result->begin(), result->end()), result->end()); | |
20effc67 | 781 | return found_dir; |
7c673cae FG |
782 | } |
783 | ||
20effc67 TL |
784 | IOStatus MockFileSystem::GetChildren(const std::string& dir, |
785 | const IOOptions& /*options*/, | |
786 | std::vector<std::string>* result, | |
787 | IODebugContext* /*dbg*/) { | |
788 | MutexLock lock(&mutex_); | |
789 | bool found_dir = GetChildrenInternal(dir, result); | |
790 | return found_dir ? IOStatus::OK() : IOStatus::NotFound(dir); | |
791 | } | |
792 | ||
793 | void MockFileSystem::DeleteFileInternal(const std::string& fname) { | |
794 | assert(fname == NormalizeMockPath(fname)); | |
7c673cae FG |
795 | const auto& pair = file_map_.find(fname); |
796 | if (pair != file_map_.end()) { | |
797 | pair->second->Unref(); | |
798 | file_map_.erase(fname); | |
799 | } | |
800 | } | |
801 | ||
20effc67 TL |
802 | IOStatus MockFileSystem::DeleteFile(const std::string& fname, |
803 | const IOOptions& /*options*/, | |
804 | IODebugContext* /*dbg*/) { | |
805 | auto fn = NormalizeMockPath(fname); | |
7c673cae FG |
806 | MutexLock lock(&mutex_); |
807 | if (file_map_.find(fn) == file_map_.end()) { | |
20effc67 | 808 | return IOStatus::PathNotFound(fn); |
7c673cae FG |
809 | } |
810 | ||
811 | DeleteFileInternal(fn); | |
20effc67 | 812 | return IOStatus::OK(); |
7c673cae FG |
813 | } |
814 | ||
20effc67 TL |
815 | IOStatus MockFileSystem::Truncate(const std::string& fname, size_t size, |
816 | const IOOptions& options, | |
817 | IODebugContext* dbg) { | |
818 | auto fn = NormalizeMockPath(fname); | |
11fdf7f2 TL |
819 | MutexLock lock(&mutex_); |
820 | auto iter = file_map_.find(fn); | |
821 | if (iter == file_map_.end()) { | |
20effc67 | 822 | return IOStatus::PathNotFound(fn); |
11fdf7f2 | 823 | } |
20effc67 TL |
824 | iter->second->Truncate(size, options, dbg); |
825 | return IOStatus::OK(); | |
11fdf7f2 TL |
826 | } |
827 | ||
20effc67 TL |
828 | IOStatus MockFileSystem::CreateDir(const std::string& dirname, |
829 | const IOOptions& /*options*/, | |
830 | IODebugContext* /*dbg*/) { | |
831 | auto dn = NormalizeMockPath(dirname); | |
832 | MutexLock lock(&mutex_); | |
7c673cae | 833 | if (file_map_.find(dn) == file_map_.end()) { |
20effc67 | 834 | MemFile* file = new MemFile(env_, dn, false); |
7c673cae FG |
835 | file->Ref(); |
836 | file_map_[dn] = file; | |
837 | } else { | |
20effc67 | 838 | return IOStatus::IOError(); |
7c673cae | 839 | } |
20effc67 | 840 | return IOStatus::OK(); |
7c673cae FG |
841 | } |
842 | ||
20effc67 TL |
843 | IOStatus MockFileSystem::CreateDirIfMissing(const std::string& dirname, |
844 | const IOOptions& options, | |
845 | IODebugContext* dbg) { | |
846 | CreateDir(dirname, options, dbg).PermitUncheckedError(); | |
847 | return IOStatus::OK(); | |
7c673cae FG |
848 | } |
849 | ||
20effc67 TL |
850 | IOStatus MockFileSystem::DeleteDir(const std::string& dirname, |
851 | const IOOptions& /*options*/, | |
852 | IODebugContext* /*dbg*/) { | |
853 | auto dir = NormalizeMockPath(dirname); | |
854 | MutexLock lock(&mutex_); | |
855 | if (file_map_.find(dir) == file_map_.end()) { | |
856 | return IOStatus::PathNotFound(dir); | |
857 | } else { | |
858 | std::vector<std::string> children; | |
859 | if (GetChildrenInternal(dir, &children)) { | |
860 | for (const auto& child : children) { | |
861 | DeleteFileInternal(child); | |
862 | } | |
863 | } | |
864 | DeleteFileInternal(dir); | |
865 | return IOStatus::OK(); | |
866 | } | |
7c673cae FG |
867 | } |
868 | ||
20effc67 TL |
869 | IOStatus MockFileSystem::GetFileSize(const std::string& fname, |
870 | const IOOptions& /*options*/, | |
871 | uint64_t* file_size, | |
872 | IODebugContext* /*dbg*/) { | |
873 | auto fn = NormalizeMockPath(fname); | |
7c673cae FG |
874 | MutexLock lock(&mutex_); |
875 | auto iter = file_map_.find(fn); | |
876 | if (iter == file_map_.end()) { | |
20effc67 | 877 | return IOStatus::PathNotFound(fn); |
7c673cae FG |
878 | } |
879 | ||
880 | *file_size = iter->second->Size(); | |
20effc67 | 881 | return IOStatus::OK(); |
7c673cae FG |
882 | } |
883 | ||
20effc67 TL |
884 | IOStatus MockFileSystem::GetFileModificationTime(const std::string& fname, |
885 | const IOOptions& /*options*/, | |
886 | uint64_t* time, | |
887 | IODebugContext* /*dbg*/) { | |
888 | auto fn = NormalizeMockPath(fname); | |
7c673cae FG |
889 | MutexLock lock(&mutex_); |
890 | auto iter = file_map_.find(fn); | |
891 | if (iter == file_map_.end()) { | |
20effc67 | 892 | return IOStatus::PathNotFound(fn); |
7c673cae FG |
893 | } |
894 | *time = iter->second->ModifiedTime(); | |
20effc67 | 895 | return IOStatus::OK(); |
7c673cae FG |
896 | } |
897 | ||
20effc67 TL |
898 | bool MockFileSystem::RenameFileInternal(const std::string& src, |
899 | const std::string& dest) { | |
900 | if (file_map_.find(src) == file_map_.end()) { | |
901 | return false; | |
902 | } else { | |
903 | std::vector<std::string> children; | |
904 | if (GetChildrenInternal(src, &children)) { | |
905 | for (const auto& child : children) { | |
906 | RenameFileInternal(src + "/" + child, dest + "/" + child); | |
907 | } | |
908 | } | |
909 | DeleteFileInternal(dest); | |
910 | file_map_[dest] = file_map_[src]; | |
911 | file_map_.erase(src); | |
912 | return true; | |
7c673cae | 913 | } |
20effc67 | 914 | } |
7c673cae | 915 | |
20effc67 TL |
916 | IOStatus MockFileSystem::RenameFile(const std::string& src, |
917 | const std::string& dest, | |
918 | const IOOptions& /*options*/, | |
919 | IODebugContext* /*dbg*/) { | |
920 | auto s = NormalizeMockPath(src); | |
921 | auto t = NormalizeMockPath(dest); | |
922 | MutexLock lock(&mutex_); | |
923 | bool found = RenameFileInternal(s, t); | |
924 | if (!found) { | |
925 | return IOStatus::PathNotFound(s); | |
926 | } else { | |
927 | return IOStatus::OK(); | |
928 | } | |
7c673cae FG |
929 | } |
930 | ||
20effc67 TL |
931 | IOStatus MockFileSystem::LinkFile(const std::string& src, |
932 | const std::string& dest, | |
933 | const IOOptions& /*options*/, | |
934 | IODebugContext* /*dbg*/) { | |
935 | auto s = NormalizeMockPath(src); | |
936 | auto t = NormalizeMockPath(dest); | |
7c673cae FG |
937 | MutexLock lock(&mutex_); |
938 | if (file_map_.find(s) == file_map_.end()) { | |
20effc67 | 939 | return IOStatus::PathNotFound(s); |
7c673cae FG |
940 | } |
941 | ||
942 | DeleteFileInternal(t); | |
943 | file_map_[t] = file_map_[s]; | |
944 | file_map_[t]->Ref(); // Otherwise it might get deleted when noone uses s | |
20effc67 | 945 | return IOStatus::OK(); |
7c673cae FG |
946 | } |
947 | ||
20effc67 TL |
948 | IOStatus MockFileSystem::NewLogger(const std::string& fname, |
949 | const IOOptions& io_opts, | |
950 | std::shared_ptr<Logger>* result, | |
951 | IODebugContext* dbg) { | |
952 | auto fn = NormalizeMockPath(fname); | |
7c673cae FG |
953 | MutexLock lock(&mutex_); |
954 | auto iter = file_map_.find(fn); | |
955 | MemFile* file = nullptr; | |
956 | if (iter == file_map_.end()) { | |
20effc67 | 957 | file = new MemFile(env_, fn, false); |
7c673cae FG |
958 | file->Ref(); |
959 | file_map_[fn] = file; | |
960 | } else { | |
961 | file = iter->second; | |
962 | } | |
20effc67 TL |
963 | std::unique_ptr<FSWritableFile> f(new MockWritableFile(file, FileOptions())); |
964 | result->reset(new TestMemLogger(std::move(f), env_, io_opts, dbg)); | |
965 | return IOStatus::OK(); | |
7c673cae FG |
966 | } |
967 | ||
20effc67 TL |
968 | IOStatus MockFileSystem::LockFile(const std::string& fname, |
969 | const IOOptions& /*options*/, | |
970 | FileLock** flock, IODebugContext* /*dbg*/) { | |
971 | auto fn = NormalizeMockPath(fname); | |
7c673cae FG |
972 | { |
973 | MutexLock lock(&mutex_); | |
974 | if (file_map_.find(fn) != file_map_.end()) { | |
975 | if (!file_map_[fn]->is_lock_file()) { | |
20effc67 | 976 | return IOStatus::InvalidArgument(fname, "Not a lock file."); |
7c673cae FG |
977 | } |
978 | if (!file_map_[fn]->Lock()) { | |
20effc67 | 979 | return IOStatus::IOError(fn, "lock is already held."); |
7c673cae FG |
980 | } |
981 | } else { | |
20effc67 | 982 | auto* file = new MemFile(env_, fn, true); |
7c673cae FG |
983 | file->Ref(); |
984 | file->Lock(); | |
985 | file_map_[fn] = file; | |
986 | } | |
987 | } | |
988 | *flock = new MockEnvFileLock(fn); | |
20effc67 | 989 | return IOStatus::OK(); |
7c673cae FG |
990 | } |
991 | ||
20effc67 TL |
992 | IOStatus MockFileSystem::UnlockFile(FileLock* flock, |
993 | const IOOptions& /*options*/, | |
994 | IODebugContext* /*dbg*/) { | |
995 | std::string fn = static_cast_with_check<MockEnvFileLock>(flock)->FileName(); | |
7c673cae FG |
996 | { |
997 | MutexLock lock(&mutex_); | |
998 | if (file_map_.find(fn) != file_map_.end()) { | |
999 | if (!file_map_[fn]->is_lock_file()) { | |
20effc67 | 1000 | return IOStatus::InvalidArgument(fn, "Not a lock file."); |
7c673cae FG |
1001 | } |
1002 | file_map_[fn]->Unlock(); | |
1003 | } | |
1004 | } | |
1005 | delete flock; | |
20effc67 | 1006 | return IOStatus::OK(); |
7c673cae FG |
1007 | } |
1008 | ||
20effc67 TL |
1009 | IOStatus MockFileSystem::GetTestDirectory(const IOOptions& /*options*/, |
1010 | std::string* path, | |
1011 | IODebugContext* /*dbg*/) { | |
7c673cae | 1012 | *path = "/test"; |
20effc67 TL |
1013 | return IOStatus::OK(); |
1014 | } | |
1015 | ||
1016 | Status MockFileSystem::CorruptBuffer(const std::string& fname) { | |
1017 | auto fn = NormalizeMockPath(fname); | |
1018 | MutexLock lock(&mutex_); | |
1019 | auto iter = file_map_.find(fn); | |
1020 | if (iter == file_map_.end()) { | |
1021 | return Status::IOError(fn, "File not found"); | |
1022 | } | |
1023 | iter->second->CorruptBuffer(); | |
7c673cae FG |
1024 | return Status::OK(); |
1025 | } | |
1026 | ||
20effc67 TL |
1027 | MockEnv::MockEnv(Env* base_env) |
1028 | : CompositeEnvWrapper(base_env, std::make_shared<MockFileSystem>(this)), | |
1029 | fake_sleep_micros_(0) {} | |
1030 | ||
7c673cae | 1031 | Status MockEnv::GetCurrentTime(int64_t* unix_time) { |
20effc67 | 1032 | auto s = CompositeEnvWrapper::GetCurrentTime(unix_time); |
11fdf7f2 TL |
1033 | if (s.ok()) { |
1034 | *unix_time += fake_sleep_micros_.load() / (1000 * 1000); | |
1035 | } | |
7c673cae FG |
1036 | return s; |
1037 | } | |
1038 | ||
1039 | uint64_t MockEnv::NowMicros() { | |
20effc67 | 1040 | return CompositeEnvWrapper::NowMicros() + fake_sleep_micros_.load(); |
7c673cae FG |
1041 | } |
1042 | ||
1043 | uint64_t MockEnv::NowNanos() { | |
20effc67 | 1044 | return CompositeEnvWrapper::NowNanos() + fake_sleep_micros_.load() * 1000; |
7c673cae FG |
1045 | } |
1046 | ||
7c673cae | 1047 | Status MockEnv::CorruptBuffer(const std::string& fname) { |
20effc67 TL |
1048 | auto mock = static_cast_with_check<MockFileSystem>(GetFileSystem().get()); |
1049 | return mock->CorruptBuffer(fname); | |
7c673cae FG |
1050 | } |
1051 | ||
1052 | void MockEnv::FakeSleepForMicroseconds(int64_t micros) { | |
1053 | fake_sleep_micros_.fetch_add(micros); | |
1054 | } | |
1055 | ||
11fdf7f2 TL |
1056 | #ifndef ROCKSDB_LITE |
1057 | // This is to maintain the behavior before swithcing from InMemoryEnv to MockEnv | |
1058 | Env* NewMemEnv(Env* base_env) { return new MockEnv(base_env); } | |
1059 | ||
1060 | #else // ROCKSDB_LITE | |
1061 | ||
1062 | Env* NewMemEnv(Env* /*base_env*/) { return nullptr; } | |
1063 | ||
1064 | #endif // !ROCKSDB_LITE | |
1065 | ||
f67539c2 | 1066 | } // namespace ROCKSDB_NAMESPACE |