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 // verify that cursor first on an empty tree with a write lock suspends the conflicting threads.
40
41 #include "test.h"
42 #include <toku_pthread.h>
43
44 struct my_callback_context {
45 DBT key;
46 DBT val;
47 };
48
blocking_first_callback(DBT const * a UU (),DBT const * b UU (),void * e UU ())49 static int blocking_first_callback(DBT const *a UU(), DBT const *b UU(), void *e UU()) {
50 DBT const *found_key = a;
51 DBT const *found_val = b;
52 struct my_callback_context *context = (struct my_callback_context *) e;
53 copy_dbt(&context->key, found_key);
54 copy_dbt(&context->val, found_val);
55 return 0;
56 }
57
blocking_first(DB_ENV * db_env,DB * db,uint64_t nrows,long sleeptime)58 static void blocking_first(DB_ENV *db_env, DB *db, uint64_t nrows, long sleeptime) {
59 int r;
60
61 struct my_callback_context context;
62 dbt_init_realloc(&context.key);
63 dbt_init_realloc(&context.val);
64
65 for (uint64_t i = 0; i < nrows; i++) {
66 DB_TXN *txn = NULL;
67 r = db_env->txn_begin(db_env, NULL, &txn, 0); assert(r == 0);
68
69 DBC *cursor = NULL;
70 r = db->cursor(db, txn, &cursor, 0); assert(r == 0); // get a write lock on -inf +inf
71 r = cursor->c_getf_first(cursor, DB_RMW, blocking_first_callback, &context); assert(r == DB_NOTFOUND);
72
73 usleep(sleeptime);
74
75 r = cursor->c_close(cursor); assert(r == 0);
76
77 r = txn->commit(txn, 0); assert(r == 0);
78 if (verbose)
79 printf("%lu %" PRIu64 "\n", (unsigned long) toku_pthread_self(), i);
80 }
81
82 toku_free(context.key.data);
83 toku_free(context.val.data);
84 }
85
86 struct blocking_first_args {
87 DB_ENV *db_env;
88 DB *db;
89 uint64_t nrows;
90 long sleeptime;
91 };
92
blocking_first_thread(void * arg)93 static void *blocking_first_thread(void *arg) {
94 struct blocking_first_args *a = (struct blocking_first_args *) arg;
95 blocking_first(a->db_env, a->db, a->nrows, a->sleeptime);
96 return arg;
97 }
98
run_test(DB_ENV * db_env,DB * db,int nthreads,uint64_t nrows,long sleeptime)99 static void run_test(DB_ENV *db_env, DB *db, int nthreads, uint64_t nrows, long sleeptime) {
100 int r;
101 toku_pthread_t tids[nthreads];
102 struct blocking_first_args a = {db_env, db, nrows, sleeptime};
103 for (int i = 0; i < nthreads - 1; i++) {
104 r = toku_pthread_create(
105 toku_uninstrumented, &tids[i], nullptr, blocking_first_thread, &a);
106 assert(r == 0);
107 }
108 blocking_first(db_env, db, nrows, sleeptime);
109 for (int i = 0; i < nthreads - 1; i++) {
110 void *ret;
111 r = toku_pthread_join(tids[i], &ret); assert(r == 0);
112 }
113 }
114
test_main(int argc,char * const argv[])115 int test_main(int argc, char * const argv[]) {
116 uint64_t cachesize = 0;
117 uint32_t pagesize = 0;
118 uint64_t nrows = 10;
119 int nthreads = 2;
120 long sleeptime = 100000;
121 const char *db_env_dir = TOKU_TEST_FILENAME;
122 const char *db_filename = "test.db";
123 int db_env_open_flags = DB_CREATE | DB_PRIVATE | DB_INIT_MPOOL | DB_INIT_TXN | DB_INIT_LOCK | DB_INIT_LOG | DB_THREAD;
124
125 // parse_args(argc, argv);
126 for (int i = 1; i < argc; i++) {
127 if (strcmp(argv[i], "-v") == 0 || strcmp(argv[i], "--verbose") == 0) {
128 verbose++;
129 continue;
130 }
131 if (strcmp(argv[i], "-q") == 0 || strcmp(argv[i], "--quiet") == 0) {
132 if (verbose > 0)
133 verbose--;
134 continue;
135 }
136 if (strcmp(argv[i], "--nrows") == 0 && i+1 < argc) {
137 nrows = atoll(argv[++i]);
138 continue;
139 }
140 if (strcmp(argv[i], "--nthreads") == 0 && i+1 < argc) {
141 nthreads = atoi(argv[++i]);
142 continue;
143 }
144 if (strcmp(argv[i], "--sleeptime") == 0 && i+1 < argc) {
145 sleeptime = atol(argv[++i]);
146 continue;
147 }
148 assert(0);
149 }
150
151 // setup env
152 int r;
153 char rm_cmd[strlen(db_env_dir) + strlen("rm -rf ") + 1];
154 snprintf(rm_cmd, sizeof(rm_cmd), "rm -rf %s", db_env_dir);
155 r = system(rm_cmd); assert(r == 0);
156
157 r = toku_os_mkdir(db_env_dir, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); assert(r == 0);
158
159 DB_ENV *db_env = NULL;
160 r = db_env_create(&db_env, 0); assert(r == 0);
161 if (cachesize) {
162 const uint64_t gig = 1 << 30;
163 r = db_env->set_cachesize(db_env, cachesize / gig, cachesize % gig, 1); assert(r == 0);
164 }
165 r = db_env->open(db_env, db_env_dir, db_env_open_flags, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); assert(r == 0);
166 r = db_env->set_lock_timeout(db_env, 30 * 1000, nullptr); assert(r == 0);
167
168 // create the db
169 DB *db = NULL;
170 r = db_create(&db, db_env, 0); assert(r == 0);
171 if (pagesize) {
172 r = db->set_pagesize(db, pagesize); assert(r == 0);
173 }
174 r = db->open(db, NULL, db_filename, NULL, DB_BTREE, DB_CREATE|DB_AUTO_COMMIT|DB_THREAD, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); assert(r == 0);
175
176 run_test(db_env, db, nthreads, nrows, sleeptime);
177
178 // close env
179 r = db->close(db, 0); assert(r == 0); db = NULL;
180 r = db_env->close(db_env, 0); assert(r == 0); db_env = NULL;
181
182 return 0;
183 }
184