1 /*-
2 * See the file LICENSE for redistribution information.
3 *
4 * Copyright (c) 2010, 2013 Oracle and/or its affiliates. All rights reserved.
5 *
6 * $Id$
7 */
8
9 /*
10 * Test a situation ([#19345]) where a deadlock is returned via
11 * a secondary index when doing and update.
12 *
13 * TestKeyExistErrorReturn.c
14 */
15 #include <stdio.h>
16 #include <stdlib.h>
17 #include <string.h>
18
19 #include "db.h"
20 #include "CuTest.h"
21
22 #ifdef _WIN32
23 #include <windows.h>
24 #define PATHD '\\'
25 extern int getopt(int, char * const *, const char *);
26 extern char *optarg;
27
28 /* Wrap Windows thread API to make it look POSIXey. */
29 typedef HANDLE thread_t;
30 #define thread_create(thrp, attr, func, arg) \
31 (((*(thrp) = CreateThread(NULL, 0, \
32 (LPTHREAD_START_ROUTINE)(func), (arg), 0, NULL)) == NULL) ? -1 : 0)
33 #define thread_join(thr, statusp) \
34 ((WaitForSingleObject((thr), INFINITE) == WAIT_OBJECT_0) && \
35 ((statusp == NULL) ? 0 : \
36 (GetExitCodeThread((thr), (LPDWORD)(statusp)) ? 0 : -1)))
37
38 typedef HANDLE mutex_t;
39 #define mutex_init(m, attr) \
40 (((*(m) = CreateMutex(NULL, FALSE, NULL)) != NULL) ? 0 : -1)
41 #define mutex_lock(m) \
42 ((WaitForSingleObject(*(m), INFINITE) == WAIT_OBJECT_0) ? 0 : -1)
43 #define mutex_unlock(m) (ReleaseMutex(*(m)) ? 0 : -1)
44 #else
45 #include <pthread.h>
46 #include <unistd.h>
47 #define PATHD '/'
48
49 typedef pthread_t thread_t;
50 #define thread_create(thrp, attr, func, arg) \
51 pthread_create((thrp), (attr), (func), (arg))
52 #define thread_join(thr, statusp) pthread_join((thr), (statusp))
53
54 typedef pthread_mutex_t mutex_t;
55 #define mutex_init(m, attr) pthread_mutex_init((m), (attr))
56 #define mutex_lock(m) pthread_mutex_lock(m)
57 #define mutex_unlock(m) pthread_mutex_unlock(m)
58 #endif
59
60 #define NUMWRITERS 5
61
62 /* Forward declarations */
63 int count_records __P((DB *, DB_TXN *));
64 int assoc_callback __P((DB *, const DBT *, const DBT *, DBT *));
65 void *writer_thread __P((void *));
66
67 int global_thread_num;
68 mutex_t thread_num_lock;
69
TestKeyExistErrorReturn(CuTest * ct)70 int TestKeyExistErrorReturn(CuTest *ct) {
71 DB *pdbp;
72 DB *sdbp;
73 DB_ENV *dbenv;
74
75 const char *sec_db_file = "secondary.db";
76 const char *pri_db_file = "primary.db";
77 const char *env_dir = "TESTDIR";
78 int i;
79 thread_t writer_threads[NUMWRITERS];
80 u_int32_t db_flags, env_flags;
81
82 pdbp = sdbp = NULL;
83 dbenv = NULL;
84 db_flags = DB_CREATE | DB_AUTO_COMMIT | DB_READ_UNCOMMITTED;
85 env_flags = DB_CREATE | DB_RECOVER | DB_INIT_LOCK | DB_INIT_LOG |
86 DB_INIT_MPOOL | DB_INIT_TXN | DB_THREAD;
87
88 TestEnvConfigTestSetup(ct);
89
90 CuAssert(ct, "db_env_create", db_env_create(&dbenv, 0) == 0);
91
92 dbenv->set_errfile(dbenv, stderr);
93 dbenv->set_errpfx(dbenv, "TestKeyExistErrorReturn");
94
95 /* Run deadlock detector on every lock conflict. */
96 CuAssert(ct, "dbenv->set_lk_detect",
97 dbenv->set_lk_detect(dbenv, DB_LOCK_MINWRITE) == 0);
98
99 CuAssert(ct, "dbenv->open",
100 dbenv->open(dbenv, env_dir, env_flags, 0) == 0);
101
102 CuAssert(ct, "db_create", db_create(&pdbp, dbenv, 0) == 0);
103 CuAssert(ct, "pdbp->open", pdbp->open(pdbp, NULL,
104 pri_db_file, NULL, DB_BTREE, db_flags, 0) == 0);
105
106 CuAssert(ct, "db_create", db_create(&sdbp, dbenv, 0) == 0);
107 CuAssert(ct, "sdbp->set_flags", sdbp->set_flags(sdbp,
108 DB_DUPSORT) == 0);
109 CuAssert(ct, "sdbp->open", sdbp->open(sdbp, NULL, sec_db_file,
110 NULL, DB_BTREE, db_flags, 0) == 0);
111
112 CuAssert(ct, "DB->associate", pdbp->associate(pdbp, NULL, sdbp,
113 assoc_callback, DB_AUTO_COMMIT) == 0);
114
115 /* Initialize a mutex. Used to help provide thread ids. */
116 (void)mutex_init(&thread_num_lock, NULL);
117
118 for (i = 0; i < NUMWRITERS; ++i)
119 (void)thread_create(&writer_threads[i], NULL,
120 writer_thread, (void *)pdbp);
121
122 for (i = 0; i < NUMWRITERS; ++i)
123 (void)thread_join(writer_threads[i], NULL);
124
125 if (sdbp != NULL)
126 CuAssert(ct, "sdbp->close", sdbp->close(sdbp, 0) == 0);
127 if (pdbp != NULL)
128 CuAssert(ct, "pdbp->close", pdbp->close(pdbp, 0) == 0);
129 if (dbenv != NULL)
130 CuAssert(ct, "dbenv->close", dbenv->close(dbenv, 0) == 0);
131
132 TestEnvConfigTestTeardown(ct);
133
134 return (EXIT_SUCCESS);
135 }
136
137 void *
writer_thread(void * args)138 writer_thread(void *args)
139 {
140 DB *dbp;
141 DB_ENV *dbenv;
142 DBT key, data;
143 DB_TXN *txn;
144
145 char *key_strings[] = {"001", "002", "003", "004", "005",
146 "006", "007", "008", "009", "010"};
147 int i, j, payload, ret, thread_num;
148 int retry_count, max_retries = 20;
149
150 dbp = (DB *)args;
151 dbenv = dbp->dbenv;
152
153 /* Get the thread number */
154 (void)mutex_lock(&thread_num_lock);
155 global_thread_num++;
156 thread_num = global_thread_num;
157 (void)mutex_unlock(&thread_num_lock);
158
159 /* Initialize the random number generator */
160 srand(thread_num);
161
162 /* Write 50 times and then quit */
163 for (i = 0; i < 50; i++) {
164 retry_count = 0; /* Used for deadlock retries */
165 retry:
166 ret = dbenv->txn_begin(dbenv, NULL, &txn, 0);
167 if (ret != 0) {
168 dbenv->err(dbenv, ret, "txn_begin failed");
169 return ((void *)EXIT_FAILURE);
170 }
171
172 memset(&key, 0, sizeof(DBT));
173 memset(&data, 0, sizeof(DBT));
174 for (j = 0; j < 10; j++) {
175 /* Set up our key and data DBTs. */
176 data.data = key_strings[j];
177 data.size = (u_int32_t)strlen(key_strings[j]) + 1;
178
179 payload = rand() + i;
180 key.data = &payload;
181 key.size = sizeof(int);
182
183 switch (ret = dbp->put(dbp, txn, &key, &data,
184 DB_NOOVERWRITE)) {
185 case 0:
186 break;
187 case DB_KEYEXIST:
188 break;
189 case DB_LOCK_DEADLOCK:
190 (void)txn->abort(txn);
191 if (retry_count < max_retries) {
192 retry_count++;
193 goto retry;
194 }
195 return ((void *)EXIT_FAILURE);
196 default:
197 dbenv->err(dbenv, ret, "db put failed");
198 ret = txn->abort(txn);
199
200 if (ret != 0)
201 dbenv->err(dbenv, ret,
202 "txn abort failed");
203 return ((void *)EXIT_FAILURE);
204 }
205 }
206
207 if ((ret = txn->commit(txn, 0)) != 0) {
208 dbenv->err(dbenv, ret, "txn commit failed");
209 return ((void *)EXIT_FAILURE);
210 }
211 }
212 return ((void *)EXIT_SUCCESS);
213 }
214
215 int
count_records(DB * dbp,DB_TXN * txn)216 count_records(DB *dbp, DB_TXN *txn)
217 {
218 DBT key, data;
219 DBC *cursorp;
220 int count, ret;
221
222 cursorp = NULL;
223 count = 0;
224
225 /* Get the cursor */
226 ret = dbp->cursor(dbp, txn, &cursorp, DB_READ_UNCOMMITTED);
227 if (ret != 0) {
228 dbp->err(dbp, ret, "count_records: cursor open failed.");
229 goto cursor_err;
230 }
231
232 /* Get the key DBT used for the database read */
233 memset(&key, 0, sizeof(DBT));
234 memset(&data, 0, sizeof(DBT));
235 do {
236 ret = cursorp->get(cursorp, &key, &data, DB_NEXT);
237 switch (ret) {
238 case 0:
239 count++;
240 break;
241 case DB_NOTFOUND:
242 break;
243 default:
244 dbp->err(dbp, ret, "Count records unspecified error");
245 goto cursor_err;
246 }
247 } while (ret == 0);
248
249 cursor_err:
250 if (cursorp != NULL) {
251 ret = cursorp->close(cursorp);
252 if (ret != 0) {
253 dbp->err(dbp, ret,
254 "count_records: cursor close failed.");
255 }
256 }
257
258 return (count);
259 }
260
261 int
assoc_callback(pdbp,pkey,pdata,skey)262 assoc_callback(pdbp, pkey, pdata, skey)
263 DB *pdbp;
264 const DBT *pkey;
265 const DBT *pdata;
266 DBT *skey;
267 {
268 memset(skey, 0, sizeof(DBT));
269 skey->data = pdata->data;
270 skey->size = pdata->size;
271
272 return (EXIT_SUCCESS);
273 }
274