LCOV - code coverage report
Current view: top level - lib/ldb/tests - ldb_lmdb_free_list_test.c (source / functions) Hit Total Coverage
Test: coverage report for master 98b443d9 Lines: 293 298 98.3 %
Date: 2024-05-31 13:13:24 Functions: 11 11 100.0 %

          Line data    Source code
       1             : /*
       2             :  * Copyright (C) Catalyst.Net Ltd 2020
       3             :  *
       4             :  * This program is free software; you can redistribute it and/or modify
       5             :  * it under the terms of the GNU General Public License as published by
       6             :  * the Free Software Foundation; either version 3 of the License, or
       7             :  * (at your option) any later version.
       8             :  *
       9             :  * This program is distributed in the hope that it will be useful,
      10             :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      11             :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      12             :  * GNU General Public License for more details.
      13             :  *
      14             :  * You should have received a copy of the GNU General Public License
      15             :  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
      16             :  *
      17             :  */
      18             : 
      19             : /*
      20             :  * Tests confirming lmdb's handling of the free space list in the presence
      21             :  * of active and stale readers.  A stale reader is a process that opens a
      22             :  * read lock and then exits without releasing the lock.
      23             :  *
      24             :  * lmdb uses MVCC to maintain databased consistency, new copies of updated
      25             :  * records are written to the database. The old entries are only
      26             :  * reused when they are no longer referenced in a read transaction.
      27             :  *
      28             :  * The tests all update a single record multiple times
      29             :  *
      30             :  * If there is a read transaction or a stale reader lmdb will report
      31             :  * out of space.
      32             :  *
      33             :  * If no read transaction and no stale reader, lmdb reclaims space from the
      34             :  * free list.
      35             :  */
      36             : 
      37             : /*
      38             :  * from cmocka.c:
      39             :  * These headers or their equivalents should be included prior to
      40             :  * including
      41             :  * this header file.
      42             :  *
      43             :  * #include <stdarg.h>
      44             :  * #include <stddef.h>
      45             :  * #include <setjmp.h>
      46             :  *
      47             :  * This allows test applications to use custom definitions of C standard
      48             :  * library functions and types.
      49             :  *
      50             :  */
      51             : 
      52             : #include <stdarg.h>
      53             : #include <stddef.h>
      54             : #include <stdint.h>
      55             : #include <setjmp.h>
      56             : #include <cmocka.h>
      57             : 
      58             : #include <errno.h>
      59             : #include <unistd.h>
      60             : #include <talloc.h>
      61             : #include <tevent.h>
      62             : #include <ldb.h>
      63             : #include <ldb_module.h>
      64             : #include <ldb_private.h>
      65             : #include <string.h>
      66             : #include <ctype.h>
      67             : 
      68             : #include <sys/wait.h>
      69             : 
      70             : #include "ldb_tdb/ldb_tdb.h"
      71             : #include "ldb_key_value/ldb_kv.h"
      72             : 
      73             : #define DEFAULT_BE "mdb"
      74             : 
      75             : #ifndef TEST_BE
      76             : #define TEST_BE DEFAULT_BE
      77             : #endif /* TEST_BE */
      78             : 
      79             : const int RECORD_SIZE = 6144;
      80             : const int ITERATIONS = 192;
      81             : 
      82             : struct test_ctx {
      83             :         struct tevent_context *ev;
      84             :         struct ldb_context *ldb;
      85             : 
      86             :         const char *dbfile;
      87             :         const char *lockfile; /* lockfile is separate */
      88             : 
      89             :         const char *dbpath;
      90             : };
      91             : 
      92           6 : static void unlink_old_db(struct test_ctx *test_ctx)
      93             : {
      94           6 :         int ret;
      95             : 
      96           6 :         errno = 0;
      97           6 :         ret = unlink(test_ctx->lockfile);
      98           6 :         if (ret == -1 && errno != ENOENT) {
      99           0 :                 fail();
     100             :         }
     101             : 
     102           6 :         errno = 0;
     103           6 :         ret = unlink(test_ctx->dbfile);
     104           6 :         if (ret == -1 && errno != ENOENT) {
     105           0 :                 fail();
     106             :         }
     107           6 : }
     108             : 
     109           3 : static int noconn_setup(void **state)
     110             : {
     111           3 :         struct test_ctx *test_ctx;
     112             : 
     113           3 :         test_ctx = talloc_zero(NULL, struct test_ctx);
     114           3 :         assert_non_null(test_ctx);
     115             : 
     116           3 :         test_ctx->ev = tevent_context_init(test_ctx);
     117           3 :         assert_non_null(test_ctx->ev);
     118             : 
     119           3 :         test_ctx->ldb = ldb_init(test_ctx, test_ctx->ev);
     120           3 :         assert_non_null(test_ctx->ldb);
     121             : 
     122           3 :         test_ctx->dbfile = talloc_strdup(test_ctx, "lmdb_free_list_test.ldb");
     123           3 :         assert_non_null(test_ctx->dbfile);
     124             : 
     125           6 :         test_ctx->lockfile =
     126           3 :             talloc_asprintf(test_ctx, "%s-lock", test_ctx->dbfile);
     127           3 :         assert_non_null(test_ctx->lockfile);
     128             : 
     129           6 :         test_ctx->dbpath =
     130           3 :             talloc_asprintf(test_ctx, TEST_BE "://%s", test_ctx->dbfile);
     131           3 :         assert_non_null(test_ctx->dbpath);
     132             : 
     133           3 :         unlink_old_db(test_ctx);
     134           3 :         *state = test_ctx;
     135           3 :         return 0;
     136             : }
     137             : 
     138           3 : static int noconn_teardown(void **state)
     139             : {
     140           3 :         struct test_ctx *test_ctx =
     141           3 :             talloc_get_type_abort(*state, struct test_ctx);
     142             : 
     143           3 :         unlink_old_db(test_ctx);
     144           3 :         talloc_free(test_ctx);
     145           3 :         return 0;
     146             : }
     147             : 
     148           3 : static int setup(void **state)
     149             : {
     150           3 :         struct test_ctx *test_ctx;
     151           3 :         int ret;
     152           3 :         struct ldb_ldif *ldif;
     153           3 :         const char *index_ldif = "dn: @INDEXLIST\n"
     154             :                                  "@IDXGUID: objectUUID\n"
     155             :                                  "@IDX_DN_GUID: GUID\n"
     156             :                                  "\n";
     157             :         /*
     158             :          * Use a 1MiB DB for this test
     159             :          */
     160           3 :         const char *options[] = {"lmdb_env_size:1048576", NULL};
     161             : 
     162           3 :         noconn_setup((void **)&test_ctx);
     163             : 
     164           3 :         ret = ldb_connect(test_ctx->ldb, test_ctx->dbpath, 0, options);
     165           3 :         assert_int_equal(ret, 0);
     166             : 
     167           6 :         while ((ldif = ldb_ldif_read_string(test_ctx->ldb, &index_ldif))) {
     168           3 :                 ret = ldb_add(test_ctx->ldb, ldif->msg);
     169           3 :                 assert_int_equal(ret, LDB_SUCCESS);
     170             :         }
     171           3 :         *state = test_ctx;
     172           3 :         return 0;
     173             : }
     174             : 
     175           3 : static int teardown(void **state)
     176             : {
     177           6 :         struct test_ctx *test_ctx =
     178           3 :             talloc_get_type_abort(*state, struct test_ctx);
     179           3 :         noconn_teardown((void **)&test_ctx);
     180           3 :         return 0;
     181             : }
     182             : 
     183           6 : static struct ldb_kv_private *get_ldb_kv(struct ldb_context *ldb)
     184             : {
     185           6 :         void *data = NULL;
     186           6 :         struct ldb_kv_private *ldb_kv = NULL;
     187             : 
     188           6 :         data = ldb_module_get_private(ldb->modules);
     189           6 :         assert_non_null(data);
     190             : 
     191           6 :         ldb_kv = talloc_get_type(data, struct ldb_kv_private);
     192           6 :         assert_non_null(ldb_kv);
     193             : 
     194           6 :         return ldb_kv;
     195             : }
     196             : 
     197           3 : static int parse(struct ldb_val key, struct ldb_val data, void *private_data)
     198             : {
     199           3 :         struct ldb_val *read = private_data;
     200             : 
     201             :         /* Yes, we leak this.  That is OK */
     202           3 :         read->data = talloc_size(NULL, data.length);
     203           3 :         assert_non_null(read->data);
     204             : 
     205           3 :         memcpy(read->data, data.data, data.length);
     206           3 :         read->length = data.length;
     207           3 :         return LDB_SUCCESS;
     208             : }
     209             : 
     210             : /*
     211             :  * This test has the same structure as the test_free_list_read_lock
     212             :  * except the parent process does not keep the read lock open while the
     213             :  * child process is performing an update.
     214             :  */
     215           1 : static void test_free_list_no_read_lock(void **state)
     216             : {
     217           1 :         int ret;
     218           1 :         struct test_ctx *test_ctx =
     219           1 :             talloc_get_type_abort(*state, struct test_ctx);
     220           1 :         struct ldb_kv_private *ldb_kv = get_ldb_kv(test_ctx->ldb);
     221           1 :         struct ldb_val key;
     222           1 :         struct ldb_val val;
     223             : 
     224           1 :         const char *KEY1 = "KEY01";
     225             : 
     226             :         /*
     227             :          * Pipes etc to coordinate the processes
     228             :          */
     229           1 :         int to_child[2];
     230           1 :         int to_parent[2];
     231           1 :         char buf[2];
     232           1 :         pid_t pid;
     233           1 :         size_t i;
     234             : 
     235           1 :         TALLOC_CTX *tmp_ctx;
     236           1 :         tmp_ctx = talloc_new(test_ctx);
     237           1 :         assert_non_null(tmp_ctx);
     238             : 
     239           1 :         ret = pipe(to_child);
     240           1 :         assert_int_equal(ret, 0);
     241           1 :         ret = pipe(to_parent);
     242           1 :         assert_int_equal(ret, 0);
     243             :         /*
     244             :          * Now fork a new process
     245             :          */
     246             : 
     247           1 :         pid = fork();
     248           2 :         if (pid == 0) {
     249             :                 /*
     250             :                  * Child process
     251             :                  */
     252             : 
     253           1 :                 struct ldb_context *ldb = NULL;
     254           1 :                 close(to_child[1]);
     255           1 :                 close(to_parent[0]);
     256             : 
     257             :                 /*
     258             :                  * Wait for the parent to get ready.
     259             :                  */
     260           1 :                 ret = read(to_child[0], buf, 2);
     261           1 :                 assert_int_equal(ret, 2);
     262             : 
     263           1 :                 ldb = ldb_init(test_ctx, test_ctx->ev);
     264           1 :                 assert_non_null(ldb);
     265             : 
     266           1 :                 ret = ldb_connect(ldb, test_ctx->dbpath, 0, NULL);
     267           1 :                 assert_int_equal(ret, LDB_SUCCESS);
     268             : 
     269           1 :                 ldb_kv = get_ldb_kv(ldb);
     270           1 :                 assert_non_null(ldb_kv);
     271             :                 /*
     272             :                  * Add a record to the database
     273             :                  */
     274           1 :                 key.data = (uint8_t *)talloc_strdup(tmp_ctx, KEY1);
     275           1 :                 key.length = strlen(KEY1) + 1;
     276           1 :                 val.data = talloc_zero_size(tmp_ctx, RECORD_SIZE);
     277           1 :                 assert_non_null(val.data);
     278           1 :                 memset(val.data, 'x', RECORD_SIZE);
     279           1 :                 val.length = RECORD_SIZE;
     280             :                 /*
     281             :                  * Do more iterations than when a read lock, stale reader
     282             :                  * active to confirm that the space is being re-used.
     283             :                  */
     284        1921 :                 for (i = 0; i < ITERATIONS * 10; i++) {
     285        1920 :                         ret = ldb_kv->kv_ops->begin_write(ldb_kv);
     286        1920 :                         assert_int_equal(ret, LDB_SUCCESS);
     287             : 
     288        1920 :                         ret = ldb_kv->kv_ops->store(ldb_kv, key, val, 0);
     289        1920 :                         assert_int_equal(ret, LDB_SUCCESS);
     290             : 
     291        1920 :                         ret = ldb_kv->kv_ops->finish_write(ldb_kv);
     292        1920 :                         assert_int_equal(ret, LDB_SUCCESS);
     293             :                 }
     294             : 
     295             :                 /*
     296             :                  * Signal the parent that we've done the updates
     297             :                  */
     298           1 :                 ret = write(to_parent[1], "GO", 2);
     299           1 :                 assert_int_equal(ret, 2);
     300           1 :                 exit(0);
     301             :         }
     302             : 
     303           1 :         close(to_child[0]);
     304           1 :         close(to_parent[1]);
     305             : 
     306             :         /*
     307             :          * Begin a read transaction
     308             :          */
     309           1 :         ret = ldb_kv->kv_ops->lock_read(test_ctx->ldb->modules);
     310           1 :         assert_int_equal(ret, LDB_SUCCESS);
     311             : 
     312             :         /*
     313             :          * Now close it
     314             :          */
     315           1 :         ret = ldb_kv->kv_ops->unlock_read(test_ctx->ldb->modules);
     316           1 :         assert_int_equal(ret, LDB_SUCCESS);
     317             : 
     318             :         /*
     319             :          * Signal the child process
     320             :          */
     321           1 :         ret = write(to_child[1], "GO", 2);
     322           1 :         assert_int_equal(2, ret);
     323             : 
     324             :         /*
     325             :          * Wait for the child process to update the record
     326             :          */
     327           1 :         ret = read(to_parent[0], buf, 2);
     328           1 :         assert_int_equal(2, ret);
     329             : 
     330             :         /*
     331             :          * Begin a read transaction
     332             :          */
     333           1 :         ret = ldb_kv->kv_ops->lock_read(test_ctx->ldb->modules);
     334           1 :         assert_int_equal(ret, LDB_SUCCESS);
     335             :         /*
     336             :          * read the record
     337             :          * and close the transaction
     338             :          */
     339           1 :         key.data = (uint8_t *)talloc_strdup(tmp_ctx, KEY1);
     340           1 :         key.length = strlen(KEY1) + 1;
     341             : 
     342           1 :         ret = ldb_kv->kv_ops->fetch_and_parse(ldb_kv, key, parse, &val);
     343           1 :         assert_int_equal(ret, LDB_SUCCESS);
     344             : 
     345           1 :         ret = ldb_kv->kv_ops->unlock_read(test_ctx->ldb->modules);
     346           1 :         assert_int_equal(ret, LDB_SUCCESS);
     347             : 
     348           1 :         close(to_child[1]);
     349           1 :         close(to_parent[0]);
     350           1 :         TALLOC_FREE(tmp_ctx);
     351           1 : }
     352             : 
     353             : /*
     354             :  * This test has the same structure as the test_free_list_read_lock
     355             :  * except the parent process keeps the read lock open while the
     356             :  * child process is performing an update.
     357             :  */
     358           1 : static void test_free_list_read_lock(void **state)
     359             : {
     360           1 :         int ret;
     361           1 :         struct test_ctx *test_ctx =
     362           1 :             talloc_get_type_abort(*state, struct test_ctx);
     363           1 :         struct ldb_kv_private *ldb_kv = get_ldb_kv(test_ctx->ldb);
     364           1 :         struct ldb_val key;
     365           1 :         struct ldb_val val;
     366             : 
     367           1 :         const char *KEY1 = "KEY01";
     368             : 
     369             :         /*
     370             :          * Pipes etc to coordinate the processes
     371             :          */
     372           1 :         int to_child[2];
     373           1 :         int to_parent[2];
     374           1 :         char buf[2];
     375           1 :         pid_t pid;
     376           1 :         size_t i;
     377             : 
     378           1 :         TALLOC_CTX *tmp_ctx;
     379           1 :         tmp_ctx = talloc_new(test_ctx);
     380           1 :         assert_non_null(tmp_ctx);
     381             : 
     382           1 :         ret = pipe(to_child);
     383           1 :         assert_int_equal(ret, 0);
     384           1 :         ret = pipe(to_parent);
     385           1 :         assert_int_equal(ret, 0);
     386             :         /*
     387             :          * Now fork a new process
     388             :          */
     389             : 
     390           1 :         pid = fork();
     391           2 :         if (pid == 0) {
     392             :                 /*
     393             :                  * Child process
     394             :                  */
     395             : 
     396           1 :                 struct ldb_context *ldb = NULL;
     397           1 :                 close(to_child[1]);
     398           1 :                 close(to_parent[0]);
     399             : 
     400             :                 /*
     401             :                  * Wait for the transaction to start
     402             :                  */
     403           1 :                 ret = read(to_child[0], buf, 2);
     404           1 :                 assert_int_equal(ret, 2);
     405             : 
     406           1 :                 ldb = ldb_init(test_ctx, test_ctx->ev);
     407           1 :                 assert_non_null(ldb);
     408             : 
     409           1 :                 ret = ldb_connect(ldb, test_ctx->dbpath, 0, NULL);
     410           1 :                 assert_int_equal(ret, LDB_SUCCESS);
     411             : 
     412           1 :                 ldb_kv = get_ldb_kv(ldb);
     413           1 :                 assert_non_null(ldb_kv);
     414             :                 /*
     415             :                  * Add a record to the database
     416             :                  */
     417           1 :                 key.data = (uint8_t *)talloc_strdup(tmp_ctx, KEY1);
     418           1 :                 key.length = strlen(KEY1) + 1;
     419           1 :                 val.data = talloc_zero_size(tmp_ctx, RECORD_SIZE);
     420           1 :                 assert_non_null(val.data);
     421           1 :                 memset(val.data, 'x', RECORD_SIZE);
     422           1 :                 val.length = RECORD_SIZE;
     423          63 :                 for (i = 0; i < ITERATIONS; i++) {
     424          63 :                         ret = ldb_kv->kv_ops->begin_write(ldb_kv);
     425          63 :                         assert_int_equal(ret, 0);
     426          63 :                         ret = ldb_kv->kv_ops->store(ldb_kv, key, val, 0);
     427          63 :                         if (ret == LDB_ERR_BUSY && i > 0) {
     428           1 :                                 int rc = ldb_kv->kv_ops->abort_write(ldb_kv);
     429           1 :                                 assert_int_equal(rc, LDB_SUCCESS);
     430           1 :                                 break;
     431             :                         }
     432          62 :                         assert_int_equal(ret, LDB_SUCCESS);
     433          62 :                         ret = ldb_kv->kv_ops->finish_write(ldb_kv);
     434          62 :                         assert_int_equal(ret, LDB_SUCCESS);
     435             :                 }
     436           1 :                 assert_int_equal(ret, LDB_ERR_BUSY);
     437           1 :                 assert_int_not_equal(i, 0);
     438             : 
     439             :                 /*
     440             :                  * Begin a read transaction
     441             :                  */
     442           1 :                 ret = ldb_kv->kv_ops->lock_read(ldb->modules);
     443           1 :                 assert_int_equal(ret, LDB_SUCCESS);
     444             :                 /*
     445             :                  * read the record
     446             :                  * and close the transaction
     447             :                  */
     448           1 :                 key.data = (uint8_t *)talloc_strdup(tmp_ctx, KEY1);
     449           1 :                 key.length = strlen(KEY1) + 1;
     450             : 
     451           1 :                 ret = ldb_kv->kv_ops->fetch_and_parse(ldb_kv, key, parse, &val);
     452           1 :                 assert_int_equal(ret, LDB_SUCCESS);
     453             : 
     454           1 :                 ret = ldb_kv->kv_ops->unlock_read(ldb->modules);
     455           1 :                 assert_int_equal(ret, LDB_SUCCESS);
     456             : 
     457             :                 /*
     458             :                  * Signal the the parent that we've done the update
     459             :                  */
     460           1 :                 ret = write(to_parent[1], "GO", 2);
     461           1 :                 assert_int_equal(ret, 2);
     462           1 :                 exit(0);
     463             :         }
     464             : 
     465           1 :         close(to_child[0]);
     466           1 :         close(to_parent[1]);
     467             : 
     468             :         /*
     469             :          * Begin a read transaction
     470             :          */
     471           1 :         ret = ldb_kv->kv_ops->lock_read(test_ctx->ldb->modules);
     472           1 :         assert_int_equal(ret, LDB_SUCCESS);
     473             : 
     474             :         /*
     475             :          * Signal the child process
     476             :          */
     477           1 :         ret = write(to_child[1], "GO", 2);
     478           1 :         assert_int_equal(ret, 2);
     479             : 
     480             :         /*
     481             :          * Wait for the child process to update the record
     482             :          */
     483           1 :         ret = read(to_parent[0], buf, 2);
     484           1 :         assert_int_equal(ret, 2);
     485             : 
     486             :         /*
     487             :          * read the record
     488             :          * and close the transaction
     489             :          */
     490           1 :         key.data = (uint8_t *)talloc_strdup(tmp_ctx, KEY1);
     491           1 :         key.length = strlen(KEY1) + 1;
     492             : 
     493           1 :         ret = ldb_kv->kv_ops->fetch_and_parse(ldb_kv, key, parse, &val);
     494           1 :         assert_int_equal(ret, LDB_ERR_NO_SUCH_OBJECT);
     495           1 :         ret = ldb_kv->kv_ops->unlock_read(test_ctx->ldb->modules);
     496           1 :         assert_int_equal(ret, 0);
     497             : 
     498           1 :         close(to_child[1]);
     499           1 :         close(to_parent[0]);
     500           1 :         TALLOC_FREE(tmp_ctx);
     501           1 : }
     502             : 
     503             : /*
     504             :  * This tests forks a child process that opens a read lock and then
     505             :  * exits. This results in a stale reader entry in the lmdb lock file.
     506             :  */
     507           1 : static void test_free_list_stale_reader(void **state)
     508             : {
     509           1 :         int ret;
     510           1 :         struct test_ctx *test_ctx =
     511           1 :             talloc_get_type_abort(*state, struct test_ctx);
     512           1 :         struct ldb_kv_private *ldb_kv = get_ldb_kv(test_ctx->ldb);
     513           1 :         struct ldb_val key;
     514           1 :         struct ldb_val val;
     515             : 
     516           1 :         const char *KEY1 = "KEY01";
     517             : 
     518             :         /*
     519             :          * Pipes etc to coordinate the processes
     520             :          */
     521           1 :         int to_child[2];
     522           1 :         int to_parent[2];
     523           1 :         char buf[2];
     524           1 :         pid_t pid;
     525           1 :         size_t i;
     526             : 
     527           1 :         TALLOC_CTX *tmp_ctx;
     528           1 :         tmp_ctx = talloc_new(test_ctx);
     529           1 :         assert_non_null(tmp_ctx);
     530             : 
     531           1 :         ret = pipe(to_child);
     532           1 :         assert_int_equal(ret, 0);
     533           1 :         ret = pipe(to_parent);
     534           1 :         assert_int_equal(ret, 0);
     535             :         /*
     536             :          * Now fork a new process
     537             :          */
     538             : 
     539           1 :         pid = fork();
     540           2 :         if (pid == 0) {
     541             :                 /*
     542             :                  * Child process
     543             :                  */
     544             : 
     545           1 :                 struct ldb_context *ldb = NULL;
     546           1 :                 close(to_child[1]);
     547           1 :                 close(to_parent[0]);
     548             : 
     549             :                 /*
     550             :                  * Wait for the parent to get ready
     551             :                  */
     552           1 :                 ret = read(to_child[0], buf, 2);
     553           1 :                 assert_int_equal(ret, 2);
     554             : 
     555           1 :                 ldb = ldb_init(test_ctx, test_ctx->ev);
     556           1 :                 assert_non_null(ldb);
     557             : 
     558           1 :                 ret = ldb_connect(ldb, test_ctx->dbpath, 0, NULL);
     559           1 :                 assert_int_equal(ret, LDB_SUCCESS);
     560             : 
     561           1 :                 ldb_kv = get_ldb_kv(ldb);
     562           1 :                 assert_non_null(ldb_kv);
     563             : 
     564             :                 /*
     565             :                  * Begin a read transaction
     566             :                  */
     567           1 :                 ret = ldb_kv->kv_ops->lock_read(ldb->modules);
     568           1 :                 assert_int_equal(ret, LDB_SUCCESS);
     569             : 
     570             :                 /*
     571             :                  * Now exit with out releasing the read lock
     572             :                  * this will result in a stale entry in the
     573             :                  * read lock table.
     574             :                  */
     575             : 
     576           1 :                 exit(0);
     577             :         }
     578             : 
     579           1 :         close(to_child[0]);
     580           1 :         close(to_parent[1]);
     581             : 
     582             :         /*
     583             :          * Tell the child to start
     584             :          */
     585           1 :         ret = write(to_child[1], "GO", 2);
     586           1 :         assert_int_equal(ret, 2);
     587             : 
     588           1 :         close(to_child[1]);
     589           1 :         close(to_parent[0]);
     590             : 
     591             :         /*
     592             :          * Now wait for the child process to complete
     593             :          */
     594           1 :         waitpid(pid, NULL, 0);
     595             : 
     596             :         /*
     597             :          * Add a record to the database
     598             :          */
     599           1 :         key.data = (uint8_t *)talloc_strdup(tmp_ctx, KEY1);
     600           1 :         key.length = strlen(KEY1) + 1;
     601           1 :         val.data = talloc_zero_size(tmp_ctx, RECORD_SIZE);
     602           1 :         assert_non_null(val.data);
     603           1 :         memset(val.data, 'x', RECORD_SIZE);
     604           1 :         val.length = RECORD_SIZE;
     605         193 :         for (i = 0; i < ITERATIONS; i++) {
     606         192 :                 ret = ldb_kv->kv_ops->begin_write(ldb_kv);
     607         192 :                 assert_int_equal(ret, LDB_SUCCESS);
     608             : 
     609         192 :                 ret = ldb_kv->kv_ops->store(ldb_kv, key, val, 0);
     610         192 :                 if (ret == LDB_ERR_BUSY && i > 0) {
     611           0 :                         int rc = ldb_kv->kv_ops->abort_write(ldb_kv);
     612           0 :                         assert_int_equal(rc, LDB_SUCCESS);
     613           0 :                         break;
     614             :                 }
     615         192 :                 assert_int_equal(ret, LDB_SUCCESS);
     616             : 
     617         192 :                 ret = ldb_kv->kv_ops->finish_write(ldb_kv);
     618         192 :                 assert_int_equal(ret, LDB_SUCCESS);
     619             :         }
     620             :         /*
     621             :          * We now do an explicit clear of stale readers at the start of a
     622             :          * write transaction so should not get LDB_ERR_BUSY any more
     623             :          * assert_int_equal(ret, LDB_ERR_BUSY);
     624             :          */
     625           1 :         assert_int_equal(ret, LDB_SUCCESS);
     626           1 :         assert_int_not_equal(i, 0);
     627             : 
     628             :         /*
     629             :          * Begin a read transaction
     630             :          */
     631           1 :         ret = ldb_kv->kv_ops->lock_read(test_ctx->ldb->modules);
     632           1 :         assert_int_equal(ret, LDB_SUCCESS);
     633             :         /*
     634             :          * read the record
     635             :          * and close the transaction
     636             :          */
     637           1 :         key.data = (uint8_t *)talloc_strdup(tmp_ctx, KEY1);
     638           1 :         key.length = strlen(KEY1) + 1;
     639             : 
     640           1 :         ret = ldb_kv->kv_ops->fetch_and_parse(ldb_kv, key, parse, &val);
     641           1 :         assert_int_equal(ret, LDB_SUCCESS);
     642             : 
     643           1 :         ret = ldb_kv->kv_ops->unlock_read(test_ctx->ldb->modules);
     644           1 :         assert_int_equal(ret, LDB_SUCCESS);
     645             : 
     646           1 :         TALLOC_FREE(tmp_ctx);
     647           1 : }
     648             : 
     649           1 : int main(int argc, const char **argv)
     650             : {
     651           1 :         const struct CMUnitTest tests[] = {
     652             :             cmocka_unit_test_setup_teardown(
     653             :                 test_free_list_no_read_lock, setup, teardown),
     654             :             cmocka_unit_test_setup_teardown(
     655             :                 test_free_list_read_lock, setup, teardown),
     656             :             cmocka_unit_test_setup_teardown(
     657             :                 test_free_list_stale_reader, setup, teardown),
     658             :         };
     659             : 
     660           1 :         cmocka_set_message_output(CM_OUTPUT_SUBUNIT);
     661             : 
     662           1 :         return cmocka_run_group_tests(tests, NULL, NULL);
     663             : }

Generated by: LCOV version 1.14