// bdlma_managedallocator.h -*-C++-*- #ifndef INCLUDED_BDLMA_MANAGEDALLOCATOR #define INCLUDED_BDLMA_MANAGEDALLOCATOR #include <bsls_ident.h> BSLS_IDENT("$Id: $") //@PURPOSE: Provide a protocol for memory allocators that support 'release'. // //@CLASSES: // bdlma::ManagedAllocator: protocol for allocators with 'release' capability // //@SEE_ALSO: bdlma_bufferedsequentialallocator // //@DESCRIPTION: This component provides a 'class', 'bdlma::ManagedAllocator', // that extends the 'bslma::Allocator' protocol to allocators that support the // ability to 'release' all memory currently allocated through the protocol // back to the memory supplier of the derived concrete allocator object. //.. // ,-----------------------. // ( bdlma::ManagedAllocator ) // `-----------------------' // | release // | // v // ,----------------. // ( bslma::Allocator ) // `----------------' // allocate // deallocate //.. // ///Usage ///----- // This section illustrates intended use of this component. // ///Example 1: Implementing the 'bdlma::ManagedAllocator' Protocol /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // The 'bdlma::ManagedAllocator' interface is especially useful for allocators // that are based on an underlying pooling mechanism (e.g., 'bdlma::Multipool' // or 'bdlma::BufferedSequentialPool'). In particular, such an allocator that // implements the 'bdlma::ManagedAllocator' interface can release, via the // 'release' method, all outstanding (pooled) memory back to the underlying // allocator making the memory available for subsequent reuse. Moreover, use // of the 'release' method can also often render superfluous the running of // destructors on the objects making use of a managed allocator. In this first // usage example, we define the 'my_BufferAllocator' class, an allocator that // implements the 'bdlma::ManagedAllocator' interface. 'my_BufferAllocator' is // a considerably pared down version of 'bdlma::BufferedSequentialAllocator', // and is intended for illustration purposes only. Please see the // 'bdlma_bufferedsequentialallocator' component for full documentation of // 'bdlma::BufferedSequentialAllocator', a managed allocator meant for // production use. // // First, we define the interface of the 'my_BufferAllocator' class: //.. // // my_bufferallocator.h // // class my_BufferAllocator : public bdlma::ManagedAllocator { // // This 'class' provides a concrete buffer allocator that implements // // the 'bdlma::ManagedAllocator' protocol. // // // DATA // char *d_buffer_p; // external buffer (held, not // // owned) // // bsls::Types::size_type d_bufferSize; // size (in bytes) of external // // buffer // // bsls::Types::IntPtr d_cursor; // offset to next available byte // // in buffer // // private: // // NOT IMPLEMENTED // my_BufferAllocator(const my_BufferAllocator&); // my_BufferAllocator& operator=(const my_BufferAllocator&); // // public: // // CREATORS // my_BufferAllocator(char *buffer, bsls::Types::size_type bufferSize); // // Create a buffer allocator for allocating maximally-aligned // // memory blocks from the specified external 'buffer' having the // // specified 'bufferSize' (in bytes). // // ~my_BufferAllocator(); // // Destroy this buffer allocator. // // // MANIPULATORS // void *allocate(bsls::Types::size_type size); // // Return the address of a maximally-aligned contiguous block of // // memory of the specified 'size' (in bytes) on success, and 0 if // // the allocation request exceeds the remaining free memory space // // in the external buffer. // // void deallocate(void *address); // // This method has no effect for this buffer allocator. // // void release(); // // Release all memory allocated through this object. This // // allocator is reset to the state it was in immediately following // // construction. // }; //.. // Next, we define the 'inline' methods of 'my_BufferAllocator'. Note that the // 'release' method resets the internal cursor to 0, effectively making the // memory from the entire external buffer supplied at construction available // for subsequent allocations, but has no effect on the contents of the buffer: //.. // // CREATORS // inline // my_BufferAllocator::my_BufferAllocator(char *buffer, // bsls::Types::size_type bufferSize) // : d_buffer_p(buffer) // , d_bufferSize(bufferSize) // , d_cursor(0) // { // } // // // MANIPULATORS // inline // void my_BufferAllocator::deallocate(void *) // { // } // // inline // void my_BufferAllocator::release() // { // d_cursor = 0; // } //.. // Finally, we provide the implementation of the 'my_BufferAllocator' methods // that are defined in the '.cpp' file. A 'static' helper function, // 'allocateFromBufferImp', provides the bulk of the implementation of the // 'allocate' method: //.. // // my_bufferallocator.cpp // // // STATIC HELPER FUNCTIONS // static // void *allocateFromBufferImp(bsls::Types::IntPtr *cursor, // char *buffer, // bsls::Types::size_type bufferSize, // bsls::Types::size_type size) // // Allocate a maximally-aligned memory block of the specified 'size' // // (in bytes) from the specified 'buffer' having the specified // // 'bufferSize' (in bytes) at the specified 'cursor' position. Return // // the address of the allocated memory block if 'buffer' contains // // sufficient available memory, and 0 otherwise. The 'cursor' is set // // to the first byte position immediately after the allocated memory if // // there is sufficient memory, and not modified otherwise. The // // behavior is undefined unless '0 < size', '0 <= *cursor', and // // '*cursor <= bufferSize'. // // { // const int offset = bsls::AlignmentUtil::calculateAlignmentOffset( // buffer + *cursor, // bsls::AlignmentUtil::BSLS_MAX_ALIGNMENT); // // if (*cursor + offset + size > bufferSize) { // insufficient space // return 0; // RETURN // } // // void *result = &buffer[*cursor + offset]; // *cursor += offset + size; // // return result; // } // // // CREATORS // my_BufferAllocator::~my_BufferAllocator() // { // } // // // MANIPULATORS // void *my_BufferAllocator::allocate(bsls::Types::size_type size) // { // return 0 == size ? 0 : allocateFromBufferImp(&d_cursor, // d_buffer_p, // d_bufferSize, // static_cast<int>(size)); // } //.. // ///Example 2: Using the 'bdlma::ManagedAllocator' Protocol ///- - - - - - - - - - - - - - - - - - - - - - - - - - - - // In this second usage example, we illustrate how the managed allocator that // was defined in Example 1, 'my_BufferAllocator', may be used. Note that // substantial portions of the sample implementation are elided as they would // only add unnecessary complications to the usage example. The portions shown // are sufficient to illustrate the use of 'bdlma::ManagedAllocator'. // // The domain of our example is financial markets. Suppose that we are given a // list of market indices (e.g., Dow Jones Industrial Average, S&P 500, etc.), // and we want to perform some computation on each index, in turn. In this // example, the essential attributes of an index are held in a 'bsl::pair' // consisting of the name of the index (e.g., "DJIA") and the number of // securities that comprise the index (e.g., 30 in the case of the DJIA). The // collection of market indices that we wish to process is given by a vector of // such pairs. Thus, we make use of these types related to indices: //.. // typedef bsl::pair<const char *, int> IndexAttributes; // typedef bsl::vector<IndexAttributes> IndexCollection; //.. // In our example, a security is defined by the unconstrained attribute type // 'my_SecurityAttributes', the interface and implementation of which is elided // except we note that it uses 'bslma' allocators: //.. // class my_SecurityAttributes { // // ... // // public: // // TRAITS // BSLMF_NESTED_TRAIT_DECLARATION(my_SecurityAttributes, // bslma::UsesBslmaAllocator); // // // ... // }; //.. // For the collection of securities comprising an index we use a vector of // 'my_SecurityAttributes': //.. // typedef bsl::vector<my_SecurityAttributes> SecurityCollection; //.. // Since some indices are quite large (e.g., Russell 3000, Wilshire 5000), for // performance reasons it is advantageous for a 'SecurityCollection' to use an // efficient memory allocation strategy. This is where 'my_BufferAllocator' // comes into play, which we will see shortly. // // The top-level function in our example takes a 'bdlma::ManagedAllocator *' // and the collection of market indices that we wish to process: //.. // static // void processIndices(bdlma::ManagedAllocator *managedAllocator, // const IndexCollection& indices); // // Process the specified market 'indices' using the specified // // 'managedAllocator' to supply memory. //.. // 'processIndices' makes use of two helper functions to process each index: //.. // static // void loadIndex(SecurityCollection *securities, // bdlma::ManagedAllocator *managedAllocator, // const IndexAttributes& index); // // Load into the specified collection of 'securities' the attributes of // // the securities comprising the specified market 'index' using the // // specified 'managedAllocator' to supply memory. // // static // void processIndex(const SecurityCollection& securities, // const IndexAttributes& index); // // Process the specified collection of 'securities' that comprise the // // specified market 'index'. //.. // Since we plan to use 'my_BufferAllocator' as our managed allocator, we need // to supply it with an external buffer. The 'calculateMaxBufferSize' function // computes the size of the buffer required to store the 'SecurityCollection' // corresponding to the largest index to be processed by a given call to // 'processIndices': //.. // int calculateMaxBufferSize(const IndexCollection& indices); // // Return the maximum buffer size (in bytes) required to process the // // specified collection of market 'indices'. //.. // Before showing the implementation of 'processIndices', where the most // interesting use of our managed allocator takes place, we show the site of // the call to 'processIndices'. // // First, assume that we have been given an 'IndexCollection' that has been // populated with one or more 'IndexAttributes': //.. // IndexCollection indices; // assume populated //.. // Next, we calculate the size of the buffer that is needed, allocate the // memory for the buffer from the default allocator, create our concrete // managed allocator (namely, an instance of 'my_BufferAllocator'), and call // 'processIndices': //.. // const int bufferSize = calculateMaxBufferSize(indices); // // bslma::Allocator *allocator = bslma::Default::defaultAllocator(); // char *buffer = static_cast<char *>(allocator->allocate(bufferSize)); // // my_BufferAllocator bufferAllocator(buffer, bufferSize); // // processIndices(&bufferAllocator, indices); //.. // Next, we show the implementation of 'processIndices', within which we // iterate over the market 'indices' that are passed to it: //.. // static // void processIndices(bdlma::ManagedAllocator *managedAllocator, // const IndexCollection& indices) // // Process the specified market 'indices' using the specified // // 'managedAllocator' to supply memory. // { // for (IndexCollection::const_iterator citer = indices.begin(); // citer != indices.end(); ++citer) { // //.. // For each index, the 'SecurityCollection' comprising that index is created. // All of the memory needs of the 'SecurityCollection' are provided by the // 'managedAllocator'. Note that even the memory for the footprint of the // collection comes from the 'managedAllocator': //.. // SecurityCollection *securities = // new (managedAllocator->allocate(sizeof(SecurityCollection))) // SecurityCollection(managedAllocator); // //.. // Next, we call 'loadIndex' to populate 'securities', followed by the call to // 'processIndex'. 'loadIndex' also uses the 'managedAllocator', the details // of which are not shown here: //.. // loadIndex(securities, managedAllocator, *citer); // // processIndex(*securities, *citer); //.. // After the index is processed, 'release' is called on the managed allocator // making all of the buffer supplied to the allocator at construction available // for reuse: //.. // managedAllocator->release(); // } //.. // Finally, we let the 'SecurityCollection' used to process the index go out of // scope intentionally without deleting 'securities'. The call to 'release' // renders superfluous the need to call the 'SecurityCollection' destructor as // well as the destructor of the contained 'my_SecurityAttributes' elements. //.. // } //.. #include <bdlscm_version.h> #include <bslma_allocator.h> namespace BloombergLP { namespace bdlma { // ====================== // class ManagedAllocator // ====================== class ManagedAllocator : public bslma::Allocator { // This protocol class extends 'bslma::Allocator' for allocators with the // ability to 'release' all memory currently allocated through the protocol // back to the memory supplier of the derived concrete allocator object. public: // MANIPULATORS virtual void release() = 0; // Release all memory currently allocated through this allocator. The // effect of using a pointer after this call that was obtained from // this allocator before this call is undefined. }; } // close package namespace } // close enterprise namespace #endif // ---------------------------------------------------------------------------- // Copyright 2016 Bloomberg Finance L.P. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // ----------------------------- END-OF-FILE ----------------------------------