1 /* This file is part of the Zebra server.
2    Copyright (C) 2004-2013 Index Data
3 
4 Zebra is free software; you can redistribute it and/or modify it under
5 the terms of the GNU General Public License as published by the Free
6 Software Foundation; either version 2, or (at your option) any later
7 version.
8 
9 Zebra is distributed in the hope that it will be useful, but WITHOUT ANY
10 WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12 for more details.
13 
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
17 
18 */
19 
20 #if HAVE_CONFIG_H
21 #include <config.h>
22 #endif
23 #include <assert.h>
24 #include <stdlib.h>
25 #include <yaz/test.h>
26 #include <yaz/log.h>
27 #if HAVE_SYS_TIME_H
28 #include <sys/time.h>
29 #endif
30 #include <time.h>
31 
32 #if HAVE_SYS_STAT_H
33 #include <sys/stat.h>
34 #endif
35 #if HAVE_SYS_TYPES_H
36 #include <sys/types.h>
37 #endif
38 #if HAVE_UNISTD_H
39 #include <unistd.h>
40 #endif
41 #if HAVE_SYS_WAIT_H
42 #include <sys/wait.h>
43 #endif
44 
45 #include <fcntl.h>
46 
47 #ifdef WIN32
48 #include <io.h>
49 #endif
50 
51 #if YAZ_POSIX_THREADS
52 #include <pthread.h>
53 #endif
54 #ifdef WIN32
55 #include <windows.h>
56 #include <process.h>
57 #endif
58 
59 #include <idzebra/flock.h>
60 #include <string.h>
61 
62 static char seq[1000];
63 static char *seqp = 0;
64 
65 #define NUM_THREADS 100
66 
67 #if YAZ_POSIX_THREADS
68 pthread_cond_t sleep_cond = PTHREAD_COND_INITIALIZER;
69 pthread_mutex_t sleep_mutex = PTHREAD_MUTEX_INITIALIZER;
70 #endif
71 
72 int test_fd = 0;
73 
small_sleep(void)74 static void small_sleep(void)
75 {
76 #ifdef WIN32
77     Sleep(2);
78 #else
79 #if YAZ_POSIX_THREADS
80     struct timespec abstime;
81     struct timeval now;
82 
83     gettimeofday(&now, 0);
84     abstime.tv_sec = now.tv_sec;
85     abstime.tv_nsec = 1000000 + now.tv_usec * 1000;
86     if (abstime.tv_nsec > 1000000000) /* 1s = 1e9 ns */
87     {
88         abstime.tv_nsec -= 1000000000;
89         abstime.tv_sec++;
90     }
91     pthread_mutex_lock(&sleep_mutex);
92     pthread_cond_timedwait(&sleep_cond, &sleep_mutex, &abstime);
93     pthread_mutex_unlock(&sleep_mutex);
94 #endif
95 #endif
96 }
97 
run_func(void * arg)98 void *run_func(void *arg)
99 {
100     int i;
101     int *pdata = (int*) arg;
102     int use_write_lock = *pdata;
103     ZebraLockHandle lh = zebra_lock_create(0, "my.LCK");
104     for (i = 0; i<2; i++)
105     {
106         int write_lock = use_write_lock;
107 
108         if (use_write_lock == 2) /* random lock */
109             write_lock = (rand() & 3) == 3 ? 1 : 0;
110 
111         if (write_lock)
112         {
113             zebra_lock_w(lh);
114 
115             write(test_fd, "L", 1);
116             *seqp++ = 'L';
117             small_sleep();
118             *seqp++ = 'U';
119             write(test_fd, "U", 1);
120 
121             zebra_unlock(lh);
122         }
123         else
124         {
125             zebra_lock_r(lh);
126 
127             write(test_fd, "l", 1);
128             *seqp++ = 'l';
129             small_sleep();
130             *seqp++ = 'u';
131             write(test_fd, "u", 1);
132 
133             zebra_unlock(lh);
134         }
135     }
136     zebra_lock_destroy(lh);
137     *pdata = 123;
138     return 0;
139 }
140 
141 #ifdef WIN32
ThreadProc(void * p)142 DWORD WINAPI ThreadProc(void *p)
143 {
144     run_func(p);
145     return 0;
146 }
147 #endif
148 
tst_thread(int num,int write_flag)149 static void tst_thread(int num, int write_flag)
150 {
151 #ifdef WIN32
152     HANDLE handles[NUM_THREADS];
153     DWORD dwThreadId[NUM_THREADS];
154 #endif
155 #if YAZ_POSIX_THREADS
156     pthread_t child_thread[NUM_THREADS];
157 #endif
158     int i, id[NUM_THREADS];
159 
160     seqp = seq;
161     assert (num <= NUM_THREADS);
162     for (i = 0; i < num; i++)
163     {
164         id[i] = write_flag;
165 #if YAZ_POSIX_THREADS
166         pthread_create(&child_thread[i], 0 /* attr */, run_func, &id[i]);
167 #endif
168 #ifdef WIN32
169         if (1)
170         {
171             void *pData = &id[i];
172             handles[i] = CreateThread(
173                 NULL,              /* default security attributes */
174                 0,                 /* use default stack size */
175                 ThreadProc,        /* thread function */
176                 pData,             /* argument to thread function */
177                 0,                 /* use default creation flags */
178                 &dwThreadId[i]);   /* returns the thread identifier */
179         }
180 
181 #endif
182     }
183 #if YAZ_POSIX_THREADS
184     for (i = 0; i<num; i++)
185         pthread_join(child_thread[i], 0);
186 #endif
187 #ifdef WIN32
188     WaitForMultipleObjects(num, handles, TRUE, INFINITE);
189 #endif
190     for (i = 0; i < num; i++)
191         YAZ_CHECK(id[i] == 123);
192     *seqp++ = '\0';
193     yaz_log(YLOG_LOG, "tst_thread(%d,%d) returns seq=%s",
194             num, write_flag, seq);
195 }
196 
tst(void)197 static void tst(void)
198 {
199     tst_thread(4, 1); /* write locks */
200     if (1)
201     {
202         int i = 0;
203         while (seq[i])
204         {
205             YAZ_CHECK_EQ(seq[i], 'L');
206             YAZ_CHECK_EQ(seq[i+1], 'U');
207             i = i + 2;
208         }
209     }
210 
211     tst_thread(6, 0);  /* read locks */
212 
213     tst_thread(20, 2); /* random locks */
214 }
215 
fork_tst(void)216 void fork_tst(void)
217 {
218 #if HAVE_SYS_WAIT_H
219     pid_t pid[2];
220     int i;
221 
222     for (i = 0; i<2; i++)
223     {
224         pid[i] = fork();
225         if (!pid[i])
226         {
227             tst();
228             exit(0);
229         }
230     }
231     for (i = 0; i<2; i++)
232     {
233         int status;
234         waitpid(pid[i], &status, 0);
235         YAZ_CHECK(status == 0);
236     }
237 #else
238     tst();
239 #endif
240 }
241 
main(int argc,char ** argv)242 int main(int argc, char **argv)
243 {
244     YAZ_CHECK_INIT(argc, argv);
245     YAZ_CHECK_LOG();
246 
247     /* ensure the flock system logs in our test */
248     yaz_log_init_level(yaz_log_mask_str("flock"));
249 
250     zebra_flock_init();
251 
252     test_fd = open("tstflock.out", (O_BINARY|O_CREAT|O_RDWR), 0666);
253     YAZ_CHECK(test_fd != -1);
254     if (test_fd != -1)
255     {
256         fork_tst();
257     }
258     YAZ_CHECK_TERM;
259 }
260 
261 
262 /*
263  * Local variables:
264  * c-basic-offset: 4
265  * c-file-style: "Stroustrup"
266  * indent-tabs-mode: nil
267  * End:
268  * vim: shiftwidth=4 tabstop=8 expandtab
269  */
270 
271