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 \
38 "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
39
40 #include "lock_request.h"
41 #include <pthread.h>
42 #include <iostream>
43 #include <thread>
44 #include "locktree.h"
45 #include "test.h"
46
47 // Suppose that 3 threads are running a lock acquire, release, retry sequence.
48 // There is a race in the retry algorithm with 2 threads running lock retry
49 // simultaneously. The first thread to run retry sets a flag that will cause
50 // the second thread to skip the lock retries. If the first thread progressed
51 // past the contended lock, then the second threa will HANG until its lock timer
52 // pops, even when the contended lock is no longer held.
53
54 // This test exposes this problem as a test hang. The group retry algorithm
55 // fixes the race in the lock request retry algorihm and this test should no
56 // longer hang.
57
58 namespace toku {
59
60 // use 1000 when after_retry_all is implemented, otherwise use 100000
61 static const int n_tests = 1000; // 100000;
62
after_retry_all(void)63 static void after_retry_all(void) { usleep(10000); }
64
run_locker(locktree * lt,TXNID txnid,const DBT * key,pthread_barrier_t * b)65 static void run_locker(locktree *lt,
66 TXNID txnid,
67 const DBT *key,
68 pthread_barrier_t *b) {
69 for (int i = 0; i < n_tests; i++) {
70 int r;
71 r = pthread_barrier_wait(b);
72 assert(r == 0 || r == PTHREAD_BARRIER_SERIAL_THREAD);
73
74 lock_request request;
75 request.create();
76
77 request.set(lt, txnid, key, key, lock_request::type::WRITE, false);
78
79 // try to acquire the lock
80 r = request.start();
81 if (r == DB_LOCK_NOTGRANTED) {
82 // wait for the lock to be granted
83 r = request.wait(1000 * 1000);
84 }
85
86 if (r == 0) {
87 // release the lock
88 range_buffer buffer;
89 buffer.create();
90 buffer.append(key, key);
91 lt->release_locks(txnid, &buffer);
92 buffer.destroy();
93
94 // retry pending lock requests
95 lock_request::retry_all_lock_requests(lt, after_retry_all);
96 }
97
98 request.destroy();
99 request.clearmem(0xab);
100
101 toku_pthread_yield();
102 if ((i % 10) == 0)
103 std::cerr << std::this_thread::get_id() << " " << i
104 << std::endl;
105 }
106 }
107
108 } /* namespace toku */
109
main(void)110 int main(void) {
111 toku::locktree lt;
112 DICTIONARY_ID dict_id = {1};
113 lt.create(nullptr, dict_id, toku::dbt_comparator);
114
115 const DBT *one = toku::get_dbt(1);
116
117 const int n_workers = 3;
118 std::thread worker[n_workers];
119 pthread_barrier_t b;
120 int r = pthread_barrier_init(&b, nullptr, n_workers);
121 assert(r == 0);
122 for (int i = 0; i < n_workers; i++) {
123 worker[i] = std::thread(toku::run_locker, <, i, one, &b);
124 }
125 for (int i = 0; i < n_workers; i++) {
126 worker[i].join();
127 }
128 r = pthread_barrier_destroy(&b);
129 assert(r == 0);
130 lt.release_reference();
131 lt.destroy();
132 return 0;
133 }
134