LCOV - code coverage report
Current view: top level - lib/tdb/test - run-allrecord-traverse-deadlock.c (source / functions) Hit Total Coverage
Test: coverage report for master 98b443d9 Lines: 92 96 95.8 %
Date: 2024-05-31 13:13:24 Functions: 4 4 100.0 %

          Line data    Source code
       1             : #include "../common/tdb_private.h"
       2             : #include "../common/io.c"
       3             : #include "../common/tdb.c"
       4             : #include "../common/lock.c"
       5             : #include "../common/freelist.c"
       6             : #include "../common/traverse.c"
       7             : #include "../common/transaction.c"
       8             : #include "../common/error.c"
       9             : #include "../common/open.c"
      10             : #include "../common/check.c"
      11             : #include "../common/hash.c"
      12             : #include "../common/mutex.c"
      13             : #include "tap-interface.h"
      14             : #include <stdlib.h>
      15             : #include <sys/types.h>
      16             : #include <sys/wait.h>
      17             : #include <stdarg.h>
      18             : #include "logging.h"
      19             : 
      20           2 : static void do_allrecord_lock(const char *name, int tdb_flags, int up,
      21             :                               int down)
      22             : {
      23             :         struct tdb_context *tdb;
      24             :         int ret;
      25             :         ssize_t nread, nwritten;
      26           2 :         char c = 0;
      27             : 
      28           2 :         tdb = tdb_open_ex(name, 3, tdb_flags,
      29             :                           O_RDWR|O_CREAT, 0755, &taplogctx, NULL);
      30           2 :         ok(tdb, "tdb_open_ex should succeed");
      31             : 
      32           2 :         ret = tdb_lockall(tdb);
      33           2 :         ok(ret == 0, "tdb_lockall should succeed");
      34             : 
      35           2 :         nwritten = write(up, &c, sizeof(c));
      36           2 :         ok(nwritten == sizeof(c), "write should succeed");
      37             : 
      38           2 :         nread = read(down, &c, sizeof(c));
      39           2 :         ok(nread == sizeof(c), "read should succeed");
      40             : 
      41           2 :         ret = tdb_traverse(tdb, NULL, NULL);
      42           2 :         ok(ret == -1, "do_allrecord_lock: traverse should fail");
      43             : 
      44           2 :         nwritten = write(up, &c, sizeof(c));
      45           2 :         ok(nwritten == sizeof(c), "write should succeed");
      46             : 
      47           2 :         exit(0);
      48             : }
      49             : 
      50           2 : static void do_traverse(const char *name, int tdb_flags, int up, int down)
      51             : {
      52             :         struct tdb_context *tdb;
      53             :         int ret;
      54             :         ssize_t nread, nwritten;
      55           2 :         char c = 0;
      56             : 
      57           2 :         tdb = tdb_open_ex(name, 3, tdb_flags,
      58             :                           O_RDWR|O_CREAT, 0755, &taplogctx, NULL);
      59           2 :         ok(tdb, "tdb_open_ex should succeed");
      60             : 
      61           2 :         ret = tdb_traverse(tdb, NULL, NULL);
      62           2 :         ok(ret == 1, "do_traverse: tdb_traverse should return 1 record");
      63             : 
      64           2 :         nwritten = write(up, &c, sizeof(c));
      65           2 :         ok(nwritten == sizeof(c), "write should succeed");
      66             : 
      67           2 :         nread = read(down, &c, sizeof(c));
      68           2 :         ok(nread == sizeof(c), "read should succeed");
      69             : 
      70           2 :         exit(0);
      71             : }
      72             : 
      73             : /*
      74             :  * Process 1: get the allrecord_lock on a tdb.
      75             :  * Process 2: start a traverse, this will stall waiting for the
      76             :  *            first chainlock: That is taken by the allrecord_lock
      77             :  * Process 1: start a traverse: This will get EDEADLK in trying to
      78             :  *            get the TRANSACTION_LOCK. It will deadlock for mutexes,
      79             :  *            which don't have built-in deadlock detection.
      80             :  */
      81             : 
      82           2 : static int do_tests(const char *name, int tdb_flags)
      83             : {
      84             :         struct tdb_context *tdb;
      85             :         int ret;
      86             :         pid_t traverse_child, allrecord_child;
      87             :         int traverse_down[2];
      88             :         int traverse_up[2];
      89             :         int allrecord_down[2];
      90             :         int allrecord_up[2];
      91             :         char c;
      92             :         ssize_t nread, nwritten;
      93             :         TDB_DATA key, data;
      94             : 
      95           2 :         key.dsize = strlen("hi");
      96           2 :         key.dptr = discard_const_p(uint8_t, "hi");
      97           2 :         data.dsize = strlen("world");
      98           2 :         data.dptr = discard_const_p(uint8_t, "world");
      99             : 
     100           2 :         tdb = tdb_open_ex(name, 3, tdb_flags,
     101             :                           O_RDWR|O_CREAT, 0755, &taplogctx, NULL);
     102           2 :         ok(tdb, "tdb_open_ex should succeed");
     103             : 
     104           2 :         ret = tdb_store(tdb, key, data, TDB_INSERT);
     105           2 :         ok(ret == 0, "tdb_store should succeed");
     106             : 
     107           2 :         ret = pipe(traverse_down);
     108           2 :         ok(ret == 0, "pipe should succeed");
     109             : 
     110           2 :         ret = pipe(traverse_up);
     111           2 :         ok(ret == 0, "pipe should succeed");
     112             : 
     113           2 :         ret = pipe(allrecord_down);
     114           2 :         ok(ret == 0, "pipe should succeed");
     115             : 
     116           2 :         ret = pipe(allrecord_up);
     117           2 :         ok(ret == 0, "pipe should succeed");
     118             : 
     119           2 :         allrecord_child = fork();
     120           4 :         ok(allrecord_child != -1, "fork should succeed");
     121             : 
     122           4 :         if (allrecord_child == 0) {
     123           2 :                 tdb_close(tdb);
     124           2 :                 close(traverse_up[0]);
     125           2 :                 close(traverse_up[1]);
     126           2 :                 close(traverse_down[0]);
     127           2 :                 close(traverse_down[1]);
     128           2 :                 close(allrecord_up[0]);
     129           2 :                 close(allrecord_down[1]);
     130           2 :                 do_allrecord_lock(name, tdb_flags,
     131             :                                   allrecord_up[1], allrecord_down[0]);
     132           0 :                 exit(0);
     133             :         }
     134           2 :         close(allrecord_up[1]);
     135           2 :         close(allrecord_down[0]);
     136             : 
     137           2 :         nread = read(allrecord_up[0], &c, sizeof(c));
     138           2 :         ok(nread == sizeof(c), "read should succeed");
     139             : 
     140           2 :         traverse_child = fork();
     141           4 :         ok(traverse_child != -1, "fork should succeed");
     142             : 
     143           4 :         if (traverse_child == 0) {
     144           2 :                 tdb_close(tdb);
     145           2 :                 close(traverse_up[0]);
     146           2 :                 close(traverse_down[1]);
     147           2 :                 close(allrecord_up[0]);
     148           2 :                 close(allrecord_down[1]);
     149           2 :                 do_traverse(name, tdb_flags,
     150             :                             traverse_up[1], traverse_down[0]);
     151           0 :                 exit(0);
     152             :         }
     153           2 :         close(traverse_up[1]);
     154           2 :         close(traverse_down[0]);
     155             : 
     156           2 :         poll(NULL, 0, 1000);
     157             : 
     158           2 :         nwritten = write(allrecord_down[1], &c, sizeof(c));
     159           2 :         ok(nwritten == sizeof(c), "write should succeed");
     160             : 
     161           2 :         nread = read(traverse_up[0], &c, sizeof(c));
     162           2 :         ok(nread == sizeof(c), "read should succeed");
     163             : 
     164           2 :         nwritten = write(traverse_down[1], &c, sizeof(c));
     165           2 :         ok(nwritten == sizeof(c), "write should succeed");
     166             : 
     167           2 :         nread = read(allrecord_up[0], &c, sizeof(c));
     168           2 :         ok(nread == sizeof(c), "ret should succeed");
     169             : 
     170           2 :         close(traverse_up[0]);
     171           2 :         close(traverse_down[1]);
     172           2 :         close(allrecord_up[0]);
     173           2 :         close(allrecord_down[1]);
     174           2 :         diag("%s tests done", name);
     175           2 :         return exit_status();
     176             : }
     177             : 
     178           1 : int main(int argc, char *argv[])
     179             : {
     180             :         int ret;
     181             :         bool mutex_support;
     182             : 
     183           1 :         mutex_support = tdb_runtime_check_for_robust_mutexes();
     184             : 
     185           1 :         ret = do_tests("marklock-deadlock-fcntl.tdb",
     186             :                        TDB_CLEAR_IF_FIRST |
     187             :                        TDB_INCOMPATIBLE_HASH);
     188           1 :         ok(ret == 0, "marklock-deadlock-fcntl.tdb tests should succeed");
     189             : 
     190           1 :         if (!mutex_support) {
     191           0 :                 skip(1, "No robust mutex support, "
     192             :                         "skipping marklock-deadlock-mutex.tdb tests");
     193           0 :                 return exit_status();
     194             :         }
     195             : 
     196           1 :         ret = do_tests("marklock-deadlock-mutex.tdb",
     197             :                        TDB_CLEAR_IF_FIRST |
     198             :                        TDB_MUTEX_LOCKING |
     199             :                        TDB_INCOMPATIBLE_HASH);
     200           1 :         ok(ret == 0, "marklock-deadlock-mutex.tdb tests should succeed");
     201             : 
     202           1 :         return exit_status();
     203             : }

Generated by: LCOV version 1.14