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, &lt, 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