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

com.google.firebase.database.utilities.DefaultRunLoop Maven / Gradle / Ivy

/*
 * 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.database.utilities;

import com.google.firebase.database.DatabaseException;
import com.google.firebase.database.FirebaseDatabase;
import com.google.firebase.database.core.RunLoop;

import com.google.firebase.internal.FirebaseScheduledExecutor;
import java.lang.Thread.UncaughtExceptionHandler;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;

public abstract class DefaultRunLoop implements RunLoop {

  private final ScheduledThreadPoolExecutor executor;
  private UncaughtExceptionHandler exceptionHandler;

  /**
   * Creates a DefaultRunLoop that optionally restarts its threads periodically. If 'context' is
   * provided, these restarts will automatically interrupt and resume all Repo connections.
   */
  protected DefaultRunLoop(ThreadFactory threadFactory) {
    executor = new FirebaseScheduledExecutor(threadFactory, "firebase-database-worker") {
      @Override
      protected void afterExecute(Runnable runnable, Throwable throwable) {
        super.afterExecute(runnable, throwable);
        if (throwable == null && runnable instanceof Future) {
          Future future = (Future) runnable;
          try {
            // Not all Futures will be done, e.g. when used with scheduledAtFixedRate
            if (future.isDone()) {
              future.get();
            }
          } catch (CancellationException ce) {
            // Cancellation exceptions are okay, we expect them to happen sometimes
          } catch (ExecutionException ee) {
            throwable = ee.getCause();
          } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
          }
        }

        if (throwable != null) {
          handleExceptionInternal(throwable);
        }
      }
    };

    // Core threads don't time out, this only takes effect when we drop the number of required
    // core threads
    executor.setKeepAliveTime(3, TimeUnit.SECONDS);
  }

  public static String messageForException(Throwable t) {
    if (t instanceof OutOfMemoryError) {
      return "Firebase Database encountered an OutOfMemoryError. You may need to reduce the"
          + " amount of data you are syncing to the client (e.g. by using queries or syncing"
          + " a deeper path). See "
          + "https://firebase.google"
          + ".com/docs/database/ios/structure-data#best_practices_for_data_structure"
          + " and "
          + "https://firebase.google.com/docs/database/android/retrieve-data#filtering_data";
    } else if (t instanceof DatabaseException) {
      // Exception should be self-explanatory and they shouldn't contact support.
      return "";
    } else {
      return "Uncaught exception in Firebase Database runloop ("
          + FirebaseDatabase.getSdkVersion()
          + "). Please report to [email protected]";
    }
  }

  private void handleExceptionInternal(Throwable e) {
    UncaughtExceptionHandler exceptionHandler;
    exceptionHandler = getExceptionHandler();
    try {
      if (exceptionHandler != null) {
        exceptionHandler.uncaughtException(Thread.currentThread(), e);
      }
    } finally {
      handleException(e);
    }
  }

  public abstract void handleException(Throwable e);

  public ScheduledExecutorService getExecutorService() {
    return this.executor;
  }

  @Override
  public void scheduleNow(final Runnable runnable) {
    executor.execute(runnable);
  }

  @Override
  @SuppressWarnings("rawtypes")
  public ScheduledFuture schedule(final Runnable runnable, long milliseconds) {
    return executor.schedule(runnable, milliseconds, TimeUnit.MILLISECONDS);
  }

  @Override
  public void shutdown() {
    executor.setCorePoolSize(0);
  }

  @Override
  public void restart() {
    executor.setCorePoolSize(1);
  }

  /**
   * Returns the exception handler currently set on this run loop. This is to be
   * used during integration testing.
   */
  public synchronized UncaughtExceptionHandler getExceptionHandler() {
    return exceptionHandler;
  }

  /**
   * Sets the specified exception handler for intercepting run loop errors. This is to be
   * used during integration testing for handling errors that may occur in the run loop's
   * worker thread.
   */
  public synchronized void setExceptionHandler(UncaughtExceptionHandler exceptionHandler) {
    this.exceptionHandler = exceptionHandler;
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy