// bdlma_autoreleaser.h                                               -*-C++-*-
#ifndef INCLUDED_BDLMA_AUTORELEASER
#define INCLUDED_BDLMA_AUTORELEASER

#include <bsls_ident.h>
BSLS_IDENT("$Id: $")

//@PURPOSE: Release memory to a managed allocator or pool at destruction.
//
//@CLASSES:
//  bdlma::AutoReleaser: proctor to release memory to a managed allocator/pool
//
//@SEE_ALSO: bslma_deallocatorproctor, bdlma_managedallocator
//
//@DESCRIPTION: This component provides a proctor object,
// 'bdlma::AutoReleaser', to manage memory allocated from a managed allocator
// or pool.  The proctor's destructor invokes the 'release' method of its
// managed allocator or pool unless the proctor's own 'release' method has been
// called.  Note that after a proctor releases management of its managed
// allocator or pool, the proctor can be reused by invoking its 'reset' method
// with another allocator or pool object (of the same (template parameter) type
// 'ALLOCATOR').
//
///Requirements
///------------
// The object of the (template parameter) type 'ALLOCATOR' must provide a
// method having the following signature:
//..
//  void release();
//..
//
///Usage
///-----
// This section illustrates intended use of this component.
//
///Example 1: Using a 'bdlma::AutoReleaser' to Preserve Exception Neutrality
///- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// A 'bdlma::AutoReleaser' proctor is often used to preserve exception
// neutrality for containers that allocate their elements using a managed
// allocator or pool.  For operations that may potentially throw an exception,
// a proctor can be used to (temporarily) manage the container's allocator or
// pool and its associated memory.  If an exception is thrown, the proctor's
// destructor invokes the 'release' method of its held allocator or pool,
// deallocating memory for all of the container's elements, thereby preventing
// a memory leak and restoring the container to the empty state.
//
// In this example, we illustrate use of a 'bdlma::AutoReleaser' proctor within
// the 'operator=' method of 'my_FastStrArray', a class that implements an
// array of C string elements.  Note that a 'my_FastStrArray' object allocates
// memory for its C string elements using a string pool, 'my_StrPool', the
// definition of which is elided.
//
// First, we define the interface of our 'my_FastStrArray' class:
//..
//  class my_FastCstrArray {
//      // This class implements an array of C string elements.  Each C string
//      // is allocated using the 'my_StrPool' member for fast memory
//      // allocation and deallocation.
//
//      // DATA
//      char             **d_array_p;      // dynamically allocated array
//      int                d_capacity;     // physical capacity of this array
//      int                d_length;       // logical length of this array
//      my_StrPool         d_strPool;      // memory manager to supply memory
//      bslma::Allocator  *d_allocator_p;  // held, not owned
//
//    private:
//      // PRIVATE MANIPULATORS
//      void increaseSize();
//
//      // Not implemented:
//      my_FastCstrArray(const my_FastCstrArray&);
//
//    public:
//      // CREATORS
//      my_FastCstrArray(bslma::Allocator *basicAllocator = 0);
//      ~my_FastCstrArray();
//
//      // MANIPULATORS
//      my_FastCstrArray& operator=(const my_FastCstrArray& rhs);
//      void append(const char *item);
//
//      // ACCESSORS
//      const char *operator[](int index) const { return d_array_p[index]; }
//      int length() const { return d_length; }
//  };
//
//  // FREE OPERATORS
//  ostream& operator<<(ostream& stream, const my_FastCstrArray& array);
//
//..
// Then, we implement the methods:
//..
//  enum {
//      k_MY_INITIAL_SIZE = 1, // initial physical capacity
//      k_MY_GROW_FACTOR  = 2  // factor by which to grow 'd_capacity'
//  };
//
//  static inline
//  int nextSize(int size)
//      // Return the specified 'size' multiplied by 'k_MY_GROW_FACTOR'.
//  {
//      return size * k_MY_GROW_FACTOR;
//  }
//
//  static inline
//  void reallocate(char             ***array,
//                  int                *size,
//                  int                 newSize,
//                  int                 length,
//                  bslma::Allocator   *allocator)
//      // Reallocate memory in the specified 'array' and update the specified
//      // 'size' to the specified 'newSize', using the specified 'allocator'
//      // to supply memory.  The specified 'length' number of leading elements
//      // are preserved.  If 'allocate' should throw an exception, this
//      // function has no effect.  The behavior is undefined unless
//      // '1 <= newSize', '0 <= length', and 'length <= newSize'.
//  {
//      ASSERT(array);
//      ASSERT(*array);
//      ASSERT(size);
//      ASSERT(1 <= newSize);
//      ASSERT(0 <= length);
//      ASSERT(length <= *size);    // sanity check
//      ASSERT(length <= newSize);  // ensure class invariant
//
//      char **tmp = *array;
//
//      *array = (char **)allocator->allocate(newSize * sizeof **array);
//
//      // commit
//      bsl::memcpy(*array, tmp, length * sizeof **array);
//      *size = newSize;
//      allocator->deallocate(tmp);
//  }
//
//  void my_FastCstrArray::increaseSize()
//  {
//      reallocate(&d_array_p,
//                 &d_capacity,
//                 nextSize(d_capacity),
//                 d_length,
//                 d_allocator_p);
//  }
//
//  // CREATORS
//  my_FastCstrArray::my_FastCstrArray(bslma::Allocator *basicAllocator)
//  : d_capacity(k_MY_INITIAL_SIZE)
//  , d_length(0)
//  , d_allocator_p(bslma::Default::allocator(basicAllocator))
//  {
//      d_array_p = (char **)d_allocator_p->allocate(
//                                             d_capacity * sizeof *d_array_p);
//  }
//
//  my_FastCstrArray::~my_FastCstrArray()
//  {
//      ASSERT(1        <= d_capacity);
//      ASSERT(0        <= d_length);
//      ASSERT(d_length <= d_capacity);
//
//      d_allocator_p->deallocate(d_array_p);
//  }
//..
// Now, we implement 'my_FastCstrArray::operator=' using a
// 'bdlma::AutoReleaser' proctor to preserve exception neutrality:
//..
//  // MANIPULATORS
//  my_FastCstrArray&
//  my_FastCstrArray::operator=(const my_FastCstrArray& rhs)
//  {
//      if (&rhs != this) {
//          d_strPool.release();
//          d_length = 0;
//
//          if (rhs.d_length > d_capacity) {
//              char **tmp = d_array_p;
//              d_array_p = (char **)d_allocator_p->allocate(
//                                           rhs.d_length * sizeof *d_array_p);
//              d_capacity = rhs.d_length;
//              d_allocator_p->deallocate(tmp);
//          }
//
//          bdlma::AutoReleaser<my_StrPool> autoReleaser(&d_strPool);
//
//          for (int i = 0; i < rhs.d_length; ++i) {
//              const int size =
//                         static_cast<int>(bsl::strlen(rhs.d_array_p[i])) + 1;
//              d_array_p[i] = (char *)d_strPool.allocate(size);
//              bsl::memcpy(d_array_p[i], rhs.d_array_p[i], size);
//          }
//
//          d_length = rhs.d_length;
//          autoReleaser.release();
//      }
//
//      return *this;
//  }
//..
// Note that a 'bdlma::AutoReleaser' proctor is used to manage the array's C
// string memory pool while allocating memory for the individual elements.  If
// an exception is thrown during the 'for' loop, the proctor's destructor
// releases memory for all elements allocated through the pool, thus ensuring
// that no memory is leaked.
//
// Finally, we complete the implementation:
//..
//  void my_FastCstrArray::append(const char *item)
//  {
//      if (d_length >= d_capacity) {
//          this->increaseSize();
//      }
//      const int sSize = static_cast<int>(bsl::strlen(item)) + 1;
//      char *elem = (char *)d_strPool.allocate(sSize);
//      bsl::memcpy(elem, item, sSize * sizeof *item);
//      d_array_p[d_length] = elem;
//      ++d_length;
//  }
//
//  // FREE OPERATORS
//  ostream& operator<<(ostream& stream, const my_FastCstrArray& array)
//  {
//      stream << "[ ";
//      for (int i = 0; i < array.length(); ++i) {
//          stream << '"' << array[i] << "\" ";
//      }
//      return stream << ']' << flush;
//  }
//..

#include <bdlscm_version.h>

namespace BloombergLP {
namespace bdlma {

                            // ==================
                            // class AutoReleaser
                            // ==================

template <class ALLOCATOR>
class AutoReleaser {
    // This class implements a proctor that invokes the 'release' method of its
    // managed allocator or pool at destruction unless the proctor's 'release'
    // method is invoked.

    // DATA
    ALLOCATOR *d_allocator_p;  // allocator or pool (held, not owned)

  private:
    // NOT IMPLEMENTED
    AutoReleaser(const AutoReleaser&);
    AutoReleaser& operator=(const AutoReleaser&);

  public:
    // CREATORS
    AutoReleaser(ALLOCATOR *originalAllocator);
        // Create a proctor object to manage the specified 'originalAllocator'.
        // Unless the 'release' method of this proctor is invoked, the
        // 'release' method of 'originalAllocator' is automatically invoked
        // upon destruction of this proctor.

    ~AutoReleaser();
        // Destroy this proctor object and, unless the 'release' method has
        // been invoked on this object with no subsequent call to 'reset',
        // invoke the 'release' method of the held allocator or pool.

    // MANIPULATORS
    void release();
        // Release from management the allocator or pool currently managed by
        // this proctor.  If no allocator or pool is currently being managed,
        // this method has no effect.

    void reset(ALLOCATOR *newAllocator);
        // Set the specified 'newAllocator' as the allocator or pool to be
        // managed by this proctor.  Note that this method releases from
        // management any previously held allocator or pool, and so may be
        // invoked with or without having called 'release' when reusing this
        // object.
};

// ============================================================================
//                             INLINE DEFINITIONS
// ============================================================================

                            // ------------------
                            // class AutoReleaser
                            // ------------------

// CREATORS
template <class ALLOCATOR>
inline
AutoReleaser<ALLOCATOR>::AutoReleaser(ALLOCATOR *originalAllocator)
: d_allocator_p(originalAllocator)
{
}

template <class ALLOCATOR>
inline
AutoReleaser<ALLOCATOR>::~AutoReleaser()
{
    if (d_allocator_p) {
        d_allocator_p->release();
    }
}

// MANIPULATORS
template <class ALLOCATOR>
inline
void AutoReleaser<ALLOCATOR>::release()
{
    d_allocator_p = 0;
}

template <class ALLOCATOR>
inline
void AutoReleaser<ALLOCATOR>::reset(ALLOCATOR *newAllocator)
{
    d_allocator_p = newAllocator;
}

}  // close package namespace
}  // close enterprise namespace

#endif

// ----------------------------------------------------------------------------
// Copyright 2015 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 ----------------------------------