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