Panda3D
|
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