tdb: fix test helper to include own header, fix function definition.
[ccan] / ccan / tdb / tools / starvation.c
1 /* Demonstrate starvation of tdb_lockall */
2 #include <ccan/tdb/tdb.h>
3 #include <err.h>
4 #include <unistd.h>
5 #include <fcntl.h>
6 #include <time.h>
7 #include <stdlib.h>
8 #include <stdbool.h>
9 #include <stdarg.h>
10 #include <stdio.h>
11 #include <string.h>
12 #include <sys/time.h>
13
14 static void usage(const char *extra)
15 {
16         errx(1, "%s%s"
17              "Usage: starvation [lockall|gradual] <num> <worktime-in-ms>\n"
18              "  Each locker holds lock for between 1/2 and 1 1/2 times\n"
19              "  worktime, then sleeps for one second.\n\n"
20              "  Main process tries tdb_lockall or tdb_lockall_gradual.",
21              extra ? extra : "", extra ? "\n" : "");
22 }
23
24 static void run_and_sleep(struct tdb_context *tdb, int parentfd, unsigned time)
25 {
26         char c;
27         struct timespec hold;
28         unsigned rand, randtime;
29         TDB_DATA key;
30
31         key.dptr = (void *)&rand;
32         key.dsize = sizeof(rand);
33
34         while (read(parentfd, &c, 1) != 0) {
35                 /* Lock a random key. */
36                 rand = random();
37                 if (tdb_chainlock(tdb, key) != 0)
38                         errx(1, "chainlock failed: %s", tdb_errorstr(tdb));
39
40                 /* Hold it for some variable time. */
41                 randtime = time / 2 + (random() % time);
42                 hold.tv_sec = randtime / 1000;
43                 hold.tv_nsec = (randtime % 1000) * 1000000;
44                 nanosleep(&hold, NULL);
45
46                 if (tdb_chainunlock(tdb, key) != 0)
47                         errx(1, "chainunlock failed: %s", tdb_errorstr(tdb));
48
49                 /* Wait for a second without the lock. */
50                 sleep(1);
51         }
52         exit(0);
53 }
54
55 static void logfn(struct tdb_context *tdb,
56                   enum tdb_debug_level level,
57                   const char *fmt, ...)
58 {
59            va_list ap;
60
61            va_start(ap, fmt);
62            vfprintf(stderr, fmt, ap);
63            va_end(ap);
64 }
65
66 int main(int argc, char *argv[])
67 {
68         int (*lockall)(struct tdb_context *);
69         unsigned int num, worktime, i;
70         int pfd[2];
71         struct tdb_context *tdb;
72         struct tdb_logging_context log = { logfn, NULL };
73         struct timeval start, end, duration;
74
75         if (argc != 4)
76                 usage(NULL);
77
78         if (strcmp(argv[1], "lockall") == 0)
79                 lockall = tdb_lockall;
80         else if (strcmp(argv[1], "gradual") == 0) {
81 #if 0
82                 lockall = tdb_lockall_gradual;
83 #else
84                 errx(1, "gradual is now the default implementation");
85 #endif
86         } else
87                 usage("Arg1 should be 'lockall' or 'gradual'");
88
89         num = atoi(argv[2]);
90         worktime = atoi(argv[3]);
91
92         if (!num || !worktime)
93                 usage("Number of threads and worktime must be non-zero");
94
95         if (pipe(pfd) != 0)
96                 err(1, "Creating pipe");
97
98         tdb = tdb_open_ex("/tmp/starvation.tdb", 10000, TDB_DEFAULT,
99                           O_RDWR|O_CREAT|O_TRUNC, 0600, &log, NULL);
100         if (!tdb)
101                 err(1, "Opening tdb /tmp/starvation.tdb");
102
103         for (i = 0; i < num; i++) {
104                 switch (fork()) {
105                 case 0:
106                         close(pfd[1]);
107                         fcntl(pfd[0], F_SETFL,
108                               fcntl(pfd[0], F_GETFL)|O_NONBLOCK);
109                         srandom(getpid() + i);
110                         if (tdb_reopen(tdb) != 0)
111                                 err(1, "Reopening tdb %s", tdb_name(tdb));
112
113                         run_and_sleep(tdb, pfd[0], worktime);
114                 case -1:
115                         err(1, "forking");
116                 }
117                 /* Stagger the children. */
118                 usleep(random() % (1000000 / num));
119         }
120
121         close(pfd[0]);
122         sleep(1);
123         gettimeofday(&start, NULL);
124         if (lockall(tdb) != 0)
125                 errx(1, "lockall failed: %s", tdb_errorstr(tdb));
126         gettimeofday(&end, NULL);
127
128         duration.tv_sec = end.tv_sec - start.tv_sec;
129         duration.tv_usec = end.tv_usec - start.tv_usec;
130         if (duration.tv_usec < 0) {
131                 --duration.tv_sec;
132                 duration.tv_usec += 1000000;
133         }
134
135         if (tdb_unlockall(tdb) != 0)
136                 errx(1, "unlockall failed: %s", tdb_errorstr(tdb));
137         tdb_close(tdb);
138         unlink("/tmp/starvation.tdb");
139
140         printf("Took %lu.%06lu seconds\n", duration.tv_sec, duration.tv_usec);
141         return 0;
142 }