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 // have multiple threads try to put key 0 into the same db. one thread should gain a write lock on the key.
40 // the other threads should block until the thread that owns the lock commits its transaction. then, one
41 // of the blocked transactions should gain the lock and its owning thread resumed.
42
43 #include "test.h"
44 #include "toku_pthread.h"
45
46 // pound on key == 0 and hold the write lock for a time less than the lock timeout
blocking_put(DB_ENV * db_env,DB * db,uint64_t nrows,long sleeptime)47 static void blocking_put(DB_ENV *db_env, DB *db, uint64_t nrows, long sleeptime) {
48 int r;
49
50 for (uint64_t i = 0; i < nrows; i++) {
51 DB_TXN *txn = NULL;
52 r = db_env->txn_begin(db_env, NULL, &txn, 0); assert(r == 0);
53
54 uint64_t k = 0;
55 DBT key = { .data = &k, .size = sizeof k};
56 DBT val = { .data = &k, .size = sizeof k};
57 r = db->put(db, txn, &key, &val, 0); assert(r == 0);
58
59 usleep(sleeptime);
60
61 r = txn->commit(txn, 0); assert(r == 0);
62 if (verbose)
63 printf("%lu %" PRIu64 "\n", (unsigned long) toku_pthread_self(), i);
64 }
65 }
66
67 struct blocking_put_args {
68 DB_ENV *db_env;
69 DB *db;
70 uint64_t nrows;
71 long sleeptime;
72 };
73
blocking_put_thread(void * arg)74 static void *blocking_put_thread(void *arg) {
75 struct blocking_put_args *a = (struct blocking_put_args *) arg;
76 blocking_put(a->db_env, a->db, a->nrows, a->sleeptime);
77 return arg;
78 }
79
test_main(int argc,char * const argv[])80 int test_main(int argc, char * const argv[]) {
81 uint64_t cachesize = 0;
82 uint32_t pagesize = 0;
83 uint64_t nrows = 100;
84 int nthreads = 2;
85 long sleeptime = 100000;
86 const char *db_env_dir = TOKU_TEST_FILENAME;
87 const char *db_filename = "test.db";
88 int db_env_open_flags = DB_CREATE | DB_PRIVATE | DB_INIT_MPOOL | DB_INIT_TXN | DB_INIT_LOCK | DB_INIT_LOG | DB_THREAD;
89
90 // parse_args(argc, argv);
91 for (int i = 1; i < argc; i++) {
92 if (strcmp(argv[i], "-v") == 0 || strcmp(argv[i], "--verbose") == 0) {
93 verbose++;
94 continue;
95 }
96 if (strcmp(argv[i], "-q") == 0 || strcmp(argv[i], "--quiet") == 0) {
97 if (verbose > 0)
98 verbose--;
99 continue;
100 }
101 if (strcmp(argv[i], "--nrows") == 0 && i+1 < argc) {
102 nrows = atoll(argv[++i]);
103 continue;
104 }
105 if (strcmp(argv[i], "--nthreads") == 0 && i+1 < argc) {
106 nthreads = atoi(argv[++i]);
107 continue;
108 }
109 if (strcmp(argv[i], "--sleeptime") == 0 && i+1 < argc) {
110 sleeptime = atol(argv[++i]);
111 continue;
112 }
113 assert(0);
114 }
115
116 // setup env
117 int r;
118 char rm_cmd[strlen(db_env_dir) + strlen("rm -rf ") + 1];
119 snprintf(rm_cmd, sizeof(rm_cmd), "rm -rf %s", db_env_dir);
120 r = system(rm_cmd); assert(r == 0);
121
122 r = toku_os_mkdir(db_env_dir, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); assert(r == 0);
123
124 DB_ENV *db_env = NULL;
125 r = db_env_create(&db_env, 0); assert(r == 0);
126 if (cachesize) {
127 const uint64_t gig = 1 << 30;
128 r = db_env->set_cachesize(db_env, cachesize / gig, cachesize % gig, 1); assert(r == 0);
129 }
130 r = db_env->open(db_env, db_env_dir, db_env_open_flags, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); assert(r == 0);
131 r = db_env->set_lock_timeout(db_env, 30 * 1000, nullptr); assert(r == 0);
132
133 // create the db
134 DB *db = NULL;
135 r = db_create(&db, db_env, 0); assert(r == 0);
136 if (pagesize) {
137 r = db->set_pagesize(db, pagesize); assert(r == 0);
138 }
139 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);
140
141 toku_pthread_t tids[nthreads];
142 struct blocking_put_args a = {db_env, db, nrows, sleeptime};
143 for (int i = 0; i < nthreads - 1; i++) {
144 r = toku_pthread_create(
145 toku_uninstrumented, &tids[i], nullptr, blocking_put_thread, &a);
146 assert(r == 0);
147 }
148 blocking_put(db_env, db, nrows, sleeptime);
149 for (int i = 0; i < nthreads - 1; i++) {
150 void *ret;
151 r = toku_pthread_join(tids[i], &ret); assert(r == 0);
152 }
153
154 // close env
155 r = db->close(db, 0); assert(r == 0); db = NULL;
156 r = db_env->close(db_env, 0); assert(r == 0); db_env = NULL;
157
158 return 0;
159 }
160