summaryrefslogtreecommitdiff
path: root/src/mongo/db/concurrency/lock_mgr_test.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/mongo/db/concurrency/lock_mgr_test.cpp')
-rw-r--r--src/mongo/db/concurrency/lock_mgr_test.cpp894
1 files changed, 0 insertions, 894 deletions
diff --git a/src/mongo/db/concurrency/lock_mgr_test.cpp b/src/mongo/db/concurrency/lock_mgr_test.cpp
deleted file mode 100644
index 8dce49e94d1..00000000000
--- a/src/mongo/db/concurrency/lock_mgr_test.cpp
+++ /dev/null
@@ -1,894 +0,0 @@
-/**
-* Copyright (C) 2014 MongoDB Inc.
-*
-* This program is free software: you can redistribute it and/or modify
-* it under the terms of the GNU Affero General Public License, version 3,
-* as published by the Free Software Foundation.
-*
-* This program is distributed in the hope that it will be useful,
-* but WITHOUT ANY WARRANTY; without even the implied warranty of
-* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-* GNU Affero General Public License for more details.
-*
-* You should have received a copy of the GNU Affero General Public License
-* along with this program. If not, see <http://www.gnu.org/licenses/>.
-*
-* As a special exception, the copyright holders give permission to link the
-* code of portions of this program with the OpenSSL library under certain
-* conditions as described in each individual source file and distribute
-* linked combinations including the program with the OpenSSL library. You
-* must comply with the GNU Affero General Public License in all respects for
-* all of the code used other than as permitted herein. If you modify file(s)
-* with this exception, you may extend this exception to your version of the
-* file(s), but you are not obligated to do so. If you do not wish to do so,
-* delete this exception statement from your version. If you delete this
-* exception statement from all source files in the program, then also delete
-* it in the license file.
-*/
-
-/**
- * tests for db/concurrency/lock_mgr.* capabilities
- *
- * Testing concurrency requires multiple threads. In this test, these are packaged as
- * instances of a ClientTransaction class. The test's main thread creates instances
- * of ClientTransactions and communicates with them through a pair of producer/consumer
- * buffers. The driver thread sends requests to ClientTransaction threads using a
- * TxCommandBuffer, and waits for responses in a TxResponseBuffer. This protocol
- * allows precise control over timing.
- *
- * The producer/consumer buffer is possibly overkill. At present there is only
- * one producer and one consumer using any one buffer. Also, the current set of
- * tests are 'lock-step', with the driver never issuing more than one command
- * before waiting for a response.
- */
-
-#include <boost/scoped_ptr.hpp>
-#include <boost/thread/thread.hpp>
-#include "mongo/unittest/unittest.h"
-#include "mongo/db/concurrency/lock_mgr.h"
-#include "mongo/util/log.h"
-#include "mongo/util/time_support.h"
-
-namespace mongo {
-
- enum TxCmd {
- ACQUIRE,
- RELEASE,
- ABORT,
- POLICY,
- QUIT,
- INVALID
- };
-
- enum TxRsp {
- ACQUIRED,
- RELEASED,
- BLOCKED,
- AWAKENED,
- ABORTED,
- INVALID_RESPONSE
- };
-
- class TxResponse {
- public:
- TxRsp rspCode;
- unsigned xid;
- LockMode mode;
- ResourceId resId;
- };
-
- class TxRequest {
- public:
- TxCmd cmd;
- unsigned xid;
- LockMode mode;
- ResourceId resId;
- LockManager::Policy policy;
- };
-
- class TxResponseBuffer {
- public:
- TxResponseBuffer() : _count(0), _readPos(0), _writePos(0) { }
-
- void post(const TxRsp& rspCode) {
- boost::unique_lock<boost::mutex> guard(_guard);
- while (_count == 10)
- _full.wait(guard);
- buffer[_writePos++].rspCode = rspCode;
- _writePos %= 10;
- _count++;
- _empty.notify_one();
- }
-
- TxResponse* consume() {
- boost::unique_lock<boost::mutex> guard(_guard);
- while (_count == 0)
- _empty.wait(guard);
- TxResponse* result = &buffer[_readPos++];
- _readPos %= 10;
- _count--;
- _full.notify_one();
- return result;
- }
-
- boost::mutex _guard;
- boost::condition_variable _full;
- boost::condition_variable _empty;
-
- size_t _count;
- size_t _readPos;
- size_t _writePos;
-
- TxResponse buffer[10];
- };
-
-class TxCommandBuffer {
-public:
- TxCommandBuffer() : _count(0), _readPos(0), _writePos(0) { }
-
- void post(const TxCmd& cmd,
- const unsigned& xid = 0,
- const LockMode& mode = kShared,
- const ResourceId& resId = 0,
- const LockManager::Policy& policy = LockManager::kPolicyFirstCome) {
- boost::unique_lock<boost::mutex> guard(_guard);
- while (_count == 10)
- _full.wait(guard);
- buffer[_writePos].cmd = cmd;
- buffer[_writePos].xid = xid;
- buffer[_writePos].mode = mode;
- buffer[_writePos].resId = resId;
- buffer[_writePos].policy = policy;
- _writePos++;
- _writePos %= 10;
- _count++;
- _empty.notify_one();
- }
-
- TxRequest* consume() {
- boost::unique_lock<boost::mutex> guard(_guard);
- while (_count == 0)
- _empty.wait(guard);
- TxRequest* result = &buffer[_readPos++];
- _readPos %= 10;
- _count--;
- _full.notify_one();
- return result;
- }
-
- boost::mutex _guard;
- boost::condition_variable _full;
- boost::condition_variable _empty;
-
- size_t _count;
- size_t _readPos;
- size_t _writePos;
-
- TxRequest buffer[10];
-};
-
-class ClientTransaction : public LockManager::Notifier {
-public:
- // these are called in the main driver program
-
- ClientTransaction(LockManager* lm, const unsigned& xid)
- : _lm(lm)
- , _tx(xid)
- , _thr(&ClientTransaction::processCmd, this) { }
-
- virtual ~ClientTransaction() { _thr.join(); }
-
- void acquire(const LockMode& mode, const ResourceId resId, const TxRsp& rspCode) {
- _cmd.post(ACQUIRE, _tx.getTxId(), mode, resId);
- TxResponse* rsp = _rsp.consume();
- ASSERT(rspCode == rsp->rspCode);
- }
-
- void release(const LockMode& mode, const ResourceId resId) {
- _cmd.post(RELEASE, _tx.getTxId(), mode, resId);
- TxResponse* rsp = _rsp.consume();
- ASSERT(RELEASED == rsp->rspCode);
- }
-
- void abort() {
- _cmd.post(ABORT, _tx.getTxId());
- TxResponse* rsp = _rsp.consume();
- ASSERT(ABORTED == rsp->rspCode);
- }
-
- void wakened() {
- TxResponse* rsp = _rsp.consume();
- ASSERT(ACQUIRED == rsp->rspCode);
- }
-
- void setPolicy(const LockManager::Policy& policy, const TxRsp& rspCode) {
- _cmd.post(POLICY, _tx.getTxId(), kShared, 0, policy);
- TxResponse* rsp = _rsp.consume();
- ASSERT(rspCode == rsp->rspCode);
- }
-
- void quit() {
- _cmd.post(QUIT);
- }
-
- // these are run within the client threads
- void processCmd() {
- bool more = true;
- while (more) {
- TxRequest* req = _cmd.consume();
- switch (req->cmd) {
- case ACQUIRE:
- try {
- _lm->acquire(&_tx, req->mode, req->resId, this);
- _rsp.post(ACQUIRED);
- } catch (const LockManager::AbortException& err) {
- _rsp.post(ABORTED);
-// log() << "t" << _tx._txId << ": aborted, ending" << endl;
- return;
- }
- break;
- case RELEASE:
- _lm->release(&_tx, req->mode, req->resId);
- _rsp.post(RELEASED);
- break;
- case ABORT:
- try {
- _lm->abort(&_tx);
- } catch (const LockManager::AbortException& err) {
- _rsp.post(ABORTED);
- }
- break;
- case POLICY:
- try {
- _lm->setPolicy(&_tx, req->policy, this);
- _rsp.post(ACQUIRED);
- } catch( const LockManager::AbortException& err) {
- _rsp.post(ABORTED);
- }
- break;
- case QUIT:
- default:
- more = false;
- break;
- }
- }
- }
-
- // inherited from Notifier, used by LockManager::acquire
- virtual void operator()(const Transaction* blocker) {
- _rsp.post(BLOCKED);
- }
-
-private:
- TxCommandBuffer _cmd;
- TxResponseBuffer _rsp;
- LockManager* _lm;
- Transaction _tx;
- boost::thread _thr;
-
-};
-
-TEST(LockManagerTest, TxError) {
- LockManager lm;
- LockManager::LockStatus status;
- Transaction tx(1);
-
- // release a lock on a resource we haven't locked
- lm.acquire(&tx, kShared, 2);
- status = lm.release(&tx, kShared, 1); // this is in error
- ASSERT(LockManager::kLockResourceNotFound == status);
- status = lm.release(&tx, kShared, 2);
- ASSERT(LockManager::kLockReleased == status);
-
- // release a record we've locked in a different mode
- lm.acquire(&tx, kShared, 1);
- status = lm.release(&tx, kExclusive, 1); // this is in error
- ASSERT(LockManager::kLockModeNotFound == status);
- status = lm.release(&tx, kShared, 1);
- ASSERT(LockManager::kLockReleased == status);
-
- lm.acquire(&tx, kExclusive, 1);
- status = lm.release(&tx, kShared, 1); // this is in error
- ASSERT(LockManager::kLockModeNotFound == status);
- status = lm.release(&tx, kExclusive, 1);
- ASSERT(LockManager::kLockReleased == status);
-
- // attempt to acquire on a transaction that aborted
- try {
- lm.abort(&tx);
- } catch (const LockManager::AbortException& err) { }
- try {
- lm.acquire(&tx, kShared, 1); // error
- ASSERT(false);
- } catch (const LockManager::AbortException& error) {
- }
-}
-
-TEST(LockManagerTest, SingleTx) {
- LockManager lm;
- Transaction t1(1);
- ResourceId r1 = 1;
- LockManager::LockStatus status;
-
- // acquire a shared record lock
- ASSERT(! lm.isLocked(&t1, kShared, r1));
- lm.acquire(&t1, kShared, r1);
- ASSERT(lm.isLocked(&t1, kShared, r1));
-
- // release a shared record lock
- lm.release(&t1, kShared, r1);
- ASSERT(! lm.isLocked(&t1, kShared, r1));
-
- // acquire a shared record lock twice, on same ResourceId
- lm.acquire(&t1, kShared, r1);
- lm.acquire(&t1, kShared, r1);
- ASSERT(lm.isLocked(&t1, kShared, r1));
-
- // release the twice-acquired lock, once. Still locked
- status = lm.release(&t1, kShared, r1);
- ASSERT(LockManager::kLockCountDecremented == status);
- ASSERT(lm.isLocked(&t1, kShared, r1));
-
- // after 2nd release, it's not locked
- status = lm.release(&t1, kShared, r1);
- ASSERT(LockManager::kLockReleased == status);
- ASSERT(!lm.isLocked(&t1, kShared, r1));
-
-
-
- // --- test downgrade and release ---
-
- // acquire an exclusive then a shared lock, on the same ResourceId
- lm.acquire(&t1, kExclusive, r1);
- ASSERT(lm.isLocked(&t1, kExclusive, r1));
- lm.acquire(&t1, kShared, r1);
- ASSERT(lm.isLocked(&t1, kExclusive, r1));
- ASSERT(lm.isLocked(&t1, kShared, r1));
-
- // release shared first, then exclusive
- lm.release(&t1, kShared, r1);
- ASSERT(! lm.isLocked(&t1, kShared, r1));
- ASSERT(lm.isLocked(&t1, kExclusive, r1));
- lm.release(&t1, kExclusive, r1);
- ASSERT(! lm.isLocked(&t1, kExclusive, r1));
-
- // release exclusive first, then shared
- lm.acquire(&t1, kExclusive, r1);
- lm.acquire(&t1, kShared, r1);
- lm.release(&t1, kExclusive, r1);
- ASSERT(! lm.isLocked(&t1, kExclusive, r1));
- ASSERT(lm.isLocked(&t1, kShared, r1));
- lm.release(&t1, kShared, r1);
- ASSERT(! lm.isLocked(&t1, kShared, r1));
-
-
-
- // --- test upgrade and release ---
-
- // acquire a shared, then an exclusive lock on the same ResourceId
- lm.acquire(&t1, kShared, r1);
- ASSERT(lm.isLocked(&t1, kShared, r1));
- lm.acquire(&t1, kExclusive, r1);
- ASSERT(lm.isLocked(&t1, kShared, r1));
- ASSERT(lm.isLocked(&t1, kExclusive, r1));
-
- // release exclusive first, then shared
- lm.release(&t1, kExclusive, r1);
- ASSERT(! lm.isLocked(&t1, kExclusive, r1));
- ASSERT(lm.isLocked(&t1, kShared, r1));
- lm.release(&t1, kShared, r1);
- ASSERT(! lm.isLocked(&t1, kShared, r1));
-
- // release shared first, then exclusive
- lm.acquire(&t1, kShared, r1);
- lm.acquire(&t1, kExclusive, r1);
- lm.release(&t1, kShared, r1);
- ASSERT(! lm.isLocked(&t1, kShared, r1));
- ASSERT(lm.isLocked(&t1, kExclusive, r1));
- lm.release(&t1, kExclusive, r1);
- ASSERT(! lm.isLocked(&t1, kExclusive, r1));
-}
-
-TEST(LockManagerTest, TxConflict) {
- LockManager lm;
- ClientTransaction t1(&lm, 1);
- ClientTransaction t2(&lm, 2);
-
- // no conflicts with shared locks on same/different objects
-
- t1.acquire(kShared, 1, ACQUIRED);
- t2.acquire(kShared, 2, ACQUIRED);
- t1.acquire(kShared, 2, ACQUIRED);
- t2.acquire(kShared, 1, ACQUIRED);
-
- t1.release(kShared, 1);
- t1.release(kShared, 2);
- t2.release(kShared, 1);
- t2.release(kShared, 2);
-
-
- // no conflicts with exclusive locks on different objects
- t1.acquire(kExclusive, 1, ACQUIRED);
- t2.acquire(kExclusive, 2, ACQUIRED);
- t1.release(kExclusive, 1);
- t2.release(kExclusive, 2);
-
-
- // shared then exclusive conflict
- t1.acquire(kShared, 1, ACQUIRED);
- // t2's request is incompatible with t1's lock, so it should block
- t2.acquire(kExclusive, 1, BLOCKED);
- t1.release(kShared, 1);
- t2.wakened(); // with t1's lock released, t2 should wake
- t2.release(kExclusive, 1);
-
- // exclusive then shared conflict
- t1.acquire(kExclusive, 1, ACQUIRED);
- t2.acquire(kShared, 1, BLOCKED);
- t1.release(kExclusive, 1);
- t2.wakened();
- t2.release(kShared, 1);
-
- // exclusive then exclusive conflict
- t1.acquire(kExclusive, 1, ACQUIRED);
- t2.acquire(kExclusive, 1, BLOCKED);
- t1.release(kExclusive, 1);
- t2.wakened();
- t2.release(kExclusive, 1);
-
- t1.quit();
- t2.quit();
-}
-
-TEST(LockManagerTest, TxDeadlock) {
- LockManager lm(LockManager::kPolicyReadersFirst);
- ClientTransaction t1(&lm, 1);
- ClientTransaction t2(&lm, 2);
-
- ClientTransaction a1(&lm, 4);
- ClientTransaction a2(&lm, 5);
- ClientTransaction a3(&lm, 6);
- ClientTransaction a4(&lm, 7);
- ClientTransaction a5(&lm, 8);
-
- // simple deadlock test 1
- t1.acquire(kShared, 1, ACQUIRED);
- a1.acquire(kShared, 2, ACQUIRED);
- t1.acquire(kExclusive, 2, BLOCKED);
- // a1's request would form a dependency cycle, so it should abort
- a1.acquire(kExclusive, 1, ABORTED);
- t1.wakened(); // with a1's locks released, t1 should wake
- t1.release(kExclusive, 2);
- t1.release(kShared, 1);
-
- // simple deadlock test 2
- a2.acquire(kShared, 1, ACQUIRED);
- t2.acquire(kShared, 2, ACQUIRED);
- t2.acquire(kExclusive, 1, BLOCKED);
- // a2's request would form a dependency cycle, so it should abort
- a2.acquire(kExclusive, 2, ABORTED);
- t2.wakened(); // with a2's locks released, t2 should wake
- t2.release(kExclusive, 1);
- t2.release(kShared, 2);
-
- // three way deadlock
- t1.acquire(kShared, 1, ACQUIRED);
- t2.acquire(kShared, 2, ACQUIRED);
- a3.acquire(kShared, 3, ACQUIRED);
- t1.acquire(kExclusive, 2, BLOCKED);
- t2.acquire(kExclusive, 3, BLOCKED);
- // a3's request would form a dependency cycle, so it should abort
- a3.acquire(kExclusive, 1, ABORTED);
- t2.wakened(); // with a3's lock release, t2 should wake
- t2.release(kShared, 2);
- t1.wakened(); // with t2's locks released, t1 should wake
- t2.release(kExclusive, 3);
- t1.release(kShared, 1);
- t1.release(kExclusive, 2);
-
- // test for phantom deadlocks
- t1.acquire(kShared, 1, ACQUIRED);
- t2.acquire(kExclusive, 1, BLOCKED);
- t1.release(kShared, 1);
- t2.wakened();
- // at this point, t2 should no longer be waiting for t1
- // so it should be OK for t1 to wait for t2
- t1.acquire(kShared, 1, BLOCKED);
- t2.release(kExclusive, 1);
- t1.wakened();
- t1.release(kShared, 1);
-
- // test for missing deadlocks
- t1.acquire(kShared, 1, ACQUIRED);
- t2.acquire(kShared, 2, ACQUIRED); // setup for deadlock with a4
- t2.acquire(kExclusive, 1, BLOCKED); // block on t1
- // after this, because readers first policy, t2 should
- // also be waiting on a4.
- a4.acquire(kShared, 1, ACQUIRED);
- // after this, t2 should be waiting ONLY on a4
- t1.release(kShared, 1);
- // So a4 should not be allowed to wait on t2's resource.
- a4.acquire(kExclusive, 2, ABORTED);
- t2.wakened();
- t2.release(kShared, 2);
- t2.release(kExclusive, 1);
-
- // test for missing deadlocks: due to downgrades
- a5.acquire(kExclusive, 1, ACQUIRED);
- a5.acquire(kShared, 1, ACQUIRED);
- t2.acquire(kShared, 2, ACQUIRED); // setup for deadlock with a5
- t2.acquire(kExclusive, 1, BLOCKED); // block on a5
- a5.release(kExclusive, 1);
- // at this point, t2 should still be blocked on a5's downgraded lock
- // So a5 should not be allowed to wait on t2's resource.
- a5.acquire(kExclusive, 2, ABORTED);
- t2.wakened();
- t2.release(kShared, 2);
- t2.release(kExclusive, 1);
-
- t1.quit();
- t2.quit();
-}
-
-TEST(LockManagerTest, TxDowngrade) {
- LockManager lm;
- ClientTransaction t1(&lm, 1);
- ClientTransaction t2(&lm, 2);
-
- t1.acquire(kExclusive, 1, ACQUIRED);
- t1.acquire(kShared, 1, ACQUIRED); // downgrade
- // t1 still has exclusive on resource 1, so t2 must wait
- t2.acquire(kShared, 1, BLOCKED);
- t1.release(kExclusive, 1);
- t2.wakened(); // with the exclusive lock released, t2 wakes
- t1.release(kShared, 1);
- t2.release(kShared, 1);
-
- t1.acquire(kExclusive, 1, ACQUIRED);
- t1.acquire(kShared, 1, ACQUIRED); // downgrade
- // t1 still has exclusive on resource 1, so t2 must wait
- t2.acquire(kShared, 1, BLOCKED);
- t1.release(kShared, 1);
- // with t1 still holding exclusive on resource 1, t2 still blocked
- t1.release(kExclusive, 1);
- t2.wakened(); // with the exclusive lock released, t2 wakes
- t2.release(kShared, 1);
-
- t1.acquire(kExclusive, 1, ACQUIRED);
- // t1 has exclusive on resource 1, so t2 must wait
- t2.acquire(kShared, 1, BLOCKED);
- // even though t2 is waiting for resource 1, t1 can still use it shared,
- // because it already owns exclusive lock and can't block on itself
- t1.acquire(kShared, 1, ACQUIRED);
- t1.release(kExclusive, 1);
- t2.wakened(); // with the exclusive lock released, t2 wakes
- t1.release(kShared, 1);
- t2.release(kShared, 1);
-
- // t2 acquires exclusive during t1's downgrade
- t1.acquire(kExclusive, 1, ACQUIRED);
- t1.acquire(kShared, 1, ACQUIRED);
- t2.acquire(kExclusive, 1, BLOCKED);
- t1.release(kExclusive, 1);
- t1.release(kShared, 1);
- t2.wakened();
- t2.release(kExclusive, 1);
-
- t1.acquire(kExclusive, 1, ACQUIRED);
- t2.acquire(kExclusive, 1, BLOCKED);
- t1.acquire(kShared, 1, ACQUIRED);
- t1.release(kExclusive, 1);
- t1.release(kShared, 1);
- t2.wakened();
- t2.release(kExclusive, 1);
-
- t1.quit();
- t2.quit();
-}
-
-TEST(LockManagerTest, TxUpgrade) {
- LockManager lm(LockManager::kPolicyReadersFirst);
- ClientTransaction t1(&lm, 1);
- ClientTransaction t2(&lm, 2);
- ClientTransaction t3(&lm, 3);
-
- ClientTransaction a2(&lm, 4);
- ClientTransaction a3(&lm, 5);
-
- // test upgrade succeeds, blocks subsequent reads
- t1.acquire(kShared, 1, ACQUIRED);
- t1.acquire(kExclusive, 1, ACQUIRED); // upgrade
- t2.acquire(kShared, 1, BLOCKED);
- t1.release(kExclusive, 1);
- t2.wakened();
- t1.release(kShared, 1);
- t2.release(kShared, 1);
-
- // test upgrade blocks, then wakes
- t1.acquire(kShared, 1, ACQUIRED);
- t2.acquire(kShared, 1, ACQUIRED);
- // t1 can't use resource 1 exclusively yet, because t2 is using it
- t1.acquire(kExclusive, 1, BLOCKED);
- t2.release(kShared, 1);
- t1.wakened(); // with t2's shared lock released, t1 wakes
- t1.release(kExclusive, 1);
- t1.release(kShared, 1);
-
- // test upgrade blocks on several, then wakes
- t1.acquire(kShared, 1, ACQUIRED);
- t2.acquire(kShared, 1, ACQUIRED);
- // t1 can't use resource 1 exclusively yet, because t2 is using it
- t1.acquire(kExclusive, 1, BLOCKED);
- t3.acquire(kShared, 1, ACQUIRED); // additional blocker
- t2.release(kShared, 1); // t1 still blocked
- t3.release(kShared, 1);
- t1.wakened(); // with t3's shared lock released, t1 wakes
- t1.release(kExclusive, 1);
- t1.release(kShared, 1);
-
- // failure to upgrade
- t1.acquire(kShared, 1, ACQUIRED);
- a2.acquire(kShared, 1, ACQUIRED);
- t1.acquire(kExclusive, 1, BLOCKED);
- a2.acquire(kExclusive, 1, ABORTED);
- // with a2's abort, t1 can wake
- t1.wakened();
- t1.release(kShared, 1);
- t1.release(kExclusive, 1);
-
- // failure to upgrade
- t1.acquire(kShared, 1, ACQUIRED);
- t2.acquire(kShared, 1, ACQUIRED);
- t1.acquire(kExclusive, 1, BLOCKED);
- a3.acquire(kShared, 1, ACQUIRED);
- t2.release(kShared, 1); // t1 still blocked on a3
- a3.acquire(kExclusive, 1, ABORTED);
-
- t1.quit();
- t2.quit();
- t3.quit();
-}
-#if 0
-TEST(LockManagerTest, TxPolicy) {
-
- {
- // Test FirstComeFirstServe policy
- LockManager lm_first;
- ClientTransaction t1(&lm_first, 1);
- ClientTransaction t2(&lm_first, 2);
- ClientTransaction t3(&lm_first, 3);
- // test1
- t1.acquire(kExclusive, 1, ACQUIRED);
- t2.acquire(kShared, 1, BLOCKED);
- t3.acquire(kExclusive, 1, BLOCKED);
- t1.release(kExclusive, 1);
- // t2 should wake first, because its request came before t3's
- t2.wakened();
- t2.release(kShared, 1);
- t3.wakened();
- t3.release(kExclusive, 1);
-
- // test2
- t1.acquire(kExclusive, 1, ACQUIRED);
- t3.acquire(kExclusive, 1, BLOCKED);
- t2.acquire(kShared, 1, BLOCKED);
- t1.release(kExclusive, 1);
- // t3 should wake first, because its request came before t2's
- t3.wakened();
- t3.release(kExclusive, 1);
- t2.wakened();
- t2.release(kShared, 1);
-
- t1.quit();
- t2.quit();
- t3.quit();
- }
-
- {
- // Test kPolicyReadersFirst
- // shared request are considered read requests
-
- LockManager lm_readers(LockManager::kPolicyReadersFirst);
- ClientTransaction t1(&lm_readers, 1);
- ClientTransaction t2(&lm_readers, 2);
- ClientTransaction t3(&lm_readers, 3);
-
- t1.acquire(kExclusive, 1, ACQUIRED);
- t3.acquire(kExclusive, 1, BLOCKED);
- t2.acquire(kShared, 1, BLOCKED);
- t1.release(kExclusive, 1);
-
- // t2 should wake first, even though t3 came first in time
- // because t2 is a reader and t3 is a writer
- t2.wakened();
- t2.release(kShared, 1);
- t3.wakened();
- t3.release(kExclusive, 1);
-
- t1.quit();
- t2.quit();
- t3.quit();
- }
-
- {
- // Test OLDEST_TX_FIRST policy
- // for now, smaller TxIds are considered older
-
- LockManager lm_oldest(LockManager::kPolicyOldestTxFirst);
- ClientTransaction t1(&lm_oldest, 1);
- ClientTransaction t2(&lm_oldest, 2);
- ClientTransaction t3(&lm_oldest, 3);
-
- // test 1
- t1.acquire(kExclusive, 1, ACQUIRED);
- t3.acquire(kExclusive, 1, BLOCKED);
- t2.acquire(kShared, 1, BLOCKED);
- t1.release(kExclusive, 1);
-
- // t2 should wake first, even though t3 came first in time
- // because t2 is older than t3
- t2.wakened();
- t2.release(kShared, 1);
- t3.wakened();
- t3.release(kExclusive, 1);
-
- // test 2
- t1.acquire(kExclusive, 1, ACQUIRED);
- t2.acquire(kShared, 1, BLOCKED);
- t3.acquire(kExclusive, 1, BLOCKED);
- t1.release(kExclusive, 1);
-
- // t2 should wake first, because it's older than t3
- t2.wakened();
- t2.release(kShared, 1);
- t3.wakened();
- t3.release(kExclusive, 1);
-
- t1.quit();
- t2.quit();
- t3.quit();
- }
-
- {
- LockManager lm_blockers(LockManager::kPolicyBlockersFirst);
- ClientTransaction t1(&lm_blockers, 1);
- ClientTransaction t2(&lm_blockers, 2);
- ClientTransaction t3(&lm_blockers, 3);
- ClientTransaction t4(&lm_blockers, 4);
-
- // BIGGEST_BLOCKER_FIRST policy
-
- // set up t3 as the biggest blocker
- t3.acquire(kExclusive, 2, ACQUIRED);
- t4.acquire(kExclusive, 2, BLOCKED);
-
- // test 1
- t1.acquire(kExclusive, 1, ACQUIRED);
- t3.acquire(kExclusive, 1, BLOCKED);
- t2.acquire(kShared, 1, BLOCKED);
- t1.release(kExclusive, 1);
- // t3 should wake first, because it's a bigger blocker than t2
- t3.wakened();
- t3.release(kExclusive, 1);
- t2.wakened();
- t2.release(kShared, 1);
-
- // test 2
- t1.acquire(kExclusive, 1, ACQUIRED);
- t2.acquire(kShared, 1, BLOCKED);
- t3.acquire(kExclusive, 1, BLOCKED);
- t1.release(kExclusive, 1);
- // t3 should wake first, even though t2 came first,
- // because it's a bigger blocker than t2
- t3.wakened();
- t3.release(kExclusive, 1);
- t2.wakened();
- t2.release(kShared, 1);
-
- t3.release(kExclusive, 2);
- t4.wakened();
- t4.release(kExclusive, 2);
-
- t1.quit();
- t2.quit();
- t3.quit();
- t4.quit();
- }
-}
-
-/*
- * test kPolicyReadersOnly and kPolicyWritersOnly
- */
-TEST(LockManagerTest, TxOnlyPolicies) {
- LockManager lm;
- ClientTransaction t1(&lm, 1);
- ClientTransaction t2(&lm, 2);
- ClientTransaction t3(&lm, 3);
- ClientTransaction t4(&lm, 4);
- ClientTransaction t5(&lm, 5);
- ClientTransaction tp(&lm, 6);
-
- // show kPolicyReadersOnly blocking writers, which
- // awake when policy reverts
- t1.acquire(kShared, 1, ACQUIRED);
- tp.setPolicy(LockManager::kPolicyReadersOnly, ACQUIRED);
- t3.acquire(kExclusive, 2, BLOCKED); // just policy conflict
- t4.acquire(kExclusive, 1, BLOCKED); // both policy & t1
- t5.acquire(kShared, 1, ACQUIRED); // even tho t4
- tp.setPolicy(LockManager::kPolicyReadersFirst, ACQUIRED);
- t3.wakened();
- t3.release(kExclusive, 2);
- t1.release(kShared, 1);
- t5.release(kShared, 1);
- t4.wakened();
- t4.release(kExclusive, 1);
-
- // show WRITERS_ONLY blocking readers, which
- // awake when policy reverts
- t1.acquire(kExclusive, 1, ACQUIRED);
- tp.setPolicy(LockManager::kPolicyWritersOnly, ACQUIRED);
- t3.acquire(kShared, 2, BLOCKED); // just policy conflict
- t4.acquire(kShared, 1, BLOCKED); // both policy & t1
- t1.release(kExclusive, 1);
- t5.acquire(kExclusive, 2, ACQUIRED); // even tho t3
- t5.release(kExclusive, 2);
- tp.setPolicy(LockManager::kPolicyReadersFirst, ACQUIRED);
- t3.wakened();
- t3.release(kShared, 2);
- t4.wakened();
- t4.release(kShared, 1);
-
- // show READERS_ONLY blocked by existing writer
- // but still blocking new writers
- t1.acquire(kExclusive, 1, ACQUIRED);
- tp.setPolicy(LockManager::kPolicyReadersOnly, BLOCKED); // blocked by t1
- t2.acquire(kExclusive, 2, BLOCKED); // just policy conflict
- t3.acquire(kShared, 2, ACQUIRED); // even tho t2
- t3.release(kShared, 2);
- t1.release(kExclusive, 1);
- tp.wakened();
- tp.setPolicy(LockManager::kPolicyReadersFirst, ACQUIRED);
- t2.wakened();
- t2.release(kExclusive, 2);
-
- // show WRITERS_ONLY blocked by existing reader
- // but still blocking new readers
- t1.acquire(kShared, 1, ACQUIRED);
- tp.setPolicy(LockManager::kPolicyWritersOnly, BLOCKED); // blocked by t1
- t2.acquire(kShared, 2, BLOCKED); // just policy conflict
- t1.release(kShared, 1);
- tp.wakened();
- tp.setPolicy(LockManager::kPolicyReadersFirst, ACQUIRED);
- t2.wakened();
- t2.release(kShared, 2);
-
- t1.quit();
- t2.quit();
- t3.quit();
- t4.quit();
- t5.quit();
- tp.quit();
-}
-#endif
-TEST(LockManagerTest, TxShutdown) {
- LockManager lm;
- ClientTransaction t1(&lm, 1);
- ClientTransaction t2(&lm, 2);
-
- t1.acquire(kShared, 1, ACQUIRED);
- lm.shutdown(3000);
-
- // t1 can still do work while quiescing
- t1.release(kShared, 1);
- t1.acquire(kShared, 2, ACQUIRED);
-#ifdef TRANSACTION_REGISTRATION
- // t2 is new and should be refused
- t2.acquire(kShared, 3, ABORTED);
-#else
- t2.quit();
-#endif
- // after the quiescing period, t1's request should be refused
- sleepsecs(3);
- t1.acquire(kShared, 4, ABORTED);
-}
-}