Commit be1fa8df authored by Grisha Kruglov's avatar Grisha Kruglov Committed by Grisha Kruglov
Browse files

Pre: introduce a RunWhenReadyQueue

This replaces the StartupTaskManager we had with a more general class.
New implementation is a thread-safe "gated task executor", which either
runs the task right away if it's marked as 'ready', or queries it to be
executed later on.

This ability to either execute or queue a task will be useful later on in the
commit series.
parent 4d139d52
Loading
Loading
Loading
Loading
+3 −3
Original line number Diff line number Diff line
@@ -150,12 +150,12 @@ open class FenixApplication : LocaleAwareApplication() {
        // }

        registerActivityLifecycleCallbacks(
            PerformanceActivityLifecycleCallbacks(components.performance.visualCompletenessTaskManager)
            PerformanceActivityLifecycleCallbacks(components.performance.visualCompletenessQueue)
        )

        components.performance.visualCompletenessTaskManager.add {
        components.performance.visualCompletenessQueue.runIfReadyOrQueue {
            GlobalScope.launch(Dispatchers.IO) {
                logger.info("Initializing storage after visual completeness...")
                logger.info("Running post-visual completeness tasks...")
                logElapsedTime(logger, "Storage initialization") {
                    components.core.historyStorage.warmUp()
                    components.core.bookmarksStorage.warmUp()
+5 −5
Original line number Diff line number Diff line
@@ -70,7 +70,7 @@ import org.mozilla.fenix.settings.logins.SavedLoginsFragmentDirections
import org.mozilla.fenix.theme.DefaultThemeManager
import org.mozilla.fenix.theme.ThemeManager
import org.mozilla.fenix.utils.BrowsersCache
import org.mozilla.fenix.utils.StartupTaskManager
import org.mozilla.fenix.utils.RunWhenReadyQueue

@SuppressWarnings("TooManyFunctions", "LargeClass")
open class HomeActivity : LocaleAwareAppCompatActivity() {
@@ -81,7 +81,7 @@ open class HomeActivity : LocaleAwareAppCompatActivity() {

    private var isVisuallyComplete = false

    private var visualCompletenessTaskManager: StartupTaskManager? = null
    private var visualCompletenessQueue: RunWhenReadyQueue? = null

    private var sessionObserver: SessionManager.Observer? = null

@@ -118,7 +118,7 @@ open class HomeActivity : LocaleAwareAppCompatActivity() {
                // This delay is temporary. We are delaying 5 seconds until the performance
                // team can locate the real point of visual completeness.
                it.postDelayed({
                    visualCompletenessTaskManager!!.start()
                    visualCompletenessQueue!!.ready()
                }, delay)
            }
        }
@@ -399,9 +399,9 @@ open class HomeActivity : LocaleAwareAppCompatActivity() {
     * The root container is null at this point, so let the HomeActivity know that
     * we are visually complete.
     */
    fun postVisualCompletenessQueue(visualCompletenessTaskManager: StartupTaskManager) {
    fun postVisualCompletenessQueue(visualCompletenessQueue: RunWhenReadyQueue) {
        isVisuallyComplete = true
        this.visualCompletenessTaskManager = visualCompletenessTaskManager
        this.visualCompletenessQueue = visualCompletenessQueue
    }

    companion object {
+2 −2
Original line number Diff line number Diff line
@@ -4,11 +4,11 @@

package org.mozilla.fenix.components

import org.mozilla.fenix.utils.StartupTaskManager
import org.mozilla.fenix.utils.RunWhenReadyQueue

/**
 * Component group for all functionality related to performance.
 */
class PerformanceComponent {
    val visualCompletenessTaskManager by lazy { StartupTaskManager() }
    val visualCompletenessQueue by lazy { RunWhenReadyQueue() }
}
+8 −12
Original line number Diff line number Diff line
@@ -520,20 +520,16 @@ class GleanMetricsService(private val context: Context) : MetricsService {

        // The code below doesn't need to execute immediately, so we'll add them to the visual
        // completeness task queue to be run later.
        val taskManager = context.components.performance.visualCompletenessTaskManager

        context.components.performance.visualCompletenessQueue.runIfReadyOrQueue {
            // We have to initialize Glean *on* the main thread, because it registers lifecycle
            // observers. However, the activation ping must be sent *off* of the main thread,
            // because it calls Google ad APIs that must be called *off* of the main thread.
            // These two things actually happen in parallel, but that should be ok because Glean
            // can handle events being recorded before it's initialized.
        taskManager.add {
            Glean.registerPings(Pings)
        }

            // setStartupMetrics is not a fast function. It does not need to be done before we can consider
            // ourselves initialized. So, let's do it, well, later.
        taskManager.add {
            setStartupMetrics()
        }
    }
+0 −55
Original line number Diff line number Diff line
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

package org.mozilla.fenix.utils

import org.mozilla.gecko.util.ThreadUtils

typealias StartupTask = () -> Unit

/**
 * A queue of tasks that are performed at specific points during Fenix startup.
 *
 * This queue contains a list of startup tasks. Each task in the queue will be started once Fenix
 * is visually complete.
 *
 * This class is not thread safe and should only be called from the main thread.
 */
class StartupTaskManager {
    private var tasks = mutableListOf<StartupTask>()
    private var hasStarted = false
        private set

    /**
     * Add a task to the queue.
     * Each task will execute on the main thread.
     *
     * @param task: The task to add to the queue.
     */
    @Synchronized
    fun add(task: StartupTask) {
        ThreadUtils.assertOnUiThread()
        if (hasStarted) {
            throw IllegalStateException("New tasks should not be added because queue already " +
                    "started, and these newly added tasks will not execute.")
        }

        tasks.add(task)
    }

    /**
     * Start all tasks in the queue. When all the tasks have been started,
     * clear the queue.
     */
    fun start() {
        ThreadUtils.assertOnUiThread()
        hasStarted = true

        tasks.forEach { it.invoke() }

        // Anything captured by the lambda will remain captured if we hold on to these tasks,
        // which takes up more memory than we need to.
        tasks.clear()
    }
}
Loading