108 lines
3.4 KiB
C++
108 lines
3.4 KiB
C++
/**
|
|
* PANDA 3D SOFTWARE
|
|
* Copyright (c) Carnegie Mellon University. All rights reserved.
|
|
*
|
|
* All use of this software is subject to the terms of the revised BSD
|
|
* license. You should have received a copy of this license along
|
|
* with this source code in a file named "LICENSE."
|
|
*
|
|
* @file deletedBufferChain.h
|
|
* @author drose
|
|
* @date 2007-07-20
|
|
*/
|
|
|
|
#ifndef DELETEDBUFFERCHAIN_H
|
|
#define DELETEDBUFFERCHAIN_H
|
|
|
|
#include "dtoolbase.h"
|
|
#include "neverFreeMemory.h"
|
|
#include "mutexImpl.h"
|
|
#include "atomicAdjust.h"
|
|
#include "numeric_types.h"
|
|
#include "typeHandle.h"
|
|
#include <assert.h>
|
|
|
|
// Though it's tempting, it doesn't seem to be possible to implement
|
|
// DeletedBufferChain via the atomic exchange operation. Specifically, a
|
|
// pointer may be removed from the head of the chain, then the same pointer
|
|
// reinserted in the chain, while another thread is waiting; and that thread
|
|
// will not detect the change. So instead, we always use a mutex.
|
|
|
|
#ifndef NDEBUG
|
|
// In development mode, we define USE_DELETEDCHAINFLAG, which triggers the
|
|
// piggyback of an additional word of data on every allocated block, so we can
|
|
// ensure that an object is not double-deleted and that the deleted chain
|
|
// remains intact.
|
|
#define USE_DELETEDCHAINFLAG 1
|
|
#endif // NDEBUG
|
|
|
|
#ifdef USE_DELETEDCHAINFLAG
|
|
enum DeletedChainFlag {
|
|
DCF_deleted = 0xfeedba0f,
|
|
DCF_alive = 0x12487654,
|
|
};
|
|
#endif
|
|
|
|
/**
|
|
* This template class can be used to provide faster allocation/deallocation
|
|
* for many Panda objects. It works by maintaining a linked list of deleted
|
|
* buffers that are all of the same size; when a new object is allocated that
|
|
* matches that size, the same space is just reused.
|
|
*
|
|
* This class manages untyped buffers of a fixed size. It can be used
|
|
* directly; or it also serves as a backbone for DeletedChain, which is a
|
|
* template class that manages object allocations.
|
|
*
|
|
* Use MemoryHook to get a new DeletedBufferChain of a particular size.
|
|
*/
|
|
class EXPCL_DTOOL_DTOOLBASE DeletedBufferChain {
|
|
protected:
|
|
DeletedBufferChain(size_t buffer_size);
|
|
|
|
public:
|
|
void *allocate(size_t size, TypeHandle type_handle);
|
|
void deallocate(void *ptr, TypeHandle type_handle);
|
|
|
|
INLINE bool validate(void *ptr);
|
|
INLINE size_t get_buffer_size() const;
|
|
|
|
private:
|
|
class ObjectNode {
|
|
public:
|
|
#ifdef USE_DELETEDCHAINFLAG
|
|
// In development mode, we piggyback this extra data. This is maintained
|
|
// out-of-band from the actual pointer returned, so we can safely use this
|
|
// flag to indicate the difference between allocated and freed pointers.
|
|
TVOLATILE AtomicAdjust::Integer _flag;
|
|
#endif
|
|
|
|
// This pointer sits within the buffer, in the same space referenced by
|
|
// the actual pointer returned (unlike _flag, above). It's only used when
|
|
// the buffer is deleted, so there's no harm in sharing space with the
|
|
// undeleted buffer.
|
|
ObjectNode *_next;
|
|
};
|
|
|
|
static INLINE void *node_to_buffer(ObjectNode *node);
|
|
static INLINE ObjectNode *buffer_to_node(void *buffer);
|
|
|
|
ObjectNode *_deleted_chain;
|
|
|
|
MutexImpl _lock;
|
|
size_t _buffer_size;
|
|
|
|
#ifndef USE_DELETEDCHAINFLAG
|
|
// Without DELETEDCHAINFLAG, we don't even store the _flag member at all.
|
|
static const size_t flag_reserved_bytes = 0;
|
|
|
|
#else
|
|
// Otherwise, we need space for the integer.
|
|
static const size_t flag_reserved_bytes = sizeof(AtomicAdjust::Integer);
|
|
#endif // USE_DELETEDCHAINFLAG
|
|
|
|
friend class MemoryHook;
|
|
};
|
|
|
|
#include "deletedBufferChain.I"
|
|
|
|
#endif
|