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
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
|
// Copyright (C) 2012-2015 Internet Systems Consortium, Inc. ("ISC")
//
// 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 MEMORY_SEGMENT_H
#define MEMORY_SEGMENT_H
#include <exceptions/exceptions.h>
#include <utility>
#include <stdlib.h>
namespace isc {
namespace util {
/// \brief Exception that can be thrown when constructing a MemorySegment
/// object.
class MemorySegmentOpenError : public Exception {
public:
MemorySegmentOpenError(const char* file, size_t line, const char* what) :
isc::Exception(file, line, what) {}
};
/// \brief Exception that is thrown, when allocating space in a MemorySegment
/// results in growing the underlying segment.
///
/// See MemorySegment::allocate() for details.
class MemorySegmentGrown : public Exception {
public:
MemorySegmentGrown(const char* file, size_t line, const char* what) :
isc::Exception(file, line, what) {}
};
/// \brief General error that can be thrown by a MemorySegment
/// implementation.
class MemorySegmentError : public Exception {
public:
MemorySegmentError(const char* file, size_t line, const char* what) :
isc::Exception(file, line, what) {}
};
/// \brief Memory Segment Class
///
/// This class specifies an interface for allocating memory segments.
/// It's intended to provide a unified interface, whether the underlying
/// memory is local to a specific process or is sharable by multiple
/// processes.
///
/// This is an abstract class and a real implementation such as
/// MemorySegmentLocal should be used in code.
class MemorySegment {
public:
/// \brief Destructor
virtual ~MemorySegment() {}
/// \brief Allocate/acquire a fragment of memory.
///
/// The source of the memory is dependent on the implementation used.
///
/// Depending on the implementation details, it may have to grow the
/// internal memory segment (again, in an implementation dependent way)
/// to allocate the required size of memory. In that case the
/// implementation must grow the internal segment sufficiently so the
/// next call to allocate() for the same size will succeed, and throw
/// a \c MemorySegmentGrown exception (not really allocating the memory
/// yet).
///
/// An application that uses this memory segment abstraction to allocate
/// memory should expect this exception, and should normally catch it
/// at an appropriate layer (which may be immediately after a call to
/// \c allocate() or a bit higher layer). It should interpret the
/// exception as any raw address that belongs to the segment may have
/// been remapped and must be re-fetched via an already established
/// named address using the \c getNamedAddress() method.
///
/// The intended use case of \c allocate() with the \c MemorySegmentGrown
/// exception is to build a complex object that would internally require
/// multiple calls to \c allocate():
///
/// \code
/// ComplicatedStuff* stuff = NULL;
/// while (!stuff) { // this must eventually succeed or result in bad_alloc
/// try {
/// // create() is a factory method that takes a memory segment
/// // and calls allocate() on it multiple times. create()
/// // provides an exception guarantee that any intermediately
/// // allocated memory will be properly deallocate()-ed on
/// // exception.
/// stuff = ComplicatedStuff::create(mem_segment);
/// } catch (const MemorySegmentGrown&) { /* just try again */ }
/// }
/// \endcode
///
/// This way, \c create() can be written as if each call to \c allocate()
/// always succeeds.
///
/// Alternatively, or in addition to this, we could introduce a "no throw"
/// version of this method with a way to tell the caller the reason of
/// any failure (whether it's really out of memory or just due to growing
/// the segment). That would be more convenient if the caller wants to
/// deal with the failures on a per-call basis rather than as a set
/// of calls like in the above example. At the moment, we don't expect
/// to have such use-cases, so we only provide the exception
/// version.
///
/// \throw std::bad_alloc The implementation cannot allocate the
/// requested storage.
/// \throw MemorySegmentGrown The memory segment doesn't have sufficient
/// space for the requested size and has grown internally.
/// \throw MemorySegmentError An attempt was made to allocate
/// storage on a read-only memory segment.
///
/// \param size The size of the memory requested in bytes.
/// \return Returns pointer to the memory allocated.
virtual void* allocate(size_t size) = 0;
/// \brief Free/release a segment of memory.
///
/// This method may throw <code>isc::OutOfRange</code> if \c size is
/// not equal to the originally allocated size. \c size could be
/// used by some implementations such as a slice allocator, where
/// freeing memory also requires the size to be specified. We also
/// use this argument in some implementations to test if all allocated
/// memory was deallocated properly.
///
/// Specific implementation may also throw \c MemorySegmentError if it
/// encounters violation of implementation specific restrictions.
///
/// In general, however, this method must succeed and exception free
/// as long as the caller passes valid parameters (\c ptr specifies
/// memory previously allocated and \c size is correct).
///
/// \throw OutOfRange The passed size doesn't match the allocated memory
/// size (when identifiable for the implementation).
/// \throw MemorySegmentError Failure of implementation specific
/// validation.
///
/// \param ptr Pointer to the block of memory to free/release. This
/// should be equal to a value returned by <code>allocate()</code>.
/// \param size The size of the memory to be freed in bytes. This
/// should be equal to the number of bytes originally allocated.
virtual void deallocate(void* ptr, size_t size) = 0;
/// \brief Check if all allocated memory was deallocated.
///
/// \return Returns <code>true</code> if all allocated memory (including
/// names associated by memory addresses by \c setNamedAddress()) was
/// deallocated, <code>false</code> otherwise.
virtual bool allMemoryDeallocated() const = 0;
/// \brief Associate specified address in the segment with a given name.
///
/// This method establishes an association between the given name and
/// the address in an implementation specific way. The stored address
/// is retrieved by the name later by calling \c getNamedAddress().
/// If the underlying memory segment is sharable by multiple processes,
/// the implementation must ensure the portability of the association;
/// if a process gives an address in the shared segment a name, another
/// process that shares the same segment should be able to retrieve the
/// corresponding address by that name (in such cases the real address
/// may be different between these two processes).
///
/// Some names are reserved for internal use by this class. If such
/// a name is passed to this method, an \c isc::InvalidParameter
/// exception will be thrown. See \c validateName() method for details.
///
/// \c addr must be 0 (NULL) or an address that belongs to this segment.
/// The latter case means it must be the return value of a previous call
/// to \c allocate(). The actual implementation is encouraged to detect
/// violation of this restriction and signal it with an exception, but
/// it's not an API requirement. It's generally the caller's
/// responsibility to meet the restriction. Note that NULL is allowed
/// as \c addr even if it wouldn't be considered to "belong to" the
/// segment in its normal sense; it can be used to indicate that memory
/// has not been allocated for the specified name. A subsequent call
/// to \c getNamedAddress() will return NamedAddressResult(true, NULL)
/// for that name.
///
/// \note Naming an address is intentionally separated from allocation
/// so that, for example, one module of a program can name a memory
/// region allocated by another module of the program.
///
/// There can be an existing association for the name; in that case the
/// association will be overridden with the newly given address.
///
/// While normally unexpected, it's possible that available space in the
/// segment is not sufficient to allocate a space (if not already exist)
/// for the specified name in the segment. In that case, if possible, the
/// implementation should try to grow the internal segment and retry
/// establishing the association. The implementation should throw
/// std::bad_alloc if even reasonable attempts of retry still fail.
///
/// This method should normally return false, but if the internal segment
/// had to grow to store the given name, it must return true. The
/// application should interpret it just like the case of
/// \c MemorySegmentGrown exception thrown from the \c allocate() method.
///
/// \note The behavior in case the internal segment grows is different
/// from that of \c allocate(). This is intentional. In intended use
/// cases (for the moment) this method will be called independently,
/// rather than as part of a set of allocations. It's also expected
/// that other raw memory addresses (which would have been invalidated
/// due to the change to the segment) won't be referenced directly
/// immediately after this call. So, the caller should normally be able
/// to call this method as mostly never-fail one (except in case of real
/// memory exhaustion) and ignore the return value.
///
/// \throw std::bad_alloc Allocation of a segment space for the given name
/// failed.
/// \throw InvalidParameter name is NULL, empty ("") or begins with
/// an underscore ('_').
/// \throw MemorySegmentError Failure of implementation specific
/// validation.
///
/// \param name A C string to be associated with \c addr. Must not be NULL.
/// \param addr A memory address returned by a prior call to \c allocate.
/// \return true if the internal segment has grown to allocate space for
/// the name; false otherwise (see above).
bool setNamedAddress(const char* name, void* addr) {
// This public method implements common validation. The actual
// work specific to the derived segment is delegated to the
// corresponding protected method.
validateName(name);
return (setNamedAddressImpl(name, addr));
}
/// \brief Type definition for result returned by getNamedAddress()
typedef std::pair<bool, void*> NamedAddressResult;
/// \brief Return the address in the segment that has the given name.
///
/// This method returns the memory address in the segment corresponding
/// to the specified \c name. The name and address must have been
/// associated by a prior call to \c setNameAddress(). If no address
/// associated with the given name is found, it returns NULL.
///
/// Some names are reserved for internal use by this class. If such
/// a name is passed to this method, an \c isc::InvalidParameter
/// exception will be thrown. See \c validateName() method for details.
///
/// This method should generally be considered exception free, but there
/// can be a small chance it throws, depending on the internal
/// implementation (e.g., if it converts the name to std::string), so the
/// API doesn't guarantee that property. In general, if this method
/// throws it should be considered a fatal condition.
///
/// \throw InvalidParameter name is NULL, empty ("") or begins with
/// an underscore ('_').
///
/// \param name A C string of which the segment memory address is to be
/// returned. Must not be NULL.
/// \return An std::pair containing a bool (set to true if the name
/// was found, or false otherwise) and the address associated with
/// the name (which is undefined if the name was not found).
NamedAddressResult getNamedAddress(const char* name) const {
// This public method implements common validation. The actual
// work specific to the derived segment is delegated to the
// corresponding protected method.
validateName(name);
return (getNamedAddressImpl(name));
}
/// \brief Delete a name previously associated with a segment address.
///
/// This method deletes the association of the given \c name to
/// a corresponding segment address previously established by
/// \c setNamedAddress(). If there is no association for the given name
/// this method returns false; otherwise it returns true.
///
/// Some names are reserved for internal use by this class. If such
/// a name is passed to this method, an \c isc::InvalidParameter
/// exception will be thrown. See \c validateName() method for details.
///
/// See \c getNamedAddress() about exception consideration.
///
/// \throw InvalidParameter name is NULL, empty ("") or begins with
/// an underscore ('_').
/// \throw MemorySegmentError Failure of implementation specific
/// validation.
///
/// \param name A C string of which the segment memory address is to be
/// deleted. Must not be NULL.
bool clearNamedAddress(const char* name) {
// This public method implements common validation. The actual
// work specific to the derived segment is delegated to the
// corresponding protected method.
validateName(name);
return (clearNamedAddressImpl(name));
}
private:
/// \brief Validate the passed name.
///
/// This method validates the passed name (for name/address pairs)
/// and throws \c InvalidParameter if the name fails
/// validation. Otherwise, it does nothing.
///
/// \throw InvalidParameter name is NULL, empty ("") or begins with
/// an underscore ('_').
static void validateName(const char* name) {
if (!name) {
isc_throw(InvalidParameter, "NULL is invalid for a name.");
} else if (*name == '\0') {
isc_throw(InvalidParameter, "Empty names are invalid.");
} else if (*name == '_') {
isc_throw(InvalidParameter,
"Names beginning with '_' are reserved for "
"internal use only.");
}
}
protected:
/// \brief Implementation of setNamedAddress beyond common validation.
virtual bool setNamedAddressImpl(const char* name, void* addr) = 0;
/// \brief Implementation of getNamedAddress beyond common validation.
virtual NamedAddressResult getNamedAddressImpl(const char* name) const = 0;
/// \brief Implementation of clearNamedAddress beyond common validation.
virtual bool clearNamedAddressImpl(const char* name) = 0;
};
} // namespace util
} // namespace isc
#endif // MEMORY_SEGMENT_H
// Local Variables:
// mode: c++
// End:
|