1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *     http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.commons.transaction.locking;
18  
19  import static junit.framework.Assert.assertEquals;
20  import static junit.framework.Assert.assertTrue;
21  
22  import java.util.concurrent.TimeUnit;
23  
24  import junit.framework.JUnit4TestAdapter;
25  
26  import org.apache.commons.logging.Log;
27  import org.apache.commons.logging.LogFactory;
28  import org.apache.commons.transaction.util.RendezvousBarrier;
29  import org.junit.Test;
30  
31  /**
32   * Tests for locking.
33   * 
34   */
35  public class LockTest {
36  
37      private Log log = LogFactory.getLog(getClass());
38  
39      private static final int CONCURRENT_TESTS = 25;
40  
41      protected static final long TIMEOUT = 1000000;
42  
43      private static int deadlockCnt = 0;
44  
45      private static String defaultResource = "resource";
46  
47      public static junit.framework.Test suite() {
48          return new JUnit4TestAdapter(LockTest.class);
49      }
50  
51      public static void main(java.lang.String[] args) {
52          junit.textui.TestRunner.run(suite());
53      }
54  
55      @Test
56      public void deadlock() throws Throwable {
57  
58          log.info("\n\nChecking deadlock detection\n\n");
59  
60          final String res1 = "res1";
61          final String res2 = "res2";
62  
63          final RWLockManager<Object, Object> manager = new RWLockManager<Object, Object>();
64  
65          final RendezvousBarrier restart = new RendezvousBarrier("restart", TIMEOUT);
66  
67          for (int i = 0; i < CONCURRENT_TESTS; i++) {
68  
69              System.out.print(".");
70  
71              final RendezvousBarrier deadlockBarrier1 = new RendezvousBarrier("deadlock1" + i,
72                      TIMEOUT);
73  
74              Thread deadlock = new Thread(new Runnable() {
75                  public void run() {
76                      try {
77                          manager.startWork(10, TimeUnit.SECONDS);
78                          // first both threads get a lock, this one on res2
79                          manager.lock(defaultResource, res2, true);
80                          synchronized (deadlockBarrier1) {
81                              deadlockBarrier1.meet();
82                              deadlockBarrier1.reset();
83                          }
84                          // if I am first, the other thread will be dead, i.e.
85                          // exactly one
86                          manager.lock(defaultResource, res1, true);
87                      } catch (LockException le) {
88                          assertEquals(le.getCode(), LockException.Code.WOULD_DEADLOCK);
89                          deadlockCnt++;
90                      } catch (InterruptedException ie) {
91                      } finally {
92                          manager.endWork();
93                          try {
94                              synchronized (restart) {
95                                  restart.meet();
96                                  restart.reset();
97                              }
98                          } catch (InterruptedException ie) {
99                          }
100                     }
101                 }
102             }, "Deadlock Thread");
103 
104             deadlock.start();
105 
106             try {
107                 manager.startWork(10, TimeUnit.SECONDS);
108                 // first both threads get a lock, this one on res2
109                 manager.lock(defaultResource, res1, false);
110                 synchronized (deadlockBarrier1) {
111                     deadlockBarrier1.meet();
112                     deadlockBarrier1.reset();
113                 }
114                 // if I am first, the other thread will be dead, i.e. exactly
115                 // one
116                 manager.lock(defaultResource, res2, true);
117             } catch (LockException le) {
118                 assertEquals(le.getCode(), LockException.Code.WOULD_DEADLOCK);
119                 deadlockCnt++;
120             } finally {
121                 manager.endWork();
122                 synchronized (restart) {
123                     restart.meet();
124                     restart.reset();
125                 }
126             }
127 
128             // XXX in special scenarios the current implementation might cause
129             // both
130             // owners to be deadlock victims
131             if (deadlockCnt != 1) {
132                 log.warn("More than one thread was deadlock victim!");
133             }
134             assertTrue(deadlockCnt >= 1);
135             deadlockCnt = 0;
136         }
137     }
138 
139     @Test
140     public void stress() throws Throwable {
141 
142         log.info("\n\nStress checking locks\n\n");
143 
144         final String res1 = "res1";
145         final String res2 = "res2";
146         final String res3 = "res3";
147 
148         final RWLockManager<Object, Object> manager = new RWLockManager<Object, Object>();
149 
150         final RendezvousBarrier restart = new RendezvousBarrier("restart", 5, TIMEOUT);
151         final RendezvousBarrier start = new RendezvousBarrier("start", 5, TIMEOUT);
152 
153         for (int i = 0; i < CONCURRENT_TESTS; i++) {
154 
155             System.out.print(".");
156 
157             Thread t1 = new Thread(new Runnable() {
158                 public void run() {
159                     try {
160                         try {
161                             synchronized (start) {
162                                 start.meet();
163                                 start.reset();
164                             }
165                             manager.startWork(10, TimeUnit.SECONDS);
166                             manager.lock(defaultResource, res1, false);
167                             manager.lock(defaultResource, res2, false);
168                             manager.lock(defaultResource, res3, true);
169                         } catch (LockException ie) {
170                         } finally {
171                             manager.endWork();
172                             synchronized (restart) {
173                                 restart.meet();
174                                 restart.reset();
175                             }
176                         }
177                     } catch (InterruptedException ie) {
178                     }
179                 }
180             }, "Thread #1");
181             t1.start();
182 
183             Thread t2 = new Thread(new Runnable() {
184                 public void run() {
185                     try {
186                         try {
187                             synchronized (start) {
188                                 start.meet();
189                                 start.reset();
190                             }
191                             manager.startWork(10, TimeUnit.SECONDS);
192                             manager.lock(defaultResource, res1, false);
193                             manager.lock(defaultResource, res2, false);
194                             manager.lock(defaultResource, res3, true);
195                         } catch (LockException ie) {
196                         } finally {
197                             manager.endWork();
198                             synchronized (restart) {
199                                 restart.meet();
200                                 restart.reset();
201                             }
202                         }
203                     } catch (InterruptedException ie) {
204                     }
205                 }
206             }, "Thread #2");
207             t2.start();
208 
209             Thread t3 = new Thread(new Runnable() {
210                 public void run() {
211                     try {
212                         try {
213                             synchronized (start) {
214                                 start.meet();
215                                 start.reset();
216                             }
217                             manager.startWork(10, TimeUnit.SECONDS);
218                             manager.lock(defaultResource, res1, false);
219                             manager.lock(defaultResource, res2, false);
220                             manager.lock(defaultResource, res3, true);
221                         } catch (LockException ie) {
222                         } finally {
223                             manager.endWork();
224                             synchronized (restart) {
225                                 restart.meet();
226                                 restart.reset();
227                             }
228                         }
229                     } catch (InterruptedException ie) {
230                     }
231                 }
232             }, "Thread #3");
233             t3.start();
234 
235             Thread t4 = new Thread(new Runnable() {
236                 public void run() {
237                     try {
238                         try {
239                             synchronized (start) {
240                                 start.meet();
241                                 start.reset();
242                             }
243                             manager.startWork(10, TimeUnit.SECONDS);
244                             manager.lock(defaultResource, res1, false);
245                             manager.lock(defaultResource, res2, false);
246                             manager.lock(defaultResource, res3, true);
247                         } catch (LockException ie) {
248                         } finally {
249                             manager.endWork();
250                             synchronized (restart) {
251                                 restart.meet();
252                                 restart.reset();
253                             }
254                         }
255                     } catch (InterruptedException ie) {
256                     }
257                 }
258             }, "Thread #4");
259             t4.start();
260 
261             try {
262                 try {
263                     synchronized (start) {
264                         start.meet();
265                         start.reset();
266                     }
267                     manager.startWork(10, TimeUnit.SECONDS);
268                     manager.lock(defaultResource, res1, false);
269                     manager.lock(defaultResource, res2, false);
270                     manager.lock(defaultResource, res3, false);
271                 } catch (LockException ie) {
272                 } finally {
273                     manager.endWork();
274                     try {
275                         synchronized (restart) {
276                             restart.meet();
277                             restart.reset();
278                         }
279                     } catch (InterruptedException ie) {
280                     }
281                 }
282             } catch (InterruptedException ie) {
283             }
284         }
285 
286     }
287 
288     @Test
289     public void choas() throws Throwable {
290 
291         log.info("\n\nChaos testing locks for internal deadlocks resp. concurrent mods\n\n");
292 
293         final String res1 = "res1";
294         final String res2 = "res2";
295         final String res3 = "res3";
296 
297         final RWLockManager<Object, Object> manager = new RWLockManager<Object, Object>();
298 
299         int concurrentThreads = 7;
300         int threads = CONCURRENT_TESTS * concurrentThreads;
301 
302         final RendezvousBarrier end = new RendezvousBarrier("end", threads + 1, TIMEOUT);
303 
304         log.info("\n\nStarting " + threads + " threads\n\n");
305 
306         for (int i = 0; i < CONCURRENT_TESTS; i++) {
307 
308             final int cnt = i;
309 
310             System.out.print(".");
311 
312             Thread t1 = new Thread(new Runnable() {
313                 public void run() {
314                     try {
315                         manager.startWork(10, TimeUnit.SECONDS);
316                         manager.lock(defaultResource, res1, false);
317                         manager.lock(defaultResource, res2, false);
318                         manager.lock(defaultResource, res3, true);
319                     } catch (LockException ie) {
320                         System.out.print("-");
321                     } finally {
322                         manager.endWork();
323                         end.call();
324                     }
325                 }
326             }, "Thread #1");
327 
328             Thread t2 = new Thread(new Runnable() {
329                 public void run() {
330                     try {
331                         manager.startWork(10, TimeUnit.SECONDS);
332                         manager.lock(defaultResource, res1, false);
333                         manager.lock(defaultResource, res2, false);
334                         manager.lock(defaultResource, res3, true);
335                     } catch (LockException ie) {
336                         System.out.print("-");
337                     } finally {
338                         manager.endWork();
339                         end.call();
340                     }
341                 }
342             }, "Thread #2");
343 
344             Thread t3 = new Thread(new Runnable() {
345                 public void run() {
346                     try {
347                         manager.startWork(10 + cnt, TimeUnit.SECONDS);
348                         manager.lock(defaultResource, res1, false);
349                         manager.lock(defaultResource, res2, false);
350                         manager.lock(defaultResource, res3, true);
351                     } catch (LockException le) {
352                         if (le.getCode() == LockException.Code.TIMED_OUT) {
353                             System.out.print("*");
354                         } else {
355                             System.out.print("-");
356                         }
357                     } finally {
358                         manager.endWork();
359                         end.call();
360                     }
361                 }
362             }, "Thread #3");
363 
364             Thread t4 = new Thread(new Runnable() {
365                 public void run() {
366                     try {
367                         manager.startWork(10, TimeUnit.SECONDS);
368                         manager.lock(defaultResource, res1, false);
369                         manager.lock(defaultResource, res2, false);
370                         manager.lock(defaultResource, res3, true);
371                     } catch (LockException le) {
372                         System.out.print("-");
373                     } finally {
374                         manager.endWork();
375                         end.call();
376                     }
377                 }
378             }, "Thread #4");
379 
380             Thread deadlock1 = new Thread(new Runnable() {
381                 public void run() {
382                     try {
383                         manager.startWork(10, TimeUnit.SECONDS);
384                         manager.lock(defaultResource, res2, true);
385                         manager.lock(defaultResource, res1, true);
386                     } catch (LockException le) {
387                         assertEquals(le.getCode(), LockException.Code.WOULD_DEADLOCK);
388                         System.out.print("-");
389                     } finally {
390                         manager.endWork();
391                         end.call();
392                     }
393                 }
394             }, "Deadlock1 Thread");
395 
396             Thread deadlock2 = new Thread(new Runnable() {
397                 public void run() {
398                     try {
399                         manager.startWork(10, TimeUnit.SECONDS);
400                         manager.lock(defaultResource, res1, false);
401                         manager.lock(defaultResource, res2, false);
402                     } catch (LockException le) {
403                         assertEquals(le.getCode(), LockException.Code.WOULD_DEADLOCK);
404                         System.out.print("-");
405                     } finally {
406                         manager.endWork();
407                         end.call();
408                     }
409                 }
410             }, "Deadlock1 Thread");
411 
412             Thread reader = new Thread(new Runnable() {
413                 public void run() {
414                     try {
415                         manager.startWork(10, TimeUnit.SECONDS);
416                         manager.lock(defaultResource, res1, false);
417                         manager.lock(defaultResource, res2, false);
418                         manager.lock(defaultResource, res3, false);
419                     } catch (LockException ie) {
420                         System.out.print("-");
421                     } finally {
422                         manager.endWork();
423                         end.call();
424                     }
425                 }
426             }, "Reader Thread");
427 
428             t4.start();
429             t3.start();
430             reader.start();
431             t1.start();
432             deadlock2.start();
433             t2.start();
434             deadlock1.start();
435         }
436         // wait until all threads have really terminated
437         end.meet();
438 
439     }
440 }