Browse Source

better algorithm for finding available pages

David Rose 18 years ago
parent
commit
67771fb75c

+ 5 - 3
panda/src/gobj/geomVertexArrayData.cxx

@@ -723,9 +723,11 @@ copy_data_from(const GeomVertexArrayDataHandle *other) {
   other->mark_used();
   other->mark_used();
 
 
   _cdata->_buffer.unclean_realloc(other->_cdata->_buffer.get_size());
   _cdata->_buffer.unclean_realloc(other->_cdata->_buffer.get_size());
-  memcpy(_cdata->_buffer.get_write_pointer(),
-         other->_cdata->_buffer.get_read_pointer(true),
-         other->_cdata->_buffer.get_size());
+
+  unsigned char *dest = _cdata->_buffer.get_write_pointer();
+  const unsigned char *source = other->_cdata->_buffer.get_read_pointer(true);
+  size_t size = other->_cdata->_buffer.get_size();
+  memcpy(dest, source, size);
 
 
   _cdata->_modified = Geom::get_next_modified();
   _cdata->_modified = Geom::get_next_modified();
 
 

+ 110 - 26
panda/src/gobj/simpleAllocator.I

@@ -23,14 +23,30 @@
 //  Description: 
 //  Description: 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE SimpleAllocator::
 INLINE SimpleAllocator::
-SimpleAllocator(size_t max_size) : 
+SimpleAllocator(size_t max_size, Mutex &lock) : 
   LinkedListNode(true),
   LinkedListNode(true),
   _total_size(0),
   _total_size(0),
   _max_size(max_size),
   _max_size(max_size),
-  _contiguous(max_size)
+  _contiguous(max_size),
+  _lock(lock)
 {
 {
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: SimpleAllocator::alloc
+//       Access: Published
+//  Description: Allocates a new block.  Returns NULL if a block of the
+//               requested size cannot be allocated.
+//
+//               To free the allocated block, call block->free(), or
+//               simply delete the block pointer.
+////////////////////////////////////////////////////////////////////
+SimpleAllocatorBlock *SimpleAllocator::
+alloc(size_t size) {
+  MutexHolder holder(_lock);
+  return do_alloc(size);
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: SimpleAllocator::is_empty
 //     Function: SimpleAllocator::is_empty
 //       Access: Published
 //       Access: Published
@@ -39,7 +55,8 @@ SimpleAllocator(size_t max_size) :
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE bool SimpleAllocator::
 INLINE bool SimpleAllocator::
 is_empty() const {
 is_empty() const {
-  return (_next == this);
+  MutexHolder holder(_lock);
+  return do_is_empty();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -49,6 +66,7 @@ is_empty() const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE size_t SimpleAllocator::
 INLINE size_t SimpleAllocator::
 get_total_size() const {
 get_total_size() const {
+  MutexHolder holder(_lock);
   return _total_size;
   return _total_size;
 }
 }
 
 
@@ -59,6 +77,7 @@ get_total_size() const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE size_t SimpleAllocator::
 INLINE size_t SimpleAllocator::
 get_max_size() const {
 get_max_size() const {
+  MutexHolder holder(_lock);
   return _max_size;
   return _max_size;
 }
 }
 
 
@@ -71,6 +90,7 @@ get_max_size() const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE void SimpleAllocator::
 INLINE void SimpleAllocator::
 set_max_size(size_t max_size) {
 set_max_size(size_t max_size) {
+  MutexHolder holder(_lock);
   _max_size = max_size;
   _max_size = max_size;
 }
 }
 
 
@@ -86,6 +106,7 @@ set_max_size(size_t max_size) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE size_t SimpleAllocator::
 INLINE size_t SimpleAllocator::
 get_contiguous() const {
 get_contiguous() const {
+  MutexHolder holder(_lock);
   return _contiguous;
   return _contiguous;
 }
 }
 
 
@@ -97,15 +118,31 @@ get_contiguous() const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE SimpleAllocatorBlock *SimpleAllocator::
 INLINE SimpleAllocatorBlock *SimpleAllocator::
 get_first_block() const {
 get_first_block() const {
+  MutexHolder holder(_lock);
   return (_next == this) ? (SimpleAllocatorBlock *)NULL : (SimpleAllocatorBlock *)_next;
   return (_next == this) ? (SimpleAllocatorBlock *)NULL : (SimpleAllocatorBlock *)_next;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: SimpleAllocator::increase_contiguous
+//     Function: SimpleAllocator::do_is_empty
+//       Access: Protected
+//  Description: Returns true if there are no blocks allocated on this
+//               page, or false if there is at least one.
+//
+//               Assumes the lock is already held.
+////////////////////////////////////////////////////////////////////
+INLINE bool SimpleAllocator::
+do_is_empty() const {
+  return (_next == this);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SimpleAllocator::mark_contiguous
 //       Access: Protected
 //       Access: Protected
 //  Description: Some space has been made available following the
 //  Description: Some space has been made available following the
 //               indicated block.  Increase the contiguous space
 //               indicated block.  Increase the contiguous space
 //               accordingly.
 //               accordingly.
+//
+//               Assumes the lock is already held.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE void SimpleAllocator::
 INLINE void SimpleAllocator::
 mark_contiguous(const LinkedListNode *block) {
 mark_contiguous(const LinkedListNode *block) {
@@ -119,10 +156,11 @@ mark_contiguous(const LinkedListNode *block) {
       space = ((SimpleAllocatorBlock *)_next)->get_start();
       space = ((SimpleAllocatorBlock *)_next)->get_start();
     }
     }
   } else {
   } else {
-    space = ((SimpleAllocatorBlock *)block)->get_max_size() - ((SimpleAllocatorBlock *)block)->get_size();
+    space = ((SimpleAllocatorBlock *)block)->do_get_max_size() - ((SimpleAllocatorBlock *)block)->get_size();
   }
   }
   if (space > _contiguous) {
   if (space > _contiguous) {
     _contiguous = space;
     _contiguous = space;
+    changed_contiguous();
   }
   }
 }
 }
 
 
@@ -160,11 +198,8 @@ INLINE SimpleAllocatorBlock::
 INLINE void SimpleAllocatorBlock::
 INLINE void SimpleAllocatorBlock::
 free() {
 free() {
   if (_allocator != (SimpleAllocator *)NULL) {
   if (_allocator != (SimpleAllocator *)NULL) {
-    _allocator->_total_size -= _size;
-    LinkedListNode *prev = _prev;
-    remove_from_list();
-    _allocator->mark_contiguous(prev);
-    _allocator = NULL;
+    MutexHolder holder(_allocator->_lock);
+    do_free();
   }
   }
 }
 }
 
 
@@ -223,10 +258,68 @@ is_free() const {
 INLINE size_t SimpleAllocatorBlock::
 INLINE size_t SimpleAllocatorBlock::
 get_max_size() const {
 get_max_size() const {
   nassertr(_allocator != (SimpleAllocator *)NULL, 0);
   nassertr(_allocator != (SimpleAllocator *)NULL, 0);
+  MutexHolder holder(_allocator->_lock);
+  return do_get_max_size();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SimpleAllocatorBlock::realloc
+//       Access: Published
+//  Description: Changes the size of this block to the specified size.
+//               Returns true if the change is accepted, false if
+//               there was not enough room.
+////////////////////////////////////////////////////////////////////
+INLINE bool SimpleAllocatorBlock::
+realloc(size_t size) {
+  nassertr(_allocator != (SimpleAllocator *)NULL, false);
+  MutexHolder holder(_allocator->_lock);
+  return do_realloc(size);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SimpleAllocatorBlock::get_next_block
+//       Access: Published
+//  Description: Returns a pointer to the next allocated block in the
+//               chain, or NULL if there are no more allocated blocks.
+////////////////////////////////////////////////////////////////////
+INLINE SimpleAllocatorBlock *SimpleAllocatorBlock::
+get_next_block() const {
+  nassertr(_allocator != (SimpleAllocator *)NULL, NULL);
+  MutexHolder holder(_allocator->_lock);
+  return (_next == _allocator) ? (SimpleAllocatorBlock *)NULL : (SimpleAllocatorBlock *)_next;
+}
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: SimpleAllocatorBlock::do_free
+//       Access: Protected
+//  Description: Releases the allocated space.
+//
+//               Assumes the lock is already held.
+////////////////////////////////////////////////////////////////////
+INLINE void SimpleAllocatorBlock::
+do_free() {
+  nassertv(_allocator != (SimpleAllocator *)NULL);
+
+  _allocator->_total_size -= _size;
+  LinkedListNode *prev = _prev;
+  remove_from_list();
+  _allocator->mark_contiguous(prev);
+  _allocator = NULL;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SimpleAllocatorBlock::do_get_max_size
+//       Access: Protected
+//  Description: Returns the maximum size this block can be
+//               reallocated to, as limited by the following block.
+//
+//               Assumes the lock is already held.
+////////////////////////////////////////////////////////////////////
+INLINE size_t SimpleAllocatorBlock::
+do_get_max_size() const {
   size_t end;
   size_t end;
   if (_next == _allocator) {
   if (_next == _allocator) {
-    end = _allocator->get_max_size();
+    end = _allocator->_max_size;
   } else {
   } else {
     end = ((SimpleAllocatorBlock *)_next)->_start;
     end = ((SimpleAllocatorBlock *)_next)->_start;
   }
   }
@@ -234,15 +327,17 @@ get_max_size() const {
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: SimpleAllocatorBlock::realloc
-//       Access: Published
+//     Function: SimpleAllocatorBlock::do_realloc
+//       Access: Protected
 //  Description: Changes the size of this block to the specified size.
 //  Description: Changes the size of this block to the specified size.
 //               Returns true if the change is accepted, false if
 //               Returns true if the change is accepted, false if
 //               there was not enough room.
 //               there was not enough room.
+//
+//               Assumes the lock is already held.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE bool SimpleAllocatorBlock::
 INLINE bool SimpleAllocatorBlock::
-realloc(size_t size) {
-  if (size > get_max_size()) {
+do_realloc(size_t size) {
+  if (size > do_get_max_size()) {
     return false;
     return false;
   }
   }
 
 
@@ -259,14 +354,3 @@ realloc(size_t size) {
   }
   }
   return true;
   return true;
 }
 }
-
-////////////////////////////////////////////////////////////////////
-//     Function: SimpleAllocatorBlock::get_next_block
-//       Access: Published
-//  Description: Returns a pointer to the next allocated block in the
-//               chain, or NULL if there are no more allocated blocks.
-////////////////////////////////////////////////////////////////////
-INLINE SimpleAllocatorBlock *SimpleAllocatorBlock::
-get_next_block() const {
-  return (_next == _allocator) ? (SimpleAllocatorBlock *)NULL : (SimpleAllocatorBlock *)_next;
-}

+ 80 - 44
panda/src/gobj/simpleAllocator.cxx

@@ -26,24 +26,60 @@
 SimpleAllocator::
 SimpleAllocator::
 ~SimpleAllocator() {
 ~SimpleAllocator() {
   // We're shutting down.  Force-free everything remaining.
   // We're shutting down.  Force-free everything remaining.
-  while (_next != (LinkedListNode *)this) {
-    nassertv(_next != (LinkedListNode *)NULL);
-    cerr << "force-deleting " << _next << "\n";
-    ((SimpleAllocatorBlock *)_next)->free();
+  if (_next != (LinkedListNode *)this) {
+    MutexHolder holder(_lock);
+    while (_next != (LinkedListNode *)this) {
+      nassertv(_next != (LinkedListNode *)NULL);
+      ((SimpleAllocatorBlock *)_next)->do_free();
+    }
   }
   }
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: SimpleAllocator::alloc
+//     Function: SimpleAllocator::output
 //       Access: Published
 //       Access: Published
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void SimpleAllocator::
+output(ostream &out) const {
+  MutexHolder holder(_lock);
+  out << "SimpleAllocator, " << _total_size << " of " << _max_size 
+      << " allocated";
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SimpleAllocator::write
+//       Access: Published
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void SimpleAllocator::
+write(ostream &out) const {
+  MutexHolder holder(_lock);
+  out << "SimpleAllocator, " << _total_size << " of " << _max_size 
+      << " allocated";
+
+  SimpleAllocatorBlock *block = (SimpleAllocatorBlock *)_next;
+  while (block->_next != this) {
+    SimpleAllocatorBlock *next = (SimpleAllocatorBlock *)block->_next;
+    
+    out << "  " << *block << "\n";
+    block = next;
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SimpleAllocator::do_alloc
+//       Access: Protected
 //  Description: Allocates a new block.  Returns NULL if a block of the
 //  Description: Allocates a new block.  Returns NULL if a block of the
 //               requested size cannot be allocated.
 //               requested size cannot be allocated.
 //
 //
 //               To free the allocated block, call block->free(), or
 //               To free the allocated block, call block->free(), or
 //               simply delete the block pointer.
 //               simply delete the block pointer.
+//
+//               Assumes the lock is already held.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 SimpleAllocatorBlock *SimpleAllocator::
 SimpleAllocatorBlock *SimpleAllocator::
-alloc(size_t size) {
+do_alloc(size_t size) {
   if (size > _contiguous) {
   if (size > _contiguous) {
     // Don't even bother.
     // Don't even bother.
     return NULL;
     return NULL;
@@ -70,6 +106,14 @@ alloc(size_t size) {
 
 
         new_block->insert_before(next);
         new_block->insert_before(next);
         _total_size += size;
         _total_size += size;
+
+        if (_max_size - _total_size < _contiguous) {
+          // Since we only have (_max_size - _total_size) bytes
+          // remaining, it follows that our largest contiguous block
+          // must be no larger than this.
+          _contiguous = _max_size - _total_size;
+          changed_contiguous();
+        }
         return new_block;
         return new_block;
       }
       }
       if (free_size > best) {
       if (free_size > best) {
@@ -89,6 +133,14 @@ alloc(size_t size) {
 
 
     new_block->insert_before(this);
     new_block->insert_before(this);
     _total_size += size;
     _total_size += size;
+
+    if (_max_size - _total_size < _contiguous) {
+      // Since we only have (_max_size - _total_size) bytes
+      // remaining, it follows that our largest contiguous block
+      // must be no larger than this.
+      _contiguous = _max_size - _total_size;
+      changed_contiguous();
+    }
     return new_block;
     return new_block;
   }
   }
 
 
@@ -98,21 +150,35 @@ alloc(size_t size) {
 
 
   // Now that we've walked through the entire list of blocks, we
   // Now that we've walked through the entire list of blocks, we
   // really do know accurately what the largest contiguous block is.
   // really do know accurately what the largest contiguous block is.
-  _contiguous = best;
+  if (_contiguous != best) {
+    _contiguous = best;
+    changed_contiguous();
+  }
 
 
   // No room for this block.
   // No room for this block.
   return NULL;
   return NULL;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: SimpleAllocator::output
-//       Access: Published
-//  Description: 
+//     Function: SimpleAllocator::make_block
+//       Access: Protected, Virtual
+//  Description: Creates a new SimpleAllocatorBlock object.  Override
+//               this function to specialize the block type returned.
+////////////////////////////////////////////////////////////////////
+SimpleAllocatorBlock *SimpleAllocator::
+make_block(size_t start, size_t size) {
+  return new SimpleAllocatorBlock(this, start, size);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SimpleAllocator::changed_contiguous
+//       Access: Protected, Virtual
+//  Description: This callback function is made whenever the estimate
+//               of contiguous available space changes, either through
+//               an alloc or free.  The lock will be held.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void SimpleAllocator::
 void SimpleAllocator::
-output(ostream &out) const {
-  out << "SimpleAllocator, " << _total_size << " of " << _max_size 
-      << " allocated";
+changed_contiguous() {
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -125,37 +191,7 @@ output(ostream &out) const {
   if (_allocator == (SimpleAllocator *)NULL) {
   if (_allocator == (SimpleAllocator *)NULL) {
     out << "free block\n";
     out << "free block\n";
   } else {
   } else {
+    MutexHolder holder(_allocator->_lock);
     out << "block of size " << _size << " at " << _start;
     out << "block of size " << _size << " at " << _start;
   }
   }
 }
 }
-
-////////////////////////////////////////////////////////////////////
-//     Function: SimpleAllocator::write
-//       Access: Published
-//  Description: 
-////////////////////////////////////////////////////////////////////
-void SimpleAllocator::
-write(ostream &out) const {
-  out << *this << ":\n";
-
-  SimpleAllocatorBlock *block = (SimpleAllocatorBlock *)_next;
-  while (block->_next != this) {
-    SimpleAllocatorBlock *next = (SimpleAllocatorBlock *)block->_next;
-
-    out << "  " << *block << "\n";
-    block = next;
-  }
-}
-
-
-////////////////////////////////////////////////////////////////////
-//     Function: SimpleAllocator::make_block
-//       Access: Protected, Virtual
-//  Description: Creates a new SimpleAllocatorBlock object.  Override
-//               this function to specialize the block type returned.
-////////////////////////////////////////////////////////////////////
-SimpleAllocatorBlock *SimpleAllocator::
-make_block(size_t start, size_t size) {
-  return new SimpleAllocatorBlock(this, start, size);
-}
-

+ 24 - 7
panda/src/gobj/simpleAllocator.h

@@ -21,6 +21,8 @@
 
 
 #include "pandabase.h"
 #include "pandabase.h"
 #include "linkedListNode.h"
 #include "linkedListNode.h"
+#include "pmutex.h"
+#include "mutexHolder.h"
 
 
 class SimpleAllocatorBlock;
 class SimpleAllocatorBlock;
 
 
@@ -31,17 +33,13 @@ 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:
-  INLINE SimpleAllocator(size_t max_size);
+  INLINE SimpleAllocator(size_t max_size, Mutex &lock);
   virtual ~SimpleAllocator();
   virtual ~SimpleAllocator();
 
 
-  SimpleAllocatorBlock *alloc(size_t size);
+  INLINE SimpleAllocatorBlock *alloc(size_t size);
 
 
   INLINE bool is_empty() const;
   INLINE bool is_empty() const;
   INLINE size_t get_total_size() const;
   INLINE size_t get_total_size() const;
@@ -55,10 +53,14 @@ PUBLISHED:
   void write(ostream &out) const;
   void write(ostream &out) const;
 
 
 protected:
 protected:
+  SimpleAllocatorBlock *do_alloc(size_t size);
+  INLINE bool do_is_empty() const;
+
   virtual SimpleAllocatorBlock *make_block(size_t start, size_t size);
   virtual SimpleAllocatorBlock *make_block(size_t start, size_t size);
   INLINE void mark_contiguous(const LinkedListNode *block);
   INLINE void mark_contiguous(const LinkedListNode *block);
+  virtual void changed_contiguous();
 
 
-private:
+protected:
   // This is implemented as a linked-list chain of allocated blocks.
   // This is implemented as a linked-list chain of allocated blocks.
   // Free blocks are implicit.  Blocks are kept in sorted order from
   // Free blocks are implicit.  Blocks are kept in sorted order from
   // beginning to end.  Allocating a block means creating a new entry
   // beginning to end.  Allocating a block means creating a new entry
@@ -76,6 +78,16 @@ private:
   // it will not be smaller.
   // it will not be smaller.
   size_t _contiguous;
   size_t _contiguous;
 
 
+  // This mutex protects all operations within this class.  The caller
+  // must pass the reference to a mutex in to the constructor, and the
+  // caller remains responsible for owning the mutex.  This allows the
+  // mutex to be shared where appropriate.
+
+  // A derived class may also use it to protect itself as well, but
+  // take care to call do_alloc() instead of alloc() etc. as
+  // necessary.
+  Mutex &_lock;
+
   friend class SimpleAllocatorBlock;
   friend class SimpleAllocatorBlock;
 };
 };
 
 
@@ -106,6 +118,11 @@ PUBLISHED:
 
 
   void output(ostream &out) const;
   void output(ostream &out) const;
 
 
+protected:
+  INLINE void do_free();
+  INLINE size_t do_get_max_size() const;
+  INLINE bool do_realloc(size_t size);
+
 private:
 private:
   SimpleAllocator *_allocator;
   SimpleAllocator *_allocator;
   size_t _start;
   size_t _start;

+ 12 - 11
panda/src/gobj/vertexDataBook.I

@@ -18,24 +18,25 @@
 
 
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: VertexDataBook::get_num_pages
+//     Function: VertexDataBook::alloc
 //       Access: Published
 //       Access: Published
-//  Description: Returns the number of pages created for the book.
+//  Description: Allocates and returns a new VertexDataBuffer of the
+//               requested size.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-INLINE int VertexDataBook::
-get_num_pages() const {
-  return _pages.size();
+INLINE VertexDataBlock *VertexDataBook::
+alloc(size_t size) {
+  MutexHolder holder(_lock);
+  return do_alloc(size);
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: VertexDataBook::get_page
+//     Function: VertexDataBook::get_num_pages
 //       Access: Published
 //       Access: Published
-//  Description: Returns the nth page created for the book.
+//  Description: Returns the number of pages created for the book.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-INLINE VertexDataPage *VertexDataBook::
-get_page(int n) const {
-  nassertr(n >= 0 && n < (int)_pages.size(), NULL);
-  return _pages[n];
+INLINE int VertexDataBook::
+get_num_pages() const {
+  return _pages.size();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////

+ 61 - 67
panda/src/gobj/vertexDataBook.cxx

@@ -17,7 +17,7 @@
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 
 
 #include "vertexDataBook.h"
 #include "vertexDataBook.h"
-#include "mutexHolder.h"
+#include "reMutexHolder.h"
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: VertexDataBook::Constructor
 //     Function: VertexDataBook::Constructor
@@ -26,7 +26,6 @@
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 VertexDataBook::
 VertexDataBook::
 VertexDataBook(size_t block_size) : _block_size(block_size) {
 VertexDataBook(size_t block_size) : _block_size(block_size) {
-  _next_pi = 0;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -38,71 +37,6 @@ VertexDataBook::
 ~VertexDataBook() {
 ~VertexDataBook() {
 }
 }
 
 
-////////////////////////////////////////////////////////////////////
-//     Function: VertexDataBook::alloc
-//       Access: Published
-//  Description: Allocates and returns a new VertexDataBuffer of the
-//               requested size.
-////////////////////////////////////////////////////////////////////
-VertexDataBlock *VertexDataBook::
-alloc(size_t size) {
-  MutexHolder holder(_lock);
-
-  // First, try to allocate from the last page that worked; then
-  // continue to the end of the list.  We consider only pages that are
-  // currently resident (or that are empty), to minimize unnecessary
-  // swapping.
-  size_t pi = _next_pi;
-  while (pi < _pages.size()) {
-    if (_pages[pi]->get_ram_class() == VertexDataPage::RC_resident ||
-        _pages[pi]->is_empty()) {
-      VertexDataBlock *block = _pages[pi]->alloc(size);
-      if (block != (VertexDataBlock *)NULL) {
-        _next_pi = pi;
-        return block;
-      }
-      if (_pages[pi]->is_empty()) {
-        // This page is empty, but must have been too small.  Create a
-        // new page in its place.
-        delete _pages[pi];
-        _pages[pi] = create_new_page(size);
-        VertexDataBlock *block = _pages[pi]->alloc(size);
-        return block;
-      }
-    }
-    ++pi;
-  }
-
-  // Then, go back to the beginning and try those pages.
-  pi = 0;
-  _next_pi = min(_next_pi, _pages.size());
-  while (pi < _next_pi) {
-    if (_pages[pi]->get_ram_class() == VertexDataPage::RC_resident ||
-        _pages[pi]->is_empty()) {
-      VertexDataBlock *block = _pages[pi]->alloc(size);
-      if (block != (VertexDataBlock *)NULL) {
-        _next_pi = pi;
-        return block;
-      }
-      if (_pages[pi]->is_empty()) {
-        // This page is empty, but must have been too small.  Create a
-        // new page in its place.
-        delete _pages[pi];
-        _pages[pi] = create_new_page(size);
-        return _pages[pi]->alloc(size);
-      }
-    }
-    ++pi;
-  }
-
-  // No page was good enough.  Create a new page.  Make it at least
-  // large enough to hold this requested block.
-  VertexDataPage *page = create_new_page(size);
-  _pages.push_back(page);
-  VertexDataBlock *block = page->alloc(size);
-  return block;
-}
-
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: VertexDataBook::count_total_page_size
 //     Function: VertexDataBook::count_total_page_size
 //       Access: Published
 //       Access: Published
@@ -111,6 +45,8 @@ alloc(size_t size) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 size_t VertexDataBook::
 size_t VertexDataBook::
 count_total_page_size() const {
 count_total_page_size() const {
+  MutexHolder holder(_lock);
+
   size_t total = 0;
   size_t total = 0;
   Pages::const_iterator pi;
   Pages::const_iterator pi;
   for (pi = _pages.begin(); pi != _pages.end(); ++pi) {
   for (pi = _pages.begin(); pi != _pages.end(); ++pi) {
@@ -128,6 +64,8 @@ count_total_page_size() const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 size_t VertexDataBook::
 size_t VertexDataBook::
 count_total_page_size(VertexDataPage::RamClass ram_class) const {
 count_total_page_size(VertexDataPage::RamClass ram_class) const {
+  MutexHolder holder(_lock);
+
   size_t total = 0;
   size_t total = 0;
   Pages::const_iterator pi;
   Pages::const_iterator pi;
   for (pi = _pages.begin(); pi != _pages.end(); ++pi) {
   for (pi = _pages.begin(); pi != _pages.end(); ++pi) {
@@ -146,6 +84,8 @@ count_total_page_size(VertexDataPage::RamClass ram_class) const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 size_t VertexDataBook::
 size_t VertexDataBook::
 count_allocated_size() const {
 count_allocated_size() const {
+  MutexHolder holder(_lock);
+
   size_t total = 0;
   size_t total = 0;
   Pages::const_iterator pi;
   Pages::const_iterator pi;
   for (pi = _pages.begin(); pi != _pages.end(); ++pi) {
   for (pi = _pages.begin(); pi != _pages.end(); ++pi) {
@@ -163,6 +103,8 @@ count_allocated_size() const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 size_t VertexDataBook::
 size_t VertexDataBook::
 count_allocated_size(VertexDataPage::RamClass ram_class) const {
 count_allocated_size(VertexDataPage::RamClass ram_class) const {
+  MutexHolder holder(_lock);
+
   size_t total = 0;
   size_t total = 0;
   Pages::const_iterator pi;
   Pages::const_iterator pi;
   for (pi = _pages.begin(); pi != _pages.end(); ++pi) {
   for (pi = _pages.begin(); pi != _pages.end(); ++pi) {
@@ -183,9 +125,61 @@ count_allocated_size(VertexDataPage::RamClass ram_class) const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void VertexDataBook::
 void VertexDataBook::
 save_to_disk() {
 save_to_disk() {
+  MutexHolder holder(_lock);
+
   Pages::iterator pi;
   Pages::iterator pi;
   for (pi = _pages.begin(); pi != _pages.end(); ++pi) {
   for (pi = _pages.begin(); pi != _pages.end(); ++pi) {
     (*pi)->save_to_disk();
     (*pi)->save_to_disk();
   }
   }
 }
 }
 
 
+
+////////////////////////////////////////////////////////////////////
+//     Function: VertexDataBook::do_alloc
+//       Access: Private
+//  Description: Allocates and returns a new VertexDataBuffer of the
+//               requested size.
+//
+//               Assumes the lock is already held.
+////////////////////////////////////////////////////////////////////
+VertexDataBlock *VertexDataBook::
+do_alloc(size_t size) {
+  // Look for an empty page of the appropriate size.  The _pages set
+  // is sorted so that the pages with the smallest available blocks
+  // are at the front.
+  
+  // Create a dummy page to use to search the set.
+  VertexDataPage size_page(size);
+  Pages::iterator pi = _pages.lower_bound(&size_page);
+
+  // Now we can start from the first element of the set that is
+  // possibly large enough to contain this block, and work up from
+  // there.
+  while (pi != _pages.end()) {
+    Pages::iterator pnext = pi;
+    ++pnext;
+
+    VertexDataPage *page = (*pi);
+
+    // Allocating a block may change the page's available contiguous
+    // size, and thereby change its position in the set, invalidating
+    // the iterator pi.  This is why we've already computed pnext.
+    VertexDataBlock *block = page->do_alloc(size);
+
+    if (block != (VertexDataBlock *)NULL) {
+      // This page worked.
+      return block;
+    }
+
+    // Try the next page.
+    pi = pnext;
+  }
+
+  // No page was good enough.  Create a new page.  Make it at least
+  // large enough to hold this requested block.
+  VertexDataPage *page = create_new_page(size);
+  _pages.insert(page);
+
+  VertexDataBlock *block = page->do_alloc(size);
+  return block;
+}

+ 11 - 4
panda/src/gobj/vertexDataBook.h

@@ -23,6 +23,8 @@
 #include "pmutex.h"
 #include "pmutex.h"
 #include "mutexHolder.h"
 #include "mutexHolder.h"
 #include "vertexDataPage.h"
 #include "vertexDataPage.h"
+#include "indirectLess.h"
+#include "plist.h"
 
 
 class VertexDataBlock;
 class VertexDataBlock;
 
 
@@ -36,10 +38,9 @@ PUBLISHED:
   VertexDataBook(size_t block_size);
   VertexDataBook(size_t block_size);
   ~VertexDataBook();
   ~VertexDataBook();
 
 
-  VertexDataBlock *alloc(size_t size);
+  INLINE VertexDataBlock *alloc(size_t size);
 
 
   INLINE int get_num_pages() const;
   INLINE int get_num_pages() const;
-  INLINE VertexDataPage *get_page(int n) const;
 
 
   size_t count_total_page_size() const;
   size_t count_total_page_size() const;
   size_t count_total_page_size(VertexDataPage::RamClass ram_class) const;
   size_t count_total_page_size(VertexDataPage::RamClass ram_class) const;
@@ -48,15 +49,21 @@ PUBLISHED:
 
 
   void save_to_disk();
   void save_to_disk();
 
 
+public:
+  void reorder_page(VertexDataPage *page);
+
 private:
 private:
   INLINE VertexDataPage *create_new_page(size_t size);
   INLINE VertexDataPage *create_new_page(size_t size);
+  VertexDataBlock *do_alloc(size_t size);
 
 
 private:
 private:
   size_t _block_size;
   size_t _block_size;
-  typedef pvector<VertexDataPage *> Pages;
+
+  typedef pset<VertexDataPage *, IndirectLess<VertexDataPage> > Pages;
   Pages _pages;
   Pages _pages;
-  size_t _next_pi;
+
   Mutex _lock;
   Mutex _lock;
+  friend class VertexDataPage;
 };
 };
 
 
 #include "vertexDataBook.I"
 #include "vertexDataBook.I"

+ 1 - 1
panda/src/gobj/vertexDataBuffer.I

@@ -141,7 +141,7 @@ clean_realloc(size_t size) {
 INLINE void VertexDataBuffer::
 INLINE void VertexDataBuffer::
 unclean_realloc(size_t size) {
 unclean_realloc(size_t size) {
   MutexHolder holder(_lock);
   MutexHolder holder(_lock);
-  do_clean_realloc(size);
+  do_unclean_realloc(size);
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////

+ 40 - 1
panda/src/gobj/vertexDataPage.I

@@ -59,6 +59,21 @@ request_resident() {
   }
   }
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: VertexDataPage::alloc
+//       Access: Published
+//  Description: Allocates a new block.  Returns NULL if a block of the
+//               requested size cannot be allocated.
+//
+//               To free the allocated block, call block->free(), or
+//               simply delete the block pointer.
+////////////////////////////////////////////////////////////////////
+INLINE VertexDataBlock *VertexDataPage::
+alloc(size_t size) {
+  MutexHolder holder(_lock);
+  return do_alloc(size);
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: VertexDataPage::get_first_block
 //     Function: VertexDataPage::get_first_block
 //       Access: Published
 //       Access: Published
@@ -164,7 +179,7 @@ has_thread() {
 INLINE unsigned char *VertexDataPage::
 INLINE unsigned char *VertexDataPage::
 get_page_data(bool force) {
 get_page_data(bool force) {
   MutexHolder holder(_lock);
   MutexHolder holder(_lock);
-  if (_ram_class != RC_resident) {
+  if (_ram_class != RC_resident || _pending_ram_class != RC_resident) {
     if (force) {
     if (force) {
       make_resident_now();
       make_resident_now();
     } else {
     } else {
@@ -180,6 +195,26 @@ get_page_data(bool force) {
   return _page_data;
   return _page_data;
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: VertexDataPage::operator
+//       Access: Public
+//  Description: This comparison method is used to order pages within
+//               a book.
+////////////////////////////////////////////////////////////////////
+INLINE bool VertexDataPage::
+operator < (const VertexDataPage &other) const {
+  // We sort pages so that the pages with the smallest number of
+  // available contiguous bytes come up first.  We store our best
+  // estimate of continguous bytes here.
+  if (_book_size != other._book_size) {
+    return _book_size < other._book_size;
+  }
+
+  // For pages of equal size, we sort based on pointers, to make it
+  // easy to quickly find a specific page.
+  return this < &other;
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: VertexDataPage::set_ram_class
 //     Function: VertexDataPage::set_ram_class
 //       Access: Private
 //       Access: Private
@@ -190,6 +225,10 @@ INLINE void VertexDataPage::
 set_ram_class(RamClass rclass) {
 set_ram_class(RamClass rclass) {
   _ram_class = rclass;
   _ram_class = rclass;
   mark_used_lru(_global_lru[rclass]);
   mark_used_lru(_global_lru[rclass]);
+
+  // Changing the ram class might make our effective available space 0
+  // and thereby change the placement within the book.
+  adjust_book_size();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////

+ 109 - 37
panda/src/gobj/vertexDataPage.cxx

@@ -71,6 +71,10 @@ SimpleLru *VertexDataPage::_global_lru[RC_end_of_list] = {
 
 
 VertexDataSaveFile *VertexDataPage::_save_file;
 VertexDataSaveFile *VertexDataPage::_save_file;
 
 
+// This mutex is (mostly) unused.  We just need a Mutex to pass to the
+// Book Constructor, below.
+Mutex VertexDataPage::_unused_mutex;
+
 PStatCollector VertexDataPage::_vdata_compress_pcollector("*:Vertex Data:Compress");
 PStatCollector VertexDataPage::_vdata_compress_pcollector("*:Vertex Data:Compress");
 PStatCollector VertexDataPage::_vdata_decompress_pcollector("*:Vertex Data:Decompress");
 PStatCollector VertexDataPage::_vdata_decompress_pcollector("*:Vertex Data:Decompress");
 PStatCollector VertexDataPage::_vdata_save_pcollector("*:Vertex Data:Save");
 PStatCollector VertexDataPage::_vdata_save_pcollector("*:Vertex Data:Save");
@@ -79,19 +83,39 @@ PStatCollector VertexDataPage::_thread_wait_pcollector("*:Wait:Idle");
 
 
 TypeHandle VertexDataPage::_type_handle;
 TypeHandle VertexDataPage::_type_handle;
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: VertexDataPage::Book Constructor
+//       Access: Public
+//  Description: This constructor is used only by VertexDataBook, to
+//               create a mostly-empty object that can be used to
+//               search for a particular page size in the set.
+////////////////////////////////////////////////////////////////////
+VertexDataPage::
+VertexDataPage(size_t book_size) : 
+  SimpleAllocator(book_size, _unused_mutex), 
+  SimpleLruPage(book_size),
+  _book_size(book_size),
+  _book(NULL)
+{
+  _page_data = NULL;
+  _size = 0;
+  _uncompressed_size = 0;
+  _ram_class = RC_resident;
+  _pending_ram_class = RC_resident;
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: VertexDataPage::Constructor
 //     Function: VertexDataPage::Constructor
-//       Access: Published
+//       Access: Public
 //  Description: 
 //  Description: 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 VertexDataPage::
 VertexDataPage::
 VertexDataPage(VertexDataBook *book, size_t page_size) : 
 VertexDataPage(VertexDataBook *book, size_t page_size) : 
-  SimpleAllocator(page_size), 
+  SimpleAllocator(page_size, book->_lock), 
   SimpleLruPage(page_size),
   SimpleLruPage(page_size),
+  _book_size(page_size),
   _book(book)
   _book(book)
 {
 {
-  nassertv(page_size == get_max_size());
-
   get_class_type().inc_memory_usage(TypeHandle::MC_array, (int)page_size);
   get_class_type().inc_memory_usage(TypeHandle::MC_array, (int)page_size);
   _page_data = new unsigned char[page_size];
   _page_data = new unsigned char[page_size];
   _size = page_size;
   _size = page_size;
@@ -103,12 +127,15 @@ VertexDataPage(VertexDataBook *book, size_t page_size) :
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: VertexDataPage::Destructor
 //     Function: VertexDataPage::Destructor
-//       Access: Published
+//       Access: Public
 //  Description: 
 //  Description: 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 VertexDataPage::
 VertexDataPage::
 ~VertexDataPage() {
 ~VertexDataPage() {
-  MutexHolder holder(_lock);
+
+  // Since the only way to delete a page is via the
+  // changed_contiguous() method, the lock will already be held.
+  // MutexHolder holder(_lock);
 
 
   {
   {
     MutexHolder holder2(_tlock);
     MutexHolder holder2(_tlock);
@@ -125,29 +152,6 @@ VertexDataPage::
   }
   }
 }
 }
 
 
-////////////////////////////////////////////////////////////////////
-//     Function: VertexDataPage::alloc
-//       Access: Published
-//  Description: Allocates a new block.  Returns NULL if a block of the
-//               requested size cannot be allocated.
-//
-//               To free the allocated block, call block->free(), or
-//               simply delete the block pointer.
-////////////////////////////////////////////////////////////////////
-VertexDataBlock *VertexDataPage::
-alloc(size_t size) {
-  MutexHolder holder(_lock);
-  VertexDataBlock *block = (VertexDataBlock *)SimpleAllocator::alloc(size);
-
-  if (block != (VertexDataBlock *)NULL && _ram_class != RC_disk) {
-    // When we allocate a new block within a resident page, we have to
-    // clear the disk cache (since we have just invalidated it).
-    _saved_block.clear();
-  }
-
-  return block;
-}
-
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: VertexDataPage::stop_thread
 //     Function: VertexDataPage::stop_thread
 //       Access: Published, Static
 //       Access: Published, Static
@@ -182,6 +186,27 @@ make_block(size_t start, size_t size) {
   return new VertexDataBlock(this, start, size);
   return new VertexDataBlock(this, start, size);
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: VertexDataPage::changed_contiguous
+//       Access: Protected, Virtual
+//  Description: This callback function is made whenever the estimate
+//               of contiguous available space changes, either through
+//               an alloc or free.  The lock will be held.
+////////////////////////////////////////////////////////////////////
+void VertexDataPage::
+changed_contiguous() {
+  if (do_is_empty()) {
+    // If the page is now empty, delete it.
+    VertexDataBook::Pages::iterator pi = _book->_pages.find(this);
+    nassertv(pi != _book->_pages.end());
+    _book->_pages.erase(pi);
+    delete this;
+    return;
+  }
+
+  adjust_book_size();
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: VertexDataPage::evict_lru
 //     Function: VertexDataPage::evict_lru
 //       Access: Public, Virtual
 //       Access: Public, Virtual
@@ -223,6 +248,30 @@ evict_lru() {
   }
   }
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: VertexDataPage::do_alloc
+//       Access: Private
+//  Description: Allocates a new block.  Returns NULL if a block of the
+//               requested size cannot be allocated.
+//
+//               To free the allocated block, call block->free(), or
+//               simply delete the block pointer.
+//
+//               Assumes the lock is already held.
+////////////////////////////////////////////////////////////////////
+VertexDataBlock *VertexDataPage::
+do_alloc(size_t size) {
+  VertexDataBlock *block = (VertexDataBlock *)SimpleAllocator::do_alloc(size);
+
+  if (block != (VertexDataBlock *)NULL && _ram_class != RC_disk) {
+    // When we allocate a new block within a resident page, we have to
+    // clear the disk cache (since we have just invalidated it).
+    _saved_block.clear();
+  }
+
+  return block;
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: VertexDataPage::make_resident_now
 //     Function: VertexDataPage::make_resident_now
 //       Access: Private
 //       Access: Private
@@ -287,9 +336,8 @@ make_resident() {
     delete[] _page_data;
     delete[] _page_data;
     _page_data = new_data;
     _page_data = new_data;
     _size = _uncompressed_size;
     _size = _uncompressed_size;
-
-  
 #endif
 #endif
+
     set_lru_size(_size);
     set_lru_size(_size);
     set_ram_class(RC_resident);
     set_ram_class(RC_resident);
   }
   }
@@ -471,6 +519,33 @@ do_restore_from_disk() {
   }
   }
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: VertexDataPage::adjust_book_size
+//       Access: Private
+//  Description: Called when the "book size"--the size of the page as
+//               recorded in its book's table--has changed for some
+//               reason.  Assumes the lock is held.
+////////////////////////////////////////////////////////////////////
+void VertexDataPage::
+adjust_book_size() {
+  size_t new_size = _contiguous;
+  if (_ram_class != RC_resident) {
+    // Let's not attempt to allocate new buffers from non-resident
+    // pages.
+    new_size = 0;
+  }
+
+  if (new_size != _book_size) {
+    VertexDataBook::Pages::iterator pi = _book->_pages.find(this);
+    nassertv(pi != _book->_pages.end());
+    _book->_pages.erase(pi);
+
+    _book_size = new_size;
+    bool inserted = _book->_pages.insert(this).second;
+    nassertv(inserted);
+  }
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: VertexDataPage::request_ram_class
 //     Function: VertexDataPage::request_ram_class
 //       Access: Private
 //       Access: Private
@@ -483,12 +558,6 @@ do_restore_from_disk() {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void VertexDataPage::
 void VertexDataPage::
 request_ram_class(RamClass ram_class) {
 request_ram_class(RamClass ram_class) {
-  if (ram_class == _ram_class) {
-    gobj_cat.warning()
-      << "Page " << this << " already has ram class " << ram_class << "\n";
-    return;
-  }
-
   if (!vertex_data_threaded_paging || !Thread::is_threading_supported()) {
   if (!vertex_data_threaded_paging || !Thread::is_threading_supported()) {
     // No threads.  Do it immediately.
     // No threads.  Do it immediately.
     switch (ram_class) {
     switch (ram_class) {
@@ -613,6 +682,9 @@ remove_page(VertexDataPage *page) {
   }
   }
 
 
   page->_pending_ram_class = page->_ram_class;
   page->_pending_ram_class = page->_ram_class;
+  
+  // Put the page back on its proper LRU.
+  page->mark_used_lru(_global_lru[page->_ram_class]);
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////

+ 14 - 4
panda/src/gobj/vertexDataPage.h

@@ -42,10 +42,11 @@ class VertexDataBlock;
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 class EXPCL_PANDA VertexDataPage : public SimpleAllocator, public SimpleLruPage {
 class EXPCL_PANDA VertexDataPage : public SimpleAllocator, public SimpleLruPage {
 public:
 public:
+  VertexDataPage(size_t book_size);
   VertexDataPage(VertexDataBook *book, size_t page_size);
   VertexDataPage(VertexDataBook *book, size_t page_size);
-PUBLISHED:
   ~VertexDataPage();
   ~VertexDataPage();
 
 
+PUBLISHED:
   // These are used to indicate the current residency state of the
   // These are used to indicate the current residency state of the
   // page, which may or may not have been temporarily evicted to
   // page, which may or may not have been temporarily evicted to
   // satisfy memory requirements.
   // satisfy memory requirements.
@@ -61,7 +62,7 @@ PUBLISHED:
   INLINE RamClass get_pending_ram_class() const;
   INLINE RamClass get_pending_ram_class() const;
   INLINE void request_resident();
   INLINE void request_resident();
 
 
-  VertexDataBlock *alloc(size_t size);
+  INLINE VertexDataBlock *alloc(size_t size);
   INLINE VertexDataBlock *get_first_block() const;
   INLINE VertexDataBlock *get_first_block() const;
 
 
   INLINE VertexDataBook *get_book() const;
   INLINE VertexDataBook *get_book() const;
@@ -77,14 +78,18 @@ PUBLISHED:
 
 
 public:
 public:
   INLINE unsigned char *get_page_data(bool force);
   INLINE unsigned char *get_page_data(bool force);
+  INLINE bool operator < (const VertexDataPage &other) const;
 
 
 protected:
 protected:
   virtual SimpleAllocatorBlock *make_block(size_t start, size_t size);
   virtual SimpleAllocatorBlock *make_block(size_t start, size_t size);
+  virtual void changed_contiguous();
   virtual void evict_lru();
   virtual void evict_lru();
 
 
 private:
 private:
   class PageThread;
   class PageThread;
 
 
+  VertexDataBlock *do_alloc(size_t size);
+
   void make_resident_now();
   void make_resident_now();
   void make_resident();
   void make_resident();
   void make_compressed();
   void make_compressed();
@@ -93,7 +98,8 @@ private:
   bool do_save_to_disk();
   bool do_save_to_disk();
   void do_restore_from_disk();
   void do_restore_from_disk();
 
 
-  PageThread *get_thread();
+  void adjust_book_size();
+
   void request_ram_class(RamClass ram_class);
   void request_ram_class(RamClass ram_class);
   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();
@@ -126,8 +132,9 @@ private:
   size_t _size, _uncompressed_size;
   size_t _size, _uncompressed_size;
   RamClass _ram_class;
   RamClass _ram_class;
   PT(VertexDataSaveBlock) _saved_block;
   PT(VertexDataSaveBlock) _saved_block;
+  size_t _book_size;
 
 
-  Mutex _lock;  // Protects above members
+  //Mutex _lock;  // Inherited from SimpleAllocator.  Protects above members.
 
 
   RamClass _pending_ram_class;  // Protected by _tlock.
   RamClass _pending_ram_class;  // Protected by _tlock.
 
 
@@ -141,6 +148,8 @@ private:
 
 
   static VertexDataSaveFile *_save_file;
   static VertexDataSaveFile *_save_file;
 
 
+  static Mutex _unused_mutex;
+
   static PStatCollector _vdata_compress_pcollector;
   static PStatCollector _vdata_compress_pcollector;
   static PStatCollector _vdata_decompress_pcollector;
   static PStatCollector _vdata_decompress_pcollector;
   static PStatCollector _vdata_save_pcollector;
   static PStatCollector _vdata_save_pcollector;
@@ -159,6 +168,7 @@ private:
   static TypeHandle _type_handle;
   static TypeHandle _type_handle;
 
 
   friend class PageThread;
   friend class PageThread;
+  friend class VertexDataBook;
 };
 };
 
 
 #include "vertexDataPage.I"
 #include "vertexDataPage.I"

+ 2 - 2
panda/src/gobj/vertexDataSaveFile.cxx

@@ -33,7 +33,7 @@
 VertexDataSaveFile::
 VertexDataSaveFile::
 VertexDataSaveFile(const Filename &directory, const string &prefix,
 VertexDataSaveFile(const Filename &directory, const string &prefix,
                    size_t max_size) :
                    size_t max_size) :
-  SimpleAllocator(max_size)
+  SimpleAllocator(max_size, _lock)
 {
 {
   Filename dir;
   Filename dir;
   if (directory.empty()) {
   if (directory.empty()) {
@@ -183,7 +183,7 @@ write_data(const unsigned char *data, size_t size, bool compressed) {
     return NULL;
     return NULL;
   }
   }
 
 
-  PT(VertexDataSaveBlock) block = (VertexDataSaveBlock *)SimpleAllocator::alloc(size);
+  PT(VertexDataSaveBlock) block = (VertexDataSaveBlock *)SimpleAllocator::do_alloc(size);
   if (block != (VertexDataSaveBlock *)NULL) {
   if (block != (VertexDataSaveBlock *)NULL) {
     block->set_compressed(compressed);
     block->set_compressed(compressed);