// bslma_testallocatormonitor.h -*-C++-*- #ifndef INCLUDED_BSLMA_TESTALLOCATORMONITOR #define INCLUDED_BSLMA_TESTALLOCATORMONITOR #include <bsls_ident.h> BSLS_IDENT("$Id: $") //@PURPOSE: Provide a mechanism to summarize 'bslma::TestAllocator' object use. // //@CLASSES: // bslma::TestAllocatorMonitor: 'bslma::TestAllocator' summary mechanism // //@SEE_ALSO: bslma_testallocator // //@DESCRIPTION: This component provides a single mechanism class, // 'bslma::TestAllocatorMonitor', which is used, in concert with // 'bslma::TestAllocator', in the implementation of test drivers. The // 'bslma::TestAllocatorMonitor' class provides boolean accessors indicating // whether associated test allocator state has changed (or not) since // construction of the monitor. Using 'bslma::TestAllocatorMonitor' objects // often result in test cases that are more concise, easier to read, and less // error prone than test cases that directly access the test allocator for // state information. // ///Statistics ///---------- // The test allocator statistics tracked by the test allocator monitor along // with the boolean accessors used to observe a change in those statistics are // shown in the table below. The change (or lack of change) reported by these // accessors are relative to the value of the test allocator statistic at the // construction of the monitor. Note that each of these statistics count // blocks of memory (i.e., number of allocations from the allocator), and do // not depend on the number of bytes in those allocated blocks. //.. // Statistic Is-Same Method Is-Up Method Is-Down Method // -------------- -------------- ------------ -------------- // numBlocksInUse isInUseSame isInUseUp isInUseDown // numBlocksMax isMaxSame isMaxUp none // numBlocksTotal isTotalSame isTotalUp none //.. // The 'numBlocksMax' and 'numBlocksTotal' statistics have values that are // monotonically non-decreasing; hence, they need no "Is-Down" methods. Note // that if a monitor is created for an allocator with outstanding blocks ("in // use"), then it is possible for the allocator's count of outstanding blocks // to drop below the value seen by the monitor at construction. // ///Usage ///----- // This section illustrates intended use of this component. // ///Example 1: Standard Usage ///- - - - - - - - - - - - - // Classes taking 'bslma::allocator' objects have many requirements (and thus, // many testing concerns) that other classes do not. Here we illustrate how // 'bslma::TestAllocatorMonitor' objects (in conjunction with // 'bslma::TestAllocator' objects) can be used in a test driver to succinctly // address many concerns of an object's use of allocators. // // First, for a test subject, we introduce 'MyClass', an unconstrained // attribute class having a single, null-terminated ascii string attribute, // 'description'. For the sake of brevity, 'MyClass' defines only a default // constructor, a primary manipulator (the 'setDescription' method), and a // basic accessor (the 'description' method). These suffice for the purposes // of these example. Note that a proper attribute class would also implement // value and copy constructors, 'operator==', an accessor for the allocator, // and other methods. //.. // class MyClass { // // This unconstrained (value-semantic) attribute class has a single, // // null-terminated ascii string attribute, 'description'. // // // DATA // size_t d_capacity; // available memory // char *d_description_p; // string data // bslma::Allocator *d_allocator_p; // held, not owned // // public: // // CREATORS // explicit MyClass(bslma::Allocator *basicAllocator = 0); // // Create a 'MyClass' object having the (default) attribute values: // //.. // // description() == "" // //.. // // Optionally specify a 'basicAllocator' used to supply memory. If // // 'basicAllocator' is 0, the currently installed default allocator // // is used. // // ~MyClass(); // // Destroy this object. // // // MANIPULATORS // void setDescription(const char *value); // // Set the null-terminated ascii string 'description' attribute of // // this object to the specified 'value'. On completion, the // // 'description' method returns the address of a copy of the ascii // // string at 'value'. // // // ACCESSORS // const char *description() const; // // Return the value of the null-terminated ascii string // // 'description' attribute of this object. // }; // // // ======================================================================== // // INLINE FUNCTION DEFINITIONS // // ======================================================================== // // // ------------- // // class MyClass // // ------------- // // // CREATORS // inline // MyClass::MyClass(bslma::Allocator *basicAllocator) // : d_capacity(0) // , d_description_p(0) // , d_allocator_p(bslma::Default::allocator(basicAllocator)) // { // } // // inline // MyClass::~MyClass() // { // BSLS_ASSERT_SAFE(0 <= d_capacity); // // d_allocator_p->deallocate(d_description_p); // } // // // MANIPULATORS // inline // void MyClass::setDescription(const char *value) // { // BSLS_ASSERT_SAFE(value); // // size_t size = std::strlen(value) + 1; // if (size > d_capacity) { // char *newMemory = (char *) d_allocator_p->allocate(size); // d_allocator_p->deallocate(d_description_p); // d_description_p = newMemory; // d_capacity = size; // // } // std::memcpy(d_description_p, value, size); // } //.. // Notice that the implementation of the manipulator allocates/deallocates // memory *before* updating the object. This ordering leaves the object // unchanged in case the allocator throws an exception (part of the strong // exception guarantee). This is an implementation detail, not a part of the // contract (in this example). //.. // // ACCESSORS // inline // const char *MyClass::description() const // { // return d_description_p ? d_description_p : ""; // } //.. // Then, we design a test-driver for 'MyClass'. Our allocator-related concerns // for 'MyClass' include: //.. // Concerns: // //: 1 Any memory allocation is from the object allocator. // //: // //: 2 Every object releases any allocated memory at destruction. // //: // //: 3 No accessor allocates any memory. // //: // //: 4 All memory allocation is exception-neutral. // //: // //: 5 QoI: The default constructor allocates no memory. // //: // //: 6 QoI: When possible, memory is cached for reuse. //.. // Notice that some of these concerns (e.g., C-5..6) are not part of the // class's documented, contractual behavior. These are classified as Quality // of Implementation (QoI) concerns. // // Next, we define a test plan. For example, a plan to test these concerns is: //.. // Plan: // //: 1 Setup global and default allocators: // //: // //: 1 Create two 'bslma::TestAllocator' objects and, for each of these, // //: create an associated 'bslma::TestAllocatorMonitor' object. // //: // //: 2 Install the two allocators as the global and default allocators. // //: // //: 2 Confirm that default construction allocates no memory: (C-5) // //: // //: 1 Construct a 'bslma::TestAllocatorMonitor' object to be used passed // //: to test objects on their construction, and an associated // //: // //: 2 In an inner block, default construct an object of 'MyClass' using // //: the designated "object" test allocator. // //: // //: 3 Allow the object to go out of scope (destroyed). Confirm that no // //: memory has been allocated from any of the allocators. // //: // //: 3 Exercise an object of 'MyClass' such that memory should be allocated, // //: and then confirm that the object allocator (only) is used: (C-2..4,6) // //: // //: 1 In another inner block, default construct a new test object using // //: the (as yet unused) object allocator. // //: // //: 2 Force the test object to allocate memory by setting its // //: 'descriptor' attribute to a value whose size exceeds the size of // //: the object itself. Confirm that the attribute was set and that // //: memory was allocated. // //: // //: 3 Confirm that the primary manipulator (the 'setDescription' method) // //: is exception-neutral (i.e., exceptions from the allocator are // //: propagated and no memory is leaked). Use the // //: 'BSLMA_TESTALLOCATOR_EXCEPTION_TEST_*' macros to manage the test, // //: and use the test allocator monitor to confirm that memory is // //: allocated on the no-exception code path. (C-4) // //: // //: 4 When the object is holding memory, create an additional test // //: allocator monitor allocator for the object allocator. Use the // //: basic accessor (i.e., the 'description' method) to confirm that the // //: object has the expected value. Check this test allocator monitor // //: to confirm that accessor allocated no memory. (C-3) // //: // //: 5 Change the attribute to a smaller value and confirm that the // //: current memory was reused (i.e., no memory is allocated). (C-6) // //: // //: 6 Destroy the test object by allowing it to go out of scope, and // //: confirm that all allocations are returned. (C-2) // //: // //: 4 Confirm that at no time were the global allocator or the default // //: allocator were used. (C-1) //.. // The implementation of the plan is shown below: // // Then, we implement the first portion of the plan. We create the trio of // test allocators, their respective test allocator monitors, and install two // of the allocators as the global and default allocators: //.. // { // if (verbose) cout << "Setup global and default allocators" << endl; // // bslma::TestAllocator ga("global", veryVeryVeryVerbose); // bslma::TestAllocator da("default", veryVeryVeryVerbose); // bslma::TestAllocatorMonitor gam(&ga); // bslma::TestAllocatorMonitor dam(&da); // // bslma::Default::setGlobalAllocator(&ga); // assert(0 == bslma::Default::setDefaultAllocator(&da)); //.. // Then, we default construct a test object using the object allocator, and // then, immediately destroy it. The object allocator monitor, 'oam', shows // that the allocator was not used. //.. // if (verbose) cout << "No allocation by Default Constructor " << endl; // // bslma::TestAllocator oa("object", veryVeryVeryVerbose); // bslma::TestAllocatorMonitor oam(&oa); // // { // MyClass obj(&oa); // assert(oam.isTotalSame()); // object allocator unused // } //.. // Next, we pass the (still unused) object allocator to another test object. // This time, we coerce the object into allocating memory by setting an // attribute. (Setting an attribute larger than the receiving object means // that the object cannot store the data within its own footprint and must // allocate memory.) //.. // if (verbose) cout << "Exercise object" << endl; // // { // MyClass obj(&oa); // // const char DESCRIPTION1[]="abcdefghijklmnopqrstuvwyz" // "abcdefghijklmnopqrstuvwyz"; // assert(sizeof(obj) < sizeof(DESCRIPTION1)); // // if (veryVerbose) cout << "\tPrimary Manipulator Allocates" << endl; // // BSLMA_TESTALLOCATOR_EXCEPTION_TEST_BEGIN(oa) { // if (veryVeryVerbose) { T_ T_ Q(ExceptionTestBody) } // // obj.setDescription(DESCRIPTION1); // assert(oam.isTotalUp()); // object allocator was used // assert(oam.isInUseUp()); // some outstanding allocation(s) // assert(oam.isMaxUp()); // a maximum was set // } BSLMA_TESTALLOCATOR_EXCEPTION_TEST_END //.. // Notice, as expected, memory was allocated from object allocator. // // Now that the allocator has been used, we create a second monitor to capture // the that state. Confirm that the basic accessor (the 'description' method) // does not use the allocator. //.. // if (veryVerbose) cout << "\tBasic Accessor does not allocate" << endl; // // bslma::TestAllocatorMonitor oam2(&oa); // Captures state of 'oa' // // with outstanding // // allocations. // // assert(0 == strcmp(DESCRIPTION1, obj.description())); // assert(oam2.isTotalSame()); // object allocator was not used //.. // Next, confirm that when a shorter value is assigned, the existing memory is // reused. //.. // obj.setDescription("a"); // assert(0 == std::strcmp("a", obj.description())); // // assert(oam2.isTotalSame()); // no allocations //.. // Notice that there are no allocations because the object had sufficient // capacity in previously allocated memory to store the short string. // // Next, as an additional test, we make the object allocate additional memory // by setting a longer attribute: one that exceeds the capacity allocated for // 'DESCRIPTION1'. Use the second monitor to confirm that an allocation was // performed. // // There are tests where using a test allocator monitor does not suffice. Our // test object is currently holding memory, if we assign a value that exceeds // its current capacity there will be two operations on the object allocator: // the allocation of larger memory, and the deallocation of its current memory: // in that order, as part of the strong exception guarantee. Thus, the maximum // number of allocations should go up by one, and no more. // // Note that absence of memory leaks due to exceptions (the other part of the // strong exception guarantee is confirmed during the destruction of the object // test allocator at the end of this test, which featured exceptions. //.. // bsls::Types::Int64 maxBeforeSet = oa.numBlocksMax(); // const char DESCRIPTION2[] = "abcdefghijklmnopqrstuvwyz" // "abcdefghijklmnopqrstuvwyz" // "abcdefghijklmnopqrstuvwyz" // "abcdefghijklmnopqrstuvwyz" // "abcdefghijklmnopqrstuvwyz"; // assert(sizeof(DESCRIPTION1) < sizeof(DESCRIPTION2)); // // obj.setDescription(DESCRIPTION2); // assert(0 == std::strcmp(DESCRIPTION2, obj.description())); // // assert(oam2.isTotalUp()); // The object allocator used. // // assert(oam2.isInUseSame()); // The outstanding block (allocation) // // count unchanged (even though byte // // outstanding byte count increased). // // assert(oam2.isMaxUp()); // Max increased as expected, but was // // did it change only by one? The // // monitor cannot answer that // // question. // // bsls::Types::Int64 maxAfterSet = oa.numBlocksMax(); // // assert(1 == maxAfterSet - maxBeforeSet); //.. // Notice that our test allocator monitor cannot confirm that the allocator's // maximum increased by exactly one. In this case, we must extract our // statistics directly from the test allocator. // // Note that increment in "max" occurs only the first time through the // allocate/deallocate scenario in 'setDescription'. //.. // bslma::TestAllocatorMonitor oam3(&oa); // // const char DESCRIPTION3[] = "abcdefghijklmnopqrstuvwyz" // "abcdefghijklmnopqrstuvwyz" // "abcdefghijklmnopqrstuvwyz" // "abcdefghijklmnopqrstuvwyz" // "abcdefghijklmnopqrstuvwyz" // "abcdefghijklmnopqrstuvwyz" // "abcdefghijklmnopqrstuvwyz" // "abcdefghijklmnopqrstuvwyz" // "abcdefghijklmnopqrstuvwyz"; // assert(sizeof(DESCRIPTION2) < sizeof(DESCRIPTION3)); // // obj.setDescription(DESCRIPTION3); // assert(0 == std::strcmp(DESCRIPTION3, obj.description())); // // assert(oam3.isTotalUp()); // The object allocator used. // // assert(oam3.isInUseSame()); // The outstanding block (allocation) // // count unchanged (even though byte // // outstanding byte count increased). // // assert(oam3.isMaxSame()); // A repeat of the scenario for // // 'DESCRIPTION2', so no change in the // // allocator's maximum. //.. // Now, we close scope and check that all object memory was deallocated //.. // } // // if (veryVerbose) cout << "\tAll memory returned object allocator" // << endl; // // assert(oam.isInUseSame()); //.. // Finally, we check that none of these operations used the default or global // allocators. //.. // if (verbose) cout << "Global and Default allocators never used" << endl; // // assert(gam.isTotalSame()); // assert(dam.isTotalSame()); //.. #include <bslscm_version.h> #include <bslma_testallocator.h> #include <bsls_assert.h> namespace BloombergLP { namespace bslma { // ========================== // class TestAllocatorMonitor // ========================== class TestAllocatorMonitor { // This mechanism provides a set of boolean accessor methods indicating // whether a change has occurred in the state of the 'TestAllocator' object // (supplied at construction) since the construction of the monitor. See // the Statistics section of @DESCRIPTION for the statics tracked. // DATA bsls::Types::Int64 d_initialInUse; // 'numBlocksInUse' bsls::Types::Int64 d_initialMax; // 'numBlocksMax' bsls::Types::Int64 d_initialTotal; // 'numBlocksTotal' const TestAllocator *d_testAllocator_p; // held, not owned // PRIVATE CLASS METHODS static const TestAllocator *validateArgument( const TestAllocator *allocator); // Return the specified 'allocator', and, if compiled in "SAFE" mode, // assert that 'allocator' is not 0. Note that this static function is // needed to perform validation on the allocator address supplied at // construction, prior to that address being dereferenced to initialize // the 'const' data members of this type. private: // NOT IMPLEMENTED TestAllocatorMonitor(const TestAllocatorMonitor&); // = delete TestAllocatorMonitor& operator=(const TestAllocatorMonitor&); // = delete public: // CREATORS explicit TestAllocatorMonitor(const TestAllocator *testAllocator); // Create a 'TestAllocatorMonitor' object to track changes in // statistics of the specified 'testAllocator'. ~TestAllocatorMonitor(); // Destroy this object. // MANIPULATOR void reset(const bslma::TestAllocator *testAllocator = 0); // Change the allocator monitored by this object to the specified // 'testAllocator' and initialize the allocator properties monitored by // this object to the current state of 'testAllocator'. If no // 'testAllocator' is passed, do not modify the allocator held by this // object and re-initialize the allocator properties monitored by this // object to the current state of that allocator. // ACCESSORS bool isInUseDown() const; // Return 'true' if the 'numBlocksInUse' statistic of the tracked test // allocator has decreased since construction of this monitor, and // 'false' otherwise. bool isInUseSame() const; // Return 'true' if the 'numBlocksInUse' statistic of the tracked test // allocator has not changed since construction of this monitor, and // 'false' otherwise. bool isInUseUp() const; // Return 'true' if the 'numBlocksInUse' statistic of the tracked test // allocator has increased since construction of this monitor, and // 'false' otherwise. bool isMaxSame() const; // Return 'true' if the 'numBlocksMax' statistic of the tracked test // allocator has not changed since construction of this monitor, and // 'false' otherwise. bool isMaxUp() const; // Return 'true' if the 'numBlocksMax' statistic of the tracked test // allocator has increased since construction of this monitor, and // 'false' otherwise. bool isTotalSame() const; // Return 'true' if the 'numBlocksTotal' statistic of the tracked test // allocator has not changed since construction of this monitor, and // 'false' otherwise. bool isTotalUp() const; // Return 'true' if the 'numBlocksTotal' statistic of the tracked test // allocator has increased since construction of this monitor, and // 'false' otherwise. bsls::Types::Int64 numBlocksInUseChange() const; // Return the change in the 'numBlocksInUse' statistic of the tracked // test allocator since construction of this monitor. bsls::Types::Int64 numBlocksMaxChange() const; // Return the change in the 'numBlocksMax' statistic of the tracked // test allocator since construction of this monitor. bsls::Types::Int64 numBlocksTotalChange() const; // Return the change in the 'numBlocksTotal' statistic of the tracked // test allocator since construction of this monitor. }; // ============================================================================ // INLINE DEFINITIONS // ============================================================================ // -------------------------- // class TestAllocatorMonitor // -------------------------- // CLASS METHODS inline const TestAllocator * TestAllocatorMonitor::validateArgument(const TestAllocator *allocator) { BSLS_ASSERT_SAFE(allocator); return allocator; } // MANIPULATOR inline void TestAllocatorMonitor::reset(const TestAllocator *testAllocator) { // This method is called inline by c'tor, hence it should precede it. if (testAllocator) { d_testAllocator_p = testAllocator; } d_initialInUse = d_testAllocator_p->numBlocksInUse(); d_initialMax = d_testAllocator_p->numBlocksMax(); d_initialTotal = d_testAllocator_p->numBlocksTotal(); BSLS_ASSERT_SAFE(0 <= d_initialMax); BSLS_ASSERT_SAFE(0 <= d_initialTotal); } // CREATORS inline TestAllocatorMonitor::TestAllocatorMonitor(const TestAllocator *testAllocator) : d_testAllocator_p(testAllocator) { BSLS_ASSERT_SAFE(d_testAllocator_p); reset(); } } // close package namespace namespace bslma { inline TestAllocatorMonitor::~TestAllocatorMonitor() { BSLS_ASSERT_SAFE(d_testAllocator_p); BSLS_ASSERT_SAFE(0 <= d_initialMax); BSLS_ASSERT_SAFE(0 <= d_initialTotal); } } // close package namespace namespace bslma { // ACCESSORS inline bool TestAllocatorMonitor::isInUseDown() const { return d_testAllocator_p->numBlocksInUse() < d_initialInUse; } inline bool TestAllocatorMonitor::isInUseSame() const { return d_testAllocator_p->numBlocksInUse() == d_initialInUse; } inline bool TestAllocatorMonitor::isInUseUp() const { return d_testAllocator_p->numBlocksInUse() > d_initialInUse; } inline bool TestAllocatorMonitor::isMaxSame() const { return d_initialMax == d_testAllocator_p->numBlocksMax(); } inline bool TestAllocatorMonitor::isMaxUp() const { return d_testAllocator_p->numBlocksMax() != d_initialMax; } inline bool TestAllocatorMonitor::isTotalSame() const { return d_testAllocator_p->numBlocksTotal() == d_initialTotal; } inline bool TestAllocatorMonitor::isTotalUp() const { return d_testAllocator_p->numBlocksTotal() != d_initialTotal; } inline bsls::Types::Int64 TestAllocatorMonitor::numBlocksInUseChange() const { return d_testAllocator_p->numBlocksInUse() - d_initialInUse; } inline bsls::Types::Int64 TestAllocatorMonitor::numBlocksMaxChange() const { return d_testAllocator_p->numBlocksMax() - d_initialMax; } inline bsls::Types::Int64 TestAllocatorMonitor::numBlocksTotalChange() const { return d_testAllocator_p->numBlocksTotal() - d_initialTotal; } } // close package namespace #ifndef BDE_OPENSOURCE_PUBLICATION // BACKWARD_COMPATIBILITY // ============================================================================ // BACKWARD COMPATIBILITY // ============================================================================ typedef bslma::TestAllocatorMonitor bslma_TestAllocatorMonitor; // 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 ----------------------------------