mirror of
https://github.com/valitydev/thrift.git
synced 2024-11-08 11:13:55 +00:00
611f90cf9e
git-svn-id: https://svn.apache.org/repos/asf/thrift/trunk@1213090 13f79535-47bb-0310-9956-ffa450edef68
169 lines
4.6 KiB
C++
169 lines
4.6 KiB
C++
/*
|
|
* Licensed to the Apache Software Foundation (ASF) under one
|
|
* or more contributor license agreements. See the NOTICE file
|
|
* distributed with this work for additional information
|
|
* regarding copyright ownership. The ASF licenses this file
|
|
* to you under the Apache License, Version 2.0 (the
|
|
* "License"); you may not use this file except in compliance
|
|
* with the License. You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing,
|
|
* software distributed under the License is distributed on an
|
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
* KIND, either express or implied. See the License for the
|
|
* specific language governing permissions and limitations
|
|
* under the License.
|
|
*/
|
|
|
|
#include <iostream>
|
|
#include <unistd.h>
|
|
|
|
#include <boost/shared_ptr.hpp>
|
|
#include <boost/test/unit_test.hpp>
|
|
|
|
#include "concurrency/Mutex.h"
|
|
#include "concurrency/PosixThreadFactory.h"
|
|
|
|
using boost::shared_ptr;
|
|
using boost::unit_test::test_suite;
|
|
using boost::unit_test::framework::master_test_suite;
|
|
|
|
using namespace apache::thrift::concurrency;
|
|
using namespace std;
|
|
|
|
class Locker : public Runnable
|
|
{
|
|
protected:
|
|
Locker(boost::shared_ptr<ReadWriteMutex> rwlock, bool writer) :
|
|
rwlock_(rwlock), writer_(writer),
|
|
started_(false), gotLock_(false), signaled_(false) { }
|
|
|
|
public:
|
|
virtual void run()
|
|
{
|
|
started_ = true;
|
|
if (writer_) {
|
|
rwlock_->acquireWrite();
|
|
} else {
|
|
rwlock_->acquireRead();
|
|
}
|
|
gotLock_ = true;
|
|
while (!signaled_) {
|
|
usleep(5000);
|
|
}
|
|
rwlock_->release();
|
|
}
|
|
|
|
bool started() const { return started_; }
|
|
bool gotLock() const { return gotLock_; }
|
|
void signal() { signaled_ = true; }
|
|
|
|
protected:
|
|
boost::shared_ptr<ReadWriteMutex> rwlock_;
|
|
bool writer_;
|
|
volatile bool started_;
|
|
volatile bool gotLock_;
|
|
volatile bool signaled_;
|
|
};
|
|
|
|
class Reader : public Locker
|
|
{
|
|
public:
|
|
Reader(boost::shared_ptr<ReadWriteMutex> rwlock) : Locker(rwlock, false) { }
|
|
};
|
|
|
|
class Writer : public Locker
|
|
{
|
|
public:
|
|
Writer(boost::shared_ptr<ReadWriteMutex> rwlock) : Locker(rwlock, true) { }
|
|
};
|
|
|
|
void test_starve(PosixThreadFactory::POLICY policy)
|
|
{
|
|
// the man pages for pthread_wrlock_rdlock suggest that any OS guarantee about
|
|
// writer starvation may be influenced by the scheduling policy, so let's try
|
|
// all 3 policies to see if any of them work.
|
|
PosixThreadFactory factory(policy);
|
|
factory.setDetached(false);
|
|
|
|
boost::shared_ptr<ReadWriteMutex> rwlock(new NoStarveReadWriteMutex());
|
|
|
|
boost::shared_ptr<Reader> reader1(new Reader(rwlock));
|
|
boost::shared_ptr<Reader> reader2(new Reader(rwlock));
|
|
boost::shared_ptr<Writer> writer(new Writer(rwlock));
|
|
|
|
boost::shared_ptr<Thread> treader1 = factory.newThread(reader1);
|
|
boost::shared_ptr<Thread> treader2 = factory.newThread(reader2);
|
|
boost::shared_ptr<Thread> twriter = factory.newThread(writer);
|
|
|
|
// launch a reader and make sure he has the lock
|
|
treader1->start();
|
|
while (!reader1->gotLock()) {
|
|
usleep(2000);
|
|
}
|
|
|
|
// launch a writer and make sure he's blocked on the lock
|
|
twriter->start();
|
|
while (!writer->started()) {
|
|
usleep(2000);
|
|
}
|
|
// tricky part... we can never be 100% sure that the writer is actually
|
|
// blocked on the lock, but we can pretty reasonably sure because we know
|
|
// he just executed the line immediately before getting the lock, and
|
|
// we'll wait a full second for him to get on it.
|
|
sleep(1);
|
|
|
|
// launch a second reader... if the RWMutex guarantees that writers won't
|
|
// starve, this reader should not be able to acquire the lock until the writer
|
|
// has acquired and released it.
|
|
treader2->start();
|
|
while (!reader2->started()) {
|
|
usleep(2000);
|
|
}
|
|
// again... can't be 100% sure the reader is waiting on (or has) the lock
|
|
// but we can be close.
|
|
sleep(1);
|
|
|
|
// tell reader 1 to let go of the lock
|
|
reader1->signal();
|
|
|
|
// wait for someone to get the lock
|
|
while (!reader2->gotLock() && !writer->gotLock()) {
|
|
usleep(2000);
|
|
}
|
|
|
|
// the test succeeded if the WRITER got the lock.
|
|
bool success = writer->gotLock();
|
|
|
|
// tell everyone we're done and wait for them to finish
|
|
reader2->signal();
|
|
writer->signal();
|
|
treader1->join();
|
|
treader2->join();
|
|
twriter->join();
|
|
|
|
// make sure it worked.
|
|
BOOST_CHECK_MESSAGE(success, "writer is starving");
|
|
}
|
|
|
|
BOOST_AUTO_TEST_SUITE( RWMutexStarveTest )
|
|
|
|
BOOST_AUTO_TEST_CASE( test_starve_other )
|
|
{
|
|
test_starve(PosixThreadFactory::OTHER);
|
|
}
|
|
|
|
BOOST_AUTO_TEST_CASE( test_starve_rr )
|
|
{
|
|
test_starve(PosixThreadFactory::ROUND_ROBIN);
|
|
}
|
|
|
|
BOOST_AUTO_TEST_CASE( test_starve_fifo )
|
|
{
|
|
test_starve(PosixThreadFactory::FIFO);
|
|
}
|
|
|
|
BOOST_AUTO_TEST_SUITE_END()
|