summaryrefslogtreecommitdiffstats
path: root/mozglue/baseprofiler/public/ProfileBufferChunkManagerSingle.h
blob: c91b38cbdbee32f3623eb60e58f163bd57b57c59 (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
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#ifndef ProfileBufferChunkManagerSingle_h
#define ProfileBufferChunkManagerSingle_h

#include "mozilla/ProfileBufferChunkManager.h"

#ifdef DEBUG
#  include "mozilla/Atomics.h"
#endif  // DEBUG

namespace mozilla {

// Manages only one Chunk.
// The first call to `Get`/`RequestChunk()` will retrieve the one chunk, and all
// subsequent calls will return nullptr. That chunk may still be released, but
// it will never be destroyed or recycled.
// Unlike others, this manager may be `Reset()`, to allow another round of
// small-data gathering.
// The main use is with short-lived ProfileChunkedBuffers that collect little
// data that can fit in one chunk, e.g., capturing one stack.
// It is not thread-safe.
class ProfileBufferChunkManagerSingle final : public ProfileBufferChunkManager {
 public:
  using Length = ProfileBufferChunk::Length;

  // Use a preallocated chunk. (Accepting null to gracefully handle OOM.)
  explicit ProfileBufferChunkManagerSingle(UniquePtr<ProfileBufferChunk> aChunk)
      : mInitialChunk(std::move(aChunk)),
        mBufferBytes(mInitialChunk ? mInitialChunk->BufferBytes() : 0) {
    MOZ_ASSERT(!mInitialChunk || !mInitialChunk->GetNext(),
               "Expected at most one chunk");
  }

  // ChunkMinBufferBytes: Minimum number of user-available bytes in the Chunk.
  // Note that Chunks use a bit more memory for their header.
  explicit ProfileBufferChunkManagerSingle(Length aChunkMinBufferBytes)
      : mInitialChunk(ProfileBufferChunk::Create(aChunkMinBufferBytes)),
        mBufferBytes(mInitialChunk ? mInitialChunk->BufferBytes() : 0) {}

#ifdef DEBUG
  ~ProfileBufferChunkManagerSingle() { MOZ_ASSERT(mVirtuallyLocked == false); }
#endif  // DEBUG

  // Reset this manager, using the provided chunk (probably coming from the
  // ProfileChunkedBuffer that just used it); if null, fallback on current or
  // released chunk.
  void Reset(UniquePtr<ProfileBufferChunk> aPossibleChunk) {
    if (aPossibleChunk) {
      mInitialChunk = std::move(aPossibleChunk);
      mReleasedChunk = nullptr;
    } else if (!mInitialChunk) {
      MOZ_ASSERT(!!mReleasedChunk, "Can't reset properly!");
      mInitialChunk = std::move(mReleasedChunk);
    }

    if (mInitialChunk) {
      mInitialChunk->MarkRecycled();
      mBufferBytes = mInitialChunk->BufferBytes();
    } else {
      mBufferBytes = 0;
    }
  }

  [[nodiscard]] size_t MaxTotalSize() const final { return mBufferBytes; }

  // One of `GetChunk` and `RequestChunk` will only work the very first time (if
  // there's even a chunk).
  [[nodiscard]] UniquePtr<ProfileBufferChunk> GetChunk() final {
    MOZ_ASSERT(mUser, "Not registered yet");
    return std::move(mInitialChunk);
  }

  void RequestChunk(std::function<void(UniquePtr<ProfileBufferChunk>)>&&
                        aChunkReceiver) final {
    MOZ_ASSERT(mUser, "Not registered yet");
    // Simple retrieval.
    std::move(aChunkReceiver)(GetChunk());
  }

  void FulfillChunkRequests() final {
    // Nothing to do here.
  }

  void ReleaseChunk(UniquePtr<ProfileBufferChunk> aChunk) final {
    MOZ_ASSERT(mUser, "Not registered yet");
    if (!aChunk) {
      return;
    }
    MOZ_ASSERT(!mReleasedChunk, "Unexpected 2nd released chunk");
    MOZ_ASSERT(!aChunk->GetNext(), "Only expected one released chunk");
    mReleasedChunk = std::move(aChunk);
  }

  void SetChunkDestroyedCallback(
      std::function<void(const ProfileBufferChunk&)>&& aChunkDestroyedCallback)
      final {
    MOZ_ASSERT(mUser, "Not registered yet");
    // The chunk-destroyed callback will never actually be called, but we keep
    // the callback here in case the caller expects it to live as long as this
    // manager.
    mChunkDestroyedCallback = std::move(aChunkDestroyedCallback);
  }

  [[nodiscard]] UniquePtr<ProfileBufferChunk> GetExtantReleasedChunks() final {
    MOZ_ASSERT(mUser, "Not registered yet");
    return std::move(mReleasedChunk);
  }

  void ForgetUnreleasedChunks() final {
    MOZ_ASSERT(mUser, "Not registered yet");
  }

  [[nodiscard]] size_t SizeOfExcludingThis(
      MallocSizeOf aMallocSizeOf) const final {
    MOZ_ASSERT(mUser, "Not registered yet");
    size_t size = 0;
    if (mInitialChunk) {
      size += mInitialChunk->SizeOfIncludingThis(aMallocSizeOf);
    }
    if (mReleasedChunk) {
      size += mReleasedChunk->SizeOfIncludingThis(aMallocSizeOf);
    }
    // Note: Missing size of std::function external resources (if any).
    return size;
  }

  [[nodiscard]] size_t SizeOfIncludingThis(
      MallocSizeOf aMallocSizeOf) const final {
    MOZ_ASSERT(mUser, "Not registered yet");
    return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
  }

 protected:
  // This manager is not thread-safe, so there's not actual locking needed.
  const ProfileBufferChunk* PeekExtantReleasedChunksAndLock() final {
    MOZ_ASSERT(mVirtuallyLocked.compareExchange(false, true));
    MOZ_ASSERT(mUser, "Not registered yet");
    return mReleasedChunk.get();
  }
  void UnlockAfterPeekExtantReleasedChunks() final {
    MOZ_ASSERT(mVirtuallyLocked.compareExchange(true, false));
  }

 private:
  // Initial chunk created with this manager, given away at first Get/Request.
  UniquePtr<ProfileBufferChunk> mInitialChunk;

  // Storage for the released chunk (which should probably not happen, as it
  // means the chunk is full).
  UniquePtr<ProfileBufferChunk> mReleasedChunk;

  // Size of the one chunk we're managing. Stored here, because the chunk may
  // be moved out and inaccessible from here.
  Length mBufferBytes;

  // The chunk-destroyed callback will never actually be called, but we keep it
  // here in case the caller expects it to live as long as this manager.
  std::function<void(const ProfileBufferChunk&)> mChunkDestroyedCallback;

#ifdef DEBUG
  mutable Atomic<bool> mVirtuallyLocked{false};
#endif  // DEBUG
};

}  // namespace mozilla

#endif  // ProfileBufferChunkManagerSingle_h