PageRenderTime 48ms CodeModel.GetById 33ms RepoModel.GetById 0ms app.codeStats 0ms

/source/db-5.0.26.NC/examples_c/txn_guide/txn_guide.c

https://github.com/akiernan/omnibus
C | 471 lines | 436 code | 14 blank | 21 comment | 6 complexity | 4fa1666e16630e81c02ce4460548ffce MD5 | raw file
Possible License(s): Apache-2.0, BSD-3-Clause
  1. /*-
  2. * See the file LICENSE for redistribution information.
  3. *
  4. * Copyright (c) 2005, 2010 Oracle and/or its affiliates. All rights reserved.
  5. *
  6. * $Id$
  7. */
  8. /* File: txn_guide.c */
  9. /* We assume an ANSI-compatible compiler */
  10. #include <stdio.h>
  11. #include <stdlib.h>
  12. #include <string.h>
  13. #include <db.h>
  14. #ifdef _WIN32
  15. #include <windows.h>
  16. #define PATHD '\\'
  17. extern int getopt(int, char * const *, const char *);
  18. extern char *optarg;
  19. /* Wrap Windows thread API to make it look POSIXey. */
  20. typedef HANDLE thread_t;
  21. #define thread_create(thrp, attr, func, arg) \
  22. (((*(thrp) = CreateThread(NULL, 0, \
  23. (LPTHREAD_START_ROUTINE)(func), (arg), 0, NULL)) == NULL) ? -1 : 0)
  24. #define thread_join(thr, statusp) \
  25. ((WaitForSingleObject((thr), INFINITE) == WAIT_OBJECT_0) && \
  26. ((statusp == NULL) ? 0 : \
  27. (GetExitCodeThread((thr), (LPDWORD)(statusp)) ? 0 : -1)))
  28. typedef HANDLE mutex_t;
  29. #define mutex_init(m, attr) \
  30. (((*(m) = CreateMutex(NULL, FALSE, NULL)) != NULL) ? 0 : -1)
  31. #define mutex_lock(m) \
  32. ((WaitForSingleObject(*(m), INFINITE) == WAIT_OBJECT_0) ? 0 : -1)
  33. #define mutex_unlock(m) (ReleaseMutex(*(m)) ? 0 : -1)
  34. #else
  35. #include <pthread.h>
  36. #include <unistd.h>
  37. #define PATHD '/'
  38. typedef pthread_t thread_t;
  39. #define thread_create(thrp, attr, func, arg) \
  40. pthread_create((thrp), (attr), (func), (arg))
  41. #define thread_join(thr, statusp) pthread_join((thr), (statusp))
  42. typedef pthread_mutex_t mutex_t;
  43. #define mutex_init(m, attr) pthread_mutex_init((m), (attr))
  44. #define mutex_lock(m) pthread_mutex_lock(m)
  45. #define mutex_unlock(m) pthread_mutex_unlock(m)
  46. #endif
  47. /* Run 5 writers threads at a time. */
  48. #define NUMWRITERS 5
  49. /*
  50. * Printing of a thread_t is implementation-specific, so we
  51. * create our own thread IDs for reporting purposes.
  52. */
  53. int global_thread_num;
  54. mutex_t thread_num_lock;
  55. /* Forward declarations */
  56. int count_records(DB *, DB_TXN *);
  57. int open_db(DB **, const char *, const char *, DB_ENV *, u_int32_t);
  58. int usage(void);
  59. void *writer_thread(void *);
  60. /* Usage function */
  61. int
  62. usage()
  63. {
  64. fprintf(stderr, " [-h <database_home_directory>]\n");
  65. return (EXIT_FAILURE);
  66. }
  67. int
  68. main(int argc, char *argv[])
  69. {
  70. /* Initialize our handles */
  71. DB *dbp = NULL;
  72. DB_ENV *envp = NULL;
  73. thread_t writer_threads[NUMWRITERS];
  74. int ch, i, ret, ret_t;
  75. u_int32_t env_flags;
  76. char *db_home_dir;
  77. /* Application name */
  78. const char *prog_name = "txn_guide";
  79. /* Database file name */
  80. const char *file_name = "mydb.db";
  81. /* Parse the command line arguments */
  82. #ifdef _WIN32
  83. db_home_dir = ".\\";
  84. #else
  85. db_home_dir = "./";
  86. #endif
  87. while ((ch = getopt(argc, argv, "h:")) != EOF)
  88. switch (ch) {
  89. case 'h':
  90. db_home_dir = optarg;
  91. break;
  92. case '?':
  93. default:
  94. return (usage());
  95. }
  96. /* Create the environment */
  97. ret = db_env_create(&envp, 0);
  98. if (ret != 0) {
  99. fprintf(stderr, "Error creating environment handle: %s\n",
  100. db_strerror(ret));
  101. goto err;
  102. }
  103. /*
  104. * Indicate that we want db to perform lock detection internally.
  105. * Also indicate that the transaction with the fewest number of
  106. * write locks will receive the deadlock notification in
  107. * the event of a deadlock.
  108. */
  109. ret = envp->set_lk_detect(envp, DB_LOCK_MINWRITE);
  110. if (ret != 0) {
  111. fprintf(stderr, "Error setting lock detect: %s\n",
  112. db_strerror(ret));
  113. goto err;
  114. }
  115. env_flags =
  116. DB_CREATE | /* Create the environment if it does not exist */
  117. DB_RECOVER | /* Run normal recovery. */
  118. DB_INIT_LOCK | /* Initialize the locking subsystem */
  119. DB_INIT_LOG | /* Initialize the logging subsystem */
  120. DB_INIT_TXN | /* Initialize the transactional subsystem. This
  121. * also turns on logging. */
  122. DB_INIT_MPOOL | /* Initialize the memory pool (in-memory cache) */
  123. DB_THREAD; /* Cause the environment to be free-threaded */
  124. /* Now actually open the environment */
  125. ret = envp->open(envp, db_home_dir, env_flags, 0);
  126. if (ret != 0) {
  127. fprintf(stderr, "Error opening environment: %s\n",
  128. db_strerror(ret));
  129. goto err;
  130. }
  131. /*
  132. * If we had utility threads (for running checkpoints or
  133. * deadlock detection, for example) we would spawn those
  134. * here. However, for a simple example such as this,
  135. * that is not required.
  136. */
  137. /* Open the database */
  138. ret = open_db(&dbp, prog_name, file_name,
  139. envp, DB_DUPSORT);
  140. if (ret != 0)
  141. goto err;
  142. /* Initialize a mutex. Used to help provide thread ids. */
  143. (void)mutex_init(&thread_num_lock, NULL);
  144. /* Start the writer threads. */
  145. for (i = 0; i < NUMWRITERS; i++)
  146. (void)thread_create(
  147. &writer_threads[i], NULL, writer_thread, (void *)dbp);
  148. /* Join the writers */
  149. for (i = 0; i < NUMWRITERS; i++)
  150. (void)thread_join(writer_threads[i], NULL);
  151. err:
  152. /* Close our database handle, if it was opened. */
  153. if (dbp != NULL) {
  154. ret_t = dbp->close(dbp, 0);
  155. if (ret_t != 0) {
  156. fprintf(stderr, "%s database close failed: %s\n",
  157. file_name, db_strerror(ret_t));
  158. ret = ret_t;
  159. }
  160. }
  161. /* Close our environment, if it was opened. */
  162. if (envp != NULL) {
  163. ret_t = envp->close(envp, 0);
  164. if (ret_t != 0) {
  165. fprintf(stderr, "environment close failed: %s\n",
  166. db_strerror(ret_t));
  167. ret = ret_t;
  168. }
  169. }
  170. /* Final status message and return. */
  171. printf("I'm all done.\n");
  172. return (ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
  173. }
  174. /*
  175. * A function that performs a series of writes to a
  176. * Berkeley DB database. The information written
  177. * to the database is largely nonsensical, but the
  178. * mechanism of transactional commit/abort and
  179. * deadlock detection is illustrated here.
  180. */
  181. void *
  182. writer_thread(void *args)
  183. {
  184. static char *key_strings[] = {
  185. "key 1", "key 2", "key 3", "key 4", "key 5",
  186. "key 6", "key 7", "key 8", "key 9", "key 10"
  187. };
  188. DB *dbp;
  189. DB_ENV *envp;
  190. DBT key, value;
  191. DB_TXN *txn;
  192. int i, j, payload, ret, thread_num;
  193. int retry_count, max_retries = 20; /* Max retry on a deadlock */
  194. dbp = (DB *)args;
  195. envp = dbp->get_env(dbp);
  196. /* Get the thread number */
  197. (void)mutex_lock(&thread_num_lock);
  198. global_thread_num++;
  199. thread_num = global_thread_num;
  200. (void)mutex_unlock(&thread_num_lock);
  201. /* Initialize the random number generator */
  202. srand(thread_num);
  203. /* Write 50 times and then quit */
  204. for (i = 0; i < 50; i++) {
  205. retry_count = 0; /* Used for deadlock retries */
  206. /*
  207. * Some think it is bad form to loop with a goto statement, but
  208. * we do it anyway because it is the simplest and clearest way
  209. * to achieve our abort/retry operation.
  210. */
  211. retry:
  212. /* Begin our transaction. We group multiple writes in
  213. * this thread under a single transaction so as to
  214. * (1) show that you can atomically perform multiple writes
  215. * at a time, and (2) to increase the chances of a
  216. * deadlock occurring so that we can observe our
  217. * deadlock detection at work.
  218. *
  219. * Normally we would want to avoid the potential for deadlocks,
  220. * so for this workload the correct thing would be to perform our
  221. * puts with autocommit. But that would excessively simplify our
  222. * example, so we do the "wrong" thing here instead.
  223. */
  224. ret = envp->txn_begin(envp, NULL, &txn, 0);
  225. if (ret != 0) {
  226. envp->err(envp, ret, "txn_begin failed");
  227. return ((void *)EXIT_FAILURE);
  228. }
  229. for (j = 0; j < 10; j++) {
  230. /* Set up our key and values DBTs */
  231. memset(&key, 0, sizeof(DBT));
  232. key.data = key_strings[j];
  233. key.size = (u_int32_t)strlen(key_strings[j]) + 1;
  234. memset(&value, 0, sizeof(DBT));
  235. payload = rand() + i;
  236. value.data = &payload;
  237. value.size = sizeof(int);
  238. /* Perform the database put. */
  239. switch (ret = dbp->put(dbp, txn, &key, &value, 0)) {
  240. case 0:
  241. break;
  242. /*
  243. * Our database is configured for sorted duplicates,
  244. * so there is a potential for a KEYEXIST error return.
  245. * If we get one, simply ignore it and continue on.
  246. *
  247. * Note that you will see KEYEXIST errors only after you
  248. * have run this program at least once.
  249. */
  250. case DB_KEYEXIST:
  251. printf("Got keyexists.\n");
  252. break;
  253. /*
  254. * Here's where we perform deadlock detection. If
  255. * DB_LOCK_DEADLOCK is returned by the put operation,
  256. * then this thread has been chosen to break a deadlock.
  257. * It must abort its operation, and optionally retry the
  258. * put.
  259. */
  260. case DB_LOCK_DEADLOCK:
  261. /*
  262. * First thing that we MUST do is abort the
  263. * transaction.
  264. */
  265. (void)txn->abort(txn);
  266. /*
  267. * Now we decide if we want to retry the operation.
  268. * If we have retried less than max_retries,
  269. * increment the retry count and goto retry.
  270. */
  271. if (retry_count < max_retries) {
  272. printf("Writer %i: Got DB_LOCK_DEADLOCK.\n",
  273. thread_num);
  274. printf("Writer %i: Retrying write operation.\n",
  275. thread_num);
  276. retry_count++;
  277. goto retry;
  278. }
  279. /*
  280. * Otherwise, just give up.
  281. */
  282. printf("Writer %i: ", thread_num);
  283. printf("Got DB_LOCK_DEADLOCK and out of retries.\n");
  284. printf("Writer %i: Giving up.\n", thread_num);
  285. return ((void *)EXIT_FAILURE);
  286. /*
  287. * If a generic error occurs, we simply abort the
  288. * transaction and exit the thread completely.
  289. */
  290. default:
  291. envp->err(envp, ret, "db put failed");
  292. ret = txn->abort(txn);
  293. if (ret != 0)
  294. envp->err(envp, ret,
  295. "txn abort failed");
  296. return ((void *)EXIT_FAILURE);
  297. } /** End case statement **/
  298. } /** End for loop **/
  299. /*
  300. * print the number of records found in the database.
  301. * See count_records() for usage information.
  302. */
  303. printf("Thread %i. Record count: %i\n", thread_num,
  304. count_records(dbp, NULL));
  305. /*
  306. * If all goes well, we can commit the transaction and
  307. * exit the thread.
  308. */
  309. ret = txn->commit(txn, 0);
  310. if (ret != 0) {
  311. envp->err(envp, ret, "txn commit failed");
  312. return ((void *)EXIT_FAILURE);
  313. }
  314. }
  315. return ((void *)EXIT_SUCCESS);
  316. }
  317. /*
  318. * This simply counts the number of records contained in the
  319. * database and returns the result. You can use this function
  320. * in three ways:
  321. *
  322. * First call it with an active txn handle.
  323. * Secondly, configure the cursor for uncommitted reads (this
  324. * is what the example currently does).
  325. * Third, call count_records AFTER the writer has committed
  326. * its transaction.
  327. *
  328. * If you do none of these things, the writer thread will
  329. * self-deadlock.
  330. *
  331. * Note that this function exists only for illustrative purposes.
  332. * A more straight-forward way to count the number of records in
  333. * a database is to use DB->stat() or DB->stat_print().
  334. */
  335. int
  336. count_records(DB *dbp, DB_TXN *txn)
  337. {
  338. DBT key, value;
  339. DBC *cursorp;
  340. int count, ret;
  341. cursorp = NULL;
  342. count = 0;
  343. /* Get the cursor */
  344. ret = dbp->cursor(dbp, txn, &cursorp,
  345. DB_READ_UNCOMMITTED);
  346. if (ret != 0) {
  347. dbp->err(dbp, ret,
  348. "count_records: cursor open failed.");
  349. goto cursor_err;
  350. }
  351. /* Get the key DBT used for the database read */
  352. memset(&key, 0, sizeof(DBT));
  353. memset(&value, 0, sizeof(DBT));
  354. do {
  355. ret = cursorp->get(cursorp, &key, &value, DB_NEXT);
  356. switch (ret) {
  357. case 0:
  358. count++;
  359. break;
  360. case DB_NOTFOUND:
  361. break;
  362. default:
  363. dbp->err(dbp, ret,
  364. "Count records unspecified error");
  365. goto cursor_err;
  366. }
  367. } while (ret == 0);
  368. cursor_err:
  369. if (cursorp != NULL) {
  370. ret = cursorp->close(cursorp);
  371. if (ret != 0) {
  372. dbp->err(dbp, ret,
  373. "count_records: cursor close failed.");
  374. }
  375. }
  376. return (count);
  377. }
  378. /* Open a Berkeley DB database */
  379. int
  380. open_db(DB **dbpp, const char *progname, const char *file_name,
  381. DB_ENV *envp, u_int32_t extra_flags)
  382. {
  383. int ret;
  384. u_int32_t open_flags;
  385. DB *dbp;
  386. /* Initialize the DB handle */
  387. ret = db_create(&dbp, envp, 0);
  388. if (ret != 0) {
  389. fprintf(stderr, "%s: %s\n", progname,
  390. db_strerror(ret));
  391. return (EXIT_FAILURE);
  392. }
  393. /* Point to the memory malloc'd by db_create() */
  394. *dbpp = dbp;
  395. if (extra_flags != 0) {
  396. ret = dbp->set_flags(dbp, extra_flags);
  397. if (ret != 0) {
  398. dbp->err(dbp, ret,
  399. "open_db: Attempt to set extra flags failed.");
  400. return (EXIT_FAILURE);
  401. }
  402. }
  403. /* Now open the database */
  404. open_flags = DB_CREATE | /* Allow database creation */
  405. DB_READ_UNCOMMITTED | /* Allow dirty reads */
  406. DB_AUTO_COMMIT; /* Allow autocommit */
  407. ret = dbp->open(dbp, /* Pointer to the database */
  408. NULL, /* Txn pointer */
  409. file_name, /* File name */
  410. NULL, /* Logical db name */
  411. DB_BTREE, /* Database type (using btree) */
  412. open_flags, /* Open flags */
  413. 0); /* File mode. Using defaults */
  414. if (ret != 0) {
  415. dbp->err(dbp, ret, "Database '%s' open failed",
  416. file_name);
  417. return (EXIT_FAILURE);
  418. }
  419. return (EXIT_SUCCESS);
  420. }