//============================================================================ // Contents: // // Definition of functions, types and objects for supporting reference // counting on any type. // // History: // // Initial version created by Kevlin Henney, kevlin@acm.org, January 1998. // // Permissions: // // Copyright Kevlin Henney, 1998. All rights reserved. // // Permission to use, copy, modify, and distribute this software for any // purpose is hereby granted without fee, provided that this copyright and // permissions notice appear in all copies and derivatives, and that no // charge may be made for the software and its documentation except to cover // cost of distribution. // // This software is provided "as is" without express or implied warranty. // // Notes: // // Although most of the functions defined are short and seem like candidates // for inlining, they are not: by defining them out of line all of the // the implementation decisions remain fully encapsulated, and any changes // to the implementation will remain binary compatible (the debugging option // available in the code is an example of this). // // If the code in this file is compiled with the DEBUG symbol defined, a // checking version of the code is produced. Wherever possible illegal use // of non-countable (or corrupted) memory is detected and in that event an // std::invalid_argument exception is thrown. // // This code has been written to conform to standard C++ (in final draft // status at the time of writing). It has been compiled successfully using // the operational subset of standard features implemented by Microsoft // Visual C++ 5.0. //============================================================================ #include "countable_new.hpp" #ifdef DEBUG #include // for std::invalid_argument #endif using namespace std; //---------------------------------------------------------------------------- // Description: // // Definition of allocated memory header type and access function from // a new(allocated) pointer. // // Requirements: // // The count type must have an alignment suitable for any type. As provided // the count type will satisfy almost all platforms, but padding members // should be added if this is not the case. // // Notes: // // In theory, attempts to access memory of the wrong type from before an // allocated region of memory are strictly undefined. In theory, there is no // difference between theory and practice, in practice there is. Compiling // with the DEBUG symbol set will add a check field to the count header, and // this is checked on each access. On detected failure std::invalid_argument // is thrown. //---------------------------------------------------------------------------- namespace { struct count { size_t value; #ifdef DEBUG int check; #endif }; const count initial_count = #ifndef DEBUG { 0 }; #else { 0, 'CHEK' }; #endif count *header(const void *ptr) { #ifdef DEBUG if((static_cast(ptr) - 1)->check != initial_count.check) { throw invalid_argument("non-countable or corrupted memory"); } #endif return const_cast(static_cast(ptr) - 1); } } //---------------------------------------------------------------------------- // Description: // // Definition of allocation functions, type and object for reference // countable memory. // // Requirements: // // The count type shall prefix memory allocated by new(countable). Initially // allocated with a reference count value of 0 and, if compiled with DEBUG, // a correctly set check value. // // The count header of an allocated object shall only be modified by the // countable functions. // // Notes: // // Note that the code is written to be independent of the debug options // provided for the count type. // // Although strictly speaking after memory has been deallocated it cannot be // legally accessed, and hence there is little point in resetting it before // deallocation, in practice it can be accessed. Ensuring that the reference // count (and optional check field) are reset can help trap some pointer // aliasing errors; sooner rather than later if the debug option is used. //---------------------------------------------------------------------------- struct countable_new {}; const countable_new countable; void *operator new(size_t size, const countable_new &) throw(bad_alloc) { count *allocated = static_cast(::operator new(sizeof(count) + size)); *allocated = initial_count; // initialise the header return allocated + 1; // adjust result to point to the body } void operator_delete(void *ptr, const countable_new &) throw() { *header(ptr) = count(); // reset header ::operator delete(header(ptr)); } //---------------------------------------------------------------------------- // Description: // // Definition of countability functions for objects allocated by // new(countable). // // Notes: // // The implementation presented is not guaranteed to count safely across // threads. Threadsafe counting can be implemented either by modifying the // code below (eg under Win32 using InterlockedIncrement instead of ++). Note // that in the case of modifying code in this file, not having inline // functions supports a binary compatible change, ie the decision as to // whether or not to use a threadsafe version can be deferred to link time. //---------------------------------------------------------------------------- void acquire(const void *ptr) { if(ptr) { ++header(ptr)->value; } } void release(const void *ptr) { if(ptr) { --header(ptr)->value; } } size_t acquired(const void *ptr) { return ptr ? header(ptr)->value : 0; }