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

com.ajjpj.afoundation.concurrent.AFutureImpl Maven / Gradle / Ivy

There is a newer version: 1.0-pre19
Show newest version
package com.ajjpj.afoundation.concurrent;

import com.ajjpj.afoundation.collection.immutable.AList;
import com.ajjpj.afoundation.collection.immutable.AOption;
import com.ajjpj.afoundation.collection.tuples.ATuple2;
import com.ajjpj.afoundation.collection.tuples.ATuple3;
import com.ajjpj.afoundation.function.AFunction1;
import com.ajjpj.afoundation.function.AStatement1NoThrow;
import com.ajjpj.afoundation.function.AStatement2NoThrow;

import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicReference;


/**
 * @author arno
 */
@SuppressWarnings ("Convert2Lambda")
class AFutureImpl extends FutureTask implements AFuture {
    private final AtomicReference>> onFinishedListeners = new AtomicReference<> (AList.>nil ());

    private final ATaskScheduler threadPool;

    private static final Callable NO_CALLABLE = new Callable () {
        @Override public Object call () throws Exception {
            return null;
        }
    };

     AFutureImpl unscheduled () {
        return unscheduled (threadPool);
    }

    @SuppressWarnings ("unchecked")
    static  AFutureImpl unscheduled (ATaskScheduler threadPool) {
        return new AFutureImpl<> (threadPool, NO_CALLABLE);
    }

    AFutureImpl (ATaskScheduler threadPool, Callable callable) {
        super (callable);
        this.threadPool = threadPool;
    }
    AFutureImpl (ATaskScheduler threadPool, Runnable runnable, T value) {
        super (runnable, value);
        this.threadPool = threadPool;
    }

     @Override public AFuture withDefaultValue (final T defaultValue) {
         final AFutureImpl result = unscheduled ();

         onFinished (new AStatement2NoThrow () {
             @Override public void apply (T param1, Throwable param2) {
                 if (param2 == null) {
                     result.set (param1);
                 }
                 else {
                     result.set (defaultValue);
                 }
             }
         });
         return result;
     }

    @Override protected void set (T t) {
        super.set (t);
    }

    @Override protected void setException (Throwable t) {
        super.setException (t);
    }

    void setTimedOut() {
        setException (new TimeoutException ());
    }

    @Override public boolean isFinished () {
        return super.isDone ();
    }
    @Override protected void done () {
        notifyListeners ();
    }

    private void notifyListeners () {
        T result = null;
        Throwable th = null;

        try {
            result = get ();
        }
        catch (Exception exc) {
            th = exc;
        }

        // This method can be called several times, even concurrently - see the comment in onFinished(). Atomically removing all listeners from the list deals with
        //  that concurrency in a robust fashion.
        for (AStatement2NoThrow l: onFinishedListeners.getAndSet (AList.>nil())) {
            l.apply (result, th);
        }
    }

    @Override public void onSuccess (final AStatement1NoThrow callback) {
        onFinished (new AStatement2NoThrow () {
            @Override public void apply (T param1, Throwable param2) {
                if (param2 == null) {
                    callback.apply (param1);
                }
            }
        });
    }

    @Override public void onFailure (final AStatement1NoThrow callback) {
        onFinished (new AStatement2NoThrow () {
            @Override public void apply (T param1, Throwable param2) {
                if (param2 != null) {
                    callback.apply (param2);
                }
            }
        });
    }

    @Override public void onFinished (AStatement2NoThrow callback) {
        AList> prev;
        do {
            prev = onFinishedListeners.get ();
        }
        while (! onFinishedListeners.compareAndSet (prev, prev.cons (callback)));

        if (isDone ()) {
            // This is necessary for notifying listeners that are registered *after* the future is done.
            // NB: Doing it this way takes care of the situation that the future is not done at the beginning of onFinished() but becomes so before the listener is registered.
            notifyListeners ();
        }
    }

    @SuppressWarnings ("unchecked")
    @Override public  AFuture mapSync (final AFunction1 f) {
        final AFutureImpl result = new AFutureImpl<> (threadPool, NO_CALLABLE);

        onFinished (new AStatement2NoThrow () {
            @Override public void apply (T param1, Throwable param2) {
                if (param2 != null) {
                    if (param2 instanceof ExecutionException) {
                        final ExecutionException ee = (ExecutionException) param2;
                        if (ee.getCause () != null) {
                            param2 = ee.getCause ();
                        }
                    }

                    result.setException (param2);
                    return;
                }

                try {
                    result.set (f.apply (param1));
                }
                catch (Exception e) {
                    result.setException (e);
                }
            }
        });

        return result;
    }

    @Override public  AFuture mapAsync (AFunction1 f, long timeout, TimeUnit timeoutUnit) {
        return mapAsync (f, threadPool, timeout, timeoutUnit);
    }

    @SuppressWarnings ("unchecked")
    @Override public  AFuture mapAsync (final AFunction1 f, final ATaskScheduler threadPool, final long timeout, final TimeUnit timeoutUnit) {
        final AFutureImpl result = new AFutureImpl<> (threadPool, NO_CALLABLE);

        onFinished (new AStatement2NoThrow () {
            @Override public void apply (final T param1, Throwable param2) {
                if (param2 != null) {
                    if (param2 instanceof ExecutionException) {
                        final ExecutionException ee = (ExecutionException) param2;
                        if (ee.getCause () != null) {
                            param2 = ee.getCause ();
                        }
                    }
                    result.setException (param2);
                    return;
                }

                try {
                    final AFuture mappedFuture = threadPool.submit (new Callable () {
                        @Override public U call () throws Exception {
                            return f.apply (param1);
                        }
                    }, timeout, timeoutUnit);

                    mappedFuture.onFinished (new AStatement2NoThrow () {
                        @Override public void apply (U param1, Throwable param2) {
                            if (param2 != null) {
                                result.setException (param2);
                            }
                            else {
                                result.set (param1);
                            }
                        }
                    });
                }
                catch (Throwable exc) {
                    result.setException (exc);
                }
            }
        });

        return result;
    }

    @SuppressWarnings ("unchecked")
    @Override public  AFuture> zip (AFuture other) {
        final AFutureImpl> result = new AFutureImpl<> (threadPool, NO_CALLABLE);

        final ResultCollector collector = new ResultCollector<> ();
        collector.set3 (collector);

        onFinished (new AStatement2NoThrow () {
            @Override public void apply (T param1, Throwable param2) {
                if (param2 != null) {
                    result.setException (param2);
                }
                else {
                    final boolean finished = collector.set1 (param1);
                    if (finished) {
                        result.set (new ATuple2<> (collector._1.get (), collector._2.get ()));
                    }
                }
            }
        });
        other.onFinished (new AStatement2NoThrow () {
            @Override public void apply (U param1, Throwable param2) {
                if (param2 != null) {
                    result.setException (param2);
                }
                else {
                    final boolean finished = collector.set2 (param1);
                    if (finished) {
                        result.set (new ATuple2<> (collector._1.get (), collector._2.get ()));
                    }
                }
            }
        });

        return result;
    }

    @SuppressWarnings ("unchecked")
    @Override public  AFuture> zip (AFuture other1, AFuture other2) {
        final AFutureImpl> result = new AFutureImpl<> (threadPool, NO_CALLABLE);

        final ResultCollector collector = new ResultCollector<> ();

        onFinished (new AStatement2NoThrow () {
            @Override public void apply (T param1, Throwable param2) {
                if (param2 != null) {
                    result.setException (param2);
                }
                else {
                    final boolean finished = collector.set1 (param1);
                    if (finished) {
                        result.set (new ATuple3<> (collector._1.get (), collector._2.get (), collector._3.get ()));
                    }
                }
            }
        });
        other1.onFinished (new AStatement2NoThrow () {
            @Override public void apply (U param1, Throwable param2) {
                if (param2 != null) {
                    result.setException (param2);
                }
                else {
                    final boolean finished = collector.set2 (param1);
                    if (finished) {
                        result.set (new ATuple3<> (collector._1.get (), collector._2.get (), collector._3.get ()));
                    }
                }
            }
        });
        other2.onFinished (new AStatement2NoThrow () {
            @Override public void apply (V param1, Throwable param2) {
                if (param2 != null) {
                    result.setException (param2);
                }
                else {
                    final boolean finished = collector.set3 (param1);
                    if (finished) {
                        result.set (new ATuple3<> (collector._1.get (), collector._2.get (), collector._3.get ()));
                    }
                }
            }
        });

        return result;
    }

    static class ResultCollector {
        private AOption _1 = AOption.none ();
        private AOption _2 = AOption.none ();
        private AOption _3 = AOption.none ();

        synchronized boolean set1 (R o) {
            _1 = AOption.some (o);
            return _2.isDefined () && _3.isDefined ();
        }
        synchronized boolean set2 (S o) {
            _2 = AOption.some (o);
            return _1.isDefined () && _3.isDefined ();
        }
        synchronized boolean set3 (T o) {
            _3 = AOption.some (o);
            return _1.isDefined () && _2.isDefined ();
        }
    }
}