SegPageLock.h

Go to the documentation of this file.
00001 /*
00002 // $Id: //open/dev/fennel/segment/SegPageLock.h#20 $
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 #ifndef Fennel_SegPageLock_Included
00025 #define Fennel_SegPageLock_Included
00026 
00027 #include "fennel/segment/Segment.h"
00028 #include "fennel/segment/SegmentAccessor.h"
00029 #include "fennel/cache/CacheAccessor.h"
00030 #include "fennel/cache/CachePage.h"
00031 
00032 #include <boost/utility.hpp>
00033 
00034 FENNEL_BEGIN_NAMESPACE
00035 
00036 // TODO:  provide debug-build support for verifying that the page owner is
00037 // correct for each page access
00038 
00039 // TODO jvs 31-Dec-2005:  Doxygen for all methods, and move inline
00040 // bodies to end of file.
00041 
00050 class FENNEL_SEGMENT_EXPORT SegPageLock
00051     : public boost::noncopyable
00052 {
00053     // NOTE: the shared pointers in segmentAccessor imply some locking
00054     // overhead during assignment.  If this is an issue, preallocate
00055     // the necessary SegPageLocks rather than stack-allocating them.
00056 
00057     SegmentAccessor segmentAccessor;
00058     CachePage *pPage;
00059     LockMode lockMode;
00060     PageId lockedPageId;
00061     bool newPage;
00062     bool isWriteVersioned;
00063 
00064     inline void resetPage()
00065     {
00066         pPage = NULL;
00067         lockedPageId = NULL_PAGE_ID;
00068         newPage = false;
00069     }
00070 
00071     inline LockMode getLockMode(LockMode origLockMode)
00072     {
00073         // If writes are versioned, then there's no need to apply an
00074         // exclusive lock on the current version of the page.  When
00075         // we need to modify the page, we'll create a new version of the
00076         // page, which we'll exclusively lock.
00077         if (isWriteVersioned) {
00078             if (origLockMode == LOCKMODE_X
00079                 || origLockMode == LOCKMODE_X_NOWAIT)
00080             {
00081                 return
00082                     (origLockMode == LOCKMODE_X) ?
00083                         LOCKMODE_S : LOCKMODE_S_NOWAIT;
00084             }
00085         }
00086 
00087         return origLockMode;
00088     }
00089 
00090     inline void initialize()
00091     {
00092         // this is a dummy to keep happy optimizing compilers which are too
00093         // smart for their own good
00094         lockMode = LOCKMODE_X;
00095         isWriteVersioned = false;
00096     }
00097 
00098 
00099 public:
00100     explicit SegPageLock()
00101     {
00102         initialize();
00103         resetPage();
00104     }
00105 
00106     explicit SegPageLock(
00107         SegmentAccessor const &segmentAccessor)
00108     {
00109         initialize();
00110         resetPage();
00111         accessSegment(segmentAccessor);
00112     }
00113 
00114     ~SegPageLock()
00115     {
00116         unlock();
00117     }
00118 
00119     inline void accessSegment(
00120         SegmentAccessor const &segmentAccessorInit)
00121     {
00122         assert(!pPage);
00123         assert(segmentAccessorInit.pSegment);
00124         assert(segmentAccessorInit.pCacheAccessor);
00125         segmentAccessor = segmentAccessorInit;
00126         isWriteVersioned = segmentAccessor.pSegment->isWriteVersioned();
00127     }
00128 
00129     inline bool isLocked() const
00130     {
00131         return pPage ? true : false;
00132     }
00133 
00134     inline CachePage &getPage() const
00135     {
00136         assert(isLocked());
00137         return *pPage;
00138     }
00139 
00140     inline PageId allocatePage(PageOwnerId ownerId = ANON_PAGE_OWNER_ID)
00141     {
00142         PageId pageId = tryAllocatePage(ownerId);
00143         permAssert(pageId != NULL_PAGE_ID);
00144         return pageId;
00145     }
00146 
00147     inline PageId tryAllocatePage(PageOwnerId ownerId = ANON_PAGE_OWNER_ID)
00148     {
00149         unlock();
00150         PageId pageId = segmentAccessor.pSegment->allocatePageId(ownerId);
00151         if (pageId == NULL_PAGE_ID) {
00152             return pageId;
00153         }
00154         lockPage(pageId,LOCKMODE_X,false);
00155         newPage = true;
00156         return pageId;
00157     }
00158 
00159     inline void deallocateLockedPage()
00160     {
00161         assert(isLocked());
00162         BlockId blockId = pPage->getBlockId();
00163         unlock();
00164         PageId pageId = segmentAccessor.pSegment->translateBlockId(blockId);
00165         // we rely on the segment to decide whether to discard the block
00166         // from cache
00167         segmentAccessor.pSegment->deallocatePageRange(pageId,pageId);
00168     }
00169 
00170     inline void deallocateUnlockedPage(PageId pageId)
00171     {
00172         assert(pageId != NULL_PAGE_ID);
00173         BlockId blockId = segmentAccessor.pSegment->translatePageId(pageId);
00174         // we rely on the segment to decide whether to discard the block
00175         // from cache
00176         segmentAccessor.pSegment->deallocatePageRange(pageId,pageId);
00177     }
00178 
00179     inline void unlock()
00180     {
00181         if (pPage) {
00182             segmentAccessor.pCacheAccessor->unlockPage(
00183                 *pPage,
00184                 lockMode);
00185             resetPage();
00186         }
00187     }
00188 
00189     inline void dontUnlock()
00190     {
00191         resetPage();
00192     }
00193 
00194     inline void lockPage(
00195         PageId pageId,LockMode lockModeInit,
00196         bool readIfUnmapped = true)
00197     {
00198         // if the page we want to lock is already locked in the desired
00199         // mode, nothing needs to be done
00200         if (isLocked() && pageId == lockedPageId && lockMode == lockModeInit) {
00201             return;
00202         }
00203         unlock();
00204         lockMode = getLockMode(lockModeInit);
00205         BlockId blockId = segmentAccessor.pSegment->translatePageId(pageId);
00206         pPage = segmentAccessor.pCacheAccessor->lockPage(
00207             blockId,
00208             lockMode,
00209             readIfUnmapped,
00210             segmentAccessor.pSegment->getMappedPageListener(blockId));
00211         lockedPageId = pageId;
00212     }
00213 
00214     inline void lockPageWithCoupling(
00215         PageId pageId,LockMode lockModeInit)
00216     {
00217         assert(lockModeInit < LOCKMODE_S_NOWAIT);
00218         BlockId blockId = segmentAccessor.pSegment->translatePageId(pageId);
00219         LockMode newLockMode = getLockMode(lockModeInit);
00220         CachePage *pNewPage = segmentAccessor.pCacheAccessor->lockPage(
00221             blockId,
00222             newLockMode,
00223             true,
00224             segmentAccessor.pSegment->getMappedPageListener(blockId));
00225         assert(pNewPage);
00226         unlock();
00227         lockMode = newLockMode;
00228         pPage = pNewPage;
00229         lockedPageId = pageId;
00230     }
00231 
00232     inline void lockShared(PageId pageId)
00233     {
00234         lockPage(pageId,LOCKMODE_S);
00235     }
00236 
00237     inline void lockExclusive(PageId pageId)
00238     {
00239         lockPage(pageId,LOCKMODE_X);
00240     }
00241 
00242     inline void lockSharedNoWait(PageId pageId)
00243     {
00244         lockPage(pageId,LOCKMODE_S_NOWAIT);
00245         lockMode = LOCKMODE_S;
00246     }
00247 
00248     inline void lockExclusiveNoWait(PageId pageId)
00249     {
00250         lockPage(pageId,LOCKMODE_X_NOWAIT);
00251         lockMode = LOCKMODE_X;
00252     }
00253 
00254     inline void updatePage()
00255     {
00256         assert(isLocked());
00257 
00258         // If the page is not newly allocated and can't be updated in-place,
00259         // lock the page that will be updated
00260         if (!newPage) {
00261             PageId origPageId =
00262                 segmentAccessor.pSegment->translateBlockId(getPage().
00263                     getBlockId());
00264             PageId updatePageId =
00265                 segmentAccessor.pSegment->updatePage(origPageId);
00266             if (updatePageId != NULL_PAGE_ID) {
00267                 lockUpdatePage(updatePageId);
00268                 return;
00269             }
00270         }
00271 
00272         // Either the page is new or the page can be updated in-place.
00273         // If we haven't locked the page exclusively yet, upgrade the
00274         // shared lock, forcing the upgrade to wait for pending IOs.
00275         if (lockMode == LOCKMODE_S) {
00276             assert(isWriteVersioned);
00277             TxnId txnId = segmentAccessor.pCacheAccessor->getTxnId();
00278             pPage->upgrade(txnId);
00279             lockMode = LOCKMODE_X;
00280         }
00281     }
00282 
00283     inline void lockUpdatePage(PageId updatePageId)
00284     {
00285         assert(isWriteVersioned);
00286         BlockId blockId =
00287             segmentAccessor.pSegment->translatePageId(updatePageId);
00288         assert(lockMode == LOCKMODE_S);
00289         CachePage *pNewPage = segmentAccessor.pCacheAccessor->lockPage(
00290             blockId,
00291             LOCKMODE_X,
00292             true,
00293             segmentAccessor.pSegment->getMappedPageListener(blockId));
00294         assert(pNewPage);
00295         // copy the original page while we have both the original and new
00296         // pages locked
00297         memcpy(
00298             pNewPage->getWritableData(),
00299             pPage->getReadableData(),
00300             segmentAccessor.pSegment->getUsablePageSize());
00301         PageId origPageId = lockedPageId;
00302         unlock();
00303         lockMode = LOCKMODE_X;
00304         pPage = pNewPage;
00305         newPage = true;
00306         // keep track of the locked page based on the original pageId
00307         // requested
00308         lockedPageId = origPageId;
00309     }
00310 
00311     inline PageId getPageId()
00312     {
00313         // note that lockedPageId may not be the same as
00314         // segmentAccessor.pSegment->translateBlockId(getPage().getBlockId())
00315         // if the page is versioned
00316         return lockedPageId;
00317     }
00318 
00319     inline void flushPage(bool async)
00320     {
00321         assert(isLocked());
00322         segmentAccessor.pCacheAccessor->flushPage(getPage(), true);
00323     }
00324 
00325     // TODO:  big warning
00326     inline void swapBuffers(SegPageLock &other)
00327     {
00328         // TODO:  assert magic numbers the same?
00329         assert(isLocked());
00330         assert(other.isLocked());
00331         assert(lockMode == LOCKMODE_X);
00332         assert(other.lockMode == LOCKMODE_X);
00333         assert(pPage != other.pPage);
00334 
00335         // since we're copying new data into other, treat it as an update
00336         other.updatePage();
00337 
00338         // both pages will end up with this page's footer, on the assumption
00339         // that other was a scratch page
00340         // TODO:  correctly swap footers as well?
00341         Segment &segment = *(segmentAccessor.pSegment);
00342         memcpy(
00343             other.pPage->getWritableData() + segment.getUsablePageSize(),
00344             pPage->getReadableData() +  segment.getUsablePageSize(),
00345             segment.getFullPageSize() - segment.getUsablePageSize());
00346         pPage->swapBuffers(*other.pPage);
00347     }
00348 
00349     inline bool tryUpgrade()
00350     {
00351         assert(isLocked());
00352         assert(lockMode == LOCKMODE_S);
00353         // REVIEW jvs 31-Dec-2005:  This should really go through
00354         // the CacheAccessor interface.
00355         TxnId txnId = segmentAccessor.pCacheAccessor->getTxnId();
00356 
00357         // If we're versioning, defer upgrading the lock until
00358         // we're actually going to be update the page.
00359         if (isWriteVersioned) {
00360             return true;
00361         } else {
00362             if (pPage->tryUpgrade(txnId)) {
00363                 lockMode = LOCKMODE_X;
00364                 return true;
00365             }
00366             return false;
00367         }
00368     }
00369 
00370     inline SharedCacheAccessor getCacheAccessor() const
00371     {
00372         return segmentAccessor.pCacheAccessor;
00373     }
00374 };
00375 
00389 struct FENNEL_SEGMENT_EXPORT StoredNode
00390 {
00394     MagicNumber magicNumber;
00395 };
00396 
00397 // TODO:  verify uniqueness of MAGIC_NUMBER
00398 
00413 template <class Node>
00414 class SegNodeLock : public SegPageLock
00415 {
00416     inline void verifyMagicNumber(Node const &node) const
00417     {
00418         assert(node.magicNumber == Node::MAGIC_NUMBER);
00419     }
00420 
00421 public:
00422     explicit SegNodeLock()
00423     {
00424     }
00425 
00426     explicit SegNodeLock(
00427         SegmentAccessor &segmentAccessor)
00428         : SegPageLock(segmentAccessor)
00429     {
00430     }
00431 
00432     inline bool checkMagicNumber() const
00433     {
00434         Node const &node =
00435             *reinterpret_cast<Node const *>(getPage().getReadableData());
00436         return (node.magicNumber == Node::MAGIC_NUMBER);
00437     }
00438 
00439     inline Node const &getNodeForRead() const
00440     {
00441         Node const &node =
00442             *reinterpret_cast<Node const *>(getPage().getReadableData());
00443         verifyMagicNumber(node);
00444         return node;
00445     }
00446 
00447     inline Node &getNodeForWrite()
00448     {
00449         updatePage();
00450         return *reinterpret_cast<Node *>(getPage().getWritableData());
00451     }
00452 
00453     inline PageId allocatePage(PageOwnerId ownerId = ANON_PAGE_OWNER_ID)
00454     {
00455         PageId pageId = SegPageLock::allocatePage(ownerId);
00456         setMagicNumber();
00457         return pageId;
00458     }
00459 
00460     inline PageId tryAllocatePage(PageOwnerId ownerId = ANON_PAGE_OWNER_ID)
00461     {
00462         PageId pageId = SegPageLock::tryAllocatePage(ownerId);
00463         if (pageId != NULL_PAGE_ID) {
00464             setMagicNumber();
00465         }
00466         return pageId;
00467     }
00468 
00469     inline void setMagicNumber()
00470     {
00471         getNodeForWrite().magicNumber = Node::MAGIC_NUMBER;
00472     }
00473 
00474     inline bool isMagicNumberValid()
00475     {
00476         Node const &node =
00477             *reinterpret_cast<Node const *>(getPage().getReadableData());
00478         return node.magicNumber == Node::MAGIC_NUMBER;
00479     }
00480 };
00481 
00482 FENNEL_END_NAMESPACE
00483 
00484 #endif
00485 
00486 // End SegPageLock.h

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