summaryrefslogtreecommitdiffstats
path: root/components/sessions/base_session_service.cc
blob: eb73b08d12b48d1e8ea9722e573523552cbc96f5 (plain)
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
// Copyright 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "components/sessions/base_session_service.h"

#include "base/bind.h"
#include "base/location.h"
#include "base/single_thread_task_runner.h"
#include "base/thread_task_runner_handle.h"
#include "base/threading/thread.h"
#include "components/sessions/base_session_service_delegate.h"
#include "components/sessions/session_backend.h"

// BaseSessionService ---------------------------------------------------------

namespace sessions {
namespace {

// Helper used by ScheduleGetLastSessionCommands. It runs callback on TaskRunner
// thread if it's not canceled.
void RunIfNotCanceled(
    const base::CancelableTaskTracker::IsCanceledCallback& is_canceled,
    const BaseSessionService::GetCommandsCallback& callback,
    ScopedVector<SessionCommand> commands) {
  if (is_canceled.Run())
    return;
  callback.Run(commands.Pass());
}

void PostOrRunInternalGetCommandsCallback(
    base::TaskRunner* task_runner,
    const BaseSessionService::GetCommandsCallback& callback,
    ScopedVector<SessionCommand> commands) {
  if (task_runner->RunsTasksOnCurrentThread()) {
    callback.Run(commands.Pass());
  } else {
    task_runner->PostTask(FROM_HERE,
                          base::Bind(callback, base::Passed(&commands)));
  }
}

}  // namespace

// Delay between when a command is received, and when we save it to the
// backend.
static const int kSaveDelayMS = 2500;

BaseSessionService::BaseSessionService(
    SessionType type,
    const base::FilePath& path,
    BaseSessionServiceDelegate* delegate)
    : pending_reset_(false),
      commands_since_reset_(0),
      delegate_(delegate),
      sequence_token_(delegate_->GetBlockingPool()->GetSequenceToken()),
      weak_factory_(this) {
  backend_ = new SessionBackend(type, path);
  DCHECK(backend_.get());
}

BaseSessionService::~BaseSessionService() {}

void BaseSessionService::MoveCurrentSessionToLastSession() {
  Save();
  RunTaskOnBackendThread(
      FROM_HERE, base::Bind(&SessionBackend::MoveCurrentSessionToLastSession,
                            backend_));
}

void BaseSessionService::DeleteLastSession() {
  RunTaskOnBackendThread(
      FROM_HERE,
      base::Bind(&SessionBackend::DeleteLastSession, backend_));
}

void BaseSessionService::ScheduleCommand(scoped_ptr<SessionCommand> command) {
  DCHECK(command);
  commands_since_reset_++;
  pending_commands_.push_back(command.release());
  StartSaveTimer();
}

void BaseSessionService::AppendRebuildCommand(
    scoped_ptr<SessionCommand> command) {
  DCHECK(command);
  pending_commands_.push_back(command.release());
}

void BaseSessionService::EraseCommand(SessionCommand* old_command) {
  ScopedVector<SessionCommand>::iterator it =
      std::find(pending_commands_.begin(),
                pending_commands_.end(),
                old_command);
  CHECK(it != pending_commands_.end());
  pending_commands_.erase(it);
}

void BaseSessionService::SwapCommand(SessionCommand* old_command,
                                     scoped_ptr<SessionCommand> new_command) {
  ScopedVector<SessionCommand>::iterator it =
      std::find(pending_commands_.begin(),
                pending_commands_.end(),
                old_command);
  CHECK(it != pending_commands_.end());
  *it = new_command.release();
  delete old_command;
}

void BaseSessionService::ClearPendingCommands() {
  pending_commands_.clear();
}

void BaseSessionService::StartSaveTimer() {
  // Don't start a timer when testing.
  if (delegate_->ShouldUseDelayedSave() && base::MessageLoop::current() &&
      !weak_factory_.HasWeakPtrs()) {
    base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
        FROM_HERE,
        base::Bind(&BaseSessionService::Save, weak_factory_.GetWeakPtr()),
        base::TimeDelta::FromMilliseconds(kSaveDelayMS));
  }
}

void BaseSessionService::Save() {
  // Inform the delegate that we will save the commands now, giving it the
  // opportunity to append more commands.
  delegate_->OnWillSaveCommands();

  if (pending_commands_.empty())
    return;

  // We create a new ScopedVector which will receive all elements from the
  // current commands. This will also clear the current list.
  RunTaskOnBackendThread(
      FROM_HERE,
      base::Bind(&SessionBackend::AppendCommands, backend_,
                 base::Passed(&pending_commands_),
                 pending_reset_));

  if (pending_reset_) {
    commands_since_reset_ = 0;
    pending_reset_ = false;
  }

  delegate_->OnSavedCommands();
}

base::CancelableTaskTracker::TaskId
BaseSessionService::ScheduleGetLastSessionCommands(
    const GetCommandsCallback& callback,
    base::CancelableTaskTracker* tracker) {
  base::CancelableTaskTracker::IsCanceledCallback is_canceled;
  base::CancelableTaskTracker::TaskId id =
      tracker->NewTrackedTaskId(&is_canceled);

  GetCommandsCallback run_if_not_canceled =
      base::Bind(&RunIfNotCanceled, is_canceled, callback);

  GetCommandsCallback callback_runner =
      base::Bind(&PostOrRunInternalGetCommandsCallback,
                 base::ThreadTaskRunnerHandle::Get(), run_if_not_canceled);

  RunTaskOnBackendThread(
      FROM_HERE,
      base::Bind(&SessionBackend::ReadLastSessionCommands, backend_,
                 is_canceled, callback_runner));
  return id;
}

void BaseSessionService::RunTaskOnBackendThread(
    const tracked_objects::Location& from_here,
    const base::Closure& task) {
  base::SequencedWorkerPool* pool = delegate_->GetBlockingPool();
  if (!pool->IsShutdownInProgress()) {
    pool->PostSequencedWorkerTask(sequence_token_, from_here, task);
  } else {
    // Fall back to executing on the main thread if the sequence
    // worker pool has been requested to shutdown (around shutdown
    // time).
    task.Run();
  }
}

}  // namespace sessions