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