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 2 threads are running a lock acquire, release, retry sequence.
48 // There is a race between the acquire and the release with 2 threads.
49 // If thread 1 acquires a lock, and thread 2 tries to acquire the same lock and
50 // fails, thread 1 may release its lock and retry pending lock requests BEFORE
51 // thread 2 adds itself to the pending lock requests. If this happens, then
52 // thread 2 will HANG until its lock timer expires even when the lock it is
53 // waiting for is FREE.
54
55 // This test exposes this problem as a test hang. If the race is fixed, then
56 // the test runs to completion.
57
58 namespace toku {
59
start_before_pending(void)60 static void start_before_pending(void) { usleep(10000); }
61
run_locker(locktree * lt,TXNID txnid,const DBT * key,pthread_barrier_t * b)62 static void run_locker(locktree *lt,
63 TXNID txnid,
64 const DBT *key,
65 pthread_barrier_t *b) {
66 for (int i = 0; i < 100000; i++) {
67 int r;
68 r = pthread_barrier_wait(b);
69 assert(r == 0 || r == PTHREAD_BARRIER_SERIAL_THREAD);
70
71 lock_request request;
72 request.create();
73 request.set(lt, txnid, key, key, lock_request::type::WRITE, false);
74
75 // if the callback is included, then the race is easy to reproduce.
76 // Otherwise, several test runs may be required before the race
77 // happens.
78 request.set_start_before_pending_test_callback(
79 start_before_pending);
80
81 // try to acquire the lock
82 r = request.start();
83 if (r == DB_LOCK_NOTGRANTED) {
84 // wait for the lock to be granted
85 r = request.wait(1000 * 1000);
86 }
87
88 if (r == 0) {
89 // release the lock
90 range_buffer buffer;
91 buffer.create();
92 buffer.append(key, key);
93 lt->release_locks(txnid, &buffer);
94 buffer.destroy();
95
96 // retry pending lock requests
97 lock_request::retry_all_lock_requests(lt);
98 }
99
100 request.destroy();
101 request.clearmem(0xab);
102
103 toku_pthread_yield();
104 if ((i % 10) == 0)
105 std::cerr << std::this_thread::get_id() << " " << i
106 << std::endl;
107 }
108 }
109
110 } /* namespace toku */
111
main(void)112 int main(void) {
113 toku::locktree lt;
114 DICTIONARY_ID dict_id = {1};
115 lt.create(nullptr, dict_id, toku::dbt_comparator);
116
117 const DBT *one = toku::get_dbt(1);
118
119 const int n_workers = 2;
120 std::thread worker[n_workers];
121 pthread_barrier_t b;
122 int r = pthread_barrier_init(&b, nullptr, n_workers);
123 assert(r == 0);
124 for (int i = 0; i < n_workers; i++) {
125 worker[i] = std::thread(toku::run_locker, <, i, one, &b);
126 }
127 for (int i = 0; i < n_workers; i++) {
128 worker[i].join();
129 }
130 r = pthread_barrier_destroy(&b);
131 assert(r == 0);
132 lt.release_reference();
133 lt.destroy();
134 return 0;
135 }
136