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