blob: e6cccf2daa59073940a119830d6ee014ba96d88a [file] [log] [blame]
Roger Meier3fc48192011-12-11 21:05:35 +00001/*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19
20#include <iostream>
21#include <unistd.h>
22
23#include <boost/shared_ptr.hpp>
24#include <boost/test/unit_test.hpp>
25
Roger Meier49ff8b12012-04-13 09:12:31 +000026#include "thrift/concurrency/Mutex.h"
27#include "thrift/concurrency/PosixThreadFactory.h"
Roger Meier3fc48192011-12-11 21:05:35 +000028
29using boost::shared_ptr;
30using boost::unit_test::test_suite;
31using boost::unit_test::framework::master_test_suite;
32
33using namespace apache::thrift::concurrency;
34using namespace std;
35
36class Locker : public Runnable
37{
38protected:
Roger Meier611f90c2011-12-11 22:08:51 +000039 Locker(boost::shared_ptr<ReadWriteMutex> rwlock, bool writer) :
Roger Meier3fc48192011-12-11 21:05:35 +000040 rwlock_(rwlock), writer_(writer),
41 started_(false), gotLock_(false), signaled_(false) { }
42
43public:
44 virtual void run()
45 {
46 started_ = true;
47 if (writer_) {
48 rwlock_->acquireWrite();
49 } else {
50 rwlock_->acquireRead();
51 }
52 gotLock_ = true;
53 while (!signaled_) {
54 usleep(5000);
55 }
56 rwlock_->release();
57 }
58
59 bool started() const { return started_; }
60 bool gotLock() const { return gotLock_; }
61 void signal() { signaled_ = true; }
62
63protected:
Roger Meier611f90c2011-12-11 22:08:51 +000064 boost::shared_ptr<ReadWriteMutex> rwlock_;
Roger Meier3fc48192011-12-11 21:05:35 +000065 bool writer_;
66 volatile bool started_;
67 volatile bool gotLock_;
68 volatile bool signaled_;
69};
70
71class Reader : public Locker
72{
73public:
Roger Meier611f90c2011-12-11 22:08:51 +000074 Reader(boost::shared_ptr<ReadWriteMutex> rwlock) : Locker(rwlock, false) { }
Roger Meier3fc48192011-12-11 21:05:35 +000075};
76
77class Writer : public Locker
78{
79public:
Roger Meier611f90c2011-12-11 22:08:51 +000080 Writer(boost::shared_ptr<ReadWriteMutex> rwlock) : Locker(rwlock, true) { }
Roger Meier3fc48192011-12-11 21:05:35 +000081};
82
83void test_starve(PosixThreadFactory::POLICY policy)
84{
85 // the man pages for pthread_wrlock_rdlock suggest that any OS guarantee about
86 // writer starvation may be influenced by the scheduling policy, so let's try
87 // all 3 policies to see if any of them work.
88 PosixThreadFactory factory(policy);
89 factory.setDetached(false);
90
Roger Meier611f90c2011-12-11 22:08:51 +000091 boost::shared_ptr<ReadWriteMutex> rwlock(new NoStarveReadWriteMutex());
Roger Meier3fc48192011-12-11 21:05:35 +000092
Roger Meier611f90c2011-12-11 22:08:51 +000093 boost::shared_ptr<Reader> reader1(new Reader(rwlock));
94 boost::shared_ptr<Reader> reader2(new Reader(rwlock));
95 boost::shared_ptr<Writer> writer(new Writer(rwlock));
Roger Meier3fc48192011-12-11 21:05:35 +000096
Roger Meier611f90c2011-12-11 22:08:51 +000097 boost::shared_ptr<Thread> treader1 = factory.newThread(reader1);
98 boost::shared_ptr<Thread> treader2 = factory.newThread(reader2);
99 boost::shared_ptr<Thread> twriter = factory.newThread(writer);
Roger Meier3fc48192011-12-11 21:05:35 +0000100
101 // launch a reader and make sure he has the lock
102 treader1->start();
103 while (!reader1->gotLock()) {
104 usleep(2000);
105 }
106
107 // launch a writer and make sure he's blocked on the lock
108 twriter->start();
109 while (!writer->started()) {
110 usleep(2000);
111 }
112 // tricky part... we can never be 100% sure that the writer is actually
113 // blocked on the lock, but we can pretty reasonably sure because we know
114 // he just executed the line immediately before getting the lock, and
115 // we'll wait a full second for him to get on it.
116 sleep(1);
117
118 // launch a second reader... if the RWMutex guarantees that writers won't
119 // starve, this reader should not be able to acquire the lock until the writer
120 // has acquired and released it.
121 treader2->start();
122 while (!reader2->started()) {
123 usleep(2000);
124 }
125 // again... can't be 100% sure the reader is waiting on (or has) the lock
126 // but we can be close.
127 sleep(1);
128
129 // tell reader 1 to let go of the lock
130 reader1->signal();
131
132 // wait for someone to get the lock
133 while (!reader2->gotLock() && !writer->gotLock()) {
134 usleep(2000);
135 }
136
137 // the test succeeded if the WRITER got the lock.
138 bool success = writer->gotLock();
139
140 // tell everyone we're done and wait for them to finish
141 reader2->signal();
142 writer->signal();
143 treader1->join();
144 treader2->join();
145 twriter->join();
146
147 // make sure it worked.
148 BOOST_CHECK_MESSAGE(success, "writer is starving");
149}
150
151BOOST_AUTO_TEST_SUITE( RWMutexStarveTest )
152
153BOOST_AUTO_TEST_CASE( test_starve_other )
154{
155 test_starve(PosixThreadFactory::OTHER);
156}
157
158BOOST_AUTO_TEST_CASE( test_starve_rr )
159{
160 test_starve(PosixThreadFactory::ROUND_ROBIN);
161}
162
163BOOST_AUTO_TEST_CASE( test_starve_fifo )
164{
165 test_starve(PosixThreadFactory::FIFO);
166}
167
168BOOST_AUTO_TEST_SUITE_END()