summaryrefslogtreecommitdiff
path: root/chromium/third_party/blink/renderer/platform/disk_data_allocator.h
blob: c7376ba292898d45561e52c5911dd455fe08127f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_DISK_DATA_ALLOCATOR_H_
#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_DISK_DATA_ALLOCATOR_H_

#include <map>
#include <memory>

#include "base/files/file.h"
#include "base/synchronization/lock.h"
#include "mojo/public/cpp/bindings/receiver.h"
#include "third_party/blink/public/mojom/disk_allocator.mojom-blink.h"
#include "third_party/blink/renderer/platform/platform_export.h"
#include "third_party/blink/renderer/platform/wtf/threading.h"
#include "third_party/blink/renderer/platform/wtf/threading_primitives.h"

namespace blink {

// Stores data onto a single file.
//
// The file is provided after construction. As a consequence, the allocator
// initially does not accept writes, that is |Write()| returns nullptr. It may
// also become not usable later, for instance if disk space is no longer
// available.
//
// Threading:
// - Reads must be done from the main thread
// - Writes can be done from any thread.
// - public methods are thread-safe, and unless otherwise noted, can be called
//   from any thread.
class PLATFORM_EXPORT DiskDataAllocator : public mojom::blink::DiskAllocator {
 public:
  class Metadata {
   public:
    int64_t start_offset() const { return start_offset_; }
    size_t size() const { return size_; }
    Metadata(Metadata&& other) = delete;

   private:
    Metadata(int64_t start_offset, size_t size)
        : start_offset_(start_offset), size_(size) {}
    Metadata(const Metadata& other) = default;
    Metadata& operator=(const Metadata& other) = default;

    int64_t start_offset_;
    size_t size_;

    friend class DiskDataAllocator;
  };

  // Must be called on the main thread.
  void ProvideTemporaryFile(::base::File file) override;

  // Whether writes may succeed. This is not a guarantee. However, when this
  // returns false, writes will fail.
  bool may_write() LOCKS_EXCLUDED(mutex_);

  // Returns |nullptr| in case of error.
  // Note that this performs a blocking disk write.
  std::unique_ptr<Metadata> Write(const void* data, size_t size);

  // Reads data. A read failure is fatal.
  // Must be called from the main thread.
  // Can be called at any time before |Discard()| destroys |metadata|.
  //
  // |data| must point to an area large enough to fit a |metadata.size|-ed
  // array. Note that this performs a blocking disk read.
  void Read(const Metadata& metadata, void* data);

  // Discards existing data pointed at by |metadata|.
  void Discard(std::unique_ptr<Metadata> metadata);

  ~DiskDataAllocator() override;
  static DiskDataAllocator& Instance();
  static void Bind(mojo::PendingReceiver<mojom::blink::DiskAllocator> receiver);

  int64_t disk_footprint() {
    MutexLocker locker(mutex_);
    return file_tail_;
  }

  size_t free_chunks_size() {
    MutexLocker locker(mutex_);
    return free_chunks_size_;
  }

 protected:
  // Protected methods for testing.
  DiskDataAllocator();
  void set_may_write_for_testing(bool may_write) LOCKS_EXCLUDED(mutex_);

 private:
  Metadata FindChunk(size_t size) EXCLUSIVE_LOCKS_REQUIRED(mutex_);
  void ReleaseChunk(const Metadata& metadata) EXCLUSIVE_LOCKS_REQUIRED(mutex_);

  // Virtual for testing.
  virtual int DoWrite(int64_t offset, const char* data, int size)
      LOCKS_EXCLUDED(mutex_);
  // CHECK()s that the read is successful.
  virtual void DoRead(int64_t offset, char* data, int size);

  mojo::Receiver<mojom::blink::DiskAllocator> receiver_{this};
  base::File file_;  // May be invalid.

 protected:  // For testing.
  Mutex mutex_;
  // Using a std::map because we rely on |{lower,upper}_bound()|.
  std::map<int64_t, size_t> free_chunks_ GUARDED_BY(mutex_);
  size_t free_chunks_size_ GUARDED_BY(mutex_);

 private:
  int64_t file_tail_ GUARDED_BY(mutex_);
  // Whether writing is possible now. This can be true if:
  // - |set_may_write_for_testing()| was called, or
  // - |file_.IsValid()| and no write error occurred (which would set
  //   |may_write_| to false).
  bool may_write_ GUARDED_BY(mutex_);
#if DCHECK_IS_ON()
  std::map<int64_t, size_t> allocated_chunks_ GUARDED_BY(mutex_);
#endif

  FRIEND_TEST_ALL_PREFIXES(DiskDataAllocatorTest, ProvideInvalidFile);
  FRIEND_TEST_ALL_PREFIXES(DiskDataAllocatorTest, ProvideValidFile);
};

}  // namespace blink
#endif  // THIRD_PARTY_BLINK_RENDERER_PLATFORM_DISK_DATA_ALLOCATOR_H_