1 /* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 // vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
3 #ident "$Id$"
4 /*======
5 This file is part of PerconaFT.
6 
7 
8 Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
9 
10     PerconaFT is free software: you can redistribute it and/or modify
11     it under the terms of the GNU General Public License, version 2,
12     as published by the Free Software Foundation.
13 
14     PerconaFT is distributed in the hope that it will be useful,
15     but WITHOUT ANY WARRANTY; without even the implied warranty of
16     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17     GNU General Public License for more details.
18 
19     You should have received a copy of the GNU General Public License
20     along with PerconaFT.  If not, see <http://www.gnu.org/licenses/>.
21 
22 ----------------------------------------
23 
24     PerconaFT is free software: you can redistribute it and/or modify
25     it under the terms of the GNU Affero General Public License, version 3,
26     as published by the Free Software Foundation.
27 
28     PerconaFT is distributed in the hope that it will be useful,
29     but WITHOUT ANY WARRANTY; without even the implied warranty of
30     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
31     GNU Affero General Public License for more details.
32 
33     You should have received a copy of the GNU Affero General Public License
34     along with PerconaFT.  If not, see <http://www.gnu.org/licenses/>.
35 ======= */
36 
37 #ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
38 
39 #include <errno.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #include <sys/time.h>
43 #include <sys/types.h>
44 
45 #include <toku_portability.h>
46 #include <toku_assert.h>
47 #include <portability/toku_pthread.h>
48 #include <portability/toku_time.h>
49 #include <util/frwlock.h>
50 #include <util/rwlock.h>
51 #include "rwlock_condvar.h"
52 
53 // We need to manually intialize partitioned counters so that the
54 // ones automatically incremented by the frwlock get handled properly.
55 #include <util/partitioned_counter.h>
56 
57 toku_mutex_t mutex;
58 toku::frwlock w;
59 
grab_write_lock(bool expensive)60 static void grab_write_lock(bool expensive) {
61     toku_mutex_lock(&mutex);
62     w.write_lock(expensive);
63     toku_mutex_unlock(&mutex);
64 }
65 
release_write_lock(void)66 static void release_write_lock(void) {
67     toku_mutex_lock(&mutex);
68     w.write_unlock();
69     toku_mutex_unlock(&mutex);
70 }
71 
grab_read_lock(void)72 static void grab_read_lock(void) {
73     toku_mutex_lock(&mutex);
74     w.read_lock();
75     toku_mutex_unlock(&mutex);
76 }
77 
release_read_lock(void)78 static void release_read_lock(void) {
79     toku_mutex_lock(&mutex);
80     w.read_unlock();
81     toku_mutex_unlock(&mutex);
82 }
83 
do_cheap_wait(void * arg)84 static void *do_cheap_wait(void *arg) {
85     grab_write_lock(false);
86     release_write_lock();
87     return arg;
88 }
89 
do_expensive_wait(void * arg)90 static void *do_expensive_wait(void *arg) {
91     grab_write_lock(true);
92     release_write_lock();
93     return arg;
94 }
95 
do_read_wait(void * arg)96 static void *do_read_wait(void *arg) {
97     grab_read_lock();
98     release_read_lock();
99     return arg;
100 }
101 
launch_cheap_waiter(void)102 static void launch_cheap_waiter(void) {
103     toku_pthread_t tid;
104     int r = toku_pthread_create(
105         toku_uninstrumented, &tid, nullptr, do_cheap_wait, nullptr);
106     assert_zero(r);
107     toku_pthread_detach(tid);
108     sleep(1);
109 }
110 
launch_expensive_waiter(void)111 static void launch_expensive_waiter(void) {
112     toku_pthread_t tid;
113     int r = toku_pthread_create(
114         toku_uninstrumented, &tid, nullptr, do_expensive_wait, nullptr);
115     assert_zero(r);
116     toku_pthread_detach(tid);
117     sleep(1);
118 }
119 
launch_reader(void)120 static void launch_reader(void) {
121     toku_pthread_t tid;
122     int r = toku_pthread_create(
123         toku_uninstrumented, &tid, nullptr, do_read_wait, nullptr);
124     assert_zero(r);
125     toku_pthread_detach(tid);
126     sleep(1);
127 }
128 
locks_are_expensive(void)129 static bool locks_are_expensive(void) {
130     toku_mutex_lock(&mutex);
131     assert(w.write_lock_is_expensive() == w.read_lock_is_expensive());
132     bool is_expensive = w.write_lock_is_expensive();
133     toku_mutex_unlock(&mutex);
134     return is_expensive;
135 }
136 
test_write_cheapness(void)137 static void test_write_cheapness(void) {
138     toku_mutex_init(toku_uninstrumented, &mutex, nullptr);
139     w.init(&mutex);
140 
141     // single expensive write lock
142     grab_write_lock(true);
143     assert(locks_are_expensive());
144     release_write_lock();
145     assert(!locks_are_expensive());
146 
147     // single cheap write lock
148     grab_write_lock(false);
149     assert(!locks_are_expensive());
150     release_write_lock();
151     assert(!locks_are_expensive());
152 
153     // multiple read locks
154     grab_read_lock();
155     assert(!locks_are_expensive());
156     grab_read_lock();
157     grab_read_lock();
158     assert(!locks_are_expensive());
159     release_read_lock();
160     release_read_lock();
161     release_read_lock();
162     assert(!locks_are_expensive());
163 
164     // expensive write lock and cheap writers waiting
165     grab_write_lock(true);
166     launch_cheap_waiter();
167     assert(locks_are_expensive());
168     launch_cheap_waiter();
169     launch_cheap_waiter();
170     assert(locks_are_expensive());
171     release_write_lock();
172     sleep(1);
173     assert(!locks_are_expensive());
174 
175     // cheap write lock and expensive writer waiter
176     grab_write_lock(false);
177     launch_expensive_waiter();
178     assert(locks_are_expensive());
179     release_write_lock();
180     sleep(1);
181 
182     // expensive write lock and expensive waiter
183     grab_write_lock(true);
184     launch_expensive_waiter();
185     assert(locks_are_expensive());
186     release_write_lock();
187     sleep(1);
188 
189     // cheap write lock and cheap waiter
190     grab_write_lock(false);
191     launch_cheap_waiter();
192     assert(!locks_are_expensive());
193     release_write_lock();
194     sleep(1);
195 
196     // read lock held and cheap waiter
197     grab_read_lock();
198     launch_cheap_waiter();
199     assert(!locks_are_expensive());
200     // add expensive waiter
201     launch_expensive_waiter();
202     assert(locks_are_expensive());
203     release_read_lock();
204     sleep(1);
205 
206     // read lock held and expensive waiter
207     grab_read_lock();
208     launch_expensive_waiter();
209     assert(locks_are_expensive());
210     // add expensive waiter
211     launch_cheap_waiter();
212     assert(locks_are_expensive());
213     release_read_lock();
214     sleep(1);
215 
216     // cheap write lock held and waiting read
217     grab_write_lock(false);
218     launch_reader();
219     assert(!locks_are_expensive());
220     launch_expensive_waiter();
221     toku_mutex_lock(&mutex);
222     assert(w.write_lock_is_expensive());
223     // tricky case here, because we have a launched reader
224     // that should be in the queue, a new read lock
225     // should piggy back off that
226     assert(!w.read_lock_is_expensive());
227     toku_mutex_unlock(&mutex);
228     release_write_lock();
229     sleep(1);
230 
231     // expensive write lock held and waiting read
232     grab_write_lock(true);
233     launch_reader();
234     assert(locks_are_expensive());
235     launch_cheap_waiter();
236     assert(locks_are_expensive());
237     release_write_lock();
238     sleep(1);
239 
240     w.deinit();
241     toku_mutex_destroy(&mutex);
242 }
243 
main(int UU (argc),const char * UU (argv[]))244 int main (int UU(argc), const char* UU(argv[])) {
245     // Ultra ugly. We manually init/destroy partitioned counters
246     // and context because normally toku_ft_layer_init() does that
247     // for us, but we don't want to initialize everything.
248     partitioned_counters_init();
249     toku_context_status_init();
250     test_write_cheapness();
251     toku_context_status_destroy();
252     partitioned_counters_destroy();
253     return 0;
254 }
255