fn initialize_inner()

in glean-core/src/lib.rs [376:578]


fn initialize_inner(
    cfg: InternalConfiguration,
    client_info: ClientInfoMetrics,
    callbacks: Box<dyn OnGleanEvents>,
) {
    if was_initialize_called() {
        log::error!("Glean should not be initialized multiple times");
        return;
    }

    let init_handle = std::thread::Builder::new()
        .name("glean.init".into())
        .spawn(move || {
            let upload_enabled = cfg.upload_enabled;
            let trim_data_to_registered_pings = cfg.trim_data_to_registered_pings;

            // Set the internal logging level.
            if let Some(level) = cfg.log_level {
                log::set_max_level(level)
            }

            let glean = match Glean::new(cfg) {
                Ok(glean) => glean,
                Err(err) => {
                    log::error!("Failed to initialize Glean: {}", err);
                    return;
                }
            };
            if core::setup_glean(glean).is_err() {
                return;
            }

            log::info!("Glean initialized");

            setup_state(State {
                client_info,
                callbacks,
            });

            let mut is_first_run = false;
            let mut dirty_flag = false;
            let mut pings_submitted = false;
            core::with_glean_mut(|glean| {
                // The debug view tag might have been set before initialize,
                // get the cached value and set it.
                let debug_tag = PRE_INIT_DEBUG_VIEW_TAG.lock().unwrap();
                if !debug_tag.is_empty() {
                    glean.set_debug_view_tag(&debug_tag);
                }

                // The log pings debug option might have been set before initialize,
                // get the cached value and set it.
                let log_pigs = PRE_INIT_LOG_PINGS.load(Ordering::SeqCst);
                if log_pigs {
                    glean.set_log_pings(log_pigs);
                }

                // The source tags might have been set before initialize,
                // get the cached value and set them.
                let source_tags = PRE_INIT_SOURCE_TAGS.lock().unwrap();
                if !source_tags.is_empty() {
                    glean.set_source_tags(source_tags.to_vec());
                }

                // Get the current value of the dirty flag so we know whether to
                // send a dirty startup baseline ping below.  Immediately set it to
                // `false` so that dirty startup pings won't be sent if Glean
                // initialization does not complete successfully.
                dirty_flag = glean.is_dirty_flag_set();
                glean.set_dirty_flag(false);

                // Perform registration of pings that were attempted to be
                // registered before init.
                let pings = PRE_INIT_PING_REGISTRATION.lock().unwrap();
                for ping in pings.iter() {
                    glean.register_ping_type(ping);
                }
                let pings = PRE_INIT_PING_ENABLED.lock().unwrap();
                for (ping, enabled) in pings.iter() {
                    glean.set_ping_enabled(ping, *enabled);
                }

                // The attribution and distribution might have been set before initialize,
                // take the cached values and set them.
                if let Some(attribution) = PRE_INIT_ATTRIBUTION.lock().unwrap().take() {
                    glean.update_attribution(attribution);
                }
                if let Some(distribution) = PRE_INIT_DISTRIBUTION.lock().unwrap().take() {
                    glean.update_distribution(distribution);
                }

                // If this is the first time ever the Glean SDK runs, make sure to set
                // some initial core metrics in case we need to generate early pings.
                // The next times we start, we would have them around already.
                is_first_run = glean.is_first_run();
                if is_first_run {
                    let state = global_state().lock().unwrap();
                    initialize_core_metrics(glean, &state.client_info);
                }

                // Deal with any pending events so we can start recording new ones
                pings_submitted = glean.on_ready_to_submit_pings(trim_data_to_registered_pings);
            });

            {
                let state = global_state().lock().unwrap();
                // We need to kick off upload in these cases:
                // 1. Pings were submitted through Glean and it is ready to upload those pings;
                // 2. Upload is disabled, to upload a possible deletion-request ping.
                if pings_submitted || !upload_enabled {
                    if let Err(e) = state.callbacks.trigger_upload() {
                        log::error!("Triggering upload failed. Error: {}", e);
                    }
                }
            }

            core::with_glean(|glean| {
                // Start the MPS if its handled within Rust.
                glean.start_metrics_ping_scheduler();
            });

            // The metrics ping scheduler might _synchronously_ submit a ping
            // so that it runs before we clear application-lifetime metrics further below.
            // For that it needs access to the `Glean` object.
            // Thus we need to unlock that by leaving the context above,
            // then re-lock it afterwards.
            // That's safe because user-visible functions will be queued and thus not execute until
            // we unblock later anyway.
            {
                let state = global_state().lock().unwrap();

                // Set up information and scheduling for Glean owned pings. Ideally, the "metrics"
                // ping startup check should be performed before any other ping, since it relies
                // on being dispatched to the API context before any other metric.
                if state.callbacks.start_metrics_ping_scheduler() {
                    if let Err(e) = state.callbacks.trigger_upload() {
                        log::error!("Triggering upload failed. Error: {}", e);
                    }
                }
            }

            core::with_glean_mut(|glean| {
                let state = global_state().lock().unwrap();

                // Check if the "dirty flag" is set. That means the product was probably
                // force-closed. If that's the case, submit a 'baseline' ping with the
                // reason "dirty_startup". We only do that from the second run.
                if !is_first_run && dirty_flag {
                    // The `submit_ping_by_name_sync` function cannot be used, otherwise
                    // startup will cause a dead-lock, since that function requests a
                    // write lock on the `glean` object.
                    // Note that unwrapping below is safe: the function will return an
                    // `Ok` value for a known ping.
                    if glean.submit_ping_by_name("baseline", Some("dirty_startup")) {
                        if let Err(e) = state.callbacks.trigger_upload() {
                            log::error!("Triggering upload failed. Error: {}", e);
                        }
                    }
                }

                // From the second time we run, after all startup pings are generated,
                // make sure to clear `lifetime: application` metrics and set them again.
                // Any new value will be sent in newly generated pings after startup.
                if !is_first_run {
                    glean.clear_application_lifetime_metrics();
                    initialize_core_metrics(glean, &state.client_info);
                }
            });

            // Signal Dispatcher that init is complete
            // bug 1839433: It is important that this happens after any init tasks
            // that shutdown() depends on. At time of writing that's only setting up
            // the global Glean, but it is probably best to flush the preinit queue
            // as late as possible in the glean.init thread.
            match dispatcher::flush_init() {
                Ok(task_count) if task_count > 0 => {
                    core::with_glean(|glean| {
                        glean_metrics::error::preinit_tasks_overflow
                            .add_sync(glean, task_count as i32);
                    });
                }
                Ok(_) => {}
                Err(err) => log::error!("Unable to flush the preinit queue: {}", err),
            }

            let state = global_state().lock().unwrap();
            state.callbacks.initialize_finished();
        })
        .expect("Failed to spawn Glean's init thread");

    // For test purposes, store the glean init thread's JoinHandle.
    INIT_HANDLES.lock().unwrap().push(init_handle);

    // Mark the initialization as called: this needs to happen outside of the
    // dispatched block!
    INITIALIZE_CALLED.store(true, Ordering::SeqCst);

    // In test mode we wait for initialization to finish.
    // This needs to run after we set `INITIALIZE_CALLED`, so it's similar to normal behavior.
    if dispatcher::global::is_test_mode() {
        join_init();
    }
}