overlapped I/O

This commit is contained in:
David Rose 2007-06-27 01:09:41 +00:00
parent c79a08611f
commit f19a1c7584
6 changed files with 158 additions and 46 deletions

View File

@ -49,5 +49,5 @@ get_num_pages() const {
INLINE VertexDataPage *VertexDataBook::
create_new_page(size_t 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);
}

View File

@ -19,13 +19,41 @@
#include "vertexDataBook.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
// Access: Published
// Description:
////////////////////////////////////////////////////////////////////
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;
}
////////////////////////////////////////////////////////////////////

View File

@ -231,6 +231,17 @@ set_ram_class(RamClass rclass) {
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
// Access: Public

View File

@ -27,6 +27,21 @@
#include <zlib.h>
#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
("max-resident-vertex-data", -1,
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),
SimpleLruPage(book_size),
_book_size(book_size),
_block_size(0),
_book(NULL)
{
_page_data = NULL;
@ -110,14 +126,15 @@ VertexDataPage(size_t book_size) :
// Description:
////////////////////////////////////////////////////////////////////
VertexDataPage::
VertexDataPage(VertexDataBook *book, size_t page_size) :
VertexDataPage(VertexDataBook *book, size_t page_size, size_t block_size) :
SimpleAllocator(page_size, book->_lock),
SimpleLruPage(page_size),
_book_size(page_size),
_block_size(block_size),
_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;
_uncompressed_size = _size;
@ -146,8 +163,7 @@ VertexDataPage::
}
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;
}
}
@ -322,7 +338,8 @@ make_resident() {
<< "Expanding page from " << _size
<< " 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;
int result = uncompress(new_data, &dest_len, _page_data, _size);
if (result != Z_OK) {
@ -332,10 +349,10 @@ make_resident() {
}
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;
_size = _uncompressed_size;
_allocated_size = new_allocated_size;
#endif
set_lru_size(_size);
@ -383,14 +400,15 @@ make_compressed() {
<< "Couldn't compress: zlib error " << result << "\n";
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);
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;
_size = buffer_size;
_allocated_size = new_allocated_size;
if (gobj_cat.is_debug()) {
gobj_cat.debug()
@ -428,8 +446,7 @@ make_disk() {
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;
_size = 0;
@ -461,7 +478,7 @@ do_save_to_disk() {
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) {
// Can't write it to disk. Too bad.
return false;
@ -500,15 +517,16 @@ do_restore_from_disk() {
<< "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");
}
get_class_type().inc_memory_usage(TypeHandle::MC_array, (int)buffer_size);
nassertv(_page_data == (unsigned char *)NULL);
_page_data = new_data;
_size = buffer_size;
_allocated_size = new_allocated_size;
set_lru_size(_size);
if (_saved_block->get_compressed()) {
@ -606,6 +624,46 @@ make_save_file() {
_save_file = new VertexDataSaveFile(vertex_save_file_directory,
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

View File

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

View File

@ -63,10 +63,13 @@ VertexDataSaveFile(const Filename &directory, const string &prefix,
#ifdef _WIN32
// 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,
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) {
// The file was successfully opened and locked.
break;
@ -194,19 +197,23 @@ write_data(const unsigned char *data, size_t size, bool compressed) {
block->set_compressed(compressed);
#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;
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
// Posix case.
@ -257,20 +264,23 @@ read_data(unsigned char *data, size_t size, VertexDataSaveBlock *block) {
nassertr(size == block->get_size(), false);
#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;
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
// Posix case.