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

us.ihmc.commons.thread.ThreadToolsTest Maven / Gradle / Ivy

There is a newer version: 0.35.0
Show newest version
package us.ihmc.commons.thread;

import org.junit.jupiter.api.Test;
import us.ihmc.commons.Conversions;
import us.ihmc.commons.time.Stopwatch;
import us.ihmc.log.LogTools;

import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

import static org.junit.jupiter.api.Assertions.*;

public class ThreadToolsTest
{
   @Test
   public void testTimeLimitScheduler()
   {
      final int ITERATIONS = 10;
      final double EPSILON = 40;

      TimeUnit timeUnit = TimeUnit.MILLISECONDS;
      long initialDelay = 0;
      long delay = 3;
      long timeLimit = 300;

      final Runnable runnable = () ->
      {
         //Do some calculation
         Math.sqrt(Math.PI);
      };

      for (int i = 0; i < ITERATIONS; i++)
      {
         long startTime = System.currentTimeMillis();
         ScheduledFuture future = ThreadTools
               .scheduleWithFixeDelayAndTimeLimit(getClass().getSimpleName(), runnable, initialDelay, delay, timeUnit, timeLimit, true);
         while (!future.isDone())
         {
            // do nothing
         }
         long endTime = System.currentTimeMillis();
         assertEquals(timeLimit, endTime - startTime, EPSILON);
      }
   }

   @Test
   public void testTimeLimitSchedulerInterrupt()
   {
      TimeUnit timeUnit = TimeUnit.MILLISECONDS;
      long initialDelay = 0;
      long delay = 3;
      long timeLimit = 300;
      final AtomicInteger counter = new AtomicInteger();


      final Runnable runnable = () ->
      {
         counter.incrementAndGet();

         try
         {
            Thread.sleep(200);
         }
         catch (InterruptedException e)
         {
            LogTools.info("Interrupted!");
         }
      };

      Stopwatch stopwatch2 = new Stopwatch().start();
      ScheduledFuture future = ThreadTools.scheduleWithFixeDelayAndTimeLimit(getClass().getSimpleName(),
                                                                                runnable,
                                                                                initialDelay,
                                                                                delay,
                                                                                timeUnit,
                                                                                timeLimit,
                                                                                true);
      while (!future.isDone())
      {
         // do nothing
      }

      double elapsedMilliseconds = Conversions.secondsToMilliseconds(stopwatch2.totalElapsed());
      LogTools.info("elapsedMilliseconds = " + elapsedMilliseconds);
      assertEquals(300, elapsedMilliseconds, 30);
      assertEquals(2, counter.get());
   }

   @Test
   public void testTimeLimitSchedulerGraceful()
   {
      TimeUnit timeUnit = TimeUnit.MILLISECONDS;
      long initialDelay = 0;
      long delay = 3;
      long timeLimit = 300;
      final AtomicInteger counter = new AtomicInteger();


      final Runnable runnable = () ->
      {
         counter.incrementAndGet();

         try
         {
            Thread.sleep(200);
         }
         catch (InterruptedException e)
         {
            LogTools.info("Interrupted!");
         }
      };

      Stopwatch stopwatch2 = new Stopwatch().start();
      ScheduledFuture future = ThreadTools.scheduleWithFixeDelayAndTimeLimit(getClass().getSimpleName(),
                                                                                runnable,
                                                                                initialDelay,
                                                                                delay,
                                                                                timeUnit,
                                                                                timeLimit,
                                                                                false);
      while (!future.isDone())
      {
         // do nothing
      }

      double elapsedMilliseconds = Conversions.secondsToMilliseconds(stopwatch2.totalElapsed());
      LogTools.info("elapsedMilliseconds = " + elapsedMilliseconds);
      assertEquals(400, elapsedMilliseconds, 30);
      assertEquals(2, counter.get());
   }

   @Test
   public void testSingleExecution()
   {
      TimeUnit timeUnit = TimeUnit.MILLISECONDS;
      long delay = 100;

      final AtomicInteger counter = new AtomicInteger();

      final Runnable runnable = () -> counter.incrementAndGet();

      ScheduledFuture future = ThreadTools.scheduleSingleExecution(getClass().getSimpleName(), runnable, delay, timeUnit);

      Stopwatch stopwatch = new Stopwatch().start();
      while (!future.isDone())
      {
         // do nothing
      }

      double elapsedMilliseconds = Conversions.secondsToMilliseconds(stopwatch.totalElapsed());
      LogTools.info("elapsedMilliseconds = " + elapsedMilliseconds);

      assertEquals(100, elapsedMilliseconds, 10);
      assertEquals(1, counter.get());
   }

   @Test
   public void testIterationLimitScheduler()
   {
      TimeUnit timeUnit = TimeUnit.MILLISECONDS;
      long initialDelay = 0;
      long delay = 10;
      final int iterations = 10;

      final AtomicInteger counter = new AtomicInteger();

      final Runnable runnable = () -> counter.incrementAndGet();

      ScheduledFuture future = ThreadTools
            .scheduleWithFixedDelayAndIterationLimit(getClass().getSimpleName(), runnable, initialDelay, delay, timeUnit, iterations);

      while (!future.isDone())
      {
         // do nothing
      }

      assertEquals(iterations, counter.get());
   }

   @Test
   public void testExecuteWithTimeout()
   {
      final StateHolder holder = new StateHolder();
      for (int i = 0; i < 10; i++)
      {
         holder.state = State.DIDNT_RUN;
         ThreadTools.executeWithTimeout("timeoutTest1", new Runnable()
         {
            @Override
            public void run()
            {
               holder.state = State.TIMED_OUT;

               ThreadTools.sleep(10);

               holder.state = State.RAN_WITHOUT_TIMING_OUT;
            }
         }, 5, TimeUnit.MILLISECONDS);
         assertFalse(holder.state.equals(State.DIDNT_RUN), "Didn't run. Should timeout.");
         assertTrue(holder.state.equals(State.TIMED_OUT), "Did not timeout.");

         holder.state = State.DIDNT_RUN;
         ThreadTools.executeWithTimeout("timeoutTest2", new Runnable()
         {
            @Override
            public void run()
            {
               holder.state = State.TIMED_OUT;

               ThreadTools.sleep(5);

               holder.state = State.RAN_WITHOUT_TIMING_OUT;
            }
         }, 10, TimeUnit.MILLISECONDS);
         assertFalse(holder.state.equals(State.DIDNT_RUN), "Didn't run. Shouldn't timeout.");
         assertTrue(holder.state.equals(State.RAN_WITHOUT_TIMING_OUT), "Timed out early.");
      }
   }

   @Test
   public void testThreadSleepEvenWhenInterrupted()
   {
      final long ONE_MILLION = 1000000;
      long millisecondsToSleep = 1100;
      int additionalNanosecondsToSleep = 500000;

      long totalNanosecondsToSleep = millisecondsToSleep * ONE_MILLION + additionalNanosecondsToSleep;

      SleepAndVerifyDespiteWakingUpRunnable runnable = new SleepAndVerifyDespiteWakingUpRunnable(millisecondsToSleep, additionalNanosecondsToSleep);

      int numberOfTimesToTest = 5;
      for (int i = 0; i < numberOfTimesToTest; i++)
      {
         Thread thread = new Thread(runnable);
         thread.start();

         while (!runnable.isDoneSleeping())
         {
            // Here we interrupt the thread to make sure that it sleeps for the total amount of time requested.
            thread.interrupt();

            try
            {
               Thread.sleep(millisecondsToSleep / 10);
            }
            catch (InterruptedException e)
            {
            }
         }

         long timeSleptInNanoseconds = runnable.getTimeSleptNanonseconds();
         long timeOverSleptInNanoseconds = timeSleptInNanoseconds - totalNanosecondsToSleep;

         // Check to make sure slept at least the amount specified. Method guarantees it sleeps at least or more than requested.
         assertTrue(timeOverSleptInNanoseconds > 0, "timeSlept = " + timeSleptInNanoseconds + ", totalNanosecondsToSleep = " + totalNanosecondsToSleep + " timeOverSleptInNanoseconds = "
                                   + timeOverSleptInNanoseconds);

         // Check to make sure didn't over sleep by more than 100 milliseconds, which seems reasonable on most operating systems.
         assertTrue(timeOverSleptInNanoseconds < 100 * ONE_MILLION, "timeSlept = " + timeSleptInNanoseconds + ", millisecondsToSleep = " + millisecondsToSleep);

         // Now make sure it doesn't get interrupted if we don't interrupt it...
         runnable = new SleepAndVerifyDespiteWakingUpRunnable(millisecondsToSleep, additionalNanosecondsToSleep);

         thread = new Thread(runnable);
         thread.start();

         while (!runnable.isDoneSleeping())
         {
            try
            {
               Thread.sleep(millisecondsToSleep / 10);
            }
            catch (InterruptedException e)
            {
            }
         }
      }
   }

   private class SleepAndVerifyDespiteWakingUpRunnable implements Runnable
   {
      private long millisecondsToSleep;
      private int additonalNanosecondsToSleep;
      private boolean isDoneSleeping;
      private long timeSleptNanosecondsMeasuredExternally;

      public SleepAndVerifyDespiteWakingUpRunnable(long millisecondsToSleep, int additonalNanosecondsToSleep)
      {
         this.millisecondsToSleep = millisecondsToSleep;
         this.additonalNanosecondsToSleep = additonalNanosecondsToSleep;
      }

      public boolean isDoneSleeping()
      {
         return isDoneSleeping;
      }

      public long getTimeSleptNanonseconds()
      {
         return timeSleptNanosecondsMeasuredExternally;
      }

      @Override
      public void run()
      {
         long startTime = System.nanoTime();
         long timeSleptMeasuredFromMethod = ThreadTools.sleep(millisecondsToSleep, additonalNanosecondsToSleep);

         long endTime = System.nanoTime();
         timeSleptNanosecondsMeasuredExternally = endTime - startTime;

         if (timeSleptNanosecondsMeasuredExternally < timeSleptMeasuredFromMethod)
         {
            throw new AssertionError(
                  "Huh: timeSleptNanosecondsMeasuredExternally = " + timeSleptNanosecondsMeasuredExternally + ", timeSleptMeasuredFromMethod = "
                        + timeSleptMeasuredFromMethod);
         }
         isDoneSleeping = true;
      }
   }

   private enum State
   {
      DIDNT_RUN, TIMED_OUT, RAN_WITHOUT_TIMING_OUT;
   }

   private class StateHolder
   {
      public State state = State.DIDNT_RUN;
   }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy