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 <stdio.h>
40 #include "locktree.h"
41 #include "test.h"
42
43 // One client locks 1,2,3...
44 // The other client locks -1,-2,-3...
45 // Eventually lock escalation runs.
46
47 using namespace toku;
48
49 static int verbose = 0;
50 static int killed = 0;
51
locktree_release_lock(locktree * lt,TXNID txn_id,int64_t left_k,int64_t right_k)52 static void locktree_release_lock(locktree *lt, TXNID txn_id, int64_t left_k, int64_t right_k) {
53 range_buffer buffer;
54 buffer.create();
55 DBT left; toku_fill_dbt(&left, &left_k, sizeof left_k);
56 DBT right; toku_fill_dbt(&right, &right_k, sizeof right_k);
57 buffer.append(&left, &right);
58 lt->release_locks(txn_id, &buffer);
59 buffer.destroy();
60 }
61
62 // grab a write range lock on int64 keys bounded by left_k and right_k
locktree_write_lock(locktree * lt,TXNID txn_id,int64_t left_k,int64_t right_k,bool big_txn)63 static int locktree_write_lock(locktree *lt, TXNID txn_id, int64_t left_k, int64_t right_k, bool big_txn) {
64 DBT left; toku_fill_dbt(&left, &left_k, sizeof left_k);
65 DBT right; toku_fill_dbt(&right, &right_k, sizeof right_k);
66 return lt->acquire_write_lock(txn_id, &left, &right, nullptr, big_txn);
67 }
68
run_big_txn(locktree_manager * mgr UU (),locktree * lt,TXNID txn_id,int64_t start_i)69 static void run_big_txn(locktree_manager *mgr UU(), locktree *lt, TXNID txn_id, int64_t start_i) {
70 fprintf(stderr, "%u run_big_txn %p %" PRIu64 " %" PRId64 "\n", toku_os_gettid(), lt, txn_id, start_i);
71 int64_t last_i = -1;
72 for (int64_t i = start_i; !killed; i++) {
73 if (0)
74 printf("%u %" PRId64 "\n", toku_os_gettid(), i);
75 uint64_t t_start = toku_current_time_microsec();
76 int r = locktree_write_lock(lt, txn_id, i, i, true);
77 if (r != 0)
78 break;
79 last_i = i;
80 uint64_t t_end = toku_current_time_microsec();
81 uint64_t t_duration = t_end - t_start;
82 if (t_duration > 100000) {
83 printf("%u %s %" PRId64 " %" PRIu64 "\n", toku_os_gettid(), __FUNCTION__, i, t_duration);
84 }
85 toku_pthread_yield();
86 }
87 if (last_i != -1)
88 locktree_release_lock(lt, txn_id, start_i, last_i); // release the range start_i .. last_i
89 }
90
91 struct arg {
92 locktree_manager *mgr;
93 locktree *lt;
94 TXNID txn_id;
95 int64_t start_i;
96 };
97
big_f(void * _arg)98 static void *big_f(void *_arg) {
99 struct arg *arg = (struct arg *) _arg;
100 run_big_txn(arg->mgr, arg->lt, arg->txn_id, arg->start_i);
101 return arg;
102 }
103
e_callback(TXNID txnid,const locktree * lt,const range_buffer & buffer,void * extra)104 static void e_callback(TXNID txnid, const locktree *lt, const range_buffer &buffer, void *extra) {
105 if (verbose)
106 printf("%u %s %" PRIu64 " %p %d %p\n", toku_os_gettid(), __FUNCTION__, txnid, lt, buffer.get_num_ranges(), extra);
107 }
108
get_escalation_count(locktree_manager & mgr)109 static uint64_t get_escalation_count(locktree_manager &mgr) {
110 LTM_STATUS_S ltm_status_test;
111 mgr.get_status(<m_status_test);
112
113 TOKU_ENGINE_STATUS_ROW key_status = NULL;
114 // lookup keyname in status
115 for (int i = 0; ; i++) {
116 TOKU_ENGINE_STATUS_ROW status = <m_status_test.status[i];
117 if (status->keyname == NULL)
118 break;
119 if (strcmp(status->keyname, "LTM_ESCALATION_COUNT") == 0) {
120 key_status = status;
121 break;
122 }
123 }
124 assert(key_status);
125 return key_status->value.num;
126 }
127
main(int argc,const char * argv[])128 int main(int argc, const char *argv[]) {
129 const int n_big = 2;
130 int n_lt = 2;
131 uint64_t stalls = 1;
132 uint64_t max_lock_memory = 1000000;
133
134 for (int i = 1; i < argc; i++) {
135 if (strcmp(argv[i], "-v") == 0 || strcmp(argv[i], "--verbose") == 0) {
136 verbose++;
137 continue;
138 }
139 if (strcmp(argv[i], "--stalls") == 0 && i+1 < argc) {
140 stalls = atoll(argv[++i]);
141 continue;
142 }
143 if (strcmp(argv[i], "--n_lt") == 0 && i+1 < argc) {
144 n_lt = atoi(argv[++i]);
145 continue;
146 }
147 if (strcmp(argv[i], "--max_lock_memory") == 0 && i+1 < argc) {
148 max_lock_memory = atoll(argv[++i]);
149 continue;
150 }
151 }
152
153 int r;
154
155 // create a manager
156 locktree_manager mgr;
157 mgr.create(nullptr, nullptr, e_callback, nullptr);
158 mgr.set_max_lock_memory(max_lock_memory);
159
160 // create lock trees
161 locktree *lt[n_big];
162 for (int i = 0; i < n_lt; i++) {
163 DICTIONARY_ID dict_id = { .dictid = (uint64_t)i };
164 lt[i] = mgr.get_lt(dict_id, dbt_comparator, nullptr);
165 assert(lt[i]);
166 }
167
168 // create the worker threads
169 struct arg big_arg[n_big];
170 pthread_t big_ids[n_big];
171 for (int i = 0; i < n_big; i++) {
172 big_arg[i] = {
173 &mgr, lt[i % n_lt], (TXNID)(1000 + i), i == 0 ? 1 : -1000000000};
174 r = toku_pthread_create(
175 toku_uninstrumented, &big_ids[i], nullptr, big_f, &big_arg[i]);
176 assert(r == 0);
177 }
178
179 // wait for some escalations to occur
180 while (get_escalation_count(mgr) < stalls) {
181 sleep(1);
182 }
183 killed = 1;
184
185 // cleanup
186 for (int i = 0; i < n_big; i++) {
187 void *ret;
188 r = toku_pthread_join(big_ids[i], &ret);
189 assert(r == 0);
190 }
191 for (int i = 0; i < n_lt ; i++) {
192 mgr.release_lt(lt[i]);
193 }
194 mgr.destroy();
195
196 return 0;
197 }
198