blob: 32c1531be68317892d2ec5af9667be80dac1d0d5 [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
Konrad Grochowski16a23a62014-11-13 15:33:38 +010036class Locker : public Runnable {
Roger Meier3fc48192011-12-11 21:05:35 +000037protected:
Konrad Grochowski16a23a62014-11-13 15:33:38 +010038 Locker(boost::shared_ptr<ReadWriteMutex> rwlock, bool writer)
39 : rwlock_(rwlock), writer_(writer), started_(false), gotLock_(false), signaled_(false) {}
Roger Meier3fc48192011-12-11 21:05:35 +000040
41public:
Konrad Grochowski16a23a62014-11-13 15:33:38 +010042 virtual void run() {
Roger Meier3fc48192011-12-11 21:05:35 +000043 started_ = true;
44 if (writer_) {
45 rwlock_->acquireWrite();
46 } else {
47 rwlock_->acquireRead();
48 }
49 gotLock_ = true;
50 while (!signaled_) {
51 usleep(5000);
52 }
53 rwlock_->release();
54 }
55
56 bool started() const { return started_; }
57 bool gotLock() const { return gotLock_; }
58 void signal() { signaled_ = true; }
59
60protected:
Roger Meier611f90c2011-12-11 22:08:51 +000061 boost::shared_ptr<ReadWriteMutex> rwlock_;
Roger Meier3fc48192011-12-11 21:05:35 +000062 bool writer_;
63 volatile bool started_;
64 volatile bool gotLock_;
65 volatile bool signaled_;
66};
67
Konrad Grochowski16a23a62014-11-13 15:33:38 +010068class Reader : public Locker {
Roger Meier3fc48192011-12-11 21:05:35 +000069public:
Konrad Grochowski16a23a62014-11-13 15:33:38 +010070 Reader(boost::shared_ptr<ReadWriteMutex> rwlock) : Locker(rwlock, false) {}
Roger Meier3fc48192011-12-11 21:05:35 +000071};
72
Konrad Grochowski16a23a62014-11-13 15:33:38 +010073class Writer : public Locker {
Roger Meier3fc48192011-12-11 21:05:35 +000074public:
Konrad Grochowski16a23a62014-11-13 15:33:38 +010075 Writer(boost::shared_ptr<ReadWriteMutex> rwlock) : Locker(rwlock, true) {}
Roger Meier3fc48192011-12-11 21:05:35 +000076};
77
Konrad Grochowski16a23a62014-11-13 15:33:38 +010078void test_starve(PosixThreadFactory::POLICY policy) {
Roger Meier3fc48192011-12-11 21:05:35 +000079 // the man pages for pthread_wrlock_rdlock suggest that any OS guarantee about
80 // writer starvation may be influenced by the scheduling policy, so let's try
81 // all 3 policies to see if any of them work.
82 PosixThreadFactory factory(policy);
83 factory.setDetached(false);
84
Roger Meier611f90c2011-12-11 22:08:51 +000085 boost::shared_ptr<ReadWriteMutex> rwlock(new NoStarveReadWriteMutex());
Roger Meier3fc48192011-12-11 21:05:35 +000086
Roger Meier611f90c2011-12-11 22:08:51 +000087 boost::shared_ptr<Reader> reader1(new Reader(rwlock));
88 boost::shared_ptr<Reader> reader2(new Reader(rwlock));
89 boost::shared_ptr<Writer> writer(new Writer(rwlock));
Roger Meier3fc48192011-12-11 21:05:35 +000090
Roger Meier611f90c2011-12-11 22:08:51 +000091 boost::shared_ptr<Thread> treader1 = factory.newThread(reader1);
92 boost::shared_ptr<Thread> treader2 = factory.newThread(reader2);
93 boost::shared_ptr<Thread> twriter = factory.newThread(writer);
Roger Meier3fc48192011-12-11 21:05:35 +000094
95 // launch a reader and make sure he has the lock
96 treader1->start();
97 while (!reader1->gotLock()) {
98 usleep(2000);
99 }
100
101 // launch a writer and make sure he's blocked on the lock
102 twriter->start();
103 while (!writer->started()) {
104 usleep(2000);
105 }
106 // tricky part... we can never be 100% sure that the writer is actually
107 // blocked on the lock, but we can pretty reasonably sure because we know
108 // he just executed the line immediately before getting the lock, and
109 // we'll wait a full second for him to get on it.
110 sleep(1);
111
112 // launch a second reader... if the RWMutex guarantees that writers won't
113 // starve, this reader should not be able to acquire the lock until the writer
114 // has acquired and released it.
115 treader2->start();
116 while (!reader2->started()) {
117 usleep(2000);
118 }
119 // again... can't be 100% sure the reader is waiting on (or has) the lock
120 // but we can be close.
121 sleep(1);
122
123 // tell reader 1 to let go of the lock
124 reader1->signal();
125
126 // wait for someone to get the lock
127 while (!reader2->gotLock() && !writer->gotLock()) {
128 usleep(2000);
129 }
130
131 // the test succeeded if the WRITER got the lock.
132 bool success = writer->gotLock();
133
134 // tell everyone we're done and wait for them to finish
135 reader2->signal();
136 writer->signal();
137 treader1->join();
138 treader2->join();
139 twriter->join();
140
141 // make sure it worked.
142 BOOST_CHECK_MESSAGE(success, "writer is starving");
143}
144
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100145BOOST_AUTO_TEST_SUITE(RWMutexStarveTest)
Roger Meier3fc48192011-12-11 21:05:35 +0000146
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100147BOOST_AUTO_TEST_CASE(test_starve_other) {
Roger Meier3fc48192011-12-11 21:05:35 +0000148 test_starve(PosixThreadFactory::OTHER);
149}
150
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100151BOOST_AUTO_TEST_CASE(test_starve_rr) {
Roger Meier3fc48192011-12-11 21:05:35 +0000152 test_starve(PosixThreadFactory::ROUND_ROBIN);
153}
154
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100155BOOST_AUTO_TEST_CASE(test_starve_fifo) {
Roger Meier3fc48192011-12-11 21:05:35 +0000156 test_starve(PosixThreadFactory::FIFO);
157}
158
159BOOST_AUTO_TEST_SUITE_END()