00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
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
00127 FileSize cbOffset = 0x40000000;
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
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
00285
00286
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
00317
00318
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
00356
00357
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
00397
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