Bug 1910362 - Create new Nimbus helper r=aaronmt,ohorvath
[gecko.git] / xpcom / base / CountingAllocatorBase.h
blob02d74f3223f229613c75000a2d012207d1482b5d
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #ifndef CountingAllocatorBase_h
8 #define CountingAllocatorBase_h
10 #include <cstdlib>
11 #include "mozilla/Assertions.h"
12 #include "mozilla/Atomics.h"
13 #include "mozilla/mozalloc.h"
14 #include "nsIMemoryReporter.h"
16 namespace mozilla {
18 // This CRTP class handles several details of wrapping allocators and should
19 // be preferred to manually counting with MOZ_DEFINE_MALLOC_SIZE_OF_ON_ALLOC
20 // and MOZ_DEFINE_MALLOC_SIZE_OF_ON_FREE. The typical use is in a memory
21 // reporter for a particular third party library:
23 // class MyMemoryReporter : public CountingAllocatorBase<MyMemoryReporter>
24 // {
25 // ...
26 // NS_IMETHOD
27 // CollectReports(nsIHandleReportCallback* aHandleReport,
28 // nsISupports* aData, bool aAnonymize) override
29 // {
30 // MOZ_COLLECT_REPORT(
31 // "explicit/path/to/somewhere", KIND_HEAP, UNITS_BYTES,
32 // MemoryAllocated(),
33 // "A description of what we are reporting.");
35 // return NS_OK;
36 // }
37 // };
39 // ...somewhere later in the code...
40 // SetThirdPartyMemoryFunctions(MyMemoryReporter::CountingAlloc,
41 // MyMemoryReporter::CountingFree);
42 template <typename T>
43 class CountingAllocatorBase {
44 public:
45 CountingAllocatorBase() {
46 #ifdef DEBUG
47 // There must be only one instance of this class, due to |sAmount| being
48 // static.
49 static bool hasRun = false;
50 MOZ_ASSERT(!hasRun);
51 hasRun = true;
52 #endif
55 static size_t MemoryAllocated() { return sAmount; }
57 static void* CountingMalloc(size_t size) {
58 void* p = malloc(size);
59 sAmount += MallocSizeOfOnAlloc(p);
60 return p;
63 static void* CountingCalloc(size_t nmemb, size_t size) {
64 void* p = calloc(nmemb, size);
65 sAmount += MallocSizeOfOnAlloc(p);
66 return p;
69 static void* CountingRealloc(void* p, size_t size) {
70 size_t oldsize = MallocSizeOfOnFree(p);
71 void* pnew = realloc(p, size);
72 if (pnew) {
73 size_t newsize = MallocSizeOfOnAlloc(pnew);
74 sAmount += newsize - oldsize;
75 } else if (size == 0) {
76 // We asked for a 0-sized (re)allocation of some existing pointer
77 // and received NULL in return. 0-sized allocations are permitted
78 // to either return NULL or to allocate a unique object per call (!).
79 // For a malloc implementation that chooses the second strategy,
80 // that allocation may fail (unlikely, but possible).
82 // Given a NULL return value and an allocation size of 0, then, we
83 // don't know if that means the original pointer was freed or if
84 // the allocation of the unique object failed. If the original
85 // pointer was freed, then we have nothing to do here. If the
86 // allocation of the unique object failed, the original pointer is
87 // still valid and we ought to undo the decrement from above.
88 // However, we have no way of knowing how the underlying realloc
89 // implementation is behaving. Assuming that the original pointer
90 // was freed is the safest course of action. We do, however, need
91 // to note that we freed memory.
92 sAmount -= oldsize;
93 } else {
94 // realloc failed. The amount allocated hasn't changed.
96 return pnew;
99 // Some library code expects that realloc(x, 0) will free x, which is not
100 // the behavior of the version of jemalloc we're using, so this wrapped
101 // version of realloc is needed.
102 static void* CountingFreeingRealloc(void* p, size_t size) {
103 if (size == 0) {
104 CountingFree(p);
105 return nullptr;
107 return CountingRealloc(p, size);
110 static void CountingFree(void* p) {
111 sAmount -= MallocSizeOfOnFree(p);
112 free(p);
115 // Infallible-allocation wrappers for the counting malloc/calloc/realloc
116 // functions, for clients that don't safely handle allocation failures
117 // themselves.
118 static void* InfallibleCountingMalloc(size_t size) {
119 void* p = moz_xmalloc(size);
120 sAmount += MallocSizeOfOnAlloc(p);
121 return p;
124 static void* InfallibleCountingCalloc(size_t nmemb, size_t size) {
125 void* p = moz_xcalloc(nmemb, size);
126 sAmount += MallocSizeOfOnAlloc(p);
127 return p;
130 static void* InfallibleCountingRealloc(void* p, size_t size) {
131 size_t oldsize = MallocSizeOfOnFree(p);
132 void* pnew = moz_xrealloc(p, size);
133 if (pnew) {
134 size_t newsize = MallocSizeOfOnAlloc(pnew);
135 sAmount += newsize - oldsize;
136 } else if (size == 0) {
137 // See comment in CountingRealloc above.
138 sAmount -= oldsize;
139 } else {
140 // realloc failed. The amount allocated hasn't changed.
142 return pnew;
145 private:
146 // |sAmount| can be (implicitly) accessed by multiple threads, so it
147 // must be thread-safe. It may be written during GC, so accesses are not
148 // recorded.
149 typedef Atomic<size_t, SequentiallyConsistent> AmountType;
150 static inline AmountType sAmount{0};
152 MOZ_DEFINE_MALLOC_SIZE_OF_ON_ALLOC(MallocSizeOfOnAlloc)
153 MOZ_DEFINE_MALLOC_SIZE_OF_ON_FREE(MallocSizeOfOnFree)
156 } // namespace mozilla
158 #endif // CountingAllocatorBase_h