YugabyteDB (2.13.1.0-b60, 21121d69985fbf76aa6958d8f04a9bfa936293b5)

Coverage Report

Created: 2022-03-22 16:43

/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