// bslma_deallocatorproctor.h                                         -*-C++-*-
#ifndef INCLUDED_BSLMA_DEALLOCATORPROCTOR
#define INCLUDED_BSLMA_DEALLOCATORPROCTOR

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

//@PURPOSE: Provide a proctor to conditionally manage a block memory.
//
//@CLASSES:
//  bslma::DeallocatorProctor: proctor to conditionally manage a memory
//
//@SEE_ALSO: bslma_deallocatorguard, bslma_autodeallocator
//
//@DESCRIPTION: This component provides a proctor class template,
// 'bslma::DeallocatorProctor', to conditionally manage a block of
// (otherwise-unmanaged) memory.  If not explicitly released, the managed
// memory is deallocated automatically when the proctor object goes out of
// scope by freeing the memory using the parameterized 'ALLOCATOR' (allocator
// or pool) supplied at construction.  Note that after a proctor object
// releases its managed memory, the same proctor can be reused to conditionally
// manage another block of memory (allocated from the same allocator or pool
// that was supplied at construction) by invoking the 'reset' method.
//
///Requirement
///-----------
// The parameterized 'ALLOCATOR' type of the 'bslma::DeallocatorProctor' class
// must provide a (possibly 'virtual') method:
//..
//  void deallocate(void *address);
//..
// to deallocate memory at the specified 'address' (originally supplied by the
// 'ALLOCATOR' object).
//
///Usage
///-----
// The 'bslma::DeallocatorProctor' is normally used to achieve *exception*
// *safety* in an *exception* *neutral* way by managing memory in a sequence of
// continuous memory allocations.  Since each memory allocation may
// potentially throw an exception, an object of this proctor class can be used
// to (temporarily) manage newly allocated memory while attempting to allocate
// additional memory.  Should an exception occur in subsequent memory
// allocation, the proctor's destructor deallocates its managed memory,
// preventing a memory leak.
//
// This example illustrate a typical use of 'bslma::DeallocatorProctor'.
// Suppose we have an array class that stores an "in-place" representation of
// objects of parameterized 'TYPE':
//..
//  // my_array.h
//  // ...
//
//  template <class TYPE>
//  class my_Array {
//      // This class implements an "in-place" array of objects of
//      // parameterized 'TYPE' stored contiguously in memory.
//
//      // DATA
//      TYPE             *d_array_p;      // dynamically allocated array
//      int               d_length;       // logical length of this array
//      int               d_size;         // physical capacity of this array
//      bslma::Allocator *d_allocator_p;  // allocator (held, not owned)
//
//    public:
//      // CREATORS
//      my_Array(bslma::Allocator *basicAllocator = 0);
//          // Create a 'my_Array' object.  Optionally specify a
//          // 'basicAllocator' used to supply memory.  If 'basicAllocator' is
//          // 0, the currently installed default allocator is used.
//
//      // ...
//
//      ~my_Array();
//          // Destroy this 'my_Array' object and all elements currently
//          // stored.
//
//      // MANIPULATORS
//      // ...
//
//      void append(const TYPE& object);
//          // Append (a copy of) the specified 'object' of parameterized
//          // 'TYPE' to (the end of) this array.
//
//      // ...
//  };
//..
// Note that the rest of the 'my_Array' interface (above) and implementation
// (below) is elided as the portion shown is sufficient to demonstrate the use
// of 'bslma::DeallocatorProctor'.
//..
//  // CREATORS
//  template <class TYPE>
//  inline
//  my_Array<TYPE>::my_Array(bslma::Allocator *basicAllocator)
//  : d_length(0)
//  , d_size(1)
//  , d_allocator_p(bslma::Default::allocator(basicAllocator))
//  {
//      d_array_p = (TYPE *)d_allocator_p->allocate(sizeof(TYPE));
//  }
//
//  template <class TYPE>
//  my_Array<TYPE>::~my_Array()
//  {
//      for (int i = 0; i < d_length; ++i) {
//          d_array_p[i].~TYPE();
//      }
//      d_allocator_p->deallocate(d_array_p);
//  }
//..
// In order to implement the 'append' function, we first have to introduce an
// 'my_AutoDestructor' 'class', which automatically destroy a sequence of
// managed objects upon destruction.  See 'bslma::AutoDestructor' for a similar
// component with full documentation:
//..
//  // my_autodestructor.h
//  // ...
//
//  template <class TYPE>
//  class my_AutoDestructor {
//      // This class implements a range proctor that, unless its 'release'
//      // method has previously been invoked, automatically invokes the
//      // destructor of each of sequence of objects it manages.
//
//      // DATA
//      TYPE * d_origin_p;
//      int    d_length;
//
//    public:
//      // CREATORS
//      my_AutoDestructor(TYPE *origin, int length)
//          // Create an 'my_AutoDestructor' to manage a contiguous sequence of
//          // objects.
//      : d_origin_p(origin)
//      , d_length(length)
//      {
//      }
//
//      ~my_AutoDestructor()
//          // Destroy this 'my_AutoDestructor' and, unless its 'release'
//          // method has previously been invoked, destroy the sequence of
//          // objects it manages by invoking the destructor of each of the
//          // (managed) objects.
//      {
//          if (d_length) {
//              for (; d_length > 0; --d_length, ++d_origin_p) {
//                  d_origin_p->~TYPE();
//              }
//          }
//      }
//
//      // MANIPULATORS
//      my_AutoDestructor<TYPE>& operator++()
//          // Increase by one the length of the sequence of objects managed by
//          // this range proctor.
//      {
//          ++d_length;
//          return *this;
//      }
//
//      void release()
//          // Release from management the sequence of objects currently
//          // managed by this range proctor.
//      {
//          d_length = 0;
//      }
//  };
//..
// We can now continue with our implementation of the 'my_Array' class:
//..
//  // my_array.h
//  // ...
//
//  // MANIPULATORS
//  template <class TYPE>
//  void my_Array<TYPE>::append(const TYPE &object)
//  {
//      if (d_length == d_size) {
//          TYPE *newArray = (TYPE *)d_allocator_p->allocate(
//                               d_size * 2 * sizeof(TYPE));  // possibly throw
//
//          //*****************************************************************
//          // Note the use of the deallocator proctor on 'newArray' (below). *
//          //*****************************************************************
//
//          bslma::DeallocatorProctor<bslma::Allocator> proctor(newArray,
//                                                              d_allocator_p);
//
//          // Note use of 'my_AutoDestructor' here to protect the copy
//          // construction of 'TYPE' objects.
//          my_AutoDestructor<TYPE> destructor(newArray, 0);
//
//          for (int i = 0; i < d_length; ++i) {
//              new(&newArray[i]) TYPE(d_array_p[i], d_allocator_p);
//                                                           // possibly throw
//              d_array_p[i].~TYPE();
//              ++destructor;
//          }
//
//          destructor.release();
//
//          //*********************************************************
//          // Note that the deallocator proctor is released (below). *
//          //*********************************************************
//
//          proctor.release();
//
//          d_allocator_p->deallocate(d_array_p);
//          d_array_p = newArray;
//          d_size   *= 2;
//      }
//
//      new(&d_array_p[d_length]) TYPE(object, d_allocator_p);
//      ++d_length;
//  }
//..
// Both the use of 'bslma::DeallocatorProctor' and 'my_AutoDestructor' are
// necessary to implement exception safety.
//
// The 'append' method defined above potentially throws in two places.  If the
// memory allocator held in 'd_allocator_p' where to throw while attempting to
// allocate the new array of parameterized 'TYPE', no memory would be leaked.
// But without subsequent use of the 'bslma::DeallocatorProctor', if the
// allocator subsequently throws while copy constructing the objects from the
// old array to the new array, the newly allocated memory block would be
// leaked.  Using the 'bslma::DeallocatorProctor' prevents the leak by
// deallocating the proctored memory automatically should the proctor go out
// of scope before the 'release' method of the proctor is called (such as when
// the function exits prematurely due to an exception).
//
// Similarly, any resources acquired as a result of copy constructing the
// objects from the old array to the new array would be leaked if the
// constructor of 'TYPE' throws.  Using the 'my_AutoDestructor' prevents the
// leak by invoking the destructor of the proctored (and newly created) objects
// in the new array should the 'my_AutoDestructor' goes out of scope before the
// 'release' method of the proctor is called.
//
// Note that the 'append' method assumes the copy constructor of 'TYPE' takes
// an allocator as a second argument.  In production code, a constructor proxy
// that checks the traits of 'TYPE' (to see whether 'TYPE' uses
// 'bslma::Allocator') should be used (see 'bslalg::ConstructorProxy').

#include <bslscm_version.h>

#include <bsls_assert.h>
#include <bsls_performancehint.h>

namespace BloombergLP {

namespace bslma {

                        // ========================
                        // class DeallocatorProctor
                        // ========================

template <class ALLOCATOR>
class DeallocatorProctor {
    // This class implements a proctor that, unless its 'release' method has
    // previously been invoked, automatically deallocates a block of managed
    // memory upon destruction by invoking the 'deallocate' method of an
    // allocator (or pool) of parameterized 'ALLOCATOR' type supplied to it at
    // construction.  The managed memory must have been provided by this
    // allocator (or pool), which must remain valid throughout the lifetime of
    // the proctor object.

    // DATA
    void      *d_memory_p;     // address of managed memory
    ALLOCATOR *d_allocator_p;  // allocator or pool (held, not owned)

    // NOT IMPLEMENTED
    DeallocatorProctor(const DeallocatorProctor&);
    DeallocatorProctor& operator=(const DeallocatorProctor&);

  public:
    // CREATORS
    DeallocatorProctor(void *memory, ALLOCATOR *allocator);
        // Create a deallocator proctor that conditionally manages the
        // specified 'memory' (if non-zero), and that uses the specified
        // 'allocator' to deallocate the block of memory managed by this
        // proctor (if not released -- see 'release') upon destruction.  The
        // behavior is undefined unless 'allocator' is non-zero and supplied
        // 'memory'.  Note that 'allocator' must remain valid throughout the
        // lifetime of this proctor.

    ~DeallocatorProctor();
        // Destroy this deallocator proctor, and deallocate the block of memory
        // it manages (if any) by invoking the 'deallocate' method of the
        // allocator (or pool) that was supplied at construction of this
        // proctor.  If no memory is currently being managed, this method has
        // no effect.

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

    void reset(void *memory);
        // Set the specified 'memory' as the block of memory to be managed by
        // this proctor.  The behavior is undefined unless 'memory' is non-zero
        // and was allocated by the allocator (or pool) supplied at
        // construction.  Note that this method releases any previously-managed
        // memory from management (without deallocating it), and so may be
        // invoked with or without having called 'release' when reusing this
        // object.
};

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

                        // ------------------------
                        // class DeallocatorProctor
                        // ------------------------

// CREATORS
template <class ALLOCATOR>
inline
DeallocatorProctor<ALLOCATOR>:: DeallocatorProctor(void *memory,
                                                   ALLOCATOR *allocator)
: d_memory_p(memory)
, d_allocator_p(allocator)
{
    BSLS_ASSERT_SAFE(allocator);
}

template <class ALLOCATOR>
inline
DeallocatorProctor<ALLOCATOR>::~DeallocatorProctor()
{
    BSLS_ASSERT_SAFE(d_allocator_p);

    if (BSLS_PERFORMANCEHINT_PREDICT_UNLIKELY(0 != d_memory_p)) {
        d_allocator_p->deallocate(d_memory_p);
    }
}

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

template <class ALLOCATOR>
inline
void DeallocatorProctor<ALLOCATOR>::reset(void *memory)
{
    BSLS_ASSERT_SAFE(memory);

    d_memory_p = memory;
}

}  // close package namespace

#ifndef BDE_OPENSOURCE_PUBLICATION  // BACKWARD_COMPATIBILITY
// ============================================================================
//                           BACKWARD COMPATIBILITY
// ============================================================================

#ifdef bslma_DeallocatorProctor
#undef bslma_DeallocatorProctor
#endif
#define bslma_DeallocatorProctor bslma::DeallocatorProctor
    // This alias is defined for backward compatibility.
#endif  // BDE_OPENSOURCE_PUBLICATION -- BACKWARD_COMPATIBILITY

}  // close enterprise namespace

#endif

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