mirror of
https://github.com/panda3d/panda3d.git
synced 2025-10-04 10:54:24 -04:00
better thread safety
This commit is contained in:
parent
036ad2b115
commit
ad6e2116cc
@ -380,7 +380,11 @@
|
|||||||
|
|
||||||
// Actually, let's *not* assume that threading implies pipelining, at
|
// Actually, let's *not* assume that threading implies pipelining, at
|
||||||
// least not until pipelining is less of a performance hit.
|
// least not until pipelining is less of a performance hit.
|
||||||
#defer DO_PIPELINING $[<= $[OPTIMIZE], 1]
|
//#defer DO_PIPELINING $[<= $[OPTIMIZE], 1]
|
||||||
|
|
||||||
|
// Pipelining is a little broken right now. Turn it off altogether
|
||||||
|
// for now.
|
||||||
|
#defer DO_PIPELINING
|
||||||
|
|
||||||
// Do you want to use one of the alternative malloc implementations?
|
// Do you want to use one of the alternative malloc implementations?
|
||||||
// This is almost always a good idea on Windows, where the standard
|
// This is almost always a good idea on Windows, where the standard
|
||||||
|
@ -39,6 +39,17 @@ dec(TVOLATILE PN_int32 &var) {
|
|||||||
return (--var) != 0;
|
return (--var) != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
// Function: AtomicAdjustDummyImpl::add
|
||||||
|
// Access: Public, Static
|
||||||
|
// Description: Atomically computes var += delta. It is legal for
|
||||||
|
// delta to be negative.
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
INLINE void AtomicAdjustDummyImpl::
|
||||||
|
add(TVOLATILE PN_int32 &var, PN_int32 delta) {
|
||||||
|
var += delta;
|
||||||
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
// Function: AtomicAdjustDummyImpl::set
|
// Function: AtomicAdjustDummyImpl::set
|
||||||
// Access: Public, Static
|
// Access: Public, Static
|
||||||
|
@ -34,6 +34,7 @@ class EXPCL_DTOOL AtomicAdjustDummyImpl {
|
|||||||
public:
|
public:
|
||||||
INLINE static void inc(TVOLATILE PN_int32 &var);
|
INLINE static void inc(TVOLATILE PN_int32 &var);
|
||||||
INLINE static bool dec(TVOLATILE PN_int32 &var);
|
INLINE static bool dec(TVOLATILE PN_int32 &var);
|
||||||
|
INLINE static void add(TVOLATILE PN_int32 &var, PN_int32 delta);
|
||||||
INLINE static PN_int32 set(TVOLATILE PN_int32 &var, PN_int32 new_value);
|
INLINE static PN_int32 set(TVOLATILE PN_int32 &var, PN_int32 new_value);
|
||||||
INLINE static PN_int32 get(const TVOLATILE PN_int32 &var);
|
INLINE static PN_int32 get(const TVOLATILE PN_int32 &var);
|
||||||
|
|
||||||
|
@ -66,6 +66,20 @@ dec(TVOLATILE PN_int32 &var) {
|
|||||||
return (c == 0);
|
return (c == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
// Function: AtomicAdjustI386Impl::add
|
||||||
|
// Access: Public, Static
|
||||||
|
// Description: Atomically computes var += delta. It is legal for
|
||||||
|
// delta to be negative.
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
INLINE void AtomicAdjustI386Impl::
|
||||||
|
add(TVOLATILE PN_int32 &var, PN_int32 delta) {
|
||||||
|
PN_int32 orig_value = var;
|
||||||
|
while (compare_and_exchange(var, orig_value, orig_value + delta) != orig_value) {
|
||||||
|
orig_value = var;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
// Function: AtomicAdjustI386Impl::set
|
// Function: AtomicAdjustI386Impl::set
|
||||||
// Access: Public, Static
|
// Access: Public, Static
|
||||||
|
@ -37,6 +37,7 @@ class EXPCL_DTOOL AtomicAdjustI386Impl {
|
|||||||
public:
|
public:
|
||||||
INLINE static void inc(TVOLATILE PN_int32 &var);
|
INLINE static void inc(TVOLATILE PN_int32 &var);
|
||||||
INLINE static bool dec(TVOLATILE PN_int32 &var);
|
INLINE static bool dec(TVOLATILE PN_int32 &var);
|
||||||
|
INLINE static void add(TVOLATILE PN_int32 &var, PN_int32 delta);
|
||||||
INLINE static PN_int32 set(TVOLATILE PN_int32 &var, PN_int32 new_value);
|
INLINE static PN_int32 set(TVOLATILE PN_int32 &var, PN_int32 new_value);
|
||||||
INLINE static PN_int32 get(const TVOLATILE PN_int32 &var);
|
INLINE static PN_int32 get(const TVOLATILE PN_int32 &var);
|
||||||
|
|
||||||
|
@ -44,6 +44,19 @@ dec(TVOLATILE PN_int32 &var) {
|
|||||||
return (result != 0);
|
return (result != 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
// Function: AtomicAdjustPosixImpl::add
|
||||||
|
// Access: Public, Static
|
||||||
|
// Description: Atomically computes var += delta. It is legal for
|
||||||
|
// delta to be negative.
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
INLINE void AtomicAdjustPosixImpl::
|
||||||
|
add(TVOLATILE PN_int32 &var, PN_int32 delta) {
|
||||||
|
pthread_mutex_lock(&_mutex);
|
||||||
|
var += delta;
|
||||||
|
pthread_mutex_unlock(&_mutex);
|
||||||
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
// Function: AtomicAdjustPosixImpl::set
|
// Function: AtomicAdjustPosixImpl::set
|
||||||
// Access: Public, Static
|
// Access: Public, Static
|
||||||
|
@ -36,6 +36,7 @@ class EXPCL_DTOOL AtomicAdjustPosixImpl {
|
|||||||
public:
|
public:
|
||||||
INLINE static void inc(TVOLATILE PN_int32 &var);
|
INLINE static void inc(TVOLATILE PN_int32 &var);
|
||||||
INLINE static bool dec(TVOLATILE PN_int32 &var);
|
INLINE static bool dec(TVOLATILE PN_int32 &var);
|
||||||
|
INLINE static void add(TVOLATILE PN_int32 &var, PN_int32 delta);
|
||||||
INLINE static PN_int32 set(TVOLATILE PN_int32 &var, PN_int32 new_value);
|
INLINE static PN_int32 set(TVOLATILE PN_int32 &var, PN_int32 new_value);
|
||||||
INLINE static PN_int32 get(const TVOLATILE PN_int32 &var);
|
INLINE static PN_int32 get(const TVOLATILE PN_int32 &var);
|
||||||
|
|
||||||
|
@ -39,6 +39,20 @@ dec(TVOLATILE PN_int32 &var) {
|
|||||||
return (InterlockedDecrement((LONG *)&var) != 0);
|
return (InterlockedDecrement((LONG *)&var) != 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
// Function: AtomicAdjustWin32Impl::add
|
||||||
|
// Access: Public, Static
|
||||||
|
// Description: Atomically computes var += delta. It is legal for
|
||||||
|
// delta to be negative.
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
INLINE void AtomicAdjustWin32Impl::
|
||||||
|
add(TVOLATILE PN_int32 &var, PN_int32 delta) {
|
||||||
|
PN_int32 orig_value = var;
|
||||||
|
while (compare_and_exchange(var, orig_value, orig_value + delta) != orig_value) {
|
||||||
|
orig_value = var;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
// Function: AtomicAdjustWin32Impl::set
|
// Function: AtomicAdjustWin32Impl::set
|
||||||
// Access: Public, Static
|
// Access: Public, Static
|
||||||
|
@ -37,6 +37,7 @@ class EXPCL_DTOOL AtomicAdjustWin32Impl {
|
|||||||
public:
|
public:
|
||||||
INLINE static void inc(TVOLATILE PN_int32 &var);
|
INLINE static void inc(TVOLATILE PN_int32 &var);
|
||||||
INLINE static bool dec(TVOLATILE PN_int32 &var);
|
INLINE static bool dec(TVOLATILE PN_int32 &var);
|
||||||
|
INLINE static void add(TVOLATILE PN_int32 &var, PN_int32 delta);
|
||||||
INLINE static PN_int32 set(TVOLATILE PN_int32 &var, PN_int32 new_value);
|
INLINE static PN_int32 set(TVOLATILE PN_int32 &var, PN_int32 new_value);
|
||||||
INLINE static PN_int32 get(const TVOLATILE PN_int32 &var);
|
INLINE static PN_int32 get(const TVOLATILE PN_int32 &var);
|
||||||
|
|
||||||
|
@ -54,7 +54,7 @@ allocate(TYPENAME pallocator_array<Type>::size_type n, TYPENAME allocator<void>:
|
|||||||
size_t alloc_size = n * sizeof(Type);
|
size_t alloc_size = n * sizeof(Type);
|
||||||
// We also need to store the total number of bytes we allocated.
|
// We also need to store the total number of bytes we allocated.
|
||||||
alloc_size += sizeof(size_t);
|
alloc_size += sizeof(size_t);
|
||||||
_type_handle.inc_memory_usage(TypeHandle::MC_array, alloc_size);
|
_type_handle.inc_memory_usage(TypeHandle::MC_array, (int)alloc_size);
|
||||||
void *ptr = (TYPENAME pallocator_array<Type>::pointer)(*global_operator_new)(alloc_size);
|
void *ptr = (TYPENAME pallocator_array<Type>::pointer)(*global_operator_new)(alloc_size);
|
||||||
*((size_t *)ptr) = alloc_size;
|
*((size_t *)ptr) = alloc_size;
|
||||||
return (TYPENAME pallocator_array<Type>::pointer)(((size_t *)ptr) + 1);
|
return (TYPENAME pallocator_array<Type>::pointer)(((size_t *)ptr) + 1);
|
||||||
@ -70,7 +70,7 @@ deallocate(TYPENAME pallocator_array<Type>::pointer p, TYPENAME pallocator_array
|
|||||||
#ifdef DO_MEMORY_USAGE
|
#ifdef DO_MEMORY_USAGE
|
||||||
// Now we need to recover the total number of bytes.
|
// Now we need to recover the total number of bytes.
|
||||||
size_t alloc_size = *(((size_t *)p) - 1);
|
size_t alloc_size = *(((size_t *)p) - 1);
|
||||||
_type_handle.dec_memory_usage(TypeHandle::MC_array, alloc_size);
|
_type_handle.dec_memory_usage(TypeHandle::MC_array, (int)alloc_size);
|
||||||
(*global_operator_delete)(((size_t *)p) - 1);
|
(*global_operator_delete)(((size_t *)p) - 1);
|
||||||
#else
|
#else
|
||||||
free(p);
|
free(p);
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
|
|
||||||
#include "typeHandle.h"
|
#include "typeHandle.h"
|
||||||
#include "typeRegistryNode.h"
|
#include "typeRegistryNode.h"
|
||||||
|
#include "atomicAdjust.h"
|
||||||
|
|
||||||
// This is initialized to zero by static initialization.
|
// This is initialized to zero by static initialization.
|
||||||
TypeHandle TypeHandle::_none;
|
TypeHandle TypeHandle::_none;
|
||||||
@ -31,7 +32,7 @@ TypeHandle TypeHandle::_none;
|
|||||||
// only updated if track-memory-usage is set true in
|
// only updated if track-memory-usage is set true in
|
||||||
// your Config.prc file.
|
// your Config.prc file.
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
size_t TypeHandle::
|
int TypeHandle::
|
||||||
get_memory_usage(MemoryClass memory_class) const {
|
get_memory_usage(MemoryClass memory_class) const {
|
||||||
assert((int)memory_class >= 0 && (int)memory_class < (int)MC_limit);
|
assert((int)memory_class >= 0 && (int)memory_class < (int)MC_limit);
|
||||||
if ((*this) == TypeHandle::none()) {
|
if ((*this) == TypeHandle::none()) {
|
||||||
@ -39,7 +40,7 @@ get_memory_usage(MemoryClass memory_class) const {
|
|||||||
} else {
|
} else {
|
||||||
TypeRegistryNode *rnode = TypeRegistry::ptr()->look_up(*this, NULL);
|
TypeRegistryNode *rnode = TypeRegistry::ptr()->look_up(*this, NULL);
|
||||||
assert(rnode != (TypeRegistryNode *)NULL);
|
assert(rnode != (TypeRegistryNode *)NULL);
|
||||||
return rnode->_memory_usage[memory_class];
|
return (size_t)AtomicAdjust::get(rnode->_memory_usage[memory_class]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif // DO_MEMORY_USAGE
|
#endif // DO_MEMORY_USAGE
|
||||||
@ -52,12 +53,12 @@ get_memory_usage(MemoryClass memory_class) const {
|
|||||||
// allocated memory for objects of this type.
|
// allocated memory for objects of this type.
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
void TypeHandle::
|
void TypeHandle::
|
||||||
inc_memory_usage(MemoryClass memory_class, size_t size) {
|
inc_memory_usage(MemoryClass memory_class, int size) {
|
||||||
assert((int)memory_class >= 0 && (int)memory_class < (int)MC_limit);
|
assert((int)memory_class >= 0 && (int)memory_class < (int)MC_limit);
|
||||||
if ((*this) != TypeHandle::none()) {
|
if ((*this) != TypeHandle::none()) {
|
||||||
TypeRegistryNode *rnode = TypeRegistry::ptr()->look_up(*this, NULL);
|
TypeRegistryNode *rnode = TypeRegistry::ptr()->look_up(*this, NULL);
|
||||||
assert(rnode != (TypeRegistryNode *)NULL);
|
assert(rnode != (TypeRegistryNode *)NULL);
|
||||||
rnode->_memory_usage[memory_class] += size;
|
AtomicAdjust::add(rnode->_memory_usage[memory_class], (PN_int32)size);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif // DO_MEMORY_USAGE
|
#endif // DO_MEMORY_USAGE
|
||||||
@ -70,12 +71,12 @@ inc_memory_usage(MemoryClass memory_class, size_t size) {
|
|||||||
// the total allocated memory for objects of this type.
|
// the total allocated memory for objects of this type.
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
void TypeHandle::
|
void TypeHandle::
|
||||||
dec_memory_usage(MemoryClass memory_class, size_t size) {
|
dec_memory_usage(MemoryClass memory_class, int size) {
|
||||||
assert((int)memory_class >= 0 && (int)memory_class < (int)MC_limit);
|
assert((int)memory_class >= 0 && (int)memory_class < (int)MC_limit);
|
||||||
if ((*this) != TypeHandle::none()) {
|
if ((*this) != TypeHandle::none()) {
|
||||||
TypeRegistryNode *rnode = TypeRegistry::ptr()->look_up(*this, NULL);
|
TypeRegistryNode *rnode = TypeRegistry::ptr()->look_up(*this, NULL);
|
||||||
assert(rnode != (TypeRegistryNode *)NULL);
|
assert(rnode != (TypeRegistryNode *)NULL);
|
||||||
rnode->_memory_usage[memory_class] -= size;
|
AtomicAdjust::add(rnode->_memory_usage[memory_class], -(PN_int32)size);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif // DO_MEMORY_USAGE
|
#endif // DO_MEMORY_USAGE
|
||||||
|
@ -127,13 +127,13 @@ PUBLISHED:
|
|||||||
INLINE int get_best_parent_from_Set(const std::set< int > &legal_vals) const;
|
INLINE int get_best_parent_from_Set(const std::set< int > &legal_vals) const;
|
||||||
|
|
||||||
#ifdef DO_MEMORY_USAGE
|
#ifdef DO_MEMORY_USAGE
|
||||||
size_t get_memory_usage(MemoryClass memory_class) const;
|
int get_memory_usage(MemoryClass memory_class) const;
|
||||||
void inc_memory_usage(MemoryClass memory_class, size_t size);
|
void inc_memory_usage(MemoryClass memory_class, int size);
|
||||||
void dec_memory_usage(MemoryClass memory_class, size_t size);
|
void dec_memory_usage(MemoryClass memory_class, int size);
|
||||||
#else
|
#else
|
||||||
INLINE size_t get_memory_usage(MemoryClass) const { return 0; }
|
INLINE size_t get_memory_usage(MemoryClass) const { return 0; }
|
||||||
INLINE void inc_memory_usage(MemoryClass, size_t) { }
|
INLINE void inc_memory_usage(MemoryClass, int) { }
|
||||||
INLINE void dec_memory_usage(MemoryClass, size_t) { }
|
INLINE void dec_memory_usage(MemoryClass, int) { }
|
||||||
#endif // DO_MEMORY_USAGE
|
#endif // DO_MEMORY_USAGE
|
||||||
|
|
||||||
INLINE int get_index() const;
|
INLINE int get_index() const;
|
||||||
|
@ -22,6 +22,7 @@
|
|||||||
#include "dtoolbase.h"
|
#include "dtoolbase.h"
|
||||||
|
|
||||||
#include "typeHandle.h"
|
#include "typeHandle.h"
|
||||||
|
#include "numeric_types.h"
|
||||||
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
@ -54,7 +55,7 @@ public:
|
|||||||
Classes _child_classes;
|
Classes _child_classes;
|
||||||
|
|
||||||
#ifdef DO_MEMORY_USAGE
|
#ifdef DO_MEMORY_USAGE
|
||||||
size_t _memory_usage[TypeHandle::MC_limit];
|
PN_int32 _memory_usage[TypeHandle::MC_limit];
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static bool _paranoid_inheritance;
|
static bool _paranoid_inheritance;
|
||||||
|
@ -31,6 +31,10 @@ class SimpleAllocatorBlock;
|
|||||||
// integers within a specified upper limit; it uses a
|
// integers within a specified upper limit; it uses a
|
||||||
// simple first-fit algorithm to find the next available
|
// simple first-fit algorithm to find the next available
|
||||||
// space.
|
// space.
|
||||||
|
//
|
||||||
|
// Note that this class is not inherently thread-safe;
|
||||||
|
// derived classes are responsible for protecting any
|
||||||
|
// calls into it within mutexes, if necessary.
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
class EXPCL_PANDA SimpleAllocator : public LinkedListNode {
|
class EXPCL_PANDA SimpleAllocator : public LinkedListNode {
|
||||||
PUBLISHED:
|
PUBLISHED:
|
||||||
|
@ -132,10 +132,20 @@ get_lru() const {
|
|||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
INLINE void SimpleLruPage::
|
INLINE void SimpleLruPage::
|
||||||
enqueue_lru(SimpleLru *lru) {
|
enqueue_lru(SimpleLru *lru) {
|
||||||
dequeue_lru();
|
MutexHolder holder(SimpleLru::_global_lock);
|
||||||
|
|
||||||
|
if (_lru != (SimpleLru *)NULL) {
|
||||||
|
remove_from_list();
|
||||||
|
_lru->_total_size -= _lru_size;
|
||||||
|
_lru = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
_lru = lru;
|
_lru = lru;
|
||||||
|
|
||||||
|
if (_lru != (SimpleLru *)NULL) {
|
||||||
_lru->_total_size += _lru_size;
|
_lru->_total_size += _lru_size;
|
||||||
insert_before(_lru);
|
insert_before(_lru);
|
||||||
|
}
|
||||||
|
|
||||||
// Let's not automatically evict pages; instead, we'll evict only on
|
// Let's not automatically evict pages; instead, we'll evict only on
|
||||||
// an explicit epoch test.
|
// an explicit epoch test.
|
||||||
@ -149,6 +159,8 @@ enqueue_lru(SimpleLru *lru) {
|
|||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
INLINE void SimpleLruPage::
|
INLINE void SimpleLruPage::
|
||||||
dequeue_lru() {
|
dequeue_lru() {
|
||||||
|
MutexHolder holder(SimpleLru::_global_lock);
|
||||||
|
|
||||||
if (_lru != (SimpleLru *)NULL) {
|
if (_lru != (SimpleLru *)NULL) {
|
||||||
remove_from_list();
|
remove_from_list();
|
||||||
_lru->_total_size -= _lru_size;
|
_lru->_total_size -= _lru_size;
|
||||||
@ -198,10 +210,11 @@ get_lru_size() const {
|
|||||||
// Function: SimpleLruPage::set_lru_size
|
// Function: SimpleLruPage::set_lru_size
|
||||||
// Access: Published
|
// Access: Published
|
||||||
// Description: Specifies the size of this page, presumably in bytes,
|
// Description: Specifies the size of this page, presumably in bytes,
|
||||||
// although any units is possible.
|
// although any unit is possible.
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
INLINE void SimpleLruPage::
|
INLINE void SimpleLruPage::
|
||||||
set_lru_size(size_t lru_size) {
|
set_lru_size(size_t lru_size) {
|
||||||
|
MutexHolder holder(SimpleLru::_global_lock);
|
||||||
if (_lru != (SimpleLru *)NULL) {
|
if (_lru != (SimpleLru *)NULL) {
|
||||||
_lru->_total_size -= _lru_size;
|
_lru->_total_size -= _lru_size;
|
||||||
_lru->_total_size += lru_size;
|
_lru->_total_size += lru_size;
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
|
|
||||||
#include "simpleLru.h"
|
#include "simpleLru.h"
|
||||||
|
|
||||||
|
Mutex SimpleLru::_global_lock;
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
// Function: SimpleLru::Constructor
|
// Function: SimpleLru::Constructor
|
||||||
@ -60,6 +61,7 @@ SimpleLru::
|
|||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
size_t SimpleLru::
|
size_t SimpleLru::
|
||||||
count_active_size() const {
|
count_active_size() const {
|
||||||
|
MutexHolder holder(_global_lock);
|
||||||
size_t total = 0;
|
size_t total = 0;
|
||||||
|
|
||||||
LinkedListNode *node = _prev;
|
LinkedListNode *node = _prev;
|
||||||
@ -78,6 +80,7 @@ count_active_size() const {
|
|||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
void SimpleLru::
|
void SimpleLru::
|
||||||
do_evict() {
|
do_evict() {
|
||||||
|
MutexHolder holder(_global_lock);
|
||||||
// Store the current end of the list. If pages re-enqueue
|
// Store the current end of the list. If pages re-enqueue
|
||||||
// themselves during this traversal, we don't want to visit them
|
// themselves during this traversal, we don't want to visit them
|
||||||
// twice.
|
// twice.
|
||||||
@ -88,8 +91,12 @@ do_evict() {
|
|||||||
while (_total_size > _max_size) {
|
while (_total_size > _max_size) {
|
||||||
SimpleLruPage *next = (SimpleLruPage *)node->_next;
|
SimpleLruPage *next = (SimpleLruPage *)node->_next;
|
||||||
|
|
||||||
|
// We must release the lock while we call evict_lru().
|
||||||
|
_global_lock.release();
|
||||||
node->evict_lru();
|
node->evict_lru();
|
||||||
if (node == end) {
|
_global_lock.lock();
|
||||||
|
|
||||||
|
if (node == end || node == _prev) {
|
||||||
// If we reach the original tail of the list, stop.
|
// If we reach the original tail of the list, stop.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -21,6 +21,8 @@
|
|||||||
|
|
||||||
#include "pandabase.h"
|
#include "pandabase.h"
|
||||||
#include "linkedListNode.h"
|
#include "linkedListNode.h"
|
||||||
|
#include "pmutex.h"
|
||||||
|
#include "mutexHolder.h"
|
||||||
|
|
||||||
class SimpleLruPage;
|
class SimpleLruPage;
|
||||||
|
|
||||||
@ -41,6 +43,9 @@ PUBLISHED:
|
|||||||
INLINE void consider_evict();
|
INLINE void consider_evict();
|
||||||
INLINE void begin_epoch();
|
INLINE void begin_epoch();
|
||||||
|
|
||||||
|
public:
|
||||||
|
static Mutex _global_lock;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void do_evict();
|
void do_evict();
|
||||||
|
|
||||||
|
@ -63,22 +63,6 @@ get_ram_class() const {
|
|||||||
return _ram_class;
|
return _ram_class;
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////
|
|
||||||
// Function: VertexDataPage::check_resident
|
|
||||||
// Access: Published
|
|
||||||
// Description: Forces the vertex data into system RAM, if it is not
|
|
||||||
// already there; also, marks it recently-used.
|
|
||||||
////////////////////////////////////////////////////////////////////
|
|
||||||
INLINE void VertexDataPage::
|
|
||||||
check_resident() const {
|
|
||||||
if (get_ram_class() != RC_resident) {
|
|
||||||
((VertexDataPage *)this)->make_resident();
|
|
||||||
} else {
|
|
||||||
((VertexDataPage *)this)->mark_used_lru();
|
|
||||||
}
|
|
||||||
nassertv(_size == _uncompressed_size);
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
// Function: VertexDataPage::get_first_block
|
// Function: VertexDataPage::get_first_block
|
||||||
// Access: Published
|
// Access: Published
|
||||||
@ -87,6 +71,7 @@ check_resident() const {
|
|||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
INLINE VertexDataBlock *VertexDataPage::
|
INLINE VertexDataBlock *VertexDataPage::
|
||||||
get_first_block() const {
|
get_first_block() const {
|
||||||
|
MutexHolder holder(_lock);
|
||||||
check_resident();
|
check_resident();
|
||||||
return (VertexDataBlock *)SimpleAllocator::get_first_block();
|
return (VertexDataBlock *)SimpleAllocator::get_first_block();
|
||||||
}
|
}
|
||||||
@ -130,6 +115,33 @@ get_save_file() {
|
|||||||
return _save_file;
|
return _save_file;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
// Function: VertexDataPage::save_to_disk
|
||||||
|
// Access: Published
|
||||||
|
// Description: Writes the page to disk, but does not evict it from
|
||||||
|
// memory or affect its LRU status. If it gets evicted
|
||||||
|
// later without having been modified, it will not need
|
||||||
|
// to write itself to disk again.
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
INLINE bool VertexDataPage::
|
||||||
|
save_to_disk() {
|
||||||
|
MutexHolder holder(_lock);
|
||||||
|
return do_save_to_disk();
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
// Function: VertexDataPage::restore_from_disk
|
||||||
|
// Access: Published
|
||||||
|
// Description: Restores the page from disk and makes it
|
||||||
|
// either compressed or resident (according to whether
|
||||||
|
// it was stored compressed on disk).
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
INLINE void VertexDataPage::
|
||||||
|
restore_from_disk() {
|
||||||
|
MutexHolder holder(_lock);
|
||||||
|
do_restore_from_disk();
|
||||||
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
// Function: VertexDataPage::get_page_data
|
// Function: VertexDataPage::get_page_data
|
||||||
// Access: Public
|
// Access: Public
|
||||||
@ -137,10 +149,29 @@ get_save_file() {
|
|||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
INLINE unsigned char *VertexDataPage::
|
INLINE unsigned char *VertexDataPage::
|
||||||
get_page_data() const {
|
get_page_data() const {
|
||||||
|
MutexHolder holder(_lock);
|
||||||
check_resident();
|
check_resident();
|
||||||
return _page_data;
|
return _page_data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
// Function: VertexDataPage::check_resident
|
||||||
|
// Access: Private
|
||||||
|
// Description: Forces the vertex data into system RAM, if it is not
|
||||||
|
// already there; also, marks it recently-used.
|
||||||
|
//
|
||||||
|
// Assumes the lock is already held.
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
INLINE void VertexDataPage::
|
||||||
|
check_resident() const {
|
||||||
|
if (_ram_class != RC_resident) {
|
||||||
|
((VertexDataPage *)this)->make_resident();
|
||||||
|
} else {
|
||||||
|
((VertexDataPage *)this)->mark_used_lru();
|
||||||
|
}
|
||||||
|
nassertv(_size == _uncompressed_size);
|
||||||
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
// Function: VertexDataPage::set_ram_class
|
// Function: VertexDataPage::set_ram_class
|
||||||
// Access: Private
|
// Access: Private
|
||||||
|
@ -20,6 +20,7 @@
|
|||||||
#include "configVariableInt.h"
|
#include "configVariableInt.h"
|
||||||
#include "vertexDataSaveFile.h"
|
#include "vertexDataSaveFile.h"
|
||||||
#include "pStatTimer.h"
|
#include "pStatTimer.h"
|
||||||
|
#include "mutexHolder.h"
|
||||||
|
|
||||||
#ifdef HAVE_ZLIB
|
#ifdef HAVE_ZLIB
|
||||||
#include <zlib.h>
|
#include <zlib.h>
|
||||||
@ -100,6 +101,8 @@ VertexDataBook::
|
|||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
VertexDataBlock *VertexDataBook::
|
VertexDataBlock *VertexDataBook::
|
||||||
alloc(size_t size) {
|
alloc(size_t size) {
|
||||||
|
MutexHolder holder(_lock);
|
||||||
|
|
||||||
// First, try to allocate from the last page that worked; then
|
// First, try to allocate from the last page that worked; then
|
||||||
// continue to the end of the list.
|
// continue to the end of the list.
|
||||||
size_t pi = _next_pi;
|
size_t pi = _next_pi;
|
||||||
@ -175,7 +178,7 @@ VertexDataPage(size_t page_size) : SimpleAllocator(page_size), SimpleLruPage(pag
|
|||||||
_size = page_size;
|
_size = page_size;
|
||||||
_uncompressed_size = _size;
|
_uncompressed_size = _size;
|
||||||
_total_page_size += _size;
|
_total_page_size += _size;
|
||||||
get_class_type().inc_memory_usage(TypeHandle::MC_array, _size);
|
get_class_type().inc_memory_usage(TypeHandle::MC_array, (int)_size);
|
||||||
set_ram_class(RC_resident);
|
set_ram_class(RC_resident);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -187,241 +190,13 @@ VertexDataPage(size_t page_size) : SimpleAllocator(page_size), SimpleLruPage(pag
|
|||||||
VertexDataPage::
|
VertexDataPage::
|
||||||
~VertexDataPage() {
|
~VertexDataPage() {
|
||||||
_total_page_size -= _size;
|
_total_page_size -= _size;
|
||||||
get_class_type().dec_memory_usage(TypeHandle::MC_array, _size);
|
get_class_type().dec_memory_usage(TypeHandle::MC_array, (int)_size);
|
||||||
|
|
||||||
if (_page_data != NULL) {
|
if (_page_data != NULL) {
|
||||||
delete[] _page_data;
|
delete[] _page_data;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////
|
|
||||||
// Function: VertexDataPage::make_resident
|
|
||||||
// Access: Published
|
|
||||||
// Description: Moves the page to fully resident status by
|
|
||||||
// expanding it or reading it from disk as necessary.
|
|
||||||
////////////////////////////////////////////////////////////////////
|
|
||||||
void VertexDataPage::
|
|
||||||
make_resident() {
|
|
||||||
if (_ram_class == RC_resident) {
|
|
||||||
// If we're already resident, just mark the page recently used.
|
|
||||||
mark_used_lru();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_ram_class == RC_disk) {
|
|
||||||
restore_from_disk();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_ram_class == RC_compressed) {
|
|
||||||
#ifdef HAVE_ZLIB
|
|
||||||
PStatTimer timer(_vdata_decompress_pcollector);
|
|
||||||
|
|
||||||
if (gobj_cat.is_debug()) {
|
|
||||||
gobj_cat.debug()
|
|
||||||
<< "Expanding page from " << _size
|
|
||||||
<< " to " << _uncompressed_size << "\n";
|
|
||||||
}
|
|
||||||
unsigned char *new_data = new unsigned char[_uncompressed_size];
|
|
||||||
uLongf dest_len = _uncompressed_size;
|
|
||||||
int result = uncompress(new_data, &dest_len, _page_data, _size);
|
|
||||||
if (result != Z_OK) {
|
|
||||||
gobj_cat.error()
|
|
||||||
<< "Couldn't expand: zlib error " << result << "\n";
|
|
||||||
nassert_raise("zlib error");
|
|
||||||
}
|
|
||||||
nassertv(dest_len == _uncompressed_size);
|
|
||||||
|
|
||||||
get_class_type().dec_memory_usage(TypeHandle::MC_array, _size);
|
|
||||||
_total_page_size -= _size;
|
|
||||||
|
|
||||||
delete[] _page_data;
|
|
||||||
_page_data = new_data;
|
|
||||||
_size = _uncompressed_size;
|
|
||||||
|
|
||||||
get_class_type().inc_memory_usage(TypeHandle::MC_array, _size);
|
|
||||||
_total_page_size += _size;
|
|
||||||
|
|
||||||
#endif
|
|
||||||
set_lru_size(_size);
|
|
||||||
set_ram_class(RC_resident);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////
|
|
||||||
// Function: VertexDataPage::make_compressed
|
|
||||||
// Access: Published
|
|
||||||
// Description: Moves the page to compressed status by
|
|
||||||
// compressing it or reading it from disk as necessary.
|
|
||||||
////////////////////////////////////////////////////////////////////
|
|
||||||
void VertexDataPage::
|
|
||||||
make_compressed() {
|
|
||||||
if (_ram_class == RC_compressed) {
|
|
||||||
// If we're already compressed, just mark the page recently used.
|
|
||||||
mark_used_lru();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_ram_class == RC_disk) {
|
|
||||||
restore_from_disk();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_ram_class == RC_resident) {
|
|
||||||
nassertv(_size == _uncompressed_size);
|
|
||||||
|
|
||||||
#ifdef HAVE_ZLIB
|
|
||||||
PStatTimer timer(_vdata_compress_pcollector);
|
|
||||||
|
|
||||||
// According to the zlib manual, we need to provide this much
|
|
||||||
// buffer to the compress algorithm: 0.1% bigger plus twelve
|
|
||||||
// bytes.
|
|
||||||
uLongf buffer_size = _uncompressed_size + ((_uncompressed_size + 999) / 1000) + 12;
|
|
||||||
Bytef *buffer = (Bytef *)alloca(buffer_size);
|
|
||||||
|
|
||||||
int result = compress2(buffer, &buffer_size,
|
|
||||||
_page_data, _uncompressed_size,
|
|
||||||
vertex_data_compression_level);
|
|
||||||
if (result != Z_OK) {
|
|
||||||
gobj_cat.error()
|
|
||||||
<< "Couldn't compress: zlib error " << result << "\n";
|
|
||||||
nassert_raise("zlib error");
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned char *new_data = new unsigned char[buffer_size];
|
|
||||||
memcpy(new_data, buffer, buffer_size);
|
|
||||||
|
|
||||||
get_class_type().dec_memory_usage(TypeHandle::MC_array, _size);
|
|
||||||
_total_page_size -= _size;
|
|
||||||
|
|
||||||
delete[] _page_data;
|
|
||||||
_page_data = new_data;
|
|
||||||
_size = buffer_size;
|
|
||||||
|
|
||||||
get_class_type().inc_memory_usage(TypeHandle::MC_array, _size);
|
|
||||||
_total_page_size += _size;
|
|
||||||
|
|
||||||
if (gobj_cat.is_debug()) {
|
|
||||||
gobj_cat.debug()
|
|
||||||
<< "Compressed " << *this << " from " << _uncompressed_size
|
|
||||||
<< " to " << _size << "\n";
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
set_lru_size(_size);
|
|
||||||
set_ram_class(RC_compressed);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////
|
|
||||||
// Function: VertexDataPage::make_disk
|
|
||||||
// Access: Published
|
|
||||||
// Description: Moves the page to disk status by
|
|
||||||
// writing it to disk as necessary.
|
|
||||||
////////////////////////////////////////////////////////////////////
|
|
||||||
void VertexDataPage::
|
|
||||||
make_disk() {
|
|
||||||
if (_ram_class == RC_disk) {
|
|
||||||
// If we're already on disk, just mark the page recently used.
|
|
||||||
mark_used_lru();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_ram_class == RC_resident || _ram_class == RC_compressed) {
|
|
||||||
if (!save_to_disk()) {
|
|
||||||
// Can't save it to disk for some reason.
|
|
||||||
mark_used_lru();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
get_class_type().dec_memory_usage(TypeHandle::MC_array, _size);
|
|
||||||
_total_page_size -= _size;
|
|
||||||
|
|
||||||
delete[] _page_data;
|
|
||||||
_page_data = NULL;
|
|
||||||
_size = 0;
|
|
||||||
|
|
||||||
set_ram_class(RC_disk);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////
|
|
||||||
// Function: VertexDataPage::save_to_disk
|
|
||||||
// Access: Published
|
|
||||||
// Description: Writes the page to disk, but does not evict it from
|
|
||||||
// memory or affect its LRU status. If it gets evicted
|
|
||||||
// later without having been modified, it will not need
|
|
||||||
// to write itself to disk again.
|
|
||||||
//
|
|
||||||
// Returns true on success, false on failure.
|
|
||||||
////////////////////////////////////////////////////////////////////
|
|
||||||
bool VertexDataPage::
|
|
||||||
save_to_disk() {
|
|
||||||
if (_ram_class == RC_resident || _ram_class == RC_compressed) {
|
|
||||||
PStatTimer timer(_vdata_save_pcollector);
|
|
||||||
|
|
||||||
if (_saved_block == (VertexDataSaveBlock *)NULL) {
|
|
||||||
if (gobj_cat.is_debug()) {
|
|
||||||
gobj_cat.debug()
|
|
||||||
<< "Storing page, " << _size << " bytes, to disk\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
bool compressed = (_ram_class == RC_compressed);
|
|
||||||
|
|
||||||
_saved_block = get_save_file()->write_data(_page_data, _size, compressed);
|
|
||||||
if (_saved_block == (VertexDataSaveBlock *)NULL) {
|
|
||||||
// Can't write it to disk. Too bad.
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (gobj_cat.is_debug()) {
|
|
||||||
gobj_cat.debug()
|
|
||||||
<< "Page already stored: " << _size << " bytes\n";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////
|
|
||||||
// Function: VertexDataPage::restore_from_disk
|
|
||||||
// Access: Published
|
|
||||||
// Description: Restores the page from disk and makes it
|
|
||||||
// either compressed or resident (according to whether
|
|
||||||
// it was stored compressed on disk).
|
|
||||||
////////////////////////////////////////////////////////////////////
|
|
||||||
void VertexDataPage::
|
|
||||||
restore_from_disk() {
|
|
||||||
if (_ram_class == RC_disk) {
|
|
||||||
nassertv(_saved_block != (VertexDataSaveBlock *)NULL);
|
|
||||||
nassertv(_page_data == (unsigned char *)NULL && _size == 0);
|
|
||||||
|
|
||||||
PStatTimer timer(_vdata_restore_pcollector);
|
|
||||||
|
|
||||||
size_t buffer_size = _saved_block->get_size();
|
|
||||||
if (gobj_cat.is_debug()) {
|
|
||||||
gobj_cat.debug()
|
|
||||||
<< "Restoring page, " << buffer_size << " bytes, from disk\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned char *new_data = new unsigned char[buffer_size];
|
|
||||||
if (!get_save_file()->read_data(new_data, buffer_size, _saved_block)) {
|
|
||||||
nassert_raise("read error");
|
|
||||||
}
|
|
||||||
|
|
||||||
_page_data = new_data;
|
|
||||||
_size = buffer_size;
|
|
||||||
|
|
||||||
get_class_type().inc_memory_usage(TypeHandle::MC_array, _size);
|
|
||||||
_total_page_size += _size;
|
|
||||||
|
|
||||||
set_lru_size(_size);
|
|
||||||
if (_saved_block->get_compressed()) {
|
|
||||||
set_ram_class(RC_compressed);
|
|
||||||
} else {
|
|
||||||
set_ram_class(RC_resident);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
// Function: VertexDataPage::alloc
|
// Function: VertexDataPage::alloc
|
||||||
// Access: Published
|
// Access: Published
|
||||||
@ -433,7 +208,9 @@ restore_from_disk() {
|
|||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
VertexDataBlock *VertexDataPage::
|
VertexDataBlock *VertexDataPage::
|
||||||
alloc(size_t size) {
|
alloc(size_t size) {
|
||||||
|
MutexHolder holder(_lock);
|
||||||
check_resident();
|
check_resident();
|
||||||
|
|
||||||
VertexDataBlock *block = (VertexDataBlock *)SimpleAllocator::alloc(size);
|
VertexDataBlock *block = (VertexDataBlock *)SimpleAllocator::alloc(size);
|
||||||
|
|
||||||
if (block != (VertexDataBlock *)NULL) {
|
if (block != (VertexDataBlock *)NULL) {
|
||||||
@ -473,6 +250,8 @@ make_block(size_t start, size_t size) {
|
|||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
void VertexDataPage::
|
void VertexDataPage::
|
||||||
evict_lru() {
|
evict_lru() {
|
||||||
|
MutexHolder holder(_lock);
|
||||||
|
|
||||||
switch (_ram_class) {
|
switch (_ram_class) {
|
||||||
case RC_resident:
|
case RC_resident:
|
||||||
if (_compressed_lru.get_max_size() == 0) {
|
if (_compressed_lru.get_max_size() == 0) {
|
||||||
@ -496,6 +275,243 @@ evict_lru() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
// Function: VertexDataPage::make_resident
|
||||||
|
// Access: Private
|
||||||
|
// Description: Moves the page to fully resident status by
|
||||||
|
// expanding it or reading it from disk as necessary.
|
||||||
|
//
|
||||||
|
// Assumes the lock is already held.
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
void VertexDataPage::
|
||||||
|
make_resident() {
|
||||||
|
if (_ram_class == RC_resident) {
|
||||||
|
// If we're already resident, just mark the page recently used.
|
||||||
|
mark_used_lru();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_ram_class == RC_disk) {
|
||||||
|
do_restore_from_disk();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_ram_class == RC_compressed) {
|
||||||
|
#ifdef HAVE_ZLIB
|
||||||
|
PStatTimer timer(_vdata_decompress_pcollector);
|
||||||
|
|
||||||
|
if (gobj_cat.is_debug()) {
|
||||||
|
gobj_cat.debug()
|
||||||
|
<< "Expanding page from " << _size
|
||||||
|
<< " to " << _uncompressed_size << "\n";
|
||||||
|
}
|
||||||
|
unsigned char *new_data = new unsigned char[_uncompressed_size];
|
||||||
|
uLongf dest_len = _uncompressed_size;
|
||||||
|
int result = uncompress(new_data, &dest_len, _page_data, _size);
|
||||||
|
if (result != Z_OK) {
|
||||||
|
gobj_cat.error()
|
||||||
|
<< "Couldn't expand: zlib error " << result << "\n";
|
||||||
|
nassert_raise("zlib error");
|
||||||
|
}
|
||||||
|
nassertv(dest_len == _uncompressed_size);
|
||||||
|
|
||||||
|
get_class_type().dec_memory_usage(TypeHandle::MC_array, (int)_size);
|
||||||
|
_total_page_size -= _size;
|
||||||
|
|
||||||
|
delete[] _page_data;
|
||||||
|
_page_data = new_data;
|
||||||
|
_size = _uncompressed_size;
|
||||||
|
|
||||||
|
get_class_type().inc_memory_usage(TypeHandle::MC_array, (int)_size);
|
||||||
|
_total_page_size += _size;
|
||||||
|
|
||||||
|
#endif
|
||||||
|
set_lru_size(_size);
|
||||||
|
set_ram_class(RC_resident);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
// Function: VertexDataPage::make_compressed
|
||||||
|
// Access: Private
|
||||||
|
// Description: Moves the page to compressed status by
|
||||||
|
// compressing it or reading it from disk as necessary.
|
||||||
|
//
|
||||||
|
// Assumes the lock is already held.
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
void VertexDataPage::
|
||||||
|
make_compressed() {
|
||||||
|
if (_ram_class == RC_compressed) {
|
||||||
|
// If we're already compressed, just mark the page recently used.
|
||||||
|
mark_used_lru();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_ram_class == RC_disk) {
|
||||||
|
do_restore_from_disk();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_ram_class == RC_resident) {
|
||||||
|
nassertv(_size == _uncompressed_size);
|
||||||
|
|
||||||
|
#ifdef HAVE_ZLIB
|
||||||
|
PStatTimer timer(_vdata_compress_pcollector);
|
||||||
|
|
||||||
|
// According to the zlib manual, we need to provide this much
|
||||||
|
// buffer to the compress algorithm: 0.1% bigger plus twelve
|
||||||
|
// bytes.
|
||||||
|
uLongf buffer_size = _uncompressed_size + ((_uncompressed_size + 999) / 1000) + 12;
|
||||||
|
Bytef *buffer = (Bytef *)alloca(buffer_size);
|
||||||
|
|
||||||
|
int result = compress2(buffer, &buffer_size,
|
||||||
|
_page_data, _uncompressed_size,
|
||||||
|
vertex_data_compression_level);
|
||||||
|
if (result != Z_OK) {
|
||||||
|
gobj_cat.error()
|
||||||
|
<< "Couldn't compress: zlib error " << result << "\n";
|
||||||
|
nassert_raise("zlib error");
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char *new_data = new unsigned char[buffer_size];
|
||||||
|
memcpy(new_data, buffer, buffer_size);
|
||||||
|
|
||||||
|
get_class_type().dec_memory_usage(TypeHandle::MC_array, (int)_size);
|
||||||
|
_total_page_size -= _size;
|
||||||
|
|
||||||
|
delete[] _page_data;
|
||||||
|
_page_data = new_data;
|
||||||
|
_size = buffer_size;
|
||||||
|
|
||||||
|
get_class_type().inc_memory_usage(TypeHandle::MC_array, (int)_size);
|
||||||
|
_total_page_size += _size;
|
||||||
|
|
||||||
|
if (gobj_cat.is_debug()) {
|
||||||
|
gobj_cat.debug()
|
||||||
|
<< "Compressed " << *this << " from " << _uncompressed_size
|
||||||
|
<< " to " << _size << "\n";
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
set_lru_size(_size);
|
||||||
|
set_ram_class(RC_compressed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
// Function: VertexDataPage::make_disk
|
||||||
|
// Access: Private
|
||||||
|
// Description: Moves the page to disk status by writing it to disk
|
||||||
|
// as necessary.
|
||||||
|
//
|
||||||
|
// Assumes the lock is already held.
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
void VertexDataPage::
|
||||||
|
make_disk() {
|
||||||
|
if (_ram_class == RC_disk) {
|
||||||
|
// If we're already on disk, just mark the page recently used.
|
||||||
|
mark_used_lru();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_ram_class == RC_resident || _ram_class == RC_compressed) {
|
||||||
|
if (!do_save_to_disk()) {
|
||||||
|
// Can't save it to disk for some reason.
|
||||||
|
mark_used_lru();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
get_class_type().dec_memory_usage(TypeHandle::MC_array, (int)_size);
|
||||||
|
_total_page_size -= _size;
|
||||||
|
|
||||||
|
delete[] _page_data;
|
||||||
|
_page_data = NULL;
|
||||||
|
_size = 0;
|
||||||
|
|
||||||
|
set_ram_class(RC_disk);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
// Function: VertexDataPage::do_save_to_disk
|
||||||
|
// Access: Private
|
||||||
|
// Description: Writes the page to disk, but does not evict it from
|
||||||
|
// memory or affect its LRU status. If it gets evicted
|
||||||
|
// later without having been modified, it will not need
|
||||||
|
// to write itself to disk again.
|
||||||
|
//
|
||||||
|
// Returns true on success, false on failure. Assumes
|
||||||
|
// the lock is already held.
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
bool VertexDataPage::
|
||||||
|
do_save_to_disk() {
|
||||||
|
if (_ram_class == RC_resident || _ram_class == RC_compressed) {
|
||||||
|
PStatTimer timer(_vdata_save_pcollector);
|
||||||
|
|
||||||
|
if (_saved_block == (VertexDataSaveBlock *)NULL) {
|
||||||
|
if (gobj_cat.is_debug()) {
|
||||||
|
gobj_cat.debug()
|
||||||
|
<< "Storing page, " << _size << " bytes, to disk\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
bool compressed = (_ram_class == RC_compressed);
|
||||||
|
|
||||||
|
_saved_block = get_save_file()->write_data(_page_data, _size, compressed);
|
||||||
|
if (_saved_block == (VertexDataSaveBlock *)NULL) {
|
||||||
|
// Can't write it to disk. Too bad.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (gobj_cat.is_debug()) {
|
||||||
|
gobj_cat.debug()
|
||||||
|
<< "Page already stored: " << _size << " bytes\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
// Function: VertexDataPage::do_restore_from_disk
|
||||||
|
// Access: Private
|
||||||
|
// Description: Restores the page from disk and makes it
|
||||||
|
// either compressed or resident (according to whether
|
||||||
|
// it was stored compressed on disk).
|
||||||
|
//
|
||||||
|
// Assumes the lock is already held.
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
void VertexDataPage::
|
||||||
|
do_restore_from_disk() {
|
||||||
|
if (_ram_class == RC_disk) {
|
||||||
|
nassertv(_saved_block != (VertexDataSaveBlock *)NULL);
|
||||||
|
nassertv(_page_data == (unsigned char *)NULL && _size == 0);
|
||||||
|
|
||||||
|
PStatTimer timer(_vdata_restore_pcollector);
|
||||||
|
|
||||||
|
size_t buffer_size = _saved_block->get_size();
|
||||||
|
if (gobj_cat.is_debug()) {
|
||||||
|
gobj_cat.debug()
|
||||||
|
<< "Restoring page, " << buffer_size << " bytes, from disk\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char *new_data = new unsigned char[buffer_size];
|
||||||
|
if (!get_save_file()->read_data(new_data, buffer_size, _saved_block)) {
|
||||||
|
nassert_raise("read error");
|
||||||
|
}
|
||||||
|
|
||||||
|
_page_data = new_data;
|
||||||
|
_size = buffer_size;
|
||||||
|
|
||||||
|
get_class_type().inc_memory_usage(TypeHandle::MC_array, (int)_size);
|
||||||
|
_total_page_size += _size;
|
||||||
|
|
||||||
|
set_lru_size(_size);
|
||||||
|
if (_saved_block->get_compressed()) {
|
||||||
|
set_ram_class(RC_compressed);
|
||||||
|
} else {
|
||||||
|
set_ram_class(RC_resident);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
// Function: VertexDataPage::make_save_file
|
// Function: VertexDataPage::make_save_file
|
||||||
// Access: Private, Static
|
// Access: Private, Static
|
||||||
|
@ -25,6 +25,8 @@
|
|||||||
#include "referenceCount.h"
|
#include "referenceCount.h"
|
||||||
#include "pStatCollector.h"
|
#include "pStatCollector.h"
|
||||||
#include "vertexDataSaveFile.h"
|
#include "vertexDataSaveFile.h"
|
||||||
|
#include "pmutex.h"
|
||||||
|
#include "mutexHolder.h"
|
||||||
|
|
||||||
class VertexDataPage;
|
class VertexDataPage;
|
||||||
class VertexDataBlock;
|
class VertexDataBlock;
|
||||||
@ -54,6 +56,7 @@ private:
|
|||||||
typedef pvector<VertexDataPage *> Pages;
|
typedef pvector<VertexDataPage *> Pages;
|
||||||
Pages _pages;
|
Pages _pages;
|
||||||
size_t _next_pi;
|
size_t _next_pi;
|
||||||
|
Mutex _lock;
|
||||||
};
|
};
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
@ -80,13 +83,6 @@ PUBLISHED:
|
|||||||
};
|
};
|
||||||
|
|
||||||
INLINE RamClass get_ram_class() const;
|
INLINE RamClass get_ram_class() const;
|
||||||
INLINE void check_resident() const;
|
|
||||||
|
|
||||||
void make_resident();
|
|
||||||
void make_compressed();
|
|
||||||
void make_disk();
|
|
||||||
bool save_to_disk();
|
|
||||||
void restore_from_disk();
|
|
||||||
|
|
||||||
VertexDataBlock *alloc(size_t size);
|
VertexDataBlock *alloc(size_t size);
|
||||||
INLINE VertexDataBlock *get_first_block() const;
|
INLINE VertexDataBlock *get_first_block() const;
|
||||||
@ -95,6 +91,9 @@ PUBLISHED:
|
|||||||
INLINE static SimpleLru *get_global_lru(RamClass rclass);
|
INLINE static SimpleLru *get_global_lru(RamClass rclass);
|
||||||
INLINE static VertexDataSaveFile *get_save_file();
|
INLINE static VertexDataSaveFile *get_save_file();
|
||||||
|
|
||||||
|
INLINE bool save_to_disk();
|
||||||
|
INLINE void restore_from_disk();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
INLINE unsigned char *get_page_data() const;
|
INLINE unsigned char *get_page_data() const;
|
||||||
|
|
||||||
@ -103,6 +102,15 @@ protected:
|
|||||||
virtual void evict_lru();
|
virtual void evict_lru();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
INLINE void check_resident() const;
|
||||||
|
|
||||||
|
void make_resident();
|
||||||
|
void make_compressed();
|
||||||
|
void make_disk();
|
||||||
|
|
||||||
|
bool do_save_to_disk();
|
||||||
|
void do_restore_from_disk();
|
||||||
|
|
||||||
INLINE void set_ram_class(RamClass ram_class);
|
INLINE void set_ram_class(RamClass ram_class);
|
||||||
static void make_save_file();
|
static void make_save_file();
|
||||||
|
|
||||||
@ -111,6 +119,8 @@ private:
|
|||||||
RamClass _ram_class;
|
RamClass _ram_class;
|
||||||
PT(VertexDataSaveBlock) _saved_block;
|
PT(VertexDataSaveBlock) _saved_block;
|
||||||
|
|
||||||
|
Mutex _lock;
|
||||||
|
|
||||||
static SimpleLru _resident_lru;
|
static SimpleLru _resident_lru;
|
||||||
static SimpleLru _compressed_lru;
|
static SimpleLru _compressed_lru;
|
||||||
static SimpleLru _disk_lru;
|
static SimpleLru _disk_lru;
|
||||||
|
@ -39,7 +39,7 @@ VertexDataBuffer(size_t size) :
|
|||||||
_resident_data(NULL),
|
_resident_data(NULL),
|
||||||
_size(0)
|
_size(0)
|
||||||
{
|
{
|
||||||
unclean_realloc(size);
|
do_unclean_realloc(size);
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
@ -62,7 +62,9 @@ VertexDataBuffer(const VertexDataBuffer ©) :
|
|||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
INLINE void VertexDataBuffer::
|
INLINE void VertexDataBuffer::
|
||||||
operator = (const VertexDataBuffer ©) {
|
operator = (const VertexDataBuffer ©) {
|
||||||
unclean_realloc(copy.get_size());
|
MutexHolder holder(_lock);
|
||||||
|
|
||||||
|
do_unclean_realloc(copy.get_size());
|
||||||
memcpy(_resident_data, copy.get_read_pointer(), _size);
|
memcpy(_resident_data, copy.get_read_pointer(), _size);
|
||||||
_source_file = copy._source_file;
|
_source_file = copy._source_file;
|
||||||
_source_pos = copy._source_pos;
|
_source_pos = copy._source_pos;
|
||||||
@ -85,6 +87,8 @@ INLINE VertexDataBuffer::
|
|||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
INLINE const unsigned char *VertexDataBuffer::
|
INLINE const unsigned char *VertexDataBuffer::
|
||||||
get_read_pointer() const {
|
get_read_pointer() const {
|
||||||
|
MutexHolder holder(_lock);
|
||||||
|
|
||||||
if (_block != (VertexDataBlock *)NULL) {
|
if (_block != (VertexDataBlock *)NULL) {
|
||||||
// We don't necessarily need to page the buffer all the way into
|
// We don't necessarily need to page the buffer all the way into
|
||||||
// independent status; it's sufficient just to return the block's
|
// independent status; it's sufficient just to return the block's
|
||||||
@ -93,7 +97,7 @@ get_read_pointer() const {
|
|||||||
}
|
}
|
||||||
if (_resident_data == (unsigned char *)NULL && !_source_file.is_null()) {
|
if (_resident_data == (unsigned char *)NULL && !_source_file.is_null()) {
|
||||||
// If we need to re-read the original source, do so.
|
// If we need to re-read the original source, do so.
|
||||||
((VertexDataBuffer *)this)->page_in();
|
((VertexDataBuffer *)this)->do_page_in();
|
||||||
}
|
}
|
||||||
|
|
||||||
return _resident_data;
|
return _resident_data;
|
||||||
@ -106,9 +110,11 @@ get_read_pointer() const {
|
|||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
INLINE unsigned char *VertexDataBuffer::
|
INLINE unsigned char *VertexDataBuffer::
|
||||||
get_write_pointer() {
|
get_write_pointer() {
|
||||||
|
MutexHolder holder(_lock);
|
||||||
|
|
||||||
if (_block != (VertexDataBlock *)NULL ||
|
if (_block != (VertexDataBlock *)NULL ||
|
||||||
_resident_data == (unsigned char *)NULL) {
|
_resident_data == (unsigned char *)NULL) {
|
||||||
page_in();
|
do_page_in();
|
||||||
}
|
}
|
||||||
_source_file.clear();
|
_source_file.clear();
|
||||||
return _resident_data;
|
return _resident_data;
|
||||||
@ -124,6 +130,20 @@ get_size() const {
|
|||||||
return _size;
|
return _size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
// Function: VertexDataBuffer::clean_realloc
|
||||||
|
// Access: Public
|
||||||
|
// Description: Changes the size of the buffer, preserving its data
|
||||||
|
// (except for any data beyond the new end of the
|
||||||
|
// buffer, if the buffer is being reduced). If the
|
||||||
|
// buffer is expanded, the new data is uninitialized.
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
INLINE void VertexDataBuffer::
|
||||||
|
clean_realloc(size_t size) {
|
||||||
|
MutexHolder holder(_lock);
|
||||||
|
do_clean_realloc(size);
|
||||||
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
// Function: VertexDataBuffer::unclean_realloc
|
// Function: VertexDataBuffer::unclean_realloc
|
||||||
// Access: Public
|
// Access: Public
|
||||||
@ -133,9 +153,8 @@ get_size() const {
|
|||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
INLINE void VertexDataBuffer::
|
INLINE void VertexDataBuffer::
|
||||||
unclean_realloc(size_t size) {
|
unclean_realloc(size_t size) {
|
||||||
// At the moment, this has no distinct definition, since the system
|
MutexHolder holder(_lock);
|
||||||
// realloc() call doesn't have an unclean variant.
|
do_clean_realloc(size);
|
||||||
clean_realloc(size);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
@ -148,6 +167,22 @@ clear() {
|
|||||||
unclean_realloc(0);
|
unclean_realloc(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
// Function: VertexDataBuffer::page_out
|
||||||
|
// Access: Public
|
||||||
|
// Description: Moves the buffer out of independent memory and puts
|
||||||
|
// it on a page in the indicated book. The buffer may
|
||||||
|
// still be directly accessible as long as its page
|
||||||
|
// remains resident. Any subsequent attempt to rewrite
|
||||||
|
// the buffer will implicitly move it off of the page
|
||||||
|
// and back into independent memory.
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
INLINE void VertexDataBuffer::
|
||||||
|
page_out(VertexDataBook &book) {
|
||||||
|
MutexHolder holder(_lock);
|
||||||
|
do_page_out(book);
|
||||||
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
// Function: VertexDataBuffer::swap
|
// Function: VertexDataBuffer::swap
|
||||||
// Access: Public
|
// Access: Public
|
||||||
@ -156,6 +191,9 @@ clear() {
|
|||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
INLINE void VertexDataBuffer::
|
INLINE void VertexDataBuffer::
|
||||||
swap(VertexDataBuffer &other) {
|
swap(VertexDataBuffer &other) {
|
||||||
|
MutexHolder holder(_lock);
|
||||||
|
MutexHolder holder2(other._lock);
|
||||||
|
|
||||||
unsigned char *resident_data = _resident_data;
|
unsigned char *resident_data = _resident_data;
|
||||||
size_t size = _size;
|
size_t size = _size;
|
||||||
PT(VertexDataBlock) block = _block;
|
PT(VertexDataBlock) block = _block;
|
||||||
@ -185,6 +223,23 @@ swap(VertexDataBuffer &other) {
|
|||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
INLINE void VertexDataBuffer::
|
INLINE void VertexDataBuffer::
|
||||||
set_file(VirtualFile *source_file, streampos source_pos) {
|
set_file(VirtualFile *source_file, streampos source_pos) {
|
||||||
|
MutexHolder holder(_lock);
|
||||||
_source_file = source_file;
|
_source_file = source_file;
|
||||||
_source_pos = source_pos;
|
_source_pos = source_pos;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
// Function: VertexDataBuffer::do_unclean_realloc
|
||||||
|
// Access: Private
|
||||||
|
// Description: Changes the size of the buffer, without regard to
|
||||||
|
// preserving its data. The buffer may contain random
|
||||||
|
// data after this call.
|
||||||
|
//
|
||||||
|
// Assumes the lock is already held.
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
INLINE void VertexDataBuffer::
|
||||||
|
do_unclean_realloc(size_t size) {
|
||||||
|
// At the moment, this has no distinct definition, since the system
|
||||||
|
// realloc() call doesn't have an unclean variant.
|
||||||
|
do_clean_realloc(size);
|
||||||
|
}
|
||||||
|
@ -23,15 +23,17 @@ PStatCollector VertexDataBuffer::_vdata_reread_pcollector("*:Vertex Data:Reread"
|
|||||||
TypeHandle VertexDataBuffer::_type_handle;
|
TypeHandle VertexDataBuffer::_type_handle;
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
// Function: VertexDataBuffer::clean_realloc
|
// Function: VertexDataBuffer::do_clean_realloc
|
||||||
// Access: Public
|
// Access: Private
|
||||||
// Description: Changes the size of the buffer, preserving its data
|
// Description: Changes the size of the buffer, preserving its data
|
||||||
// (except for any data beyond the new end of the
|
// (except for any data beyond the new end of the
|
||||||
// buffer, if the buffer is being reduced). If the
|
// buffer, if the buffer is being reduced). If the
|
||||||
// buffer is expanded, the new data is uninitialized.
|
// buffer is expanded, the new data is uninitialized.
|
||||||
|
//
|
||||||
|
// Assumes the lock is already held.
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
void VertexDataBuffer::
|
void VertexDataBuffer::
|
||||||
clean_realloc(size_t size) {
|
do_clean_realloc(size_t size) {
|
||||||
if (size != _size) {
|
if (size != _size) {
|
||||||
_source_file.clear();
|
_source_file.clear();
|
||||||
|
|
||||||
@ -43,7 +45,7 @@ clean_realloc(size_t size) {
|
|||||||
if (_resident_data != (unsigned char *)NULL) {
|
if (_resident_data != (unsigned char *)NULL) {
|
||||||
free(_resident_data);
|
free(_resident_data);
|
||||||
_resident_data = NULL;
|
_resident_data = NULL;
|
||||||
get_class_type().dec_memory_usage(TypeHandle::MC_array, _size);
|
get_class_type().dec_memory_usage(TypeHandle::MC_array, (int)_size);
|
||||||
}
|
}
|
||||||
_block = NULL;
|
_block = NULL;
|
||||||
|
|
||||||
@ -51,16 +53,15 @@ clean_realloc(size_t size) {
|
|||||||
// Page in if we're currently paged out.
|
// Page in if we're currently paged out.
|
||||||
if (_block != (VertexDataBlock *)NULL ||
|
if (_block != (VertexDataBlock *)NULL ||
|
||||||
_resident_data == (unsigned char *)NULL) {
|
_resident_data == (unsigned char *)NULL) {
|
||||||
page_in();
|
do_page_in();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_resident_data == (unsigned char *)NULL) {
|
if (_resident_data == (unsigned char *)NULL) {
|
||||||
_resident_data = (unsigned char *)malloc(size);
|
_resident_data = (unsigned char *)malloc(size);
|
||||||
get_class_type().inc_memory_usage(TypeHandle::MC_array, size);
|
get_class_type().inc_memory_usage(TypeHandle::MC_array, (int)size);
|
||||||
} else {
|
} else {
|
||||||
_resident_data = (unsigned char *)::realloc(_resident_data, size);
|
_resident_data = (unsigned char *)::realloc(_resident_data, size);
|
||||||
get_class_type().dec_memory_usage(TypeHandle::MC_array, _size);
|
get_class_type().inc_memory_usage(TypeHandle::MC_array, (int)size - (int)_size);
|
||||||
get_class_type().inc_memory_usage(TypeHandle::MC_array, size);
|
|
||||||
}
|
}
|
||||||
nassertv(_resident_data != (unsigned char *)NULL);
|
nassertv(_resident_data != (unsigned char *)NULL);
|
||||||
}
|
}
|
||||||
@ -70,17 +71,19 @@ clean_realloc(size_t size) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
// Function: VertexDataBuffer::page_out
|
// Function: VertexDataBuffer::do_page_out
|
||||||
// Access: Public
|
// Access: Private
|
||||||
// Description: Moves the buffer out of independent memory and puts
|
// Description: Moves the buffer out of independent memory and puts
|
||||||
// it on a page in the indicated book. The buffer may
|
// it on a page in the indicated book. The buffer may
|
||||||
// still be directly accessible as long as its page
|
// still be directly accessible as long as its page
|
||||||
// remains resident. Any subsequent attempt to rewrite
|
// remains resident. Any subsequent attempt to rewrite
|
||||||
// the buffer will implicitly move it off of the page
|
// the buffer will implicitly move it off of the page
|
||||||
// and back into independent memory.
|
// and back into independent memory.
|
||||||
|
//
|
||||||
|
// Assumes the lock is already held.
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
void VertexDataBuffer::
|
void VertexDataBuffer::
|
||||||
page_out(VertexDataBook &book) {
|
do_page_out(VertexDataBook &book) {
|
||||||
if (_block != (VertexDataBlock *)NULL || _size == 0) {
|
if (_block != (VertexDataBlock *)NULL || _size == 0) {
|
||||||
// We're already paged out.
|
// We're already paged out.
|
||||||
return;
|
return;
|
||||||
@ -99,18 +102,20 @@ page_out(VertexDataBook &book) {
|
|||||||
|
|
||||||
free(_resident_data);
|
free(_resident_data);
|
||||||
_resident_data = NULL;
|
_resident_data = NULL;
|
||||||
get_class_type().dec_memory_usage(TypeHandle::MC_array, _size);
|
get_class_type().dec_memory_usage(TypeHandle::MC_array, (int)_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
// Function: VertexDataBuffer::page_in
|
// Function: VertexDataBuffer::do_page_in
|
||||||
// Access: Public
|
// Access: Private
|
||||||
// Description: Moves the buffer off of its current page and into
|
// Description: Moves the buffer off of its current page and into
|
||||||
// independent memory. If the page is not already
|
// independent memory. If the page is not already
|
||||||
// resident, it is forced resident first.
|
// resident, it is forced resident first.
|
||||||
|
//
|
||||||
|
// Assumes the lock is already held.
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
void VertexDataBuffer::
|
void VertexDataBuffer::
|
||||||
page_in() {
|
do_page_in() {
|
||||||
if (_source_file != (VirtualFile *)NULL && _resident_data == (unsigned char *)NULL) {
|
if (_source_file != (VirtualFile *)NULL && _resident_data == (unsigned char *)NULL) {
|
||||||
// Re-read the data from its original source.
|
// Re-read the data from its original source.
|
||||||
PStatTimer timer(_vdata_reread_pcollector);
|
PStatTimer timer(_vdata_reread_pcollector);
|
||||||
|
@ -24,11 +24,40 @@
|
|||||||
#include "pointerTo.h"
|
#include "pointerTo.h"
|
||||||
#include "virtualFile.h"
|
#include "virtualFile.h"
|
||||||
#include "pStatCollector.h"
|
#include "pStatCollector.h"
|
||||||
|
#include "pmutex.h"
|
||||||
|
#include "mutexHolder.h"
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
// Class : VertexDataBuffer
|
// Class : VertexDataBuffer
|
||||||
// Description : A block of bytes that stores the actual raw vertex
|
// Description : A block of bytes that stores the actual raw vertex
|
||||||
// data referenced by a GeomVertexArrayData object.
|
// data referenced by a GeomVertexArrayData object.
|
||||||
|
//
|
||||||
|
// At any point, a buffer may be in any of two states:
|
||||||
|
//
|
||||||
|
// independent - the buffer's memory is resident, and
|
||||||
|
// owned by the VertexDataBuffer object itself (in
|
||||||
|
// _resident_data).
|
||||||
|
//
|
||||||
|
// paged - the buffer's memory is owned by a
|
||||||
|
// VertexDataBlock. That block might itself be
|
||||||
|
// resident, compressed, or paged to disk. If it is
|
||||||
|
// resident, the memory may still be accessed directly
|
||||||
|
// from the block. However, this memory is considered
|
||||||
|
// read-only.
|
||||||
|
//
|
||||||
|
// VertexDataBuffers start out in independent state.
|
||||||
|
// They get moved to paged state when their owning
|
||||||
|
// GeomVertexArrayData objects get evicted from the
|
||||||
|
// _independent_lru. They can get moved back to
|
||||||
|
// independent state if they are modified
|
||||||
|
// (e.g. get_write_pointer() or realloc() is called).
|
||||||
|
//
|
||||||
|
// The idea is to keep the highly dynamic and
|
||||||
|
// frequently-modified VertexDataBuffers resident in
|
||||||
|
// easy-to-access memory, while collecting the static
|
||||||
|
// and rarely accessed VertexDataBuffers together onto
|
||||||
|
// pages, where they may be written to disk as a block
|
||||||
|
// when necessary.
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
class EXPCL_PANDA VertexDataBuffer {
|
class EXPCL_PANDA VertexDataBuffer {
|
||||||
public:
|
public:
|
||||||
@ -42,23 +71,29 @@ public:
|
|||||||
INLINE unsigned char *get_write_pointer();
|
INLINE unsigned char *get_write_pointer();
|
||||||
|
|
||||||
INLINE size_t get_size() const;
|
INLINE size_t get_size() const;
|
||||||
void clean_realloc(size_t size);
|
INLINE void clean_realloc(size_t size);
|
||||||
INLINE void unclean_realloc(size_t size);
|
INLINE void unclean_realloc(size_t size);
|
||||||
INLINE void clear();
|
INLINE void clear();
|
||||||
|
|
||||||
INLINE void swap(VertexDataBuffer &other);
|
INLINE void page_out(VertexDataBook &book);
|
||||||
|
|
||||||
void page_out(VertexDataBook &book);
|
INLINE void swap(VertexDataBuffer &other);
|
||||||
void page_in();
|
|
||||||
|
|
||||||
INLINE void set_file(VirtualFile *source_file, streampos source_pos);
|
INLINE void set_file(VirtualFile *source_file, streampos source_pos);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void do_clean_realloc(size_t size);
|
||||||
|
INLINE void do_unclean_realloc(size_t size);
|
||||||
|
|
||||||
|
void do_page_out(VertexDataBook &book);
|
||||||
|
void do_page_in();
|
||||||
|
|
||||||
unsigned char *_resident_data;
|
unsigned char *_resident_data;
|
||||||
size_t _size;
|
size_t _size;
|
||||||
PT(VertexDataBlock) _block;
|
PT(VertexDataBlock) _block;
|
||||||
PT(VirtualFile) _source_file;
|
PT(VirtualFile) _source_file;
|
||||||
streampos _source_pos;
|
streampos _source_pos;
|
||||||
|
Mutex _lock;
|
||||||
|
|
||||||
static PStatCollector _vdata_reread_pcollector;
|
static PStatCollector _vdata_reread_pcollector;
|
||||||
|
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
#include "vertexDataSaveFile.h"
|
#include "vertexDataSaveFile.h"
|
||||||
|
#include "mutexHolder.h"
|
||||||
|
|
||||||
#ifndef _WIN32
|
#ifndef _WIN32
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
@ -176,6 +177,8 @@ VertexDataSaveFile::
|
|||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
PT(VertexDataSaveBlock) VertexDataSaveFile::
|
PT(VertexDataSaveBlock) VertexDataSaveFile::
|
||||||
write_data(const unsigned char *data, size_t size, bool compressed) {
|
write_data(const unsigned char *data, size_t size, bool compressed) {
|
||||||
|
MutexHolder holder(_lock);
|
||||||
|
|
||||||
if (!_is_valid) {
|
if (!_is_valid) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
@ -229,6 +232,8 @@ write_data(const unsigned char *data, size_t size, bool compressed) {
|
|||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
bool VertexDataSaveFile::
|
bool VertexDataSaveFile::
|
||||||
read_data(unsigned char *data, size_t size, VertexDataSaveBlock *block) {
|
read_data(unsigned char *data, size_t size, VertexDataSaveBlock *block) {
|
||||||
|
MutexHolder holder(_lock);
|
||||||
|
|
||||||
if (!_is_valid) {
|
if (!_is_valid) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -22,6 +22,7 @@
|
|||||||
#include "pandabase.h"
|
#include "pandabase.h"
|
||||||
#include "simpleAllocator.h"
|
#include "simpleAllocator.h"
|
||||||
#include "filename.h"
|
#include "filename.h"
|
||||||
|
#include "pmutex.h"
|
||||||
|
|
||||||
#if defined(_WIN32)
|
#if defined(_WIN32)
|
||||||
#define WIN32_LEAN_AND_MEAN
|
#define WIN32_LEAN_AND_MEAN
|
||||||
@ -60,6 +61,7 @@ private:
|
|||||||
Filename _filename;
|
Filename _filename;
|
||||||
bool _is_valid;
|
bool _is_valid;
|
||||||
size_t _total_file_size;
|
size_t _total_file_size;
|
||||||
|
Mutex _lock;
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
HANDLE _handle;
|
HANDLE _handle;
|
||||||
|
@ -60,8 +60,6 @@ bool BamFile::
|
|||||||
open_read(const Filename &bam_filename, bool report_errors) {
|
open_read(const Filename &bam_filename, bool report_errors) {
|
||||||
close();
|
close();
|
||||||
|
|
||||||
VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
|
|
||||||
|
|
||||||
if (!_din.open(bam_filename)) {
|
if (!_din.open(bam_filename)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -39,7 +39,8 @@ CopyOnWriteObject()
|
|||||||
#endif
|
#endif
|
||||||
#ifdef HAVE_THREADS
|
#ifdef HAVE_THREADS
|
||||||
_lock_status = LS_unlocked;
|
_lock_status = LS_unlocked;
|
||||||
#endif
|
_locking_thread = NULL;
|
||||||
|
#endif // HAVE_THREADS
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
@ -59,7 +60,8 @@ CopyOnWriteObject(const CopyOnWriteObject ©) :
|
|||||||
#endif
|
#endif
|
||||||
#ifdef HAVE_THREADS
|
#ifdef HAVE_THREADS
|
||||||
_lock_status = LS_unlocked;
|
_lock_status = LS_unlocked;
|
||||||
#endif
|
_locking_thread = NULL;
|
||||||
|
#endif // HAVE_THREADS
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
|
@ -39,6 +39,7 @@ unref() const {
|
|||||||
bool is_zero = CachedTypedWritableReferenceCount::unref();
|
bool is_zero = CachedTypedWritableReferenceCount::unref();
|
||||||
if (get_ref_count() == get_cache_ref_count()) {
|
if (get_ref_count() == get_cache_ref_count()) {
|
||||||
((CopyOnWriteObject *)this)->_lock_status = LS_unlocked;
|
((CopyOnWriteObject *)this)->_lock_status = LS_unlocked;
|
||||||
|
((CopyOnWriteObject *)this)->_locking_thread = NULL;
|
||||||
((CopyOnWriteObject *)this)->_lock_cvar.signal();
|
((CopyOnWriteObject *)this)->_lock_cvar.signal();
|
||||||
}
|
}
|
||||||
return is_zero;
|
return is_zero;
|
||||||
|
@ -57,6 +57,7 @@ private:
|
|||||||
Mutex _lock_mutex;
|
Mutex _lock_mutex;
|
||||||
ConditionVar _lock_cvar;
|
ConditionVar _lock_cvar;
|
||||||
LockStatus _lock_status;
|
LockStatus _lock_status;
|
||||||
|
Thread *_locking_thread;
|
||||||
#endif // HAVE_THREADS
|
#endif // HAVE_THREADS
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
#include "copyOnWritePointer.h"
|
#include "copyOnWritePointer.h"
|
||||||
#include "mutexHolder.h"
|
#include "mutexHolder.h"
|
||||||
#include "config_util.h"
|
#include "config_util.h"
|
||||||
|
#include "config_pipeline.h"
|
||||||
|
|
||||||
#ifdef HAVE_THREADS
|
#ifdef HAVE_THREADS
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
@ -37,12 +38,18 @@ get_read_pointer() const {
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Thread *current_thread = Thread::get_current_thread();
|
||||||
|
|
||||||
MutexHolder holder(_object->_lock_mutex);
|
MutexHolder holder(_object->_lock_mutex);
|
||||||
while (_object->_lock_status == CopyOnWriteObject::LS_locked_write) {
|
while (_object->_lock_status == CopyOnWriteObject::LS_locked_write) {
|
||||||
|
if (_object->_locking_thread == current_thread) {
|
||||||
|
return _object;
|
||||||
|
}
|
||||||
_object->_lock_cvar.wait();
|
_object->_lock_cvar.wait();
|
||||||
}
|
}
|
||||||
|
|
||||||
_object->_lock_status = CopyOnWriteObject::LS_locked_read;
|
_object->_lock_status = CopyOnWriteObject::LS_locked_read;
|
||||||
|
_object->_locking_thread = Thread::get_current_thread();
|
||||||
return _object;
|
return _object;
|
||||||
}
|
}
|
||||||
#endif // HAVE_THREADS
|
#endif // HAVE_THREADS
|
||||||
@ -67,15 +74,23 @@ get_write_pointer() {
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Thread *current_thread = Thread::get_current_thread();
|
||||||
|
|
||||||
MutexHolder holder(_object->_lock_mutex);
|
MutexHolder holder(_object->_lock_mutex);
|
||||||
while (_object->_lock_status == CopyOnWriteObject::LS_locked_write) {
|
while (_object->_lock_status == CopyOnWriteObject::LS_locked_write) {
|
||||||
|
if (_object->_locking_thread == current_thread) {
|
||||||
|
return _object;
|
||||||
|
}
|
||||||
_object->_lock_cvar.wait();
|
_object->_lock_cvar.wait();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_object->_lock_status == CopyOnWriteObject::LS_locked_read) {
|
if (_object->_lock_status == CopyOnWriteObject::LS_locked_read) {
|
||||||
// Someone else has a read copy of this pointer; we need to make
|
|
||||||
// our own writable copy.
|
|
||||||
nassertr(_object->get_ref_count() > _object->get_cache_ref_count(), NULL);
|
nassertr(_object->get_ref_count() > _object->get_cache_ref_count(), NULL);
|
||||||
|
if (_object->_locking_thread == current_thread) {
|
||||||
|
_object->_lock_status = CopyOnWriteObject::LS_locked_write;
|
||||||
|
return _object;
|
||||||
|
}
|
||||||
|
|
||||||
if (util_cat.is_debug()) {
|
if (util_cat.is_debug()) {
|
||||||
util_cat.debug()
|
util_cat.debug()
|
||||||
<< "Making copy of " << _object->get_type()
|
<< "Making copy of " << _object->get_type()
|
||||||
@ -112,6 +127,7 @@ get_write_pointer() {
|
|||||||
// have saved himself a reference.
|
// have saved himself a reference.
|
||||||
}
|
}
|
||||||
_object->_lock_status = CopyOnWriteObject::LS_locked_write;
|
_object->_lock_status = CopyOnWriteObject::LS_locked_write;
|
||||||
|
_object->_locking_thread = Thread::get_current_thread();
|
||||||
|
|
||||||
return _object;
|
return _object;
|
||||||
}
|
}
|
||||||
|
@ -31,6 +31,10 @@
|
|||||||
//
|
//
|
||||||
// Typically, each node of the linked list, as well as
|
// Typically, each node of the linked list, as well as
|
||||||
// the root of the list, will inherit from this class.
|
// the root of the list, will inherit from this class.
|
||||||
|
//
|
||||||
|
// Note that this class is not inherently thread-safe;
|
||||||
|
// derived classes are responsible for protecting any
|
||||||
|
// calls into it within mutexes, if necessary.
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
class EXPCL_PANDA LinkedListNode {
|
class EXPCL_PANDA LinkedListNode {
|
||||||
protected:
|
protected:
|
||||||
|
Loading…
x
Reference in New Issue
Block a user