DaveAKing / guava-libraries

Automatically exported from code.google.com/p/guava-libraries
Apache License 2.0
0 stars 0 forks source link

Futures.immediateFuture alternative with timeout #1696

Closed GoogleCodeExporter closed 9 years ago

GoogleCodeExporter commented 9 years ago
Hi,

I use immediateFuture a lot in unit testing. I would like to have a method 
added that does almost the same thing, but instead of returning immediately, it 
will return after a configured timeout. Something like:

Futures.afterDurationFuture( value, 5, TimeUnit.SECONDS );

For easy implementation, the countdown of the time starts when the get() method 
is called.

I implemented something myself, but it is not complete enough and used 
Joda-Time. It would be great to have this out of the box with Guava:

import com.google.common.util.concurrent.ListenableFuture;
import org.joda.time.Duration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.annotation.Nullable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;

import static com.google.common.base.Preconditions.checkNotNull;

public class MoreFutures
{
    public static <V> ListenableFuture<V> returnAfterDurationFuture( @Nullable V value, Duration duration )
    {
        return new AfterDurationSuccessfulFuture<V>( value, duration );
    }

    private static class AfterDurationSuccessfulFuture<V>
            implements ListenableFuture<V>
    {

        private static final Logger logger = LoggerFactory.getLogger( AfterDurationSuccessfulFuture.class );

        @Nullable
        private final V m_value;

        private Duration m_duration;
        private AtomicBoolean m_doneSleeping = new AtomicBoolean();

        public AfterDurationSuccessfulFuture( @Nullable V value, Duration duration )
        {
            this.m_value = value;
            m_duration = duration;
        }

        @Override
        public void addListener( Runnable listener, Executor executor )
        {
            checkNotNull( listener, "Runnable was null." );
            checkNotNull( executor, "Executor was null." );
            try
            {
                executor.execute( listener );
            }
            catch (RuntimeException e)
            {
                // ListenableFuture's contract is that it will not throw unchecked
                // exceptions, so log the bad runnable and/or executor and swallow it.
                logger.error( "RuntimeException while executing runnable "
                        + listener + " with executor " + executor, e );
            }
        }

        @Override
        public boolean cancel( boolean mayInterruptIfRunning )
        {
            return false;
        }

        @Override
        public V get()
        {
            waitUntilDoneSleeping();
            return m_value;
        }

        @Override
        public V get( long timeout, TimeUnit unit ) throws ExecutionException
        {
            checkNotNull( unit );
            return get();
        }

        @Override
        public boolean isCancelled()
        {
            return false;
        }

        @Override
        public boolean isDone()
        {
            return m_doneSleeping.get();
        }

        private void waitUntilDoneSleeping()
        {
            try
            {
                long millis = m_duration.getMillis();
                logger.debug( "Sleeping {} ms", millis );
                Thread.sleep( millis );
                m_doneSleeping.set( true );
            }
            catch (InterruptedException e)
            {
                Thread.currentThread().interrupt();
            }
        }
    }
}

I find this very useful when I want to see what impact to performance a certain 
async operation can have with different durations.

Original issue reported on code.google.com by wim.debl...@gmail.com on 14 Mar 2014 at 9:06

GoogleCodeExporter commented 9 years ago
We might add a Future that automatically completes after a delay. The most 
likely implementation would be with a ScheduledExecutorService. With the 
proposed implementation, a listener that calls Future.get() (as many do) would 
block for 5 seconds (or however long). This would distort the performance 
measurements that you're attempting to make. Similar distortions would arise 
from multiple calls to get(), each of which would block anew.

Original comment by cpov...@google.com on 14 Mar 2014 at 1:58

GoogleCodeExporter commented 9 years ago
isn't this a simple matter of:

ListeningScheduledExecutorService service = ...;

public <T> ListenableFuture<T> delayed(T v, long t, TimeUnit u) {
  return service.schedule(Callables.returning(v), t, u);
}

Original comment by lu...@google.com on 14 Mar 2014 at 3:20

GoogleCodeExporter commented 9 years ago
Thanks for the code snippet with the scheduled service, it is as simple as that 
indeed.

Original comment by wim.debl...@gmail.com on 18 Mar 2014 at 8:00

GoogleCodeExporter commented 9 years ago
Excellent. Thanks, Luke.

Original comment by cpov...@google.com on 18 Mar 2014 at 1:31

GoogleCodeExporter commented 9 years ago
This issue has been migrated to GitHub.

It can be found at https://github.com/google/guava/issues/<id>

Original comment by cgdecker@google.com on 1 Nov 2014 at 4:09

GoogleCodeExporter commented 9 years ago

Original comment by cgdecker@google.com on 3 Nov 2014 at 9:07