Commit 627c4366 authored by benwells@chromium.org's avatar benwells@chromium.org

Revert 247772 "Cache feedback reports to disk in case of send fa..."

> Cache feedback reports to disk in case of send failure.
> 
> R=zork@chromium.org
> BUG=249853
> 
> Committed: https://src.chromium.org/viewvc/chrome?view=rev&revision=246992
> 
> Review URL: https://codereview.chromium.org/141433011

This introduced a race condition picked up by the TSAN bots.

BUG=339326
TBR=rkc@chromium.org

Review URL: https://codereview.chromium.org/149993002

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@247816 0039d316-1c4b-4281-b951-d872f2087c98
parent c23ad8c1
......@@ -55,7 +55,6 @@
#include "chrome/browser/extensions/extension_protocols.h"
#include "chrome/browser/extensions/extension_service.h"
#include "chrome/browser/extensions/startup_helper.h"
#include "chrome/browser/feedback/feedback_profile_observer.h"
#include "chrome/browser/first_run/first_run.h"
#include "chrome/browser/first_run/upgrade_util.h"
#include "chrome/browser/google/google_search_counter.h"
......@@ -1039,10 +1038,6 @@ void ChromeBrowserMainParts::PreMainMessageLoopRun() {
void ChromeBrowserMainParts::PreProfileInit() {
TRACE_EVENT0("startup", "ChromeBrowserMainParts::PreProfileInit");
// Initialize the feedback uploader so it can setup notifications for profile
// creation.
feedback::FeedbackProfileObserver::Initialize();
for (size_t i = 0; i < chrome_extra_parts_.size(); ++i)
chrome_extra_parts_[i]->PreProfileInit();
......
// Copyright 2014 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 "chrome/browser/feedback/feedback_profile_observer.h"
#include "base/callback.h"
#include "chrome/browser/chrome_notification_types.h"
#include "chrome/browser/feedback/feedback_report.h"
#include "chrome/browser/feedback/feedback_uploader.h"
#include "chrome/browser/feedback/feedback_uploader_factory.h"
#include "chrome/browser/profiles/profile.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/notification_service.h"
using content::BrowserThread;
static base::LazyInstance<feedback::FeedbackProfileObserver>::Leaky
g_feedback_profile_observer = LAZY_INSTANCE_INITIALIZER;
namespace feedback {
// static
void FeedbackProfileObserver::Initialize() {
g_feedback_profile_observer.Get();
}
FeedbackProfileObserver::FeedbackProfileObserver() {
prefs_registrar_.Add(this, chrome::NOTIFICATION_PROFILE_CREATED,
content::NotificationService::AllSources());
}
FeedbackProfileObserver::~FeedbackProfileObserver() {
prefs_registrar_.RemoveAll();
}
void FeedbackProfileObserver::Observe(
int type,
const content::NotificationSource& source,
const content::NotificationDetails& details) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
DCHECK_EQ(chrome::NOTIFICATION_PROFILE_CREATED, type);
Profile* profile = content::Source<Profile>(source).ptr();
if (!profile->IsOffTheRecord())
QueueUnsentReports(profile);
}
void FeedbackProfileObserver::QueueUnsentReports(
content::BrowserContext* context) {
feedback::FeedbackUploader* uploader =
feedback::FeedbackUploaderFactory::GetForBrowserContext(context);
BrowserThread::PostBlockingPoolTask(FROM_HERE,
base::Bind(
&FeedbackReport::LoadReportsAndQueue, context, base::Bind(
&FeedbackUploader::QueueReport, uploader->AsWeakPtr())));
}
} // namespace feedback
// Copyright 2014 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.
#ifndef CHROME_BROWSER_FEEDBACK_FEEDBACK_PROFILE_OBSERVER_H_
#define CHROME_BROWSER_FEEDBACK_FEEDBACK_PROFILE_OBSERVER_H_
#include "base/basictypes.h"
#include "base/lazy_instance.h"
#include "content/public/browser/notification_observer.h"
#include "content/public/browser/notification_registrar.h"
namespace content {
class BrowserContext;
}
namespace feedback {
// FeedbackProfileObserver waits on profile creation notifications to check
// if the profile has any pending feedback reports to upload. If it does, it
// queues those reports for upload.
class FeedbackProfileObserver : public content::NotificationObserver {
public:
static void Initialize();
private:
friend struct base::DefaultLazyInstanceTraits<FeedbackProfileObserver>;
FeedbackProfileObserver();
virtual ~FeedbackProfileObserver();
// content::NotificationObserver override
virtual void Observe(int type,
const content::NotificationSource& source,
const content::NotificationDetails& details) OVERRIDE;
// Loads any unsent reports from disk and queues them to be uploaded in
// the given browser context.
void QueueUnsentReports(content::BrowserContext* context);
// Used to track creation of profiles so we can load any unsent reports
// for that profile.
content::NotificationRegistrar prefs_registrar_;
DISALLOW_COPY_AND_ASSIGN(FeedbackProfileObserver);
};
} // namespace feedback
#endif // CHROME_BROWSER_FEEDBACK_FEEDBACK_PROFILE_OBSERVER_H_
// Copyright 2014 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 "chrome/browser/feedback/feedback_report.h"
#include "base/file_util.h"
#include "base/files/file_enumerator.h"
#include "base/files/important_file_writer.h"
#include "base/guid.h"
#include "base/strings/string_number_conversions.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/browser_thread.h"
#include "net/base/directory_lister.h"
using content::BrowserThread;
namespace {
const base::FilePath::CharType kFeedbackReportPath[] =
FILE_PATH_LITERAL("Feedback Reports");
const base::FilePath::CharType kFeedbackReportFilenameWildcard[] =
FILE_PATH_LITERAL("Feedback Report.*");
const char kFeedbackReportFilenamePrefix[] = "Feedback Report.";
base::FilePath GetFeedbackReportsPath(content::BrowserContext* context) {
return context->GetPath().Append(kFeedbackReportPath);
}
} // namespace
namespace feedback {
FeedbackReport::FeedbackReport(content::BrowserContext* context,
const base::Time& upload_at,
const std::string& data)
: context_(context),
upload_at_(upload_at),
data_(data) {
base::FilePath reports_path = GetFeedbackReportsPath(context);
if (reports_path.empty())
return;
file_ = reports_path.AppendASCII(
kFeedbackReportFilenamePrefix + base::GenerateGUID());
BrowserThread::PostBlockingPoolTask(FROM_HERE, base::Bind(
&FeedbackReport::WriteReportOnBlockingPool, this));
}
FeedbackReport::~FeedbackReport() {}
void FeedbackReport::DeleteReportOnDisk() {
BrowserThread::PostBlockingPoolTask(FROM_HERE, base::Bind(
base::IgnoreResult(&base::DeleteFile), file_, false));
file_.clear();
}
void FeedbackReport::WriteReportOnBlockingPool() {
base::FilePath reports_path = GetFeedbackReportsPath(context_);
if (!base::DirectoryExists(reports_path)) {
base::File::Error error;
if (!base::CreateDirectoryAndGetError(reports_path, &error))
return;
}
if (!file_.empty())
base::ImportantFileWriter::WriteFileAtomically(file_, data_);
}
// static
void FeedbackReport::LoadReportsAndQueue(
content::BrowserContext* context, QueueCallback callback) {
base::FilePath reports_path = GetFeedbackReportsPath(context);
if (reports_path.empty())
return;
base::FileEnumerator enumerator(reports_path,
false,
base::FileEnumerator::FILES,
kFeedbackReportFilenameWildcard);
for (base::FilePath name = enumerator.Next();
!name.empty();
name = enumerator.Next()) {
std::string data;
if (ReadFileToString(name, &data))
BrowserThread::PostTask(
BrowserThread::UI, FROM_HERE, base::Bind(callback, data));
base::DeleteFile(name, false);
}
}
} // namespace feedback
// Copyright 2014 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.
#ifndef CHROME_BROWSER_FEEDBACK_FEEDBACK_REPORT_H_
#define CHROME_BROWSER_FEEDBACK_FEEDBACK_REPORT_H_
#include <string>
#include "base/basictypes.h"
#include "base/callback_forward.h"
#include "base/files/file_path.h"
#include "base/memory/ref_counted.h"
#include "base/time/time.h"
namespace content {
class BrowserContext;
}
namespace feedback {
typedef base::Callback<void(const std::string&)> QueueCallback;
// This class holds a feedback report. Once a report is created, a disk backup
// for it is created automatically. This backup is deleted once this object
// dies.
class FeedbackReport : public base::RefCountedThreadSafe<FeedbackReport> {
public:
FeedbackReport(content::BrowserContext* context,
const base::Time& upload_at,
const std::string& data);
// Stops the disk write of the report and deletes the report file if already
// written.
void DeleteReportOnDisk();
base::Time upload_at() { return upload_at_; }
const std::string& data() { return data_; }
// Loads the reports still on disk and queues then using the given callback.
// This call blocks on the file reads.
static void LoadReportsAndQueue(content::BrowserContext* context,
QueueCallback callback);
private:
friend class base::RefCountedThreadSafe<FeedbackReport>;
virtual ~FeedbackReport();
void WriteReportOnBlockingPool();
// Name of the file corresponding to this report.
base::FilePath file_;
content::BrowserContext* context_;
base::Time upload_at_; // Upload this report at or after this time.
std::string data_;
DISALLOW_COPY_AND_ASSIGN(FeedbackReport);
};
} // namespace feedback
#endif // CHROME_BROWSER_FEEDBACK_FEEDBACK_REPORT_H_
......@@ -9,7 +9,6 @@
#include "base/files/file_path.h"
#include "base/task_runner_util.h"
#include "base/threading/sequenced_worker_pool.h"
#include "chrome/browser/feedback/feedback_report.h"
#include "chrome/common/chrome_switches.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/browser_thread.h"
......@@ -30,9 +29,28 @@ const int64 kRetryDelayMinutes = 60;
} // namespace
struct FeedbackReport {
FeedbackReport(const base::Time& upload_at, scoped_ptr<std::string> data)
: upload_at(upload_at), data(data.Pass()) {}
FeedbackReport(const FeedbackReport& report) {
upload_at = report.upload_at;
data = report.data.Pass();
}
FeedbackReport& operator=(const FeedbackReport& report) {
upload_at = report.upload_at;
data = report.data.Pass();
return *this;
}
base::Time upload_at; // Upload this report at or after this time.
mutable scoped_ptr<std::string> data;
};
bool FeedbackUploader::ReportsUploadTimeComparator::operator()(
FeedbackReport* a, FeedbackReport* b) const {
return a->upload_at() > b->upload_at();
const FeedbackReport& a, const FeedbackReport& b) const {
return a.upload_at > b.upload_at;
}
FeedbackUploader::FeedbackUploader(content::BrowserContext* context)
......@@ -43,15 +61,15 @@ FeedbackUploader::FeedbackUploader(content::BrowserContext* context)
AsWeakPtr());
}
FeedbackUploader::~FeedbackUploader() {}
FeedbackUploader::~FeedbackUploader() {
}
void FeedbackUploader::QueueReport(const std::string& data) {
reports_queue_.push(
new FeedbackReport(context_, base::Time::Now(), data));
void FeedbackUploader::QueueReport(scoped_ptr<std::string> data) {
reports_queue_.push(FeedbackReport(base::Time::Now(), data.Pass()));
UpdateUploadTimer();
}
void FeedbackUploader::DispatchReport(const std::string& data) {
void FeedbackUploader::DispatchReport(scoped_ptr<std::string> data) {
GURL post_url;
if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kFeedbackServer))
post_url = GURL(CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
......@@ -59,14 +77,17 @@ void FeedbackUploader::DispatchReport(const std::string& data) {
else
post_url = GURL(kFeedbackPostUrl);
// Save the report data pointer since the report.Pass() in the next statement
// will invalidate the scoper.
std::string* data_ptr = data.get();
net::URLFetcher* fetcher = net::URLFetcher::Create(
post_url, net::URLFetcher::POST,
new FeedbackUploaderDelegate(
data,
data.Pass(),
base::Bind(&FeedbackUploader::UpdateUploadTimer, AsWeakPtr()),
base::Bind(&FeedbackUploader::RetryReport, AsWeakPtr())));
fetcher->SetUploadData(std::string(kProtBufMimeType), data);
fetcher->SetUploadData(std::string(kProtBufMimeType), *data_ptr);
fetcher->SetRequestContext(context_->GetRequestContext());
fetcher->SetLoadFlags(net::LOAD_DO_NOT_SAVE_COOKIES |
net::LOAD_DO_NOT_SEND_COOKIES);
......@@ -77,26 +98,25 @@ void FeedbackUploader::UpdateUploadTimer() {
if (reports_queue_.empty())
return;
scoped_refptr<FeedbackReport> report = reports_queue_.top();
const FeedbackReport& report = reports_queue_.top();
base::Time now = base::Time::Now();
if (report->upload_at() <= now) {
if (report.upload_at <= now) {
scoped_ptr<std::string> data = report.data.Pass();
reports_queue_.pop();
dispatch_callback_.Run(report->data());
report->DeleteReportOnDisk();
dispatch_callback_.Run(data.Pass());
} else {
// Stop the old timer and start an updated one.
if (upload_timer_.IsRunning())
upload_timer_.Stop();
upload_timer_.Start(
FROM_HERE, report->upload_at() - now, this,
FROM_HERE, report.upload_at - now, this,
&FeedbackUploader::UpdateUploadTimer);
}
}
void FeedbackUploader::RetryReport(const std::string& data) {
reports_queue_.push(new FeedbackReport(context_,
base::Time::Now() + retry_delay_,
data));
void FeedbackUploader::RetryReport(scoped_ptr<std::string> data) {
reports_queue_.push(
FeedbackReport(base::Time::Now() + retry_delay_, data.Pass()));
UpdateUploadTimer();
}
......
......@@ -22,7 +22,7 @@ class BrowserContext;
namespace feedback {
class FeedbackReport;
struct FeedbackReport;
// FeedbackUploader is used to add a feedback report to the queue of reports
// being uploaded. In case uploading a report fails, it is written to disk and
......@@ -33,27 +33,22 @@ class FeedbackUploader : public BrowserContextKeyedService,
explicit FeedbackUploader(content::BrowserContext* context);
virtual ~FeedbackUploader();
// Queues a report for uploading.
void QueueReport(const std::string& data);
void QueueReport(scoped_ptr<std::string> data);
private:
friend class FeedbackUploaderTest;
struct ReportsUploadTimeComparator {
bool operator()(FeedbackReport* a, FeedbackReport* b) const;
bool operator()(const FeedbackReport& a, const FeedbackReport& b) const;
};
// Dispatches the report to be uploaded.
void DispatchReport(const std::string& data);
// Loads any unsent reports from disk and queues them to be uploaded in
// the given browser context.
void QueueUnsentReports(content::BrowserContext* context);
void DispatchReport(scoped_ptr<std::string> data);
// Update our timer for uploading the next report.
void UpdateUploadTimer();
// Requeue this report with a delay.
void RetryReport(const std::string& data);
void RetryReport(scoped_ptr<std::string> data);
void setup_for_test(const ReportDataCallback& dispatch_callback,
const base::TimeDelta& retry_delay);
......@@ -64,10 +59,12 @@ class FeedbackUploader : public BrowserContextKeyedService,
base::OneShotTimer<FeedbackUploader> upload_timer_;
// Priority queue of reports prioritized by the time the report is supposed
// to be uploaded at.
std::priority_queue<scoped_refptr<FeedbackReport>,
std::vector<scoped_refptr<FeedbackReport> >,
std::priority_queue<FeedbackReport,
std::vector<FeedbackReport>,
ReportsUploadTimeComparator> reports_queue_;
std::vector<FeedbackReport> loaded_reports_;
ReportDataCallback dispatch_callback_;
base::TimeDelta retry_delay_;
......
......@@ -21,10 +21,10 @@ const int kHttpPostFailServerError = 500;
} // namespace
FeedbackUploaderDelegate::FeedbackUploaderDelegate(
const std::string& post_body,
scoped_ptr<std::string> post_body,
const base::Closure& success_callback,
const ReportDataCallback& error_callback)
: post_body_(post_body),
: post_body_(post_body.Pass()),
success_callback_(success_callback),
error_callback_(error_callback) {
}
......@@ -52,7 +52,7 @@ void FeedbackUploaderDelegate::OnURLFetchComplete(
} else {
error_stream << "Unknown error: HTTP response code " << response_code;
}
error_callback_.Run(post_body_);
error_callback_.Run(post_body_.Pass());
}
LOG(WARNING) << "FEEDBACK: Submission to feedback server ("
......
......@@ -9,18 +9,19 @@
#include "base/basictypes.h"
#include "base/callback.h"
#include "base/memory/scoped_ptr.h"
#include "net/url_request/url_fetcher_delegate.h"
namespace feedback {
typedef base::Callback<void(const std::string&)> ReportDataCallback;
typedef base::Callback<void(scoped_ptr<std::string>)> ReportDataCallback;
// FeedbackUploaderDelegate is a simple http uploader for a feedback report. On
// succes or failure, it deletes itself, but on failure it also notifies the
// error callback specified when constructing the class instance.
class FeedbackUploaderDelegate : public net::URLFetcherDelegate {
public:
FeedbackUploaderDelegate(const std::string& post_body,
FeedbackUploaderDelegate(scoped_ptr<std::string> post_body,
const base::Closure& success_callback,
const ReportDataCallback& error_callback);
virtual ~FeedbackUploaderDelegate();
......@@ -29,7 +30,7 @@ class FeedbackUploaderDelegate : public net::URLFetcherDelegate {
// Overridden from net::URLFetcherDelegate.
virtual void OnURLFetchComplete(const net::URLFetcher* source) OVERRIDE;
std::string post_body_;
scoped_ptr<std::string> post_body_;
base::Closure success_callback_;
ReportDataCallback error_callback_;
......
......@@ -54,15 +54,15 @@ class FeedbackUploaderTest : public testing::Test {
}
void QueueReport(const std::string& data) {
uploader_->QueueReport(data);
uploader_->QueueReport(make_scoped_ptr(new std::string(data)));
}
void ReportFailure(const std::string& data) {
uploader_->RetryReport(data);
uploader_->RetryReport(make_scoped_ptr(new std::string(data)));
}
void MockDispatchReport(const std::string& report_data) {
dispatched_reports_.push_back(report_data);
void MockDispatchReport(scoped_ptr<std::string> report_data) {
dispatched_reports_.push_back(*report_data.get());
// Dispatch will always update the timer, whether successful or not,
// simulate the same behavior.
......@@ -90,13 +90,7 @@ class FeedbackUploaderTest : public testing::Test {
size_t expected_reports_;
};
#if defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_MACOSX)
#define MAYBE_QueueMultiple QueueMultiple
#else
// crbug.com/330547
#define MAYBE_QueueMultiple DISABLED_QueueMultiple
#endif
TEST_F(FeedbackUploaderTest, MAYBE_QueueMultiple) {
TEST_F(FeedbackUploaderTest, QueueMultiple) {
dispatched_reports_.clear();
QueueReport(kReportOne);
QueueReport(kReportTwo);
......@@ -110,11 +104,11 @@ TEST_F(FeedbackUploaderTest, MAYBE_QueueMultiple) {
EXPECT_EQ(dispatched_reports_[3], kReportFour);
}
#if defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_MACOSX)
#define MAYBE_QueueMultipleWithFailures QueueMultipleWithFailures
#else
#if defined(OS_WIN)
// crbug.com/330547
#define MAYBE_QueueMultipleWithFailures DISABLED_QueueMultipleWithFailures
#else
#define MAYBE_QueueMultipleWithFailures QueueMultipleWithFailures
#endif
TEST_F(FeedbackUploaderTest, MAYBE_QueueMultipleWithFailures) {
dispatched_reports_.clear();
......
......@@ -239,12 +239,12 @@ void SendReport(scoped_refptr<FeedbackData> data) {
// This pointer will eventually get deleted by the PostCleanup class, after
// we've either managed to successfully upload the report or died trying.
std::string post_body;
feedback_data.SerializeToString(&post_body);
scoped_ptr<std::string> post_body(new std::string);
feedback_data.SerializeToString(post_body.get());
feedback::FeedbackUploader *uploader =
feedback::FeedbackUploaderFactory::GetForBrowserContext(data->profile());
uploader->QueueReport(post_body);
uploader->QueueReport(post_body.Pass());
}
bool ZipString(const base::FilePath& filename,
......
......@@ -359,10 +359,6 @@
'browser/browsing_data/cookies_tree_model.h',
'browser/feedback/feedback_data.cc',
'browser/feedback/feedback_data.h',
'browser/feedback/feedback_profile_observer.cc',
'browser/feedback/feedback_profile_observer.h',
'browser/feedback/feedback_report.cc',
'browser/feedback/feedback_report.h',
'browser/feedback/feedback_util.cc',
'browser/feedback/feedback_util.h',
'browser/feedback/feedback_uploader.cc',
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment