1// Copyright (c) 2010 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "chrome/browser/sync/notifier/chrome_system_resources.h"
6
7#include <cstdlib>
8#include <cstring>
9#include <string>
10
11#include "base/logging.h"
12#include "base/message_loop.h"
13#include "base/stl_util-inl.h"
14#include "base/string_util.h"
15#include "base/stringprintf.h"
16#include "chrome/browser/sync/notifier/invalidation_util.h"
17
18namespace sync_notifier {
19
20ChromeSystemResources::ChromeSystemResources(StateWriter* state_writer)
21    : state_writer_(state_writer),
22      created_on_loop_(MessageLoop::current()) {
23  DCHECK(non_thread_safe_.CalledOnValidThread());
24  CHECK(created_on_loop_);
25  DCHECK(state_writer_);
26}
27
28ChromeSystemResources::~ChromeSystemResources() {
29  DCHECK(non_thread_safe_.CalledOnValidThread());
30  CHECK_EQ(created_on_loop_, MessageLoop::current());
31  StopScheduler();
32}
33
34invalidation::Time ChromeSystemResources::current_time() {
35  DCHECK(non_thread_safe_.CalledOnValidThread());
36  CHECK_EQ(created_on_loop_, MessageLoop::current());
37  return base::Time::Now();
38}
39
40void ChromeSystemResources::StartScheduler() {
41  DCHECK(non_thread_safe_.CalledOnValidThread());
42  CHECK_EQ(created_on_loop_, MessageLoop::current());
43  scoped_runnable_method_factory_.reset(
44      new ScopedRunnableMethodFactory<ChromeSystemResources>(this));
45}
46
47void ChromeSystemResources::StopScheduler() {
48  DCHECK(non_thread_safe_.CalledOnValidThread());
49  CHECK_EQ(created_on_loop_, MessageLoop::current());
50  scoped_runnable_method_factory_.reset();
51  STLDeleteElements(&posted_tasks_);
52}
53
54void ChromeSystemResources::ScheduleWithDelay(
55    invalidation::TimeDelta delay,
56    invalidation::Closure* task) {
57  DCHECK(non_thread_safe_.CalledOnValidThread());
58  CHECK_EQ(created_on_loop_, MessageLoop::current());
59  Task* task_to_post = MakeTaskToPost(task);
60  if (!task_to_post) {
61    return;
62  }
63  MessageLoop::current()->PostDelayedTask(
64      FROM_HERE, task_to_post, delay.InMillisecondsRoundedUp());
65}
66
67void ChromeSystemResources::ScheduleImmediately(
68    invalidation::Closure* task) {
69  DCHECK(non_thread_safe_.CalledOnValidThread());
70  CHECK_EQ(created_on_loop_, MessageLoop::current());
71  Task* task_to_post = MakeTaskToPost(task);
72  if (!task_to_post) {
73    return;
74  }
75  MessageLoop::current()->PostTask(FROM_HERE, task_to_post);
76}
77
78// The listener thread is just our current thread (i.e., the
79// notifications thread).
80void ChromeSystemResources::ScheduleOnListenerThread(
81    invalidation::Closure* task) {
82  DCHECK(non_thread_safe_.CalledOnValidThread());
83  CHECK_EQ(created_on_loop_, MessageLoop::current());
84  ScheduleImmediately(task);
85}
86
87// 'Internal thread' means 'not the listener thread'.  Since the
88// listener thread is the notifications thread, always return false.
89bool ChromeSystemResources::IsRunningOnInternalThread() {
90  DCHECK(non_thread_safe_.CalledOnValidThread());
91  CHECK_EQ(created_on_loop_, MessageLoop::current());
92  return false;
93}
94
95void ChromeSystemResources::Log(
96    LogLevel level, const char* file, int line,
97    const char* format, ...) {
98  DCHECK(non_thread_safe_.CalledOnValidThread());
99  logging::LogSeverity log_severity = logging::LOG_INFO;
100  switch (level) {
101    case INFO_LEVEL:
102      log_severity = logging::LOG_INFO;
103      break;
104    case WARNING_LEVEL:
105      log_severity = logging::LOG_WARNING;
106      break;
107    case SEVERE_LEVEL:
108      log_severity = logging::LOG_ERROR;
109      break;
110  }
111  // We treat LOG(INFO) as VLOG(1).
112  if ((log_severity >= logging::GetMinLogLevel()) &&
113      ((log_severity != logging::LOG_INFO) ||
114       (1 <= logging::GetVlogLevelHelper(file, ::strlen(file))))) {
115    va_list ap;
116    va_start(ap, format);
117    std::string result;
118    base::StringAppendV(&result, format, ap);
119    logging::LogMessage(file, line, log_severity).stream() << result;
120    va_end(ap);
121  }
122}
123
124void ChromeSystemResources::RunAndDeleteStorageCallback(
125    invalidation::StorageCallback* callback) {
126  callback->Run(true);
127  delete callback;
128}
129
130void ChromeSystemResources::WriteState(
131    const invalidation::string& state,
132    invalidation::StorageCallback* callback) {
133  CHECK(state_writer_);
134  state_writer_->WriteState(state);
135  // According to the cache invalidation API folks, we can do this as
136  // long as we make sure to clear the persistent state that we start
137  // up the cache invalidation client with.  However, we musn't do it
138  // right away, as we may be called under a lock that the callback
139  // uses.
140  ScheduleImmediately(
141      invalidation::NewPermanentCallback(
142          this, &ChromeSystemResources::RunAndDeleteStorageCallback,
143          callback));
144}
145
146Task* ChromeSystemResources::MakeTaskToPost(
147    invalidation::Closure* task) {
148  DCHECK(non_thread_safe_.CalledOnValidThread());
149  DCHECK(invalidation::IsCallbackRepeatable(task));
150  CHECK_EQ(created_on_loop_, MessageLoop::current());
151  if (!scoped_runnable_method_factory_.get()) {
152    delete task;
153    return NULL;
154  }
155  posted_tasks_.insert(task);
156  Task* task_to_post =
157      scoped_runnable_method_factory_->NewRunnableMethod(
158          &ChromeSystemResources::RunPostedTask, task);
159  return task_to_post;
160}
161
162void ChromeSystemResources::RunPostedTask(invalidation::Closure* task) {
163  DCHECK(non_thread_safe_.CalledOnValidThread());
164  CHECK_EQ(created_on_loop_, MessageLoop::current());
165  RunAndDeleteClosure(task);
166  posted_tasks_.erase(task);
167}
168
169}  // namespace sync_notifier
170