From: Jim Starkey Date: January 28 2009 7:30pm Subject: Cycle Locking (was Problems with record visibility and how it is computed) List-Archive: http://lists.mysql.com/falcon/438 Message-Id: <4980B272.8060500@nimbusdb.com> MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="------------090007040809060005030503" --------------090007040809060005030503 Content-Type: text/plain; charset=ISO-8859-1; format=flowed Content-Transfer-Encoding: 7bit OK, let's look closely at cycle locking. Attached is the latest rev of the cycle lock object. It's intended use is a stack local -- the constructor takes the lock and the destructor releases it. One issue is the interaction of cycle locks and long (i.e. transaction) waits. One really doesn't want to block the cycle manager if we're waiting for someone to come back from vacation to commit his updates (or get rightly fired and the updates rolled back). My solution is to release the cycle lock before a "long" wait and reacquire it after waking up. The CycleLock object uses thread specific data to register the address of the CycleLock object so it doesn't need to be passed around. I believe this solves the first order problem. The second order problems are the ramifications of releasing and reacquiring the cycle lock. As long as the code didn't retain a pointer acquired before the wait, everything should be just fine. I don't think any of the candidates for cycle locking would be affected at all. The immediate case -- transaction object lifetime control -- are OK as long as we don't trust a transaction pointer acquired from before the wait. In most cases we can just refetch it from where ever we got it originally. If the pointer is now NULL, we can deal with it. Otherwise the pointer is protected by the new cycle. The other candidate is purging intermediate record versions. Since it tautological that these versions of are no interest to anyone, there is no change that a miscreant would retain a pointer to one over the wait. I think the only serious problem with cycle locking is getting used to the idea. I've been using it in Nimbus for some time now, and it's as natural as SyncObjects. So let's hear the issues and work through them. Maybe Kevin is right and we'll find some messy and/or complicated cases; if not, then we've got another tool for our toolbox. The more tools we have, the less that all problems look like nails. -- Jim Starkey President, NimbusDB, Inc. 978 526-1376 --------------090007040809060005030503 Content-Type: text/plain; name="CycleLock.cpp" Content-Transfer-Encoding: 7bit Content-Disposition: inline; filename="CycleLock.cpp" #include "Cloud.h" #include "CycleLock.h" #include "MasterCatalog.h" #include "Thread.h" static int threadIndex = Thread::getThreadSpecificIndex(); CycleLock::CycleLock(MasterCatalog *masterCatalog) { syncObject = masterCatalog->currentCycle; syncObject->lock(NULL, Shared); locked = true; Thread::setThreadSpecific(threadIndex, this); } CycleLock::~CycleLock(void) { if (locked) syncObject->unlock(NULL, Shared); Thread::setThreadSpecific(threadIndex, NULL); } void CycleLock::lock(void) { CycleLock *cycleLock = (CycleLock*) Thread::getThreadSpecific(threadIndex); cycleLock->lockCycle(); } void CycleLock::unlock(void) { CycleLock *cycleLock = (CycleLock*) Thread::getThreadSpecific(threadIndex); cycleLock->unlockCycle(); } void CycleLock::lockCycle(void) { if (!locked) { syncObject->lock(NULL, Shared); locked = true; } } void CycleLock::unlockCycle(void) { if (locked) { syncObject->unlock(NULL, Shared); locked = false; } } --------------090007040809060005030503 Content-Type: text/plain; name="CycleLock.h" Content-Transfer-Encoding: 7bit Content-Disposition: inline; filename="CycleLock.h" #ifndef _CYCLE_LOCK_H_ #define _CYCLE_LOCK_H_ class SyncObject; class MasterCatalog; class CycleLock { public: CycleLock(MasterCatalog *masterCatalog); ~CycleLock(void); void lockCycle(void); void unlockCycle(void); static void lock(void); static void unlock(void); SyncObject *syncObject; bool locked; }; #endif --------------090007040809060005030503--