summaryrefslogtreecommitdiffstats
path: root/mfbt/AllocPolicy.h
blob: e5c62bcd640c5f97350254ba5e644772ded5bd6b (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
173
174
175
/* -*- Mode: C++; tab-width: 8; 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/. */

/*
 * An allocation policy concept, usable for structures and algorithms to
 * control how memory is allocated and how failures are handled.
 */

#ifndef mozilla_AllocPolicy_h
#define mozilla_AllocPolicy_h

#include "mozilla/Attributes.h"
#include "mozilla/Assertions.h"
#include "mozilla/TemplateLib.h"

#include <stddef.h>
#include <stdlib.h>

namespace mozilla {

/*
 * Allocation policies are used to implement the standard allocation behaviors
 * in a customizable way.  Additionally, custom behaviors may be added to these
 * behaviors, such as additionally reporting an error through an out-of-band
 * mechanism when OOM occurs.  The concept modeled here is as follows:
 *
 *  - public copy constructor, assignment, destructor
 *  - template <typename T> T* maybe_pod_malloc(size_t)
 *      Fallible, but doesn't report an error on OOM.
 *  - template <typename T> T* maybe_pod_calloc(size_t)
 *      Fallible, but doesn't report an error on OOM.
 *  - template <typename T> T* maybe_pod_realloc(T*, size_t, size_t)
 *      Fallible, but doesn't report an error on OOM.  The old allocation
 *      size is passed in, in addition to the new allocation size requested.
 *  - template <typename T> T* pod_malloc(size_t)
 *      Responsible for OOM reporting when null is returned.
 *  - template <typename T> T* pod_calloc(size_t)
 *      Responsible for OOM reporting when null is returned.
 *  - template <typename T> T* pod_realloc(T*, size_t, size_t)
 *      Responsible for OOM reporting when null is returned.  The old allocation
 *      size is passed in, in addition to the new allocation size requested.
 *  - template <typename T> void free_(T*, size_t)
 *      The capacity passed in must match the old allocation size.
 *  - template <typename T> void free_(T*)
 *      Frees a buffer without knowing its allocated size. This might not be
 *      implemented by allocation policies that need the allocation size.
 *  - void reportAllocOverflow() const
 *      Called on allocation overflow (that is, an allocation implicitly tried
 *      to allocate more than the available memory space -- think allocating an
 *      array of large-size objects, where N * size overflows) before null is
 *      returned.
 *  - bool checkSimulatedOOM() const
 *      Some clients generally allocate memory yet in some circumstances won't
 *      need to do so. For example, appending to a vector with a small amount of
 *      inline storage generally allocates memory, but no allocation occurs
 *      unless appending exceeds inline storage. But for testing purposes, it
 *      can be useful to treat *every* operation as allocating.
 *      Clients (such as this hypothetical append method implementation) should
 *      call this method in situations that don't allocate, but could generally,
 *      to support this. The default behavior should return true; more
 *      complicated behavior might be to return false only after a certain
 *      number of allocations-or-check-simulated-OOMs (coordinating with the
 *      other AllocPolicy methods) have occurred.
 *
 * mfbt provides (and typically uses by default) only MallocAllocPolicy, which
 * does nothing more than delegate to the malloc/alloc/free functions.
 */

/*
 * A policy that straightforwardly uses malloc/calloc/realloc/free and adds no
 * extra behaviors.
 */
class MallocAllocPolicy {
 public:
  template <typename T>
  T* maybe_pod_malloc(size_t aNumElems) {
    if (aNumElems & mozilla::tl::MulOverflowMask<sizeof(T)>::value) {
      return nullptr;
    }
    return static_cast<T*>(malloc(aNumElems * sizeof(T)));
  }

  template <typename T>
  T* maybe_pod_calloc(size_t aNumElems) {
    return static_cast<T*>(calloc(aNumElems, sizeof(T)));
  }

  template <typename T>
  T* maybe_pod_realloc(T* aPtr, size_t aOldSize, size_t aNewSize) {
    if (aNewSize & mozilla::tl::MulOverflowMask<sizeof(T)>::value) {
      return nullptr;
    }
    return static_cast<T*>(realloc(aPtr, aNewSize * sizeof(T)));
  }

  template <typename T>
  T* pod_malloc(size_t aNumElems) {
    return maybe_pod_malloc<T>(aNumElems);
  }

  template <typename T>
  T* pod_calloc(size_t aNumElems) {
    return maybe_pod_calloc<T>(aNumElems);
  }

  template <typename T>
  T* pod_realloc(T* aPtr, size_t aOldSize, size_t aNewSize) {
    return maybe_pod_realloc<T>(aPtr, aOldSize, aNewSize);
  }

  template <typename T>
  void free_(T* aPtr, size_t aNumElems = 0) {
    free(aPtr);
  }

  void reportAllocOverflow() const {}

  [[nodiscard]] bool checkSimulatedOOM() const { return true; }
};

/*
 * A policy which always fails to allocate memory, returning nullptr. Methods
 * which expect an existing allocation assert.
 *
 * This type should be used in situations where you want to use a MFBT type with
 * inline storage, and don't want to allow it to allocate on the heap.
 */
class NeverAllocPolicy {
 public:
  template <typename T>
  T* maybe_pod_malloc(size_t aNumElems) {
    return nullptr;
  }

  template <typename T>
  T* maybe_pod_calloc(size_t aNumElems) {
    return nullptr;
  }

  template <typename T>
  T* maybe_pod_realloc(T* aPtr, size_t aOldSize, size_t aNewSize) {
    MOZ_CRASH("NeverAllocPolicy::maybe_pod_realloc");
  }

  template <typename T>
  T* pod_malloc(size_t aNumElems) {
    return nullptr;
  }

  template <typename T>
  T* pod_calloc(size_t aNumElems) {
    return nullptr;
  }

  template <typename T>
  T* pod_realloc(T* aPtr, size_t aOldSize, size_t aNewSize) {
    MOZ_CRASH("NeverAllocPolicy::pod_realloc");
  }

  template <typename T>
  void free_(T* aPtr, size_t aNumElems = 0) {
    MOZ_CRASH("NeverAllocPolicy::free_");
  }

  void reportAllocOverflow() const {}

  [[nodiscard]] bool checkSimulatedOOM() const { return true; }
};

}  // namespace mozilla

#endif /* mozilla_AllocPolicy_h */