RandomAccessFileDeviceTest.cpp

Go to the documentation of this file.
00001 /*
00002 // $Id: //open/dev/fennel/test/RandomAccessFileDeviceTest.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/common/FileSystem.h"
00026 #include "fennel/test/TestBase.h"
00027 #include "fennel/synch/SynchObj.h"
00028 #include "fennel/device/RandomAccessFileDevice.h"
00029 #include "fennel/device/RandomAccessRequest.h"
00030 #include "fennel/device/DeviceAccessScheduler.h"
00031 #include "fennel/device/DeviceAccessSchedulerParams.h"
00032 #include "fennel/cache/VMAllocator.h"
00033 #include <memory>
00034 #include <fstream>
00035 #include <boost/test/test_tools.hpp>
00036 #include <boost/scoped_array.hpp>
00037 
00038 using namespace fennel;
00039 
00040 class RandomAccessFileDeviceTest : virtual public TestBase
00041 {
00042     static const uint ZERO_SIZE;
00043     static const uint HALF_SIZE;
00044     static const uint FULL_SIZE;
00045 
00046     DeviceAccessSchedulerParams schedParams;
00047     SharedRandomAccessDevice pRandomAccessDevice;
00048     DeviceMode baseMode;
00049 
00050     void openDevice(DeviceMode openMode,std::string devName)
00051     {
00052         if (openMode.create) {
00053             FileSystem::remove(devName.c_str());
00054         }
00055         pRandomAccessDevice.reset(
00056             new RandomAccessFileDevice(devName,openMode));
00057     }
00058 
00059     void closeDevice()
00060     {
00061         pRandomAccessDevice.reset();
00062     }
00063 
00064     void testDeviceCreation()
00065     {
00066         const char *devName = "test.dat";
00067         DeviceMode openMode = baseMode;
00068         openMode.create = 1;
00069         openDevice(openMode,devName);
00070         closeDevice();
00071         if (openMode.temporary) {
00072             if (FileSystem::doesFileExist(devName)) {
00073                 std::cerr << "temporary test.dat not deleted" << std::endl;
00074             }
00075         } else {
00076             openMode.create = 0;
00077             openDevice(openMode,devName);
00078             closeDevice();
00079         }
00080     }
00081 
00082     void testGrow()
00083     {
00084         const char *devName = "grow.dat";
00085         DeviceMode openMode = baseMode;
00086         openMode.create = 1;
00087         openDevice(openMode,devName);
00088         BOOST_CHECK_EQUAL(ZERO_SIZE, pRandomAccessDevice->getSizeInBytes());
00089         pRandomAccessDevice->setSizeInBytes(FULL_SIZE);
00090         BOOST_CHECK_EQUAL(FULL_SIZE, pRandomAccessDevice->getSizeInBytes());
00091         closeDevice();
00092         if (openMode.temporary) {
00093             return;
00094         }
00095         openMode.create = 0;
00096         openDevice(openMode,devName);
00097         BOOST_CHECK_EQUAL(FULL_SIZE, pRandomAccessDevice->getSizeInBytes());
00098         closeDevice();
00099     }
00100 
00101     void testShrink()
00102     {
00103         const char *devName = "shrink.dat";
00104         DeviceMode openMode = baseMode;
00105         openMode.create = 1;
00106         openDevice(openMode,devName);
00107         BOOST_CHECK_EQUAL(ZERO_SIZE, pRandomAccessDevice->getSizeInBytes());
00108         pRandomAccessDevice->setSizeInBytes(FULL_SIZE);
00109         BOOST_CHECK_EQUAL(FULL_SIZE, pRandomAccessDevice->getSizeInBytes());
00110         closeDevice();
00111         if (openMode.temporary) {
00112             return;
00113         }
00114         openMode.create = 0;
00115         openDevice(openMode,devName);
00116         BOOST_CHECK_EQUAL(FULL_SIZE, pRandomAccessDevice->getSizeInBytes());
00117         pRandomAccessDevice->setSizeInBytes(HALF_SIZE);
00118         closeDevice();
00119         openDevice(openMode,devName);
00120         BOOST_CHECK_EQUAL(HALF_SIZE, pRandomAccessDevice->getSizeInBytes());
00121         closeDevice();
00122     }
00123 
00124     void testLargeFile()
00125     {
00126         // Create a 5G file in order to test beyond 32-bit unsigned.
00127         FileSize cbOffset = 0x40000000; // 1G
00128         cbOffset *= 5;
00129         testAsyncIO(cbOffset);
00130     }
00131 
00132     class Listener
00133     {
00134         StrictMutex mutex;
00135         LocalCondition cond;
00136 
00137     public:
00138         int nTarget;
00139         int nSuccess;
00140         int nCompleted;
00141 
00142         explicit Listener(int nTargetInit)
00143         {
00144             nTarget = nTargetInit;
00145             nSuccess = 0;
00146             nCompleted = 0;
00147         }
00148         virtual ~Listener()
00149         {
00150         }
00151 
00152         StrictMutex &getMutex()
00153         {
00154             return mutex;
00155         }
00156 
00157         void notifyTransferCompletion(bool b)
00158         {
00159             StrictMutexGuard mutexGuard(mutex);
00160             if (b) {
00161                 nSuccess++;
00162             }
00163             nCompleted++;
00164             if (nCompleted == nTarget) {
00165                 cond.notify_all();
00166             }
00167         }
00168 
00169         void waitForAll()
00170         {
00171             StrictMutexGuard mutexGuard(mutex);
00172             while (nCompleted < nTarget) {
00173                 cond.wait(mutexGuard);
00174             }
00175         }
00176     };
00177 
00178     class Binding : public RandomAccessRequestBinding
00179     {
00180         Listener &listener;
00181         uint cb;
00182         PBuffer pBuffer;
00183     public:
00184         explicit Binding(
00185             Listener &listenerInit,uint cbInit,PBuffer pBufferInit)
00186             : listener(listenerInit),cb(cbInit),pBuffer(pBufferInit)
00187         {
00188         }
00189 
00190         virtual ~Binding()
00191         {
00192         }
00193 
00194         virtual PBuffer getBuffer() const
00195         {
00196             return pBuffer;
00197         }
00198 
00199         virtual uint getBufferSize() const
00200         {
00201             return cb;
00202         }
00203 
00204         virtual void notifyTransferCompletion(bool bSuccess)
00205         {
00206             listener.notifyTransferCompletion(bSuccess);
00207         }
00208     };
00209 
00210     void testAsyncIO()
00211     {
00212         testAsyncIO(0);
00213     }
00214 
00215     void testRetryAsyncIO()
00216     {
00217         testAsyncIO(0, 5000);
00218     }
00219 
00220     void testAsyncIO(FileSize cbOffset, int n = 5)
00221     {
00222         uint cbSector = HALF_SIZE;
00223         VMAllocator allocator(cbSector*n);
00224         void *pBuf = allocator.allocate();
00225         void *pBuf2 = allocator.allocate();
00226         BOOST_REQUIRE(pBuf != NULL);
00227         try {
00228             testAsyncIOImpl(
00229                 n, cbSector,
00230                 reinterpret_cast<PBuffer>(pBuf),
00231                 reinterpret_cast<PBuffer>(pBuf2),
00232                 cbOffset);
00233         } catch (...) {
00234             allocator.deallocate(pBuf);
00235             allocator.deallocate(pBuf2);
00236             throw;
00237         }
00238         allocator.deallocate(pBuf);
00239         allocator.deallocate(pBuf2);
00240     }
00241 
00242     void testAsyncIOImpl(
00243         int n, uint cbSector,
00244         PBuffer pBuf, PBuffer pBuf2, FileSize cbOffset = 0)
00245     {
00246         DeviceAccessScheduler *pScheduler =
00247             DeviceAccessScheduler::newScheduler(schedParams);
00248 
00249         const char *devName = "async.dat";
00250         DeviceMode openMode = baseMode;
00251         openMode.create = 1;
00252         openDevice(openMode,devName);
00253         FileSize cbFile = cbOffset;
00254         cbFile += n*cbSector;
00255         pRandomAccessDevice->setSizeInBytes(cbFile);
00256 
00257         // close and re-open to get the actual file size
00258         if (!openMode.temporary) {
00259             closeDevice();
00260             openMode.create = 0;
00261             openDevice(openMode,devName);
00262             FileSize cbFileActual = pRandomAccessDevice->getSizeInBytes();
00263             BOOST_CHECK_EQUAL(cbFile, cbFileActual);
00264         }
00265 
00266         pScheduler->registerDevice(pRandomAccessDevice);
00267         std::string s = "Four score and seven years ago.";
00268         char const *writeBuf = s.c_str();
00269         uint cb = s.size();
00270 
00271         Listener writeListener(n);
00272         RandomAccessRequest writeRequest;
00273         writeRequest.pDevice = pRandomAccessDevice.get();
00274         writeRequest.cbOffset = cbOffset;
00275         writeRequest.cbTransfer=n*cbSector;
00276         writeRequest.type = RandomAccessRequest::WRITE;
00277         memcpy(pBuf, writeBuf, cb);
00278         for (int i = 0; i < n; i++) {
00279             Binding *pBinding = new Binding(
00280                 writeListener,cbSector,PBuffer(pBuf));
00281             writeRequest.bindingList.push_back(*pBinding);
00282         }
00283 
00284         // LER-7110: take a redundant mutex on the listener around the request
00285         // to confirm that attempts to notify the listener don't deadlock in
00286         // the case where the async I/O queue is full
00287         StrictMutexGuard mutexGuard(writeListener.getMutex());
00288         pScheduler->schedule(writeRequest);
00289         mutexGuard.unlock();
00290 
00291         writeListener.waitForAll();
00292         BOOST_CHECK_EQUAL(n, writeListener.nSuccess);
00293         pRandomAccessDevice->flush();
00294 
00295         if (!openMode.temporary) {
00296             pScheduler->unregisterDevice(pRandomAccessDevice);
00297             closeDevice();
00298             openMode.create = 0;
00299             openDevice(openMode,devName);
00300             pScheduler->registerDevice(pRandomAccessDevice);
00301         }
00302 
00303         Listener readListener(n + 1);
00304         RandomAccessRequest readRequest;
00305         readRequest.pDevice = pRandomAccessDevice.get();
00306         readRequest.cbOffset = cbOffset;
00307         readRequest.cbTransfer = n*cbSector;
00308         readRequest.type = RandomAccessRequest::READ;
00309         for (int i = 0; i < n; i++) {
00310             Binding *pBinding = new Binding(
00311                 readListener,cbSector,
00312                 pBuf + i*cbSector);
00313             readRequest.bindingList.push_back(*pBinding);
00314         }
00315 
00316         // Test a simultaneous read on the same device which intersects
00317         // with the reads above; this simulates something like an online
00318         // backup reading into private buffers, aliasing the cache.
00319         RandomAccessRequest readRequest2;
00320         readRequest2.pDevice = pRandomAccessDevice.get();
00321         readRequest2.cbOffset = cbOffset;
00322         readRequest2.cbTransfer = cbSector;
00323         readRequest2.type = RandomAccessRequest::READ;
00324         Binding *pBinding = new Binding(
00325             readListener, cbSector, pBuf2);
00326         readRequest2.bindingList.push_back(*pBinding);
00327 
00328         pScheduler->schedule(readRequest);
00329         pScheduler->schedule(readRequest2);
00330         readListener.waitForAll();
00331         BOOST_CHECK_EQUAL(n + 1, readListener.nSuccess);
00332         for (int i = 0; i < n; i++) {
00333             std::string s2(reinterpret_cast<char *>(pBuf + i*cbSector),cb);
00334             BOOST_CHECK_EQUAL(s,s2);
00335         }
00336         std::string s3(reinterpret_cast<char *>(pBuf2),cb);
00337         BOOST_CHECK_EQUAL(s,s3);
00338 
00339         pScheduler->unregisterDevice(pRandomAccessDevice);
00340         closeDevice();
00341 
00342         pScheduler->stop();
00343         delete pScheduler;
00344     }
00345 
00346 public:
00347     explicit RandomAccessFileDeviceTest()
00348     {
00349         schedParams.readConfig(configMap);
00350         FENNEL_UNIT_TEST_CASE(RandomAccessFileDeviceTest,testPermanentNoDirect);
00351         FENNEL_UNIT_TEST_CASE(RandomAccessFileDeviceTest,testTemporary);
00352         FENNEL_UNIT_TEST_CASE(RandomAccessFileDeviceTest,testPermanentDirect);
00353         FENNEL_UNIT_TEST_CASE(RandomAccessFileDeviceTest,testRetryAsyncIO);
00354 
00355         // NOTE jvs 11-Feb-2006:  This is optional since it creates
00356         // a 5G file.  On operating systems with sparse-file support, it
00357         // doesn't actually take up that much disk space.
00358         FENNEL_EXTRA_UNIT_TEST_CASE(
00359             RandomAccessFileDeviceTest,testLargeFile);
00360     }
00361 
00362     void testPermanentNoDirect()
00363     {
00364         baseMode = DeviceMode::load;
00365         runModeTests();
00366     }
00367 
00368     void testTemporary()
00369     {
00370         baseMode = DeviceMode::load;
00371         baseMode.temporary = true;
00372         runModeTests();
00373     }
00374 
00375     void testPermanentDirect()
00376     {
00377         baseMode = DeviceMode::load;
00378         baseMode.direct = true;
00379         runModeTests();
00380     }
00381 
00382     void runModeTests()
00383     {
00384         testDeviceCreation();
00385         testGrow();
00386         testShrink();
00387         testAsyncIO();
00388     }
00389 
00390     virtual void testCaseTearDown()
00391     {
00392         closeDevice();
00393     }
00394 };
00395 
00396 // NOTE:  HALF_SIZE and FULL_SIZE have to be multiples of the disk sector size
00397 // for direct I/O to work on Windows.
00398 const uint RandomAccessFileDeviceTest::ZERO_SIZE = 0;
00399 const uint RandomAccessFileDeviceTest::HALF_SIZE = 4096;
00400 const uint RandomAccessFileDeviceTest::FULL_SIZE = 2*HALF_SIZE;
00401 
00402 FENNEL_UNIT_TEST_SUITE(RandomAccessFileDeviceTest);
00403 
00404 // End RandomAccessFileDeviceTest.cpp

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