File: model_load_manager.cc

package info (click to toggle)
chromium 139.0.7258.127-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 6,122,156 kB
  • sloc: cpp: 35,100,771; ansic: 7,163,530; javascript: 4,103,002; python: 1,436,920; asm: 946,517; xml: 746,709; pascal: 187,653; perl: 88,691; sh: 88,436; objc: 79,953; sql: 51,488; cs: 44,583; fortran: 24,137; makefile: 22,147; tcl: 15,277; php: 13,980; yacc: 8,984; ruby: 7,485; awk: 3,720; lisp: 3,096; lex: 1,327; ada: 727; jsp: 228; sed: 36
file content (309 lines) | stat: -rw-r--r-- 12,133 bytes parent folder | download | duplicates (5)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
// Copyright 2014 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "components/sync/service/model_load_manager.h"

#include <map>
#include <utility>

#include "base/barrier_closure.h"
#include "base/debug/alias.h"
#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/logging.h"
#include "base/metrics/histogram_functions.h"
#include "base/timer/elapsed_timer.h"
#include "components/sync/base/data_type.h"
#include "components/sync/base/features.h"
#include "components/sync/base/sync_stop_metadata_fate.h"
#include "components/sync/service/data_type_controller.h"
#include "components/sync/service/sync_error.h"

namespace syncer {

namespace {

bool ModelIsLoadedOrFailed(const DataTypeController& mtc) {
  switch (mtc.state()) {
    case DataTypeController::NOT_RUNNING:
    case DataTypeController::MODEL_STARTING:
    case DataTypeController::STOPPING:
      return false;
    case DataTypeController::MODEL_LOADED:
    case DataTypeController::RUNNING:
    case DataTypeController::FAILED:
      return true;
  }
}

}  // namespace

const base::TimeDelta kSyncLoadModelsTimeoutDuration = base::Seconds(30);

ModelLoadManager::ModelLoadManager(
    const DataTypeController::TypeMap* controllers,
    ModelLoadManagerDelegate* processor)
    : controllers_(controllers), delegate_(processor) {}

ModelLoadManager::~ModelLoadManager() = default;

void ModelLoadManager::Configure(DataTypeSet preferred_types_without_errors,
                                 DataTypeSet preferred_types,
                                 const ConfigureContext& context) {
  // `preferred_types_without_errors` must be a subset of `preferred_types`.
  DCHECK(preferred_types.HasAll(preferred_types_without_errors))
      << " desired: "
      << DataTypeSetToDebugString(preferred_types_without_errors)
      << ", preferred: " << DataTypeSetToDebugString(preferred_types);

  const bool sync_mode_changed =
      configure_context_.has_value() &&
      configure_context_->sync_mode != context.sync_mode;

  configure_context_ = context;

  // Only keep types that have controllers.
  preferred_types_without_errors_.Clear();
  for (DataType type : preferred_types_without_errors) {
    auto dtc_iter = controllers_->find(type);
    if (dtc_iter != controllers_->end()) {
      const DataTypeController* dtc = dtc_iter->second.get();
      // Controllers in a FAILED state or with preconditions not met should have
      // been filtered out by the DataTypeManager.
      CHECK_NE(dtc->state(), DataTypeController::FAILED);
      preferred_types_without_errors_.Put(type);
    }
  }

  DVLOG(1) << "ModelLoadManager: Initializing for "
           << DataTypeSetToDebugString(preferred_types_without_errors_);

  delegate_waiting_for_ready_for_configure_ = true;

  if (sync_mode_changed) {
    // When the sync mode changes (between full-sync and transport mode),
    // restart all data types so that they can re-wire to the correct storage.
    DVLOG(1) << "ModelLoadManager: Stopping all types because the sync mode "
                "changed.";

    for (const auto& [type, dtc] : *controllers_) {
      // Use CLEAR_METADATA in this case to avoid that two independent model
      // instances maintain their own copy of sync metadata.
      StopDatatypeImpl(/*error=*/std::nullopt,
                       SyncStopMetadataFate::CLEAR_METADATA, dtc.get(),
                       base::DoNothing());
    }
  } else {
    // If the sync mode hasn't changed, stop only the types that are not
    // preferred anymore.
    DVLOG(1) << "ModelLoadManager: Stopping disabled types.";
    for (const auto& [type, dtc] : *controllers_) {
      if (!preferred_types_without_errors_.Has(dtc->type())) {
        // Call Stop() even on types not running to allow clearing metadata.
        // This is useful to clear metadata for types which were disabled during
        // configuration. Also clear metadata depending on the precondition
        // state.
        SyncStopMetadataFate metadata_fate =
            SyncStopMetadataFate::KEEP_METADATA;
        if (!preferred_types.Has(dtc->type()) ||
            dtc->GetPreconditionState() ==
                DataTypeController::PreconditionState::kMustStopAndClearData) {
          metadata_fate = SyncStopMetadataFate::CLEAR_METADATA;
        }
        DVLOG(1) << "ModelLoadManager: stop " << dtc->name()
                 << " with metadata fate " << static_cast<int>(metadata_fate);
        StopDatatypeImpl(/*error=*/std::nullopt, metadata_fate, dtc.get(),
                         base::DoNothing());
      }
    }
  }

  // Note: At this point, some types may still be in the STOPPING state, i.e.
  // they cannot be loaded right now. LoadDesiredTypes() takes care to wait for
  // the desired types to finish stopping before starting them again. And for
  // undesired types, it doesn't matter in what state they are.
  LoadDesiredTypes();
}

void ModelLoadManager::StopDatatype(DataType type,
                                    SyncStopMetadataFate metadata_fate,
                                    SyncError error) {
  preferred_types_without_errors_.Remove(type);

  DataTypeController* dtc = controllers_->find(type)->second.get();
  // Call stop on data types even if they are
  // already stopped since we may still want to clear the metadata.
  StopDatatypeImpl(error, metadata_fate, dtc, base::DoNothing());

  // Removing a desired type may mean all models are now loaded.
  NotifyDelegateIfReadyForConfigure();
}

void ModelLoadManager::StopDatatypeImpl(
    const std::optional<SyncError>& error,
    SyncStopMetadataFate metadata_fate,
    DataTypeController* dtc,
    DataTypeController::StopCallback callback) {
  const DataType data_type = dtc->type();

  // Avoid that the local variable is optimized away, motivated by
  // crbug.com/1456872.
  base::debug::Alias(&data_type);

  delegate_->OnSingleDataTypeWillStop(data_type, error);

  // Note: Depending on `metadata_fate`, data types will clear their metadata
  // in response to Stop().
  dtc->Stop(metadata_fate, std::move(callback));
}

void ModelLoadManager::LoadDesiredTypes() {
  // Note: `preferred_types_without_errors_` might be modified during iteration
  // (e.g. in ModelLoadCallback()), so make a copy.
  const DataTypeSet types = preferred_types_without_errors_;

  // Start timer to measure time for loading to complete.
  load_models_elapsed_timer_ = std::make_unique<base::ElapsedTimer>();

  for (DataType type : types) {
    auto dtc_iter = controllers_->find(type);
    CHECK(dtc_iter != controllers_->end());
    DataTypeController* dtc = dtc_iter->second.get();
    if (dtc->state() == DataTypeController::NOT_RUNNING) {
      LoadModelsForType(dtc);
    } else if (dtc->state() == DataTypeController::STOPPING) {
      // If the datatype is already STOPPING, we wait for it to stop before
      // starting it up again.
      auto stop_callback =
          base::BindRepeating(&ModelLoadManager::LoadModelsForType,
                              weak_ptr_factory_.GetWeakPtr(), dtc);
      dtc->Stop(SyncStopMetadataFate::KEEP_METADATA, std::move(stop_callback));
    }
  }

  // Start a timeout timer for load.
  load_models_timeout_timer_.Start(FROM_HERE, kSyncLoadModelsTimeoutDuration,
                                   this,
                                   &ModelLoadManager::OnLoadModelsTimeout);

  // It's possible that all models are already loaded.
  NotifyDelegateIfReadyForConfigure();
}

void ModelLoadManager::Stop(SyncStopMetadataFate metadata_fate) {
  // Ignore callbacks from controllers.
  weak_ptr_factory_.InvalidateWeakPtrs();

  // Stop all data types. Note that stop is also called on data types that are
  // already stopped to allow clearing the metadata.
  for (const auto& [type, dtc] : *controllers_) {
    // We don't really wait until all datatypes have been fully stopped, which
    // is only required (and in fact waited for) when Configure() is called.
    StopDatatypeImpl(/*error=*/std::nullopt, metadata_fate, dtc.get(),
                     base::DoNothing());
    DVLOG(1) << "ModelLoadManager: Stopped " << dtc->name();
  }

  load_models_timeout_timer_.Stop();
  delegate_waiting_for_ready_for_configure_ = false;

  preferred_types_without_errors_.Clear();
}

void ModelLoadManager::ModelLoadCallback(
    DataType type,
    const std::optional<ModelError>& error) {
  DVLOG(1) << "ModelLoadManager: ModelLoadCallback for "
           << DataTypeToDebugString(type);

  if (error.has_value()) {
    DVLOG(1) << "ModelLoadManager: Type encountered an error.";
    preferred_types_without_errors_.Remove(type);
    DataTypeController* dtc = controllers_->find(type)->second.get();
    StopDatatypeImpl(
        SyncError(error->location(), SyncError::MODEL_ERROR, error->message()),
        SyncStopMetadataFate::KEEP_METADATA, dtc, base::DoNothing());
    NotifyDelegateIfReadyForConfigure();
    return;
  }

  // This happens when slow loading type is disabled by new configuration or
  // the model came unready during loading.
  if (!preferred_types_without_errors_.Has(type)) {
    return;
  }

  NotifyDelegateIfReadyForConfigure();
}

void ModelLoadManager::NotifyDelegateIfReadyForConfigure() {
  if (!delegate_waiting_for_ready_for_configure_) {
    return;
  }

  // Check (and early-return) if any type is not ready.
  for (DataType type : preferred_types_without_errors_) {
    if (!ModelIsLoadedOrFailed(*controllers_->find(type)->second)) {
      return;
    }
  }

  // It may be possible that `load_models_elapsed_timer_` was never set, e.g.
  // if StopDatatype() was called before Configure().
  if (load_models_elapsed_timer_) {
    base::UmaHistogramMediumTimes("Sync.ModelLoadManager.LoadModelsElapsedTime",
                                  load_models_elapsed_timer_->Elapsed());
    // Needs to be measured only when NotifyDelegateIfReadyForConfigure() is
    // called for the first time after all types have been loaded.
    load_models_elapsed_timer_.reset();
  }

  // Cancel the timer since all the desired types are now loaded.
  load_models_timeout_timer_.Stop();

  delegate_waiting_for_ready_for_configure_ = false;
  delegate_->OnAllDataTypesReadyForConfigure();
}

void ModelLoadManager::OnLoadModelsTimeout() {
  const DataTypeSet types = preferred_types_without_errors_;
  for (DataType type : types) {
    if (!ModelIsLoadedOrFailed(*controllers_->find(type)->second)) {
      base::UmaHistogramEnumeration("Sync.ModelLoadManager.LoadModelsTimeout",
                                    DataTypeHistogramValue(type));
      // All the types which have not loaded yet are removed from
      // `preferred_types_without_errors_`. This will cause ModelLoadCallback()
      // to stop these types when they finish loading. The intention here is to
      // not wait for these types and continue with connecting the loaded data
      // types, while also ensuring the DataTypeManager does not think the
      // datatype is stopped before the controller actually comes to a stopped
      // state.
      preferred_types_without_errors_.Remove(type);
    }
  }
  // Stop waiting for the data types to load and go ahead with connecting the
  // loaded types.
  NotifyDelegateIfReadyForConfigure();
}

void ModelLoadManager::LoadModelsForType(DataTypeController* dtc) {
  // FAILED is possible if the type was STOPPING but then encountered an error
  // before the type actually stopped.
  if (dtc->state() == DataTypeController::FAILED) {
    ModelLoadCallback(dtc->type(),
                      ModelError(FROM_HERE, "Data type in FAILED state."));
    return;
  }

  // TODO(crbug.com/41492467): Avoid calling LoadModelsForType() multiple times
  // upon stop, and re-introduce a CHECK for state to be NOT_RUNNING only.
  if (dtc->state() == DataTypeController::NOT_RUNNING) {
    dtc->LoadModels(
        *configure_context_,
        base::BindRepeating(&ModelLoadManager::ModelLoadCallback,
                            weak_ptr_factory_.GetWeakPtr(), dtc->type()));
  }
}

}  // namespace syncer