Panda3D

vertexDataSaveFile.cxx

00001 // Filename: vertexDataSaveFile.cxx
00002 // Created by:  drose (12May07)
00003 //
00004 ////////////////////////////////////////////////////////////////////
00005 //
00006 // PANDA 3D SOFTWARE
00007 // Copyright (c) Carnegie Mellon University.  All rights reserved.
00008 //
00009 // All use of this software is subject to the terms of the revised BSD
00010 // license.  You should have received a copy of this license along
00011 // with this source code in a file named "LICENSE."
00012 //
00013 ////////////////////////////////////////////////////////////////////
00014 
00015 #include "vertexDataSaveFile.h"
00016 #include "mutexHolder.h"
00017 #include "clockObject.h"
00018 
00019 #ifndef _WIN32
00020 #include <sys/types.h>
00021 #include <sys/stat.h>
00022 #include <fcntl.h>
00023 #include <errno.h>
00024 #endif  // _WIN32
00025 
00026 ////////////////////////////////////////////////////////////////////
00027 //     Function: VertexDataSaveFile::Constructor
00028 //       Access: Public
00029 //  Description: 
00030 ////////////////////////////////////////////////////////////////////
00031 VertexDataSaveFile::
00032 VertexDataSaveFile(const Filename &directory, const string &prefix,
00033                    size_t max_size) :
00034   SimpleAllocator(max_size, _lock)
00035 {
00036   Filename dir;
00037   if (directory.empty()) {
00038     dir = Filename::get_temp_directory();
00039   } else {
00040     dir = directory;
00041   }
00042 
00043   _is_valid = false;
00044   _total_file_size = 0;
00045 
00046   // Try to open and lock a writable temporary filename.
00047   int index = 0;
00048   while (true) {
00049     ++index;
00050     ostringstream strm;
00051     strm << prefix << "_" << index << ".dat";
00052 
00053     string basename = strm.str();
00054     _filename = Filename(dir, basename);
00055     string os_specific = _filename.to_os_specific();
00056 
00057     if (gobj_cat.is_debug()) {
00058       gobj_cat.debug()
00059         << "Creating vertex data save file " << os_specific << "\n"; 
00060     }
00061 
00062 #ifdef _WIN32
00063     // Windows case.
00064     DWORD flags = FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE | FILE_FLAG_RANDOM_ACCESS;
00065 #if defined(HAVE_THREADS) && defined(SIMPLE_THREADS)
00066     // In SIMPLE_THREADS mode, we use "overlapped" I/O.
00067     flags |= FILE_FLAG_OVERLAPPED | FILE_FLAG_NO_BUFFERING;
00068 #endif
00069     _handle = CreateFile(os_specific.c_str(), GENERIC_READ | GENERIC_WRITE,
00070                          0, NULL, CREATE_ALWAYS, flags, NULL);
00071     if (_handle != INVALID_HANDLE_VALUE) {
00072       // The file was successfully opened and locked.
00073       break;
00074 
00075     } else {
00076       // Couldn't open the file.  Either the directory was bad, or the
00077       // file was already locked by another.
00078       DWORD err = GetLastError();
00079 
00080       if (err != ERROR_SHARING_VIOLATION) {
00081         // File couldn't be opened; permission problem or bogus directory.
00082         if (!dir.empty()) {
00083           // Try the current directory, once.
00084           dir = Filename();
00085         } else {
00086           gobj_cat.error()
00087             << "Couldn't open vertex data save file.\n";
00088           return;
00089         }
00090       }
00091 
00092       // Couldn't lock the file.  Try the next one.
00093     }
00094 
00095 #else
00096     // Posix case.
00097     int flags = O_RDWR | O_CREAT;
00098 #if defined(HAVE_THREADS) && defined(SIMPLE_THREADS)
00099     // In SIMPLE_THREADS mode, we use non-blocking I/O.
00100     flags |= O_NONBLOCK;
00101 #endif
00102 
00103     _fd = ::open(os_specific.c_str(), flags, 0666);
00104     while (_fd == -1 && errno == EAGAIN) {
00105       Thread::force_yield();
00106       _fd = ::open(os_specific.c_str(), flags, 0666);
00107     }
00108 
00109     if (_fd == -1) {
00110       // Couldn't open the file: permissions problem or bad directory.
00111       if (!_filename.exists()) {
00112         // It must be a bad directory.
00113         if (!dir.empty()) {
00114           // Try the current directory, once.
00115           dir = Filename();
00116         } else {
00117           gobj_cat.error()
00118             << "Couldn't open vertex data save file.\n";
00119           return;
00120         }
00121       }
00122 
00123       // If it's a permissions problem, it might be a user-level
00124       // permissions issue.  Continue to the next.
00125       continue;
00126     }
00127     
00128     // Now try to lock the file, so we can be sure that no other
00129     // process is simultaneously writing to the same save file.
00130     int result = lockf(_fd, F_TLOCK, 0);
00131     if (result == 0) {
00132       // We've got the file.  Truncate it first, for good measure, in
00133       // case there's an old version of the file we picked up.
00134       if (ftruncate(_fd, 0) < 0) {
00135         gobj_cat.warning()
00136           << "Couldn't truncate vertex data save file.\n";
00137       }
00138 
00139       // On Unix, it's safe to unlink (delete) the temporary file
00140       // after it's been opened.  The file remains open, but
00141       // disappears from the directory.  This is kind of like
00142       // DELETE_ON_CLOSE, to ensure the temporary file won't
00143       // accidentally get left behind, except it's a little more
00144       // proactive.
00145       unlink(os_specific.c_str());
00146       _filename = Filename();
00147       break;
00148     }
00149 
00150     // Try the next file.
00151     close(_fd);
00152 #endif  // _WIN32
00153   }
00154 
00155   _is_valid = true;
00156 }
00157 
00158 ////////////////////////////////////////////////////////////////////
00159 //     Function: VertexDataSaveFile::Destructor
00160 //       Access: Public
00161 //  Description: 
00162 ////////////////////////////////////////////////////////////////////
00163 VertexDataSaveFile::
00164 ~VertexDataSaveFile() {
00165 #ifdef _WIN32
00166   if (_handle != NULL) {
00167     CloseHandle(_handle);
00168   }
00169 #else
00170   if (_fd != -1) {
00171     close(_fd);
00172   }
00173 #endif  // _WIN32
00174 
00175   // No need to remove the file, since in both above cases we have
00176   // already removed it.  And removing it now, after we have closed
00177   // and unlocked it, might accidentally remove someone else's copy.
00178   /*
00179   if (!_filename.empty()) {
00180     _filename.unlink();
00181   }
00182   */
00183 }
00184 
00185 ////////////////////////////////////////////////////////////////////
00186 //     Function: VertexDataSaveFile::write_data
00187 //       Access: Public
00188 //  Description: Writes a block of data to the file, and returns a
00189 //               handle to the block handle.  Returns NULL if the data
00190 //               cannot be written (e.g. no remaining space on the
00191 //               file).
00192 ////////////////////////////////////////////////////////////////////
00193 PT(VertexDataSaveBlock) VertexDataSaveFile::
00194 write_data(const unsigned char *data, size_t size, bool compressed) {
00195   MutexHolder holder(_lock);
00196 
00197   if (!_is_valid) {
00198     return NULL;
00199   }
00200 
00201   PT(VertexDataSaveBlock) block = (VertexDataSaveBlock *)SimpleAllocator::do_alloc(size);
00202   if (block != (VertexDataSaveBlock *)NULL) {
00203     _total_file_size = max(_total_file_size, block->get_start() + size);
00204     block->set_compressed(compressed);
00205 
00206 #ifdef _WIN32
00207     OVERLAPPED overlapped;
00208     memset(&overlapped, 0, sizeof(overlapped));
00209     overlapped.Offset = block->get_start();
00210 
00211     DWORD bytes_written = 0;
00212     double start_time = ClockObject::get_global_clock()->get_real_time();
00213     int num_passes = 0;
00214     BOOL success = WriteFile(_handle, data, size, &bytes_written, &overlapped);
00215     while (!success) {
00216       DWORD error = GetLastError();
00217       if (error == ERROR_IO_INCOMPLETE || error == ERROR_IO_PENDING) {
00218         // Wait for more later.
00219         Thread::force_yield();
00220         ++num_passes;
00221       } else {
00222         gobj_cat.error()
00223           << "Error writing " << size
00224           << " bytes to save file, windows error code 0x" << hex
00225           << error << dec << ".  Disk full?\n";
00226         return NULL;
00227       }
00228       success = GetOverlappedResult(_handle, &overlapped, &bytes_written, false);
00229     }
00230     nassertr(bytes_written == size, NULL);
00231     double finish_time = ClockObject::get_global_clock()->get_real_time();
00232     if (gobj_cat.is_debug()) {
00233       gobj_cat.debug()
00234         << "Wrote " << size << " bytes in " << *Thread::get_current_thread() << " over " << floor((finish_time - start_time) * 1000.0) << " ms and " << num_passes << " passes.\n";
00235     }
00236 #else
00237     // Posix case.
00238     if (lseek(_fd, block->get_start(), SEEK_SET) == -1) {
00239       gobj_cat.error()
00240         << "Error seeking to position " << block->get_start() << " in save file.\n";
00241       return NULL;
00242     }
00243 
00244     while (size > 0) {
00245       ssize_t result = ::write(_fd, data, size);
00246       if (result < 0) {
00247         if (errno == EAGAIN) {
00248           Thread::force_yield();
00249         } else {
00250           gobj_cat.error()
00251             << "Error writing " << size << " bytes to save file.  Disk full?\n";
00252           return NULL;
00253         }
00254         continue;
00255       }
00256 
00257       Thread::consider_yield();
00258       data += result;
00259       size -= result;
00260     }
00261 #endif  // _WIN32
00262   }
00263 
00264   return block;
00265 }
00266 
00267 ////////////////////////////////////////////////////////////////////
00268 //     Function: VertexDataSaveFile::read_data
00269 //       Access: Public
00270 //  Description: Reads a block of data from the file, and returns true
00271 //               on success, false on failure.
00272 ////////////////////////////////////////////////////////////////////
00273 bool VertexDataSaveFile::
00274 read_data(unsigned char *data, size_t size, VertexDataSaveBlock *block) {
00275   MutexHolder holder(_lock);
00276 
00277   if (!_is_valid) {
00278     return false;
00279   }
00280 
00281   nassertr(size == block->get_size(), false);
00282 
00283   /*
00284   static ConfigVariableBool allow_mainthread_read("allow-mainthread-read", 1);
00285   if (!allow_mainthread_read) {
00286     nassertr(Thread::get_current_thread() != Thread::get_main_thread(), false);
00287   }
00288   */
00289 
00290 #ifdef _WIN32
00291   OVERLAPPED overlapped;
00292   memset(&overlapped, 0, sizeof(overlapped));
00293   overlapped.Offset = block->get_start();
00294 
00295   DWORD bytes_read = 0;
00296   double start_time = ClockObject::get_global_clock()->get_real_time();
00297   int num_passes = 0;
00298   BOOL success = ReadFile(_handle, data, size, &bytes_read, &overlapped);
00299   while (!success) {
00300     DWORD error = GetLastError();
00301     if (error == ERROR_IO_INCOMPLETE || error == ERROR_IO_PENDING) {
00302       // Wait for more later.
00303       Thread::force_yield();
00304       ++num_passes;
00305     } else {
00306       gobj_cat.error()
00307         << "Error reading " << size
00308         << " bytes from save file, windows error code 0x" << hex
00309         << error << dec << ".\n";
00310       return NULL;
00311     }
00312     success = GetOverlappedResult(_handle, &overlapped, &bytes_read, false);
00313   }
00314   nassertr(bytes_read == size, NULL);
00315   double finish_time = ClockObject::get_global_clock()->get_real_time();
00316   if (gobj_cat.is_debug()) {
00317     gobj_cat.debug()
00318       << "Read " << size << " bytes in " << *Thread::get_current_thread() << " over " << floor((finish_time - start_time) * 1000.0) << " ms and " << num_passes << " passes.\n";
00319   }
00320 
00321 #else
00322   // Posix case.
00323   if (lseek(_fd, block->get_start(), SEEK_SET) == -1) {
00324     gobj_cat.error()
00325       << "Error seeking to position " << block->get_start() << " in save file.\n";
00326     return false;
00327   }
00328   while (size > 0) {
00329     ssize_t result = read(_fd, data, size);
00330     if (result == -1) {
00331       if (errno == EAGAIN) {
00332         Thread::force_yield();
00333       } else {
00334         gobj_cat.error()
00335           << "Error reading " << size << " bytes from save file.\n";
00336         return false;
00337       }
00338     }
00339 
00340     Thread::consider_yield();
00341     data += result;
00342     size -= result;
00343   }
00344 #endif  // _WIN32
00345 
00346   return true;
00347 }
00348 
00349 ////////////////////////////////////////////////////////////////////
00350 //     Function: VertexDataSaveFile::make_block
00351 //       Access: Protected, Virtual
00352 //  Description: Creates a new SimpleAllocatorBlock object.  Override
00353 //               this function to specialize the block type returned.
00354 ////////////////////////////////////////////////////////////////////
00355 SimpleAllocatorBlock *VertexDataSaveFile::
00356 make_block(size_t start, size_t size) {
00357   return new VertexDataSaveBlock(this, start, size);
00358 }
00359 
 All Classes Functions Variables Enumerations