51f88c11685c2b85fb197a8de0173ab279b9c543
[samba.git] / source / cluster / ctdb / tests / lockwait.c
1 /* 
2    test a lock wait idea
3
4    Copyright (C) Andrew Tridgell  2006
5
6    This library is free software; you can redistribute it and/or
7    modify it under the terms of the GNU Lesser General Public
8    License as published by the Free Software Foundation; either
9    version 2 of the License, or (at your option) any later version.
10
11    This library is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14    Lesser General Public License for more details.
15
16    You should have received a copy of the GNU Lesser General Public
17    License along with this library; if not, write to the Free Software
18    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19 */
20
21 #include "includes.h"
22 #include "lib/events/events.h"
23 #include "system/filesys.h"
24 #include "system/wait.h"
25 #include "popt.h"
26 #include "cmdline.h"
27
28
29 struct lockwait_handle {
30         struct fd_event *fde;
31         int fd[2];
32         pid_t child;
33         void *private_data;
34         void (*callback)(void *);
35 };
36
37 static void lockwait_handler(struct event_context *ev, struct fd_event *fde, 
38                              uint16_t flags, void *private_data)
39 {
40         struct lockwait_handle *h = talloc_get_type(private_data, 
41                                                      struct lockwait_handle);
42         void (*callback)(void *) = h->callback;
43         void *p = h->private_data;
44         talloc_set_destructor(h, NULL);
45         close(h->fd[0]);
46         talloc_free(h); 
47         callback(p);
48         waitpid(h->child, NULL, 0);
49 }
50
51 static int lockwait_destructor(struct lockwait_handle *h)
52 {
53         close(h->fd[0]);
54         kill(h->child, SIGKILL);
55         waitpid(h->child, NULL, 0);
56         return 0;
57 }
58
59
60 static struct lockwait_handle *lockwait(struct event_context *ev, 
61                                         TALLOC_CTX *mem_ctx,
62                                         int fd, off_t ofs, size_t len,
63                                         void (*callback)(void *), void *private_data)
64 {
65         struct lockwait_handle *h;
66         int ret;
67
68         h = talloc_zero(mem_ctx, struct lockwait_handle);
69         if (h == NULL) {
70                 return NULL;
71         }
72
73         ret = pipe(h->fd);
74         if (ret != 0) {
75                 talloc_free(h);
76                 return NULL;
77         }
78
79         h->child = fork();
80         if (h->child == (pid_t)-1) {
81                 close(h->fd[0]);
82                 close(h->fd[1]);
83                 talloc_free(h);
84                 return NULL;
85         }
86
87         h->callback = callback;
88         h->private_data = private_data;
89
90         if (h->child == 0) {
91                 /* in child */
92                 struct flock lock;
93                 close(h->fd[0]);
94                 lock.l_type = F_WRLCK;
95                 lock.l_whence = SEEK_SET;
96                 lock.l_start = ofs;
97                 lock.l_len = len;
98                 lock.l_pid = 0;
99                 fcntl(fd,F_SETLKW,&lock);
100                 _exit(0);
101         }
102
103         close(h->fd[1]);
104         talloc_set_destructor(h, lockwait_destructor);
105
106         h->fde = event_add_fd(ev, h, h->fd[0], EVENT_FD_READ, lockwait_handler, h);
107         if (h->fde == NULL) {
108                 talloc_free(h);
109                 return NULL;
110         }
111
112         return h;
113 }
114
115
116
117
118 static void fcntl_lock_callback(void *p)
119 {
120         int *got_lock = (int *)p;
121         *got_lock = 1;
122 }
123
124 /*
125   get an fcntl lock - waiting if necessary
126  */
127 static int fcntl_lock(struct event_context *ev,
128                       int fd, int op, off_t offset, off_t count, int type)
129 {
130         struct flock lock;
131         int ret;
132         int use_lockwait = (op == F_SETLKW);
133         int got_lock = 0;
134
135         lock.l_type = type;
136         lock.l_whence = SEEK_SET;
137         lock.l_start = offset;
138         lock.l_len = count;
139         lock.l_pid = 0;
140
141         do {
142                 ret = fcntl(fd,use_lockwait?F_SETLK:op,&lock);
143                 if (ret == 0) {
144                         return 0;
145                 }
146                 if (ret == -1 && 
147                     (errno == EACCES || errno == EAGAIN || errno == EDEADLK)) {
148                         struct lockwait_handle *h;
149                         h = lockwait(ev, ev, fd, offset, count, 
150                                      fcntl_lock_callback, &got_lock);
151                         if (h == NULL) {
152                                 errno = ENOLCK;
153                                 return -1;
154                         }
155                         /* in real code we would return to the event loop */
156                         while (!got_lock) {
157                                 event_loop_once(ev);
158                         }
159                         got_lock = 0;
160                 }
161         } while (!got_lock);
162
163         return ret;
164 }
165
166 static void child(struct event_context *ev, int n)
167 {
168         int fd;
169         int count=0;
170         struct timeval tv;
171         fd = open("test.dat", O_CREAT|O_RDWR, 0666);
172         if (fd == -1) {
173                 perror("test.dat");
174                 exit(1);
175         }
176
177         tv = timeval_current();
178
179         while (timeval_elapsed(&tv) < 10) {
180                 int ret;
181                 ret = fcntl_lock(ev, fd, F_SETLKW, 0, 1, F_WRLCK);
182                 if (ret != 0) {
183                         printf("Failed to get lock in child %d!\n", n);
184                         break;
185                 }
186                 fcntl_lock(ev, fd, F_SETLK, 0, 1, F_UNLCK);
187                 count++;
188         }
189
190         printf("child %2d %.0f ops/sec\n", n, count/timeval_elapsed(&tv));
191         _exit(0);
192 }
193
194 static int timelimit = 10;
195
196 /*
197   main program
198 */
199 int main(int argc, const char *argv[])
200 {
201         pid_t *pids;
202         int nprogs = 2;
203         int i;
204         struct event_context *ev;
205         struct poptOption popt_options[] = {
206                 POPT_AUTOHELP
207                 { "timelimit", 't', POPT_ARG_INT, &timelimit, 0, "timelimit", "integer" },
208                 { "num-progs", 'n', POPT_ARG_INT, &nprogs, 0, "num_progs", "integer" },
209                 POPT_TABLEEND
210         };
211         poptContext pc;
212         int opt;
213
214         pc = poptGetContext(argv[0], argc, argv, popt_options, POPT_CONTEXT_KEEP_FIRST);
215
216         while ((opt = poptGetNextOpt(pc)) != -1) {
217                 switch (opt) {
218                 default:
219                         fprintf(stderr, "Invalid option %s: %s\n", 
220                                 poptBadOption(pc, 0), poptStrerror(opt));
221                         exit(1);
222                 }
223         }
224
225         ev = event_context_init(NULL);
226
227         pids = talloc_array(ev, pid_t, nprogs);
228
229         /* create N processes fighting over the same lock */
230         for (i=0;i<nprogs;i++) {
231                 pids[i] = fork();
232                 if (pids[i] == 0) {
233                         child(ev, i);
234                 }
235         }
236
237         printf("Waiting for %d children ...\n", nprogs);
238
239         /* wait for our kids to finish playing */
240         for (i=0;i<nprogs;i++) {
241                 waitpid(pids[i], NULL, 0);
242         }       
243
244         return 0;
245 }