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