All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.gemstone.gemfire.distributed.internal.locks.CollaborationJUnitTest Maven / Gradle / Ivy

There is a newer version: 2.0-BETA
Show newest version
/*
 * Copyright (c) 2010-2015 Pivotal Software, Inc. All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you
 * may not use this file except in compliance with the License. You
 * may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
 * implied. See the License for the specific language governing
 * permissions and limitations under the License. See accompanying
 * LICENSE file.
 */
package com.gemstone.gemfire.distributed.internal.locks;

import junit.framework.*;
import java.util.*;

import com.gemstone.gemfire.CancelCriterion;
import com.gemstone.gemfire.LogWriter;
import com.gemstone.gemfire.SystemFailure;
import com.gemstone.gemfire.internal.LocalLogWriter;
import com.gemstone.gemfire.internal.LogWriterImpl;

import io.snappydata.test.dunit.DistributedTestBase;
import io.snappydata.test.dunit.DistributedTestBase.WaitCriterion;

/**
 * Tests the Collaboration Lock used internally by dlock service.
 *
 * @author Kirk Lund
 * @since 4.1.1
 */
public class CollaborationJUnitTest extends TestCase {

  protected LogWriter log = new LocalLogWriter(LogWriterImpl.INFO_LEVEL);
  protected Collaboration collaboration;
  
  public CollaborationJUnitTest(String name) {
    super(name);
  }
  
  public void setUp() throws Exception {
    this.collaboration = new Collaboration(new CancelCriterion() {

      public String cancelInProgress() {
        // TODO Auto-generated method stub
        return null;
      }

      public RuntimeException generateCancelledException(Throwable e) {
        // TODO Auto-generated method stub
        return null;
      }
      
    });
    this.collaboration.enableDebug(this.log.convertToLogWriterI18n());
  }
  
  public void tearDown() throws Exception {
    this.collaboration = null;
  }
  
  protected volatile boolean flagTestBlocksUntilRelease = false;
  protected volatile boolean threadBStartedTestBlocksUntilRelease = false;
  public void testBlocksUntilRelease() throws Exception {
    this.log.info("[testBlocksUntilRelease]");
    Thread threadA = new Thread(group, new Runnable() {
      public void run() {
        collaboration.acquireUninterruptibly("topicA");
        try {
          flagTestBlocksUntilRelease = true;
          while(flagTestBlocksUntilRelease) {
            try {
              Thread.sleep(10);
            }
            catch (InterruptedException ignore) {fail("interrupted");}
          }
        }
        finally {
          collaboration.release();
        }
      }
    });
    
    // thread one acquires
    threadA.start();
    WaitCriterion ev = new WaitCriterion() {
      public boolean done() {
        return CollaborationJUnitTest.this.flagTestBlocksUntilRelease;
      }
      public String description() {
        return "waiting for thread";
      }
    };
    DistributedTestBase.waitForCriterion(ev, 5 * 1000, 200, true);
    assertTrue(this.collaboration.hasCurrentTopic(threadA));
    
    // thread two blocks until one releeases
    Thread threadB = new Thread(group, new Runnable() {
      public void run() {
        threadBStartedTestBlocksUntilRelease = true;
        collaboration.acquireUninterruptibly("topicB");
        try {
          flagTestBlocksUntilRelease = true;
          WaitCriterion ev2 = new WaitCriterion() {
            public boolean done() {
              return !flagTestBlocksUntilRelease;
            }
            public String description() {
              return "waiting for release";
            }
          };
          DistributedTestBase.waitForCriterion(ev2, 20 * 1000, 200, true);
        }
        finally {
          collaboration.release();
        }
      }
    });
    
    // start up threadB
    threadB.start();
    ev = new WaitCriterion() {
      public boolean done() {
        return threadBStartedTestBlocksUntilRelease;
      }
      public String description() {
        return "waiting for thread b";
      }
    };
    DistributedTestBase.waitForCriterion(ev, 5 * 1000, 200, true);
    
    // threadA holds topic and threadB is waiting...
    assertTrue(this.collaboration.hasCurrentTopic(threadA));
    assertFalse(this.collaboration.hasCurrentTopic(threadB));

    // let threadA release so that threadB gets lock
    this.flagTestBlocksUntilRelease = false;
    DistributedTestBase.join(threadA, 30 * 1000, null);
    
    // make sure threadB is doing what it's supposed to do...
    ev = new WaitCriterion() {
      public boolean done() {
        return flagTestBlocksUntilRelease;
      }
      public String description() {
        return "threadB";
      }
    };
    DistributedTestBase.waitForCriterion(ev, 5 * 1000, 200, true);
    // threadB must have lock now... let threadB release
    assertTrue(this.collaboration.hasCurrentTopic(threadB));
    this.flagTestBlocksUntilRelease = false;
    DistributedTestBase.join(threadB, 30 * 1000, null);

    // collaboration should be free now    
    assertFalse(this.collaboration.hasCurrentTopic(threadA));
    assertFalse(this.collaboration.hasCurrentTopic(threadB));
    assertFalse(this.collaboration.hasCurrentTopic());
  }
  
  protected volatile boolean threadAFlag_TestLateComerJoinsIn = false;
  protected volatile boolean threadBFlag_TestLateComerJoinsIn = false;
  protected volatile boolean threadCFlag_TestLateComerJoinsIn = true;
  protected volatile boolean threadDFlag_TestLateComerJoinsIn = false;
  public void testLateComerJoinsIn() throws Exception {
    this.log.info("[testLateComerJoinsIn]");
    
    final Object topicA = "topicA";
    final Object topicB = "topicB";
    
    // threads one and two acquire
    Thread threadA = new Thread(group, new Runnable() {
      public void run() {
        collaboration.acquireUninterruptibly(topicA);
        try {
          threadAFlag_TestLateComerJoinsIn = true;
          WaitCriterion ev = new WaitCriterion() {
            public boolean done() {
              return !threadAFlag_TestLateComerJoinsIn;
            }
            public String description() {
              return null;
            }
          };
          DistributedTestBase.waitForCriterion(ev, 60 * 1000, 200, true);
        }
        finally {
          collaboration.release();
        }
      }
    });
    threadA.start();
    WaitCriterion ev = new WaitCriterion() {
      public boolean done() {
        return threadAFlag_TestLateComerJoinsIn;
      }
      public String description() {
        return "wait for ThreadA";
      }
    };
    DistributedTestBase.waitForCriterion(ev, 30 * 1000, 200, true);
    assertTrue(this.collaboration.hasCurrentTopic(threadA));
    assertTrue(this.collaboration.isCurrentTopic(topicA));
    
    Thread threadB = new Thread(group, new Runnable() {
      public void run() {
        collaboration.acquireUninterruptibly(topicA);
        try {
          threadBFlag_TestLateComerJoinsIn = true;
          WaitCriterion ev2 = new WaitCriterion() {
            public boolean done() {
              return !threadBFlag_TestLateComerJoinsIn;
            }
            public String description() {
              return null;
            }
          };
          DistributedTestBase.waitForCriterion(ev2, 60 * 1000, 200, true);
        }
        finally {
          collaboration.release();
        }
      }
    });
    threadB.start();
    ev = new WaitCriterion() {
      public boolean done() {
        return threadBFlag_TestLateComerJoinsIn;
      }
      public String description() {
        return "";
      }
    };
    DistributedTestBase.waitForCriterion(ev, 60 * 1000, 200, true);
    assertTrue(this.collaboration.hasCurrentTopic(threadB));
    
    // thread three blocks for new topic
    Thread threadC = new Thread(group, new Runnable() {
      public void run() {
        threadCFlag_TestLateComerJoinsIn = false;
        collaboration.acquireUninterruptibly(topicB);
        try {
          threadCFlag_TestLateComerJoinsIn = true;
          WaitCriterion ev2 = new WaitCriterion() {
            public boolean done() {
              return !threadCFlag_TestLateComerJoinsIn;
            }
            public String description() {
              return null;
            }
          };
          DistributedTestBase.waitForCriterion(ev2, 60 * 1000, 200, true);
        }
        finally {
          collaboration.release();
        }
      }
    });
    threadC.start();
    ev = new WaitCriterion() {
      public boolean done() {
        return threadCFlag_TestLateComerJoinsIn;
      }
      public String description() {
        return null;
      }
    };
    DistributedTestBase.waitForCriterion(ev, 60 * 1000, 200, true);
    assertFalse(this.collaboration.hasCurrentTopic(threadC));
    assertFalse(this.collaboration.isCurrentTopic(topicB));
    
    // thread four (lateComer) acquires current topic immediately
    Thread threadD = new Thread(group, new Runnable() {
      public void run() {
        collaboration.acquireUninterruptibly(topicA);
        try {
          threadDFlag_TestLateComerJoinsIn = true;
          while(threadDFlag_TestLateComerJoinsIn) {
            try {
              Thread.sleep(10);
            }
            catch (InterruptedException ignore) {fail("interrupted");}
          }
        }
        finally {
          collaboration.release();
        }
      }
    });
    threadD.start();
    ev = new WaitCriterion() {
      public boolean done() {
        return threadDFlag_TestLateComerJoinsIn;
      }
      public String description() {
        return null;
      }
    };
    DistributedTestBase.waitForCriterion(ev, 60 * 1000, 200, true);
    assertTrue(this.collaboration.hasCurrentTopic(threadD));
    
    // release threadA
    this.threadAFlag_TestLateComerJoinsIn = false;
    DistributedTestBase.join(threadA, 30 * 1000, null);
    assertFalse(this.collaboration.hasCurrentTopic(threadA));
    assertTrue(this.collaboration.hasCurrentTopic(threadB));
    assertFalse(this.collaboration.hasCurrentTopic(threadC));
    assertTrue(this.collaboration.hasCurrentTopic(threadD));
    assertTrue(this.collaboration.isCurrentTopic(topicA));
    assertFalse(this.collaboration.isCurrentTopic(topicB));
    
    // release threadB
    this.threadBFlag_TestLateComerJoinsIn = false;
    DistributedTestBase.join(threadB, 30 * 1000, null);
    assertFalse(this.collaboration.hasCurrentTopic(threadB));
    assertFalse(this.collaboration.hasCurrentTopic(threadC));
    assertTrue(this.collaboration.hasCurrentTopic(threadD));
    assertTrue(this.collaboration.isCurrentTopic(topicA));
    assertFalse(this.collaboration.isCurrentTopic(topicB));
    
    // release threadD
    this.threadDFlag_TestLateComerJoinsIn = false;
    DistributedTestBase.join(threadD, 30 * 1000, null);
    ev = new WaitCriterion() {
      public boolean done() {
        return threadCFlag_TestLateComerJoinsIn;
      }
      public String description() {
        return null;
      }
    };
    DistributedTestBase.waitForCriterion(ev, 60 * 1000, 200, true);
    assertTrue(this.collaboration.hasCurrentTopic(threadC));
    assertFalse(this.collaboration.hasCurrentTopic(threadD));
    assertFalse(this.collaboration.isCurrentTopic(topicA));
    assertTrue(this.collaboration.isCurrentTopic(topicB));
    
    // release threadC
    this.threadCFlag_TestLateComerJoinsIn = false;
    DistributedTestBase.join(threadC, 30 * 1000, null);
    assertFalse(this.collaboration.hasCurrentTopic(threadC));
    assertFalse(this.collaboration.isCurrentTopic(topicA));
    assertFalse(this.collaboration.isCurrentTopic(topicB));
  }
  
  protected List waitingList = Collections.synchronizedList(new ArrayList());
  protected List fairnessList = Collections.synchronizedList(new ArrayList());
  protected volatile boolean runTestFairnessStressfully = true;
  public void testFairnessStressfully() throws Exception {
    this.log.info("[testFairnessStressfully]");
    final int numThreads = 20;
    Thread threads[] = new Thread[numThreads];
    
    Runnable run = new Runnable() {
      public void run() {
        boolean released = false;
        try {
          String uniqueTopic = Thread.currentThread().getName();
          while(runTestFairnessStressfully) {
            waitingList.add(uniqueTopic);
            collaboration.acquireUninterruptibly(uniqueTopic);
            try {
              released = false;
              fairnessList.add(uniqueTopic);
              waitingList.remove(uniqueTopic);
            }
            finally {
              // wait for the other threads to line up...
              WaitCriterion ev = new WaitCriterion() {
                public boolean done() {
                  return !runTestFairnessStressfully || waitingList.size() >= numThreads - 1;
                }
                public String description() {
                  return "other threads lining up";
                }
              };
              DistributedTestBase.waitForCriterion(ev, 60 * 1000, 200, true);
              collaboration.release();
              released = true;
            }
          }
        }
        finally {
          if (!released) {
            collaboration.release();
          }
        }
      }
    };
    
    try {
      // many threads loop: acquire and release with unique topic
      for (int t = 0; t < threads.length; t++) {
        threads[t] = new Thread(group, run, String.valueOf(t));
        threads[t].start();
      }

      log.info("Started all threads... waiting for test to complete.");
            
      // wait for numThreads * 10
      WaitCriterion ev = new WaitCriterion() {
        public boolean done() {
          return fairnessList.size() >= numThreads * 20;
        }
        public String description() {
          return "waiting for numThreads * 10";
        }
      };
      DistributedTestBase.waitForCriterion(ev, 5 * 60 * 1000, 200, true);
    }
    finally {
      if (this.runTestFairnessStressfully) {
        this.runTestFairnessStressfully = false;
      }
    }
    
    for (int t = 0; t < threads.length; t++) {
      DistributedTestBase.join(threads[t], 30 * 1000, null);
    }
    
    // assert that all topics are acquired in order
    // count number of occurrences of each thread
    int count[] = new int[numThreads];
    for (int i = 0; i < count.length; i++) { // shouldn't be necessary
      count[i] = 0;
    }
    synchronized(this.fairnessList) {
      for (Iterator iter = this.fairnessList.iterator(); iter.hasNext();) {
        int id = Integer.valueOf((String)iter.next()).intValue();
        count[id] = count[id]+1;
      }
    }
    
    int totalLocks = 0;
    int minLocks = Integer.MAX_VALUE;
    int maxLocks = 0;
    for (int i = 0; i < count.length; i++) {
      int locks = count[i];
      this.log.fine("testFairnessStressfully thread-" + i + " acquired topic " + 
        locks + " times.");
      if (locks < minLocks) minLocks = locks;
      if (locks > maxLocks) maxLocks = locks;
      totalLocks = totalLocks + locks;
    }

    this.log.info("[testFairnessStressfully] totalLocks=" + totalLocks + 
                  " minLocks=" + minLocks +
                  " maxLocks=" + maxLocks);

    int expectedLocks = (totalLocks / numThreads) + 1;
    
    // NOTE: if you turn on fine logs, this deviation may be too small...
    // slower machines may also fail depending on thread scheduling
    int deviation = (int)(expectedLocks * 0.25);
    int lowThreshold = expectedLocks - deviation;
    int highThreshold = expectedLocks + deviation;

    this.log.info("[testFairnessStressfully] deviation=" + deviation +
                  " expectedLocks=" + expectedLocks + 
                  " lowThreshold=" + lowThreshold +
                  " highThreshold=" + highThreshold);
                        
    // if these assertions keep failing we'll have to rewrite the test
    // to handle scheduling of the threads...
                  
    assertTrue("minLocks is less than lowThreshold",
               minLocks >= lowThreshold);
    assertTrue("maxLocks is greater than highThreshold",
               maxLocks <= highThreshold);
  }
  
  public void testHasCurrentTopic() throws Exception {
    this.log.info("[testHasCurrentTopic]");
    assertTrue(!this.collaboration.hasCurrentTopic());
    this.collaboration.acquireUninterruptibly("testHasCurrentTopic");
    try {
      assertTrue(this.collaboration.hasCurrentTopic());
    }
    finally {
      this.collaboration.release();
    }
    assertTrue(!this.collaboration.hasCurrentTopic());
  }
  
  protected volatile boolean flagTestThreadHasCurrentTopic = false;
  public void testThreadHasCurrentTopic() throws Exception {
    this.log.info("[testThreadHasCurrentTopic]");
    Thread thread = new Thread(group, new Runnable() {
      public void run() {
        collaboration.acquireUninterruptibly("testThreadHasCurrentTopic");
        try {
          flagTestThreadHasCurrentTopic = true;
          WaitCriterion ev = new WaitCriterion() {
            public boolean done() {
              return !flagTestThreadHasCurrentTopic;
            }
            public String description() {
              return null;
            }
          };
          DistributedTestBase.waitForCriterion(ev, 60 * 1000, 200, true);
        }
        finally {
          collaboration.release();
        }
      }
    });
    
    // before starting thread, hasCurrentTopic(thread) returns false
    assertTrue(!this.collaboration.hasCurrentTopic(thread));
    thread.start();
    WaitCriterion ev = new WaitCriterion() {
      public boolean done() {
        return flagTestThreadHasCurrentTopic;
      }
      public String description() {
        return null;
      }
    };
    DistributedTestBase.waitForCriterion(ev, 60 * 1000, 200, true);
    
    // after starting thread, hasCurrentTopic(thread) returns true
    assertTrue(this.collaboration.hasCurrentTopic(thread));
    this.flagTestThreadHasCurrentTopic = false;
    DistributedTestBase.join(thread, 30 * 1000, null);
    
    // after thread finishes, hasCurrentTopic(thread) returns false
    assertTrue(!this.collaboration.hasCurrentTopic(thread));
  }
  
  public void testIsCurrentTopic() throws Exception {
    this.log.info("[testIsCurrentTopic]");
    Object topic = "testIsCurrentTopic";
    assertTrue(!this.collaboration.isCurrentTopic(topic));
    this.collaboration.acquireUninterruptibly(topic);
    try {
      assertTrue(this.collaboration.isCurrentTopic(topic));
    }
    finally {
      this.collaboration.release();
    }
    assertTrue(!this.collaboration.isCurrentTopic(topic));
  }

  protected final ThreadGroup group = 
      new ThreadGroup("CollaborationJUnitTest Threads") {
        public void uncaughtException(Thread t, Throwable e)
        {
          if (e instanceof VirtualMachineError) {
            SystemFailure.setFailure((VirtualMachineError)e); // don't throw
          }
          String s = "Uncaught exception in thread " + t;
          log.error(s, e);
          fail(s);
        }
      };
}





© 2015 - 2024 Weber Informatics LLC | Privacy Policy