/* * Copyright 2017 Google Inc. * * 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. */ package com.google.firebase.tasks; import static com.google.common.base.Preconditions.checkNotNull; import com.google.common.base.Preconditions; import com.google.firebase.internal.GuardedBy; import com.google.firebase.internal.NonNull; import com.google.firebase.internal.Nullable; import java.util.concurrent.Executor; /** * Default implementation of {@link Task}. */ final class TaskImpl<T> extends Task<T> { private final Object lock = new Object(); private final TaskCompletionListenerQueue<T> listenerQueue = new TaskCompletionListenerQueue<>(); @GuardedBy("lock") private boolean complete; @GuardedBy("lock") private T result; @GuardedBy("lock") private Exception exception; @Override public boolean isComplete() { synchronized (lock) { return complete; } } @Override public boolean isSuccessful() { synchronized (lock) { return complete && exception == null; } } @Override public T getResult() { synchronized (lock) { checkCompleteLocked(); if (exception != null) { throw new RuntimeExecutionException(exception); } return result; } } @Override public <X extends Throwable> T getResult(@NonNull Class<X> exceptionType) throws X { synchronized (lock) { checkCompleteLocked(); if (exceptionType.isInstance(exception)) { throw exceptionType.cast(exception); } if (exception != null) { throw new RuntimeExecutionException(exception); } return result; } } public void setResult(T result) { synchronized (lock) { checkNotCompleteLocked(); complete = true; this.result = result; } // Intentionally outside the lock. listenerQueue.flush(this); } @Nullable @Override public Exception getException() { synchronized (lock) { return exception; } } @SuppressWarnings("ThrowableResultOfMethodCallIgnored") public void setException(@NonNull Exception e) { checkNotNull(e, "Exception must not be null"); synchronized (lock) { checkNotCompleteLocked(); complete = true; exception = e; } // Intentionally outside the lock. listenerQueue.flush(this); } @NonNull @Override public Task<T> addOnSuccessListener(@NonNull OnSuccessListener<? super T> listener) { return addOnSuccessListener(TaskExecutors.DEFAULT_THREAD_POOL, listener); } @NonNull @Override public Task<T> addOnSuccessListener( @NonNull Executor executor, @NonNull OnSuccessListener<? super T> listener) { listenerQueue.add(new OnSuccessCompletionListener<>(executor, listener)); flushIfComplete(); return this; } @NonNull @Override public Task<T> addOnFailureListener(@NonNull OnFailureListener listener) { return addOnFailureListener(TaskExecutors.DEFAULT_THREAD_POOL, listener); } @NonNull @Override public Task<T> addOnFailureListener( @NonNull Executor executor, @NonNull OnFailureListener listener) { listenerQueue.add(new OnFailureCompletionListener<T>(executor, listener)); flushIfComplete(); return this; } @NonNull @Override public Task<T> addOnCompleteListener(@NonNull OnCompleteListener<T> listener) { return addOnCompleteListener(TaskExecutors.DEFAULT_THREAD_POOL, listener); } @NonNull @Override public Task<T> addOnCompleteListener( @NonNull Executor executor, @NonNull OnCompleteListener<T> listener) { listenerQueue.add(new OnCompleteCompletionListener<>(executor, listener)); flushIfComplete(); return this; } @NonNull @Override public <R> Task<R> continueWith(@NonNull Continuation<T, R> continuation) { return continueWith(TaskExecutors.DEFAULT_THREAD_POOL, continuation); } @NonNull @Override public <R> Task<R> continueWith( @NonNull Executor executor, @NonNull Continuation<T, R> continuation) { TaskImpl<R> continuationTask = new TaskImpl<>(); listenerQueue.add( new ContinueWithCompletionListener<>(executor, continuation, continuationTask)); flushIfComplete(); return continuationTask; } @NonNull @Override public <R> Task<R> continueWithTask(@NonNull Continuation<T, Task<R>> continuation) { return continueWithTask(TaskExecutors.DEFAULT_THREAD_POOL, continuation); } @NonNull @Override public <R> Task<R> continueWithTask( @NonNull Executor executor, @NonNull Continuation<T, Task<R>> continuation) { TaskImpl<R> continuationTask = new TaskImpl<>(); listenerQueue.add( new ContinueWithTaskCompletionListener<>(executor, continuation, continuationTask)); flushIfComplete(); return continuationTask; } public boolean trySetResult(T result) { synchronized (lock) { if (complete) { return false; } complete = true; this.result = result; } // Intentionally outside the lock. listenerQueue.flush(this); return true; } @SuppressWarnings("ThrowableResultOfMethodCallIgnored") public boolean trySetException(@NonNull Exception e) { checkNotNull(e, "Exception must not be null"); synchronized (lock) { if (complete) { return false; } complete = true; exception = e; } // Intentionally outside the lock. listenerQueue.flush(this); return true; } @GuardedBy("lock") private void checkCompleteLocked() { Preconditions.checkState(complete, "Task is not yet complete"); } @GuardedBy("lock") private void checkNotCompleteLocked() { Preconditions.checkState(!complete, "Task is already complete"); } private void flushIfComplete() { synchronized (lock) { if (!complete) { return; } } // Intentionally outside the lock. listenerQueue.flush(this); } }