FileDevice.cpp

Go to the documentation of this file.
00001 /*
00002 // $Id: //open/dev/fennel/device/FileDevice.cpp#14 $
00003 // Fennel is a library of data storage and processing components.
00004 // Copyright (C) 2005-2009 The Eigenbase Project
00005 // Copyright (C) 2005-2009 SQLstream, Inc.
00006 // Copyright (C) 2005-2009 LucidEra, Inc.
00007 // Portions Copyright (C) 1999-2009 John V. Sichi
00008 //
00009 // This program is free software; you can redistribute it and/or modify it
00010 // under the terms of the GNU General Public License as published by the Free
00011 // Software Foundation; either version 2 of the License, or (at your option)
00012 // any later version approved by The Eigenbase Project.
00013 //
00014 // This program is distributed in the hope that it will be useful,
00015 // but WITHOUT ANY WARRANTY; without even the implied warranty of
00016 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00017 // GNU General Public License for more details.
00018 //
00019 // You should have received a copy of the GNU General Public License
00020 // along with this program; if not, write to the Free Software
00021 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00022 */
00023 
00024 #include "fennel/common/CommonPreamble.h"
00025 #include "fennel/device/FileDevice.h"
00026 #include "fennel/device/RandomAccessRequest.h"
00027 #include "fennel/common/SysCallExcn.h"
00028 #include <sys/types.h>
00029 #include <sys/stat.h>
00030 
00031 #ifndef __MSVC__
00032 #include <sys/file.h>
00033 #endif
00034 
00035 #include <fcntl.h>
00036 #include <sstream>
00037 
00038 #ifdef __MSVC__
00039 #include <windows.h>
00040 #endif
00041 
00042 FENNEL_BEGIN_CPPFILE("$Id: //open/dev/fennel/device/FileDevice.cpp#14 $");
00043 
00044 FileDevice::FileDevice(
00045     std::string filenameInit,DeviceMode openMode,FileSize initialSize)
00046 {
00047     filename = filenameInit;
00048     mode = openMode;
00049 
00050 #ifdef __MSVC__
00051 
00052     DWORD fdwCreate = mode.create ? CREATE_ALWAYS : OPEN_EXISTING;
00053 
00054     DWORD fdwFlags = FILE_FLAG_OVERLAPPED;
00055 
00056     DWORD fdwAccess = GENERIC_READ;
00057     if (!mode.readOnly) {
00058         fdwAccess |= GENERIC_WRITE;
00059     }
00060     if (mode.direct) {
00061         fdwFlags |= FILE_FLAG_NO_BUFFERING;
00062     }
00063     if (mode.sequential) {
00064         fdwFlags |= FILE_FLAG_SEQUENTIAL_SCAN;
00065     } else {
00066         fdwFlags |= FILE_FLAG_RANDOM_ACCESS;
00067     }
00068     if (mode.temporary) {
00069         fdwFlags |= FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE;
00070     } else {
00071         fdwFlags |= FILE_ATTRIBUTE_NORMAL;
00072     }
00073 
00074     // REVIEW:  I used FILE_SHARE_ so that recovery tests could reopen a
00075     // log file for read while it was still open for write by the original
00076     // txn.  Should probably fix the tests instead, in case allowing sharing
00077     // could hinder performance.
00078 
00079     handle = reinterpret_cast<int>(
00080         CreateFile(
00081             filename.c_str(),
00082             fdwAccess,
00083             FILE_SHARE_READ | FILE_SHARE_WRITE,
00084             NULL,
00085             fdwCreate,
00086             fdwFlags,
00087             NULL));
00088 
00089     if (!isOpen()) {
00090         std::ostringstream oss;
00091         oss << "Failed to open file " << filename;
00092         throw SysCallExcn(oss.str());
00093     }
00094 
00095     DWORD cbHigh = 0;
00096     DWORD cbLow = GetFileSize(HANDLE(handle),&cbHigh);
00097     if (cbLow == INVALID_FILE_SIZE) {
00098         std::ostringstream oss;
00099         oss << "Failed to get size for file " << filename;
00100         throw SysCallExcn(oss.str());
00101     }
00102     LARGE_INTEGER cbLarge;
00103     cbLarge.LowPart = cbLow;
00104     cbLarge.HighPart = cbHigh;
00105     cbFile = cbLarge.QuadPart;
00106     if (mode.create && initialSize > 0) {
00107         setSizeInBytes(initialSize);
00108     }
00109 
00110 #else
00111 
00112     int access = O_LARGEFILE;
00113     int permission = S_IRUSR;
00114     if (mode.readOnly) {
00115         access |= O_RDONLY;
00116     } else {
00117         access |= O_RDWR;
00118         permission |= S_IWUSR;
00119     }
00120     if (mode.create) {
00121         access |= O_CREAT | O_TRUNC;
00122     }
00123 
00124     if (mode.direct) {
00125         // REVIEW jvs 4-Dec-2008: Comment below used to be true, but probably
00126         // only on 2.4 kernels.  2.6 kernels seem to be happy with
00127         // O_DIRECT+pwrite.
00128         // (http://lkml.indiana.edu/hypermail/linux/kernel/0511.2/1758.html).
00129         // So we can probably clean this up now.
00130         access |= O_SYNC;
00131         // NOTE:  We don't actually set O_DIRECT here, because on Linux
00132         // that results in EINVAL errors from pwrite.  Instead,
00133         // O_DIRECT is set from AioLinuxScheduler, because it is required
00134         // for libaio.
00135     }
00136 
00137     handle = ::open(filename.c_str(), access, permission);
00138     if (!isOpen()) {
00139         std::ostringstream oss;
00140         oss << "Failed to open file " << filename;
00141         throw SysCallExcn(oss.str());
00142     }
00143     if (flock(handle, LOCK_SH | LOCK_NB) < 0) {
00144         throw SysCallExcn("File lock failed");
00145     }
00146     cbFile = ::lseek(handle,0,SEEK_END);
00147 
00148     // Preallocate the file if we're creating the file, and an initial size
00149     // is specified.
00150     if (mode.create && initialSize > 0) {
00151         int rc = posix_fallocate(handle, 0, initialSize);
00152         if (rc) {
00153             throw SysCallExcn("File allocation failed", rc);
00154         }
00155         cbFile = initialSize;
00156     }
00157 
00158 #endif
00159 }
00160 
00161 FileDevice::~FileDevice()
00162 {
00163     if (isOpen()) {
00164         close();
00165     }
00166 }
00167 
00168 void FileDevice::close()
00169 {
00170     assert(isOpen());
00171 #ifdef __MSVC__
00172     CloseHandle(HANDLE(handle));
00173 #else
00174     ::close(handle);
00175     if (mode.temporary) {
00176         ::unlink(filename.c_str());
00177     }
00178 #endif
00179     handle = -1;
00180 }
00181 
00182 void FileDevice::flush()
00183 {
00184     if (mode.readOnly) {
00185         return;
00186     }
00187 #ifdef __MSVC__
00188     if (!FlushFileBuffers(HANDLE(handle))) {
00189         throw SysCallExcn("Flush failed");
00190     }
00191 #else
00192     if (::fdatasync(handle)) {
00193         throw SysCallExcn("Flush failed");
00194     }
00195 #endif
00196 }
00197 
00198 void FileDevice::setSizeInBytes(FileSize cbFileNew)
00199 {
00200 #ifdef __MSVC__
00201     LARGE_INTEGER cbLarge;
00202     cbLarge.QuadPart = cbFileNew;
00203     if (!SetFilePointerEx(HANDLE(handle),cbLarge,NULL,FILE_BEGIN)) {
00204         throw SysCallExcn("Resize file failed:  SetFilePointer");
00205     }
00206     if (!SetEndOfFile(HANDLE(handle))) {
00207         throw SysCallExcn("Resize file failed:  SetEndOfFile");
00208     }
00209 #else
00210     if (::ftruncate(handle,cbFileNew)) {
00211         throw SysCallExcn("Resize file failed");
00212     }
00213 #endif
00214     cbFile = cbFileNew;
00215 }
00216 
00217 void FileDevice::transfer(RandomAccessRequest const &request)
00218 {
00219     FileSize cbActual;
00220     assert(request.bindingList.size() == 1);
00221 #ifdef __MSVC__
00222     LARGE_INTEGER largeInt;
00223     RandomAccessRequestBinding &binding = request.bindingList.front();
00224     largeInt.QuadPart = request.cbOffset;
00225     binding.Offset = largeInt.LowPart;
00226     binding.OffsetHigh = largeInt.HighPart;
00227 
00228     DWORD dwActual = 0;
00229     BOOL bCompleted;
00230     if (request.type == RandomAccessRequest::READ) {
00231         bCompleted = ReadFile(
00232             HANDLE(handle),
00233             request.bindingList.front().getBuffer(),
00234             request.cbTransfer,
00235             &dwActual,
00236             &binding);
00237     } else {
00238         bCompleted = WriteFile(
00239             HANDLE(handle),
00240             request.bindingList.front().getBuffer(),
00241             request.cbTransfer,
00242             &dwActual,
00243             &binding);
00244     }
00245     if (!bCompleted) {
00246         if (GetLastError() == ERROR_IO_PENDING) {
00247             if (!GetOverlappedResult(
00248                     HANDLE(handle),
00249                     &binding,
00250                     &dwActual,
00251                     TRUE))
00252             {
00253                 dwActual = 0;
00254             }
00255         } else {
00256             dwActual = 0;
00257         }
00258     }
00259     cbActual = dwActual;
00260 #else
00261     if (request.type == RandomAccessRequest::READ) {
00262         cbActual = ::pread(
00263             handle,
00264             request.bindingList.front().getBuffer(),
00265             request.cbTransfer,
00266             request.cbOffset);
00267     } else {
00268         cbActual = ::pwrite(
00269             handle,
00270             request.bindingList.front().getBuffer(),
00271             request.cbTransfer,
00272             request.cbOffset);
00273     }
00274 #endif
00275     request.bindingList.front().notifyTransferCompletion(
00276         cbActual == request.cbTransfer);
00277 }
00278 
00279 FENNEL_END_CPPFILE("$Id: //open/dev/fennel/device/FileDevice.cpp#14 $");
00280 
00281 // End FileDevice.cpp

Generated on Mon Jun 22 04:00:18 2009 for Fennel by  doxygen 1.5.1