KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jboss > cache > tests > lock > NonBlockingWriterLockTest


1 package org.jboss.cache.tests.lock;
2
3 import EDU.oswego.cs.dl.util.concurrent.Sync;
4 import junit.framework.Test;
5 import junit.framework.TestCase;
6 import junit.framework.TestSuite;
7 import org.jboss.cache.lock.NonBlockingWriterLock;
8
9 import java.io.FileWriter JavaDoc;
10 import java.io.IOException JavaDoc;
11 import java.io.Writer JavaDoc;
12 import java.util.Vector JavaDoc;
13
14 /**
15  * NonBlockingWriterLock is a read/write lock (with upgrade) that has
16  * non-blocking write lock acquisition on existing read lock(s).
17  * <p>Note that the write lock is exclusive among write locks, e.g.,
18  * only one write lock can be granted at one time, but the write lock
19  * is independent of the read locks. For example,
20  * a read lock to be acquired will be blocked if there is existing write lock, but
21  * will not be blocked if there are mutiple read locks already granted to other
22  * owners. On the other hand, a write lock can be acquired as long as there
23  * is no existing write lock, regardless how many read locks have been
24  * granted.
25  *
26  * @author <a HREF="mailto:cavin_song@yahoo.com">Cavin Song</a> April 22, 2004
27  * @version 1.0
28  */

29 public class NonBlockingWriterLockTest extends TestCase
30 {
31    static final NonBlockingWriterLock lock_ = new NonBlockingWriterLock();
32    static long SLEEP_MSECS = 500;
33    Vector JavaDoc lockResult = new Vector JavaDoc();
34    int NO_MORE_OP = 0;
35    int INVOKE_READ = 1;
36    int INVOKE_WRITE = 2;
37    int INVOKE_UPGRADE = 3;
38
39    public NonBlockingWriterLockTest(String JavaDoc name)
40    {
41       super(name);
42    }
43
44
45    public static void main(String JavaDoc[] args) throws Exception JavaDoc
46    {
47       log("\nBeginning NonBlockingWriterLock automated testing ...\n");
48
49       junit.textui.TestRunner.run(suite());
50    }
51
52    // Needed for JUnit.
53
public static Test suite()
54    {
55       TestSuite suite = new TestSuite();
56       // Adding test cases here ...
57
suite.addTestSuite(NonBlockingWriterLockTest.class);
58       return suite;
59    }
60
61    public void setUp()
62    {
63       logX("\n");
64       log("Setting up test case ...");
65    }
66
67    public void tearDown()
68    {
69       log("Tearing down test case ...");
70    }
71
72    public static void _sleep(long msecs)
73    {
74       try {
75          Thread.sleep(msecs);
76       } catch (InterruptedException JavaDoc ex) {
77       }
78    }
79
80    public static void log(String JavaDoc str)
81    {
82       System.out.println(Thread.currentThread() + ": "
83             + java.util.Calendar.getInstance().getTime() + " : " + str);
84    }
85
86    // For debugging purpose
87
public static void logX(String JavaDoc str)
88    {
89       log(str);
90 // try {
91
// logToFile(str);
92
// } catch (IOException ioe) {
93
// }
94
}
95
96    // Debugging intrnal function
97
public static void logToFile(String JavaDoc str) throws IOException JavaDoc
98    {
99       Writer JavaDoc out = new FileWriter JavaDoc("./ReadCommittedLog.txt", true/*append*/);
100       out.write(str);
101       out.close();
102    }
103
104    /***************************************************************/
105    /* Utility functions to creat threads for RL, WL and UL */
106    /***************************************************************/
107    /**
108     * Creates a new thread and acquires a read lock with a timeout
109     * value specified by the caller. Optionally, the caller can request
110     * a second read or write lock after the first read lock request.
111     * The locking result is stored in a vector with the following
112     * format:
113     * <p><DL>
114     * <DD>'case number'-'thread name'-[RL|WL|UL]-[0|1]
115     * </DL>
116     * <p>where:
117     * <DL>
118     * <DD> 'case number' is the passed in test case # by the caller.
119     * <DD> 'thread name' is the passed in thread name by the caller.
120     * <DD> RL - indicating was doing read lock request.
121     * <DD> WL - indicating was doing write lock request.
122     * <DD> UL - indicating was doing upgrade lock request.
123     * <DD> 0 - indicating the locking request failed.
124     * <DD> 1 - indicating the locking request succeeded.
125     * </DL>
126     * <p>
127     * After all threads in each test case terminate, the test case
128     * should make the following call to verify the test result:
129     * <DL>
130     * <DD> asssertTrue(checkLockingResult(expected-result-string);
131     * </DL>
132     * <p>
133     * 'expected-result-string' is the locking result string
134     * described above. For example, "8-t1-RL-0" means that thread
135     * t1 in test case #8 doing a Read Lock request expects the
136     * operation to fail. If the expected result string can't be
137     * found then the test case is considered FAILED (ie, either
138     * the read lock request was successful or did not complete).
139     * <p>
140     * Each test case should also call cleanLockingResult() to reset
141     * result vector for the next test cases.
142     * @param caseNum Arbitrary string for the test case number.
143     * @param name Arbitrary string for the calling thread name.
144     * @param msecs Milliseconds that the thread should sleep after
145     * acquiring the read lock.
146     * @param errMsg Error msg to log in case of error.
147     * @param secondOP Set to NO_MORE_OP if a 2nd lock request is not required.
148     * Set to INVOKE_READ, INVOKE_READ or INVOKE_UPGRADE
149     * respectively if the 2nd lock is a read, write or
150     * upgrade request respectively.
151     */

152   
153    protected Thread JavaDoc readThread(final String JavaDoc caseNum, final String JavaDoc name,
154                                final long msecs, final long sleepSecs,
155                                final String JavaDoc errMsg, final int secondOP)
156    {
157       return new Thread JavaDoc(name)
158       {
159          public void run()
160          {
161                Sync rlock = lock_.readLock();
162             try {
163                if (! rlock.attempt(msecs)) {
164                   logX(caseNum+"-"+name+" requesting read lock failed!\n");
165                   String JavaDoc str = caseNum + "-" + name + "-RL-0";
166                   postLockingResult(str);
167                   return;
168                }
169                // OK, read lock obtained, sleep and release it.
170
logX(caseNum+"-"+name+" requesting read lock succeeded!\n");
171                String JavaDoc str = caseNum + "-" + name + "-RL-1";
172                postLockingResult(str);
173                _sleep(sleepSecs);
174
175            if (secondOP == INVOKE_READ)
176                    acquireReadLock(caseNum, name, msecs, errMsg);
177                else if (secondOP == INVOKE_WRITE)
178                    acquireWriteLock(caseNum, name, msecs, errMsg);
179                else if (secondOP == INVOKE_UPGRADE)
180                    acquireUpgradeLock(caseNum, name, msecs, errMsg);
181                  
182                rlock.release();
183                logX(caseNum+"-"+name+" releasing read lock.\n");
184             } catch (Exception JavaDoc ex) {
185             }
186          }
187       };
188    }
189
190    /**
191     * Creates a new thread and acquires a write lock with a timeout
192     * value specified by the caller. Similar to {@link #readThread readThread()}
193     * except it's used for write locks.
194     * @see #readThread readThread()
195     */

196    protected Thread JavaDoc writeThread(final String JavaDoc caseNum, final String JavaDoc name,
197                                 final long msecs, final long sleepSecs,
198                                 final String JavaDoc errMsg, final int secondOP)
199    {
200       return new Thread JavaDoc(name)
201       {
202          public void run()
203          {
204             try {
205                Sync wlock = lock_.writeLock();
206                if (! wlock.attempt(msecs)) {
207                   logX(caseNum+"-"+name+" requesting write lock failed!\n");
208                   String JavaDoc str = caseNum + "-" + name + "-WL-0";
209                   postLockingResult(str);
210                   return;
211                }
212                // OK, write lock obtained, sleep and release it.
213
logX(caseNum+"-"+name+" requesting write lock succeeded!\n");
214                String JavaDoc str = caseNum + "-" + name + "-WL-1";
215                postLockingResult(str);
216                _sleep(sleepSecs);
217
218            if (secondOP == INVOKE_READ)
219                    acquireReadLock(caseNum, name, msecs, errMsg);
220                else if (secondOP == INVOKE_WRITE)
221                    acquireWriteLock(caseNum, name, msecs, errMsg);
222                else if (secondOP == INVOKE_UPGRADE)
223                    acquireUpgradeLock(caseNum, name, msecs, errMsg);
224
225                wlock.release();
226                logX(caseNum+"-"+name+" releasing write lock.\n");
227             } catch (Exception JavaDoc ex) {
228             }
229          }
230       };
231    }
232
233    /**
234     * Creates a new thread, acquires a read lock, sleeps for a while
235     * and then tries to upgrade the read lock to a write one. Similar
236     * to {@link #readThread readThread()} except it's used for upgrading
237     * locks.
238     * @see #readThread readThread()
239     */

240    protected Thread JavaDoc upgradeThread(final String JavaDoc caseNum, final String JavaDoc name,
241                                   final long msecs, final String JavaDoc errMsg)
242    {
243       return new Thread JavaDoc(name)
244       {
245          public void run()
246          {
247             try {
248                Sync rlock = lock_.readLock();
249                Sync wlock = null;
250                if (! rlock.attempt(msecs)) {
251                   logX(caseNum+"-"+name+" requesting read lock failed!\n");
252                   String JavaDoc str = caseNum + "-" + name + "-RL-0";
253                   postLockingResult(str);
254                   return;
255                }
256                // OK, read lock obtained, sleep and upgrade it later.
257
logX(caseNum+"-"+name+" requesting read lock succeeded (upgrade later)!\n");
258                _sleep(SLEEP_MSECS/2);
259                String JavaDoc str = caseNum + "-" + name + "-UL-";
260                if ((wlock = lock_.upgradeLockAttempt(msecs)) == null)
261                {
262                  logX(caseNum+"-"+name+" requesting upgrade lock failed!\n");
263                  str += "0";
264                }
265                else
266                {
267                  logX(caseNum+"-"+name+" requesting upgrade lock succeeded!\n");
268                  str += "1";
269                }
270                postLockingResult(str);
271                // Sleep again and then release the lock.
272
_sleep(SLEEP_MSECS);
273                if (wlock != null)
274                {
275                  wlock.release();
276                  logX(caseNum+"-"+name+" releasing upgrade lock.\n");
277                }
278                rlock.release();
279             } catch (Exception JavaDoc ex) {
280             }
281          }
282       };
283    }
284
285    /***************************************************************/
286    /* Utility functions to acquire RL and WL (no thread) */
287    /***************************************************************/
288    /**
289     * This routine tries to acquire a read lock with a timeout value
290     * passed in by the caller. Like {@link #readThread readThread()}
291     * it then stores the locking result in the result vector depending
292     * on the outcome of the request.
293     */

294   
295    protected void acquireReadLock(final String JavaDoc caseNum, final String JavaDoc name,
296                                   final long msecs, final String JavaDoc errMsg)
297    {
298             try {
299                Sync rlock = lock_.readLock();
300                if (! rlock.attempt(msecs)) {
301                   logX(caseNum+"-"+name+" requesting read lock failed!\n");
302                   String JavaDoc str = caseNum + "-" + name + "-RL-0";
303                   postLockingResult(str);
304                   return;
305                }
306                // OK, read lock obtained, sleep and release it.
307
logX(caseNum+"-"+name+" requesting read lock succeeded!\n");
308                String JavaDoc str = caseNum + "-" + name + "-RL-1";
309                postLockingResult(str);
310                _sleep(SLEEP_MSECS);
311                rlock.release();
312                logX(caseNum+"-"+name+" releasing read lock.\n");
313             } catch (Exception JavaDoc ex) {
314             }
315    }
316
317    /**
318     * Same as {@link #acquireReadLock acquireReadLock()} except
319     * it's for write lock request.
320     */

321    protected void acquireWriteLock(final String JavaDoc caseNum, final String JavaDoc name,
322                                    final long msecs, final String JavaDoc errMsg)
323    {
324             try {
325                Sync wlock = lock_.writeLock();
326                if (! wlock.attempt(msecs)) {
327                   logX(caseNum+"-"+name+" requesting write lock failed!\n");
328                   String JavaDoc str = caseNum + "-" + name + "-WL-0";
329                   postLockingResult(str);
330                   return;
331                }
332                // OK, write lock obtained, sleep and release it.
333
logX(caseNum+"-"+name+" requesting write lock succeeded!\n");
334                String JavaDoc str = caseNum + "-" + name + "-WL-1";
335                postLockingResult(str);
336                _sleep(SLEEP_MSECS);
337                wlock.release();
338                logX(caseNum+"-"+name+" releasing write lock.\n");
339             } catch (Exception JavaDoc ex) {
340             }
341    }
342
343    /**
344     * Same as {@link #acquireReadLock acquireReadLock()} except
345     * it's for upgrade lock request.
346     */

347    protected void acquireUpgradeLock(final String JavaDoc caseNum, final String JavaDoc name,
348                                      final long msecs, final String JavaDoc errMsg)
349    {
350             try {
351                Sync ulock = null;
352                if ((ulock = lock_.upgradeLockAttempt(msecs)) == null) {
353                   logX(caseNum+"-"+name+" requesting upgrade lock failed!\n");
354                   String JavaDoc str = caseNum + "-" + name + "-UL-0";
355                   postLockingResult(str);
356                   return;
357                }
358                // OK, write lock obtained, sleep and release it.
359
logX(caseNum+"-"+name+" requesting upgrade lock succeeded!\n");
360                String JavaDoc str = caseNum + "-" + name + "-UL-1";
361                postLockingResult(str);
362                _sleep(SLEEP_MSECS);
363                ulock.release();
364                logX(caseNum+"-"+name+" releasing upgrade lock.\n");
365             } catch (Exception JavaDoc ex) {
366             }
367    }
368
369    /***************************************************************/
370    /* Synchronized methods handling locking result vector */
371    /***************************************************************/
372    /**
373     * Clean/remove all locking results in the vector.
374     */

375    protected synchronized void cleanLockingResult()
376    {
377        lockResult.removeAllElements();
378    }
379
380    /**
381     * Post a locking result to the vector for later verification.
382     */

383    protected synchronized void postLockingResult(Object JavaDoc obj)
384    {
385        logX(" Added *" + (String JavaDoc)obj + "* to the result vector\n");
386        // Make sure we only have one in the vector
387
//if (!checkLockingResult((String)obj))
388
lockResult.addElement(obj);
389    }
390    
391    /**
392     * Check if a given expected locking result is in the vector.
393     */

394    protected synchronized boolean checkLockingResult(String JavaDoc expected)
395    {
396        boolean rc = false;
397        for (int i=0; i<lockResult.size(); i++)
398        {
399          Object JavaDoc ele = lockResult.elementAt(i);
400          String JavaDoc str = (String JavaDoc)ele;
401          if (expected.equals(str))
402          {
403             rc = true;
404             break;
405          }
406        }
407        if (rc)
408          logX(" Searching for *" + expected + "* SUCCEEDED.\n");
409        else
410          logX(" Searching for *" + expected + "* FAILED.\n");
411        return rc;
412    }
413
414    /***************************************************************/
415    /* T e s t C a s e s */
416    /***************************************************************/
417    /** Case #10 - T1 acquires RL, T2 acquires RL followed by WL. */
418    public void testWriteWithMultipleReaders() throws Exception JavaDoc
419    {
420       String JavaDoc caseNum = "10";
421       Thread JavaDoc t1=readThread(caseNum, "t1", 0, SLEEP_MSECS*2,
422                            "1st read lock attempt failed", NO_MORE_OP);
423       Thread JavaDoc t2=readThread(caseNum, "t2", 0, SLEEP_MSECS,
424                            "2nd read lock attempt failed", INVOKE_WRITE);
425
426       t1.start();
427       t2.start();
428       t1.join(3000);
429       t2.join(3000);
430       assertTrue(checkLockingResult(caseNum+"-t1-RL-1") &&
431                  checkLockingResult(caseNum+"-t2-RL-1") &&
432                  checkLockingResult(caseNum+"-t2-WL-1"));
433       cleanLockingResult();
434       // possilbe deadlock check
435
if (t1.isAlive() || t2.isAlive())
436          fail("Possible deadlock resulted in testRead.");
437    }
438
439    /** Case #11 - T1 acquires RL followed by WL, T2 acquires RL. */
440    public void testUpgradeWithMultipleReadersOn1() throws Exception JavaDoc
441    {
442       String JavaDoc caseNum = "11";
443       Thread JavaDoc t1=readThread(caseNum, "t1", 0, SLEEP_MSECS,
444                            "1st read lock attempt failed", INVOKE_WRITE);
445       Thread JavaDoc t2=readThread(caseNum, "t2", 0, SLEEP_MSECS*2,
446                            "2nd read lock attempt failed", NO_MORE_OP);
447
448       t1.start();
449       t2.start();
450       t1.join(3000);
451       t2.join(3000);
452       assertTrue(checkLockingResult(caseNum+"-t1-RL-1") &&
453                  checkLockingResult(caseNum+"-t2-RL-1") &&
454                  checkLockingResult(caseNum+"-t1-WL-1"));
455       cleanLockingResult();
456       // possilbe deadlock check
457
if (t1.isAlive() || t2.isAlive())
458          fail("Possible deadlock resulted in testRead.");
459    }
460
461    /** Case #2 - T1 acquires RL followed by UL. */
462    public void testUpgradeReadLock() throws Exception JavaDoc
463    {
464       String JavaDoc caseNum = "2";
465       Thread JavaDoc t1=readThread(caseNum, "t1", 0, SLEEP_MSECS,
466                            "1st read lock attempt failed", INVOKE_UPGRADE);
467
468       t1.start();
469       t1.join(3000);
470       assertTrue(checkLockingResult(caseNum+"-t1-RL-1") &&
471                  checkLockingResult(caseNum+"-t1-UL-1"));
472       cleanLockingResult();
473    }
474
475    /** Case #3 - T1 acquires RL followed by WL. */
476
477    public void testReadThenWrite() throws Exception JavaDoc
478    {
479       String JavaDoc caseNum = "3";
480       acquireReadLock(caseNum, "t1", 0, "1st read lock attempt failed");
481       acquireWriteLock(caseNum, "t1.1", 0, "2nd write lock attempt failed");
482       assertTrue(checkLockingResult(caseNum+"-t1-RL-1") &&
483                  checkLockingResult(caseNum+"-t1.1-WL-1"));
484       cleanLockingResult();
485    }
486
487
488    /** Case #5 - T1 acquires WL followed by RL.*/
489
490    public void testWriteThenRead() throws Exception JavaDoc
491    {
492       String JavaDoc caseNum = "5";
493       acquireWriteLock(caseNum, "t1", 0, "1st write lock attempt failed");
494       acquireReadLock(caseNum, "t1.1", 0, "2nd read lock attempt failed");
495       assertTrue(checkLockingResult(caseNum+"-t1-WL-1") &&
496                  checkLockingResult(caseNum+"-t1.1-RL-1"));
497       cleanLockingResult();
498    }
499
500
501    /** Case #6 - T1 acquires RL, T2 acquires RL.*/
502    public void testMultipleReadlock() throws Exception JavaDoc
503    {
504       String JavaDoc caseNum = "6";
505       Thread JavaDoc t1=readThread(caseNum, "t1", 0, SLEEP_MSECS,
506                            "1st read lock attempt failed", NO_MORE_OP);
507       Thread JavaDoc t2=readThread(caseNum, "t2", 0, SLEEP_MSECS,
508                            "2nd read lock attempt failed", NO_MORE_OP);
509
510       t1.start();
511       t2.start();
512       t1.join(3000);
513       t2.join(3000);
514       assertTrue(checkLockingResult(caseNum+"-t1-RL-1") &&
515                  checkLockingResult(caseNum+"-t2-RL-1"));
516       cleanLockingResult();
517       // possilbe deadlock check
518
if (t1.isAlive() || t2.isAlive())
519          fail("Possible deadlock resulted in testRead.");
520    }
521
522    /** Case #8 - T1 acquires RL, T2 acquires WL.*/
523    public void testWriteWithExistingReader() throws Exception JavaDoc
524    {
525       String JavaDoc caseNum = "8";
526       Thread JavaDoc t1=readThread(caseNum, "t1", 0, SLEEP_MSECS,
527                            "1st write lock attempt failed", NO_MORE_OP);
528       Thread JavaDoc t2=writeThread(caseNum, "t2", 0, SLEEP_MSECS,
529                            "2nd read lock attempt failed", NO_MORE_OP);
530
531       t1.start();
532       t2.start();
533       t1.join(3000);
534       t2.join(3000);
535       assertTrue(checkLockingResult(caseNum+"-t1-RL-1") &&
536                  checkLockingResult(caseNum+"-t2-WL-1"));
537       cleanLockingResult();
538       // possilbe deadlock check
539
if (t1.isAlive() || t2.isAlive())
540          fail("Possible deadlock resulted in testRead.");
541    }
542
543    /** Case #13 - T1 acquires RL, T2 acquires WL.*/
544    public void testReadWithExistingWriter() throws Exception JavaDoc
545    {
546       String JavaDoc caseNum = "13";
547       Thread JavaDoc t1=writeThread(caseNum, "t1", 0, SLEEP_MSECS,
548                            "1st write lock attempt failed", NO_MORE_OP);
549       Thread JavaDoc t2=readThread(caseNum, "t2", 0, SLEEP_MSECS,
550                            "2nd read lock attempt failed", NO_MORE_OP);
551
552       t1.start();
553       t2.start();
554       t1.join(3000);
555       t2.join(3000);
556       assertTrue(checkLockingResult(caseNum+"-t1-WL-1") &&
557                  checkLockingResult(caseNum+"-t2-RL-0"));
558       cleanLockingResult();
559       // possilbe deadlock check
560
if (t1.isAlive() || t2.isAlive())
561          fail("Possible deadlock resulted in testRead.");
562    }
563
564    /** Case #14 - T1 acquires WL, T2 acquires WL.*/
565    public void testMultipleWritelocks() throws Exception JavaDoc
566    {
567       String JavaDoc caseNum = "14";
568       Thread JavaDoc t1=writeThread(caseNum, "t1", 0, SLEEP_MSECS,
569                            "1st write lock attempt failed", NO_MORE_OP);
570       Thread JavaDoc t2=writeThread(caseNum, "t2", 0, SLEEP_MSECS,
571                            "2nd write lock attempt failed", NO_MORE_OP);
572
573       t1.start();
574       t2.start();
575       t1.join(3000);
576       t2.join(3000);
577       assertTrue(checkLockingResult(caseNum+"-t1-WL-1") &&
578                  checkLockingResult(caseNum+"-t2-WL-0"));
579       cleanLockingResult();
580       // possilbe deadlock check
581
if (t1.isAlive() || t2.isAlive())
582          fail("Possible deadlock resulted in testRead.");
583    }
584
585    /** Case #7 - T1 acquires RL, T2 acquires UL.*/
586    public void testUpgradeWithExistingReader() throws Exception JavaDoc
587    {
588       String JavaDoc caseNum = "7";
589       Thread JavaDoc t1=readThread(caseNum, "t1", 0, SLEEP_MSECS,
590                            "1st read lock attempt failed", NO_MORE_OP);
591       Thread JavaDoc t2=upgradeThread(caseNum, "t2", 0,
592                            "2nd upgrade lock attempt failed");
593
594       t1.start();
595       t2.start();
596       t1.join(3000);
597       t2.join(3000);
598       assertTrue(checkLockingResult(caseNum+"-t1-RL-1") &&
599                  checkLockingResult(caseNum+"-t2-UL-1"));
600       cleanLockingResult();
601       // possilbe deadlock check
602
if (t1.isAlive() || t2.isAlive())
603          fail("Possible deadlock resulted in testRead.");
604    }
605
606    /** Case #9 - T1 acquires RL, T2 acquires RL followed by UL.*/
607    public void testUpgradeWithMultipleReaders() throws Exception JavaDoc
608    {
609       String JavaDoc caseNum = "9";
610       Thread JavaDoc t1=readThread(caseNum, "t1", 0, SLEEP_MSECS*2,
611                            "1st read lock attempt failed", NO_MORE_OP);
612       Thread JavaDoc t2=readThread(caseNum, "t2", 0, SLEEP_MSECS,
613                            "2nd read lock attempt failed", INVOKE_UPGRADE);
614
615       t1.start();
616       t2.start();
617       t1.join(3000);
618       t2.join(3000);
619       assertTrue(checkLockingResult(caseNum+"-t1-RL-1") &&
620                  checkLockingResult(caseNum+"-t2-RL-1") &&
621                  checkLockingResult(caseNum+"-t2-UL-1"));
622       cleanLockingResult();
623       // possilbe deadlock check
624
if (t1.isAlive() || t2.isAlive())
625          fail("Possible deadlock resulted in testRead.");
626    }
627 }
628
629
Popular Tags