Browse Source

overlapped I/O

David Rose 18 years ago
parent
commit
f19a1c7584

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

@@ -49,5 +49,5 @@ get_num_pages() const {
 INLINE VertexDataPage *VertexDataBook::
 INLINE VertexDataPage *VertexDataBook::
 create_new_page(size_t size) {
 create_new_page(size_t size) {
   size_t page_size = ((size + _block_size - 1) / _block_size) * _block_size;
   size_t page_size = ((size + _block_size - 1) / _block_size) * _block_size;
-  return new VertexDataPage(this, page_size);
+  return new VertexDataPage(this, page_size, _block_size);
 }
 }

+ 29 - 1
panda/src/gobj/vertexDataBook.cxx

@@ -19,13 +19,41 @@
 #include "vertexDataBook.h"
 #include "vertexDataBook.h"
 #include "reMutexHolder.h"
 #include "reMutexHolder.h"
 
 
+#ifdef WIN32
+
+// Windows case.
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+#else
+
+// Posix case.
+#include <unistd.h>
+
+#endif  // WIN32
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: VertexDataBook::Constructor
 //     Function: VertexDataBook::Constructor
 //       Access: Published
 //       Access: Published
 //  Description: 
 //  Description: 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 VertexDataBook::
 VertexDataBook::
-VertexDataBook(size_t block_size) : _block_size(block_size) {
+VertexDataBook(size_t block_size) {
+  // Make sure the block_size is an integer multiple of the system's
+  // page size.
+  size_t system_page_size;
+#ifdef WIN32
+  // Windows case.
+  SYSTEM_INFO sysinfo;
+  GetSystemInfo(&sysinfo);
+
+  system_page_size = (size_t)sysinfo.dwPageSize;
+#else
+  // Posix case.
+  system_page_size = getpagesize();
+#endif  // WIN32
+
+  _block_size = ((block_size + system_page_size - 1) / system_page_size) * system_page_size;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////

+ 11 - 0
panda/src/gobj/vertexDataPage.I

@@ -231,6 +231,17 @@ set_ram_class(RamClass rclass) {
   adjust_book_size();
   adjust_book_size();
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: VertexDataPage::round_up
+//       Access: Private
+//  Description: Round page_size up to the next multiple of
+//               _block_size.
+////////////////////////////////////////////////////////////////////
+INLINE size_t VertexDataPage::
+round_up(size_t page_size) const {
+  return ((page_size + _block_size - 1) / _block_size) * _block_size;
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: VertexDataPage::PageThread::Constructor
 //     Function: VertexDataPage::PageThread::Constructor
 //       Access: Public
 //       Access: Public

+ 76 - 18
panda/src/gobj/vertexDataPage.cxx

@@ -27,6 +27,21 @@
 #include <zlib.h>
 #include <zlib.h>
 #endif
 #endif
 
 
+#ifdef WIN32
+
+// Windows case (VirtualAlloc)
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+#else
+
+// Posix case (mmap)
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/mman.h>
+
+#endif
+
 ConfigVariableInt max_resident_vertex_data
 ConfigVariableInt max_resident_vertex_data
 ("max-resident-vertex-data", -1,
 ("max-resident-vertex-data", -1,
  PRC_DESC("Specifies the maximum number of bytes of all vertex data "
  PRC_DESC("Specifies the maximum number of bytes of all vertex data "
@@ -95,6 +110,7 @@ VertexDataPage(size_t book_size) :
   SimpleAllocator(book_size, _unused_mutex), 
   SimpleAllocator(book_size, _unused_mutex), 
   SimpleLruPage(book_size),
   SimpleLruPage(book_size),
   _book_size(book_size),
   _book_size(book_size),
+  _block_size(0),
   _book(NULL)
   _book(NULL)
 {
 {
   _page_data = NULL;
   _page_data = NULL;
@@ -110,14 +126,15 @@ VertexDataPage(size_t book_size) :
 //  Description: 
 //  Description: 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 VertexDataPage::
 VertexDataPage::
-VertexDataPage(VertexDataBook *book, size_t page_size) : 
+VertexDataPage(VertexDataBook *book, size_t page_size, size_t block_size) : 
   SimpleAllocator(page_size, book->_lock), 
   SimpleAllocator(page_size, book->_lock), 
   SimpleLruPage(page_size),
   SimpleLruPage(page_size),
   _book_size(page_size),
   _book_size(page_size),
+  _block_size(block_size),
   _book(book)
   _book(book)
 {
 {
-  get_class_type().inc_memory_usage(TypeHandle::MC_array, (int)page_size);
-  _page_data = new unsigned char[page_size];
+  _allocated_size = round_up(page_size);
+  _page_data = alloc_page_data(_allocated_size);
   _size = page_size;
   _size = page_size;
 
 
   _uncompressed_size = _size;
   _uncompressed_size = _size;
@@ -146,8 +163,7 @@ VertexDataPage::
   }
   }
 
 
   if (_page_data != NULL) {
   if (_page_data != NULL) {
-    get_class_type().dec_memory_usage(TypeHandle::MC_array, (int)_size);
-    delete[] _page_data;
+    free_page_data(_page_data, _allocated_size);
     _size = 0;
     _size = 0;
   }
   }
 }
 }
@@ -322,7 +338,8 @@ make_resident() {
         << "Expanding page from " << _size
         << "Expanding page from " << _size
         << " to " << _uncompressed_size << "\n";
         << " to " << _uncompressed_size << "\n";
     }
     }
-    unsigned char *new_data = new unsigned char[_uncompressed_size];
+    size_t new_allocated_size = round_up(_uncompressed_size);
+    unsigned char *new_data = alloc_page_data(new_allocated_size);
     uLongf dest_len = _uncompressed_size;
     uLongf dest_len = _uncompressed_size;
     int result = uncompress(new_data, &dest_len, _page_data, _size);
     int result = uncompress(new_data, &dest_len, _page_data, _size);
     if (result != Z_OK) {
     if (result != Z_OK) {
@@ -332,10 +349,10 @@ make_resident() {
     }
     }
     nassertv(dest_len == _uncompressed_size);
     nassertv(dest_len == _uncompressed_size);
 
 
-    get_class_type().inc_memory_usage(TypeHandle::MC_array, (int)_uncompressed_size - (int)_size);
-    delete[] _page_data;
+    free_page_data(_page_data, _allocated_size);
     _page_data = new_data;
     _page_data = new_data;
     _size = _uncompressed_size;
     _size = _uncompressed_size;
+    _allocated_size = new_allocated_size;
 #endif
 #endif
 
 
     set_lru_size(_size);
     set_lru_size(_size);
@@ -383,14 +400,15 @@ make_compressed() {
         << "Couldn't compress: zlib error " << result << "\n";
         << "Couldn't compress: zlib error " << result << "\n";
       nassert_raise("zlib error");
       nassert_raise("zlib error");
     }
     }
-    
-    unsigned char *new_data = new unsigned char[buffer_size];
+
+    size_t new_allocated_size = round_up(buffer_size);
+    unsigned char *new_data = alloc_page_data(new_allocated_size);
     memcpy(new_data, buffer, buffer_size);
     memcpy(new_data, buffer, buffer_size);
 
 
-    get_class_type().inc_memory_usage(TypeHandle::MC_array, (int)buffer_size - (int)_size);
-    delete[] _page_data;
+    free_page_data(_page_data, _allocated_size);
     _page_data = new_data;
     _page_data = new_data;
     _size = buffer_size;
     _size = buffer_size;
+    _allocated_size = new_allocated_size;
 
 
     if (gobj_cat.is_debug()) {
     if (gobj_cat.is_debug()) {
       gobj_cat.debug()
       gobj_cat.debug()
@@ -428,8 +446,7 @@ make_disk() {
       return;
       return;
     }
     }
 
 
-    get_class_type().dec_memory_usage(TypeHandle::MC_array, (int)_size);
-    delete[] _page_data;
+    free_page_data(_page_data, _allocated_size);
     _page_data = NULL;
     _page_data = NULL;
     _size = 0;
     _size = 0;
     
     
@@ -461,7 +478,7 @@ do_save_to_disk() {
 
 
       bool compressed = (_ram_class == RC_compressed);
       bool compressed = (_ram_class == RC_compressed);
       
       
-      _saved_block = get_save_file()->write_data(_page_data, _size, compressed);
+      _saved_block = get_save_file()->write_data(_page_data, _allocated_size, compressed);
       if (_saved_block == (VertexDataSaveBlock *)NULL) {
       if (_saved_block == (VertexDataSaveBlock *)NULL) {
         // Can't write it to disk.  Too bad.
         // Can't write it to disk.  Too bad.
         return false;
         return false;
@@ -500,15 +517,16 @@ do_restore_from_disk() {
         << "Restoring page, " << buffer_size << " bytes, from disk\n";
         << "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)) {
+    size_t new_allocated_size = round_up(buffer_size);
+    unsigned char *new_data = alloc_page_data(new_allocated_size);
+    if (!get_save_file()->read_data(new_data, new_allocated_size, _saved_block)) {
       nassert_raise("read error");
       nassert_raise("read error");
     }
     }
 
 
-    get_class_type().inc_memory_usage(TypeHandle::MC_array, (int)buffer_size);
     nassertv(_page_data == (unsigned char *)NULL);
     nassertv(_page_data == (unsigned char *)NULL);
     _page_data = new_data;
     _page_data = new_data;
     _size = buffer_size;
     _size = buffer_size;
+    _allocated_size = new_allocated_size;
 
 
     set_lru_size(_size);
     set_lru_size(_size);
     if (_saved_block->get_compressed()) {
     if (_saved_block->get_compressed()) {
@@ -606,6 +624,46 @@ make_save_file() {
   _save_file = new VertexDataSaveFile(vertex_save_file_directory,
   _save_file = new VertexDataSaveFile(vertex_save_file_directory,
                                       vertex_save_file_prefix, max_size);
                                       vertex_save_file_prefix, max_size);
 }
 }
+
+////////////////////////////////////////////////////////////////////
+//     Function: VertexDataPage::alloc_page_data
+//       Access: Private
+//  Description: Allocates and returns a freshly-allocated buffer of
+//               at least the indicated size for holding vertex data.
+////////////////////////////////////////////////////////////////////
+unsigned char *VertexDataPage::
+alloc_page_data(size_t page_size) const {
+  get_class_type().inc_memory_usage(TypeHandle::MC_array, page_size);
+  
+  unsigned char *buffer;
+#ifdef WIN32
+  // Windows case.
+  buffer = (unsigned char *)VirtualAlloc(NULL, page_size, MEM_COMMIT | MEM_RESERVE,
+                                         PAGE_READWRITE);
+#else
+  // Posix case.
+  buffer = (unsigned char *)mmap(NULL, page_size, PROT_READ | PROT_WRITE, 
+                                 MAP_PRIVATE | MAP_ANON, -1, 0);
+#endif
+
+  return buffer;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: VertexDataPage::free_page_data
+//       Access: Private
+//  Description: Releases a buffer allocated via alloc_page_data().
+////////////////////////////////////////////////////////////////////
+void VertexDataPage::
+free_page_data(unsigned char *page_data, size_t page_size) const {
+  get_class_type().dec_memory_usage(TypeHandle::MC_array, page_size);
+
+#ifdef WIN32
+  VirtualFree(page_data, 0, MEM_RELEASE);
+#else  
+  munmap(page_data, page_size);
+#endif
+}
  
  
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: VertexDataPage::PageThread::add_page
 //     Function: VertexDataPage::PageThread::add_page

+ 7 - 2
panda/src/gobj/vertexDataPage.h

@@ -43,7 +43,7 @@ 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(size_t book_size);
-  VertexDataPage(VertexDataBook *book, size_t page_size);
+  VertexDataPage(VertexDataBook *book, size_t page_size, size_t block_size);
   ~VertexDataPage();
   ~VertexDataPage();
 
 
 PUBLISHED:
 PUBLISHED:
@@ -104,6 +104,10 @@ private:
   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();
 
 
+  INLINE size_t round_up(size_t page_size) const;
+  unsigned char *alloc_page_data(size_t page_size) const;
+  void free_page_data(unsigned char *page_data, size_t page_size) const;
+
   typedef pdeque<VertexDataPage *> PendingPages;
   typedef pdeque<VertexDataPage *> PendingPages;
 
 
   class PageThread : public Thread {
   class PageThread : public Thread {
@@ -129,10 +133,11 @@ private:
   static Mutex _tlock;  // Protects the thread members.
   static Mutex _tlock;  // Protects the thread members.
 
 
   unsigned char *_page_data;
   unsigned char *_page_data;
-  size_t _size, _uncompressed_size;
+  size_t _size, _allocated_size, _uncompressed_size;
   RamClass _ram_class;
   RamClass _ram_class;
   PT(VertexDataSaveBlock) _saved_block;
   PT(VertexDataSaveBlock) _saved_block;
   size_t _book_size;
   size_t _book_size;
+  size_t _block_size;
 
 
   //Mutex _lock;  // Inherited from SimpleAllocator.  Protects above members.
   //Mutex _lock;  // Inherited from SimpleAllocator.  Protects above members.
 
 

+ 34 - 24
panda/src/gobj/vertexDataSaveFile.cxx

@@ -63,10 +63,13 @@ VertexDataSaveFile(const Filename &directory, const string &prefix,
 
 
 #ifdef _WIN32
 #ifdef _WIN32
     // Windows case.
     // Windows case.
+    DWORD flags = FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE | FILE_FLAG_RANDOM_ACCESS;
+#if defined(HAVE_THREADS) && defined(SIMPLE_THREADS)
+    // In SIMPLE_THREADS mode, we use "overlapped" I/O.
+    flags |= FILE_FLAG_OVERLAPPED | FILE_FLAG_NO_BUFFERING;
+#endif
     _handle = CreateFile(os_specific.c_str(), GENERIC_READ | GENERIC_WRITE,
     _handle = CreateFile(os_specific.c_str(), GENERIC_READ | GENERIC_WRITE,
-                         0, NULL, CREATE_ALWAYS, 
-                         FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE | FILE_FLAG_RANDOM_ACCESS,
-                         NULL);
+                         0, NULL, CREATE_ALWAYS, flags, NULL);
     if (_handle != INVALID_HANDLE_VALUE) {
     if (_handle != INVALID_HANDLE_VALUE) {
       // The file was successfully opened and locked.
       // The file was successfully opened and locked.
       break;
       break;
@@ -194,19 +197,23 @@ write_data(const unsigned char *data, size_t size, bool compressed) {
     block->set_compressed(compressed);
     block->set_compressed(compressed);
 
 
 #ifdef _WIN32
 #ifdef _WIN32
-    if (SetFilePointer(_handle, block->get_start(), NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER && GetLastError() != NO_ERROR) {
-      gobj_cat.error()
-        << "Error seeking to position " << block->get_start() << " in save file.\n";
-      return false;
-    }
+    OVERLAPPED overlapped;
+    memset(&overlapped, 0, sizeof(overlapped));
+    overlapped.Offset = block->get_start();
 
 
+    WriteFile(_handle, data, size, NULL, &overlapped);
     DWORD bytes_written;
     DWORD bytes_written;
-    if (!WriteFile(_handle, data, size, &bytes_written, NULL) ||
-        bytes_written != size) {
-      gobj_cat.error()
-        << "Error writing " << size << " bytes to save file.  Disk full?\n";
-      return NULL;
+    while (!GetOverlappedResult(_handle, &overlapped, &bytes_written, false)) {
+      if (GetLastError() == ERROR_IO_INCOMPLETE) {
+        // Wait for more later.
+        Thread::force_yield();
+      } else {
+        gobj_cat.error()
+          << "Error writing " << size << " bytes to save file.  Disk full?\n";
+        return NULL;
+      }
     }
     }
+    nassertr(bytes_written == size, NULL);
 
 
 #else
 #else
     // Posix case.
     // Posix case.
@@ -257,20 +264,23 @@ read_data(unsigned char *data, size_t size, VertexDataSaveBlock *block) {
   nassertr(size == block->get_size(), false);
   nassertr(size == block->get_size(), false);
 
 
 #ifdef _WIN32
 #ifdef _WIN32
-  if (SetFilePointer(_handle, block->get_start(), NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER && GetLastError() != NO_ERROR) {
-    gobj_cat.error()
-      << "Error seeking to position " << block->get_start() << " in save file.\n";
-    return false;
-  }
+  OVERLAPPED overlapped;
+  memset(&overlapped, 0, sizeof(overlapped));
+  overlapped.Offset = block->get_start();
 
 
+  ReadFile(_handle, data, size, NULL, &overlapped);
   DWORD bytes_read;
   DWORD bytes_read;
-  if (!ReadFile(_handle, data, size, &bytes_read, NULL) ||
-      bytes_read != size) {
-    gobj_cat.error()
-      << "Error writing " << size << " bytes to save file.  Disk full?\n";
-    delete block;
-    return NULL;
+  while (!GetOverlappedResult(_handle, &overlapped, &bytes_read, false)) {
+    if (GetLastError() == ERROR_IO_INCOMPLETE) {
+      // Wait for more later.
+      Thread::force_yield();
+    } else {
+      gobj_cat.error()
+        << "Error reading " << size << " bytes from save file.\n";
+      return NULL;
+    }
   }
   }
+  nassertr(bytes_read == size, NULL);
   
   
 #else
 #else
   // Posix case.
   // Posix case.