diff options
author | ahendrickson@chromium.org <ahendrickson@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-10-21 00:09:52 +0000 |
---|---|---|
committer | ahendrickson@chromium.org <ahendrickson@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-10-21 00:09:52 +0000 |
commit | 2606e5a98b9a92fad68ed66bb02ec71409d42ef6 (patch) | |
tree | 087ec7b2bb1c1e338e41396a01723c89682253c6 /content/browser/download | |
parent | 4a14bca90a556d9f920e8f2bef5116430176d56c (diff) | |
download | chromium_src-2606e5a98b9a92fad68ed66bb02ec71409d42ef6.zip chromium_src-2606e5a98b9a92fad68ed66bb02ec71409d42ef6.tar.gz chromium_src-2606e5a98b9a92fad68ed66bb02ec71409d42ef6.tar.bz2 |
DownloadBuffer is used in two different ways, only one of which is used across threads.
This CL splits a ContentVector typedef from the DownloadBuffer class.
DownloadBuffer is also made RefCountedThreadSafe.
BUG=None
TEST=None
Review URL: http://codereview.chromium.org/8036038
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@106640 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'content/browser/download')
-rw-r--r-- | content/browser/download/download_buffer.cc | 71 | ||||
-rw-r--r-- | content/browser/download/download_buffer.h | 70 | ||||
-rw-r--r-- | content/browser/download/download_buffer_unittest.cc | 123 | ||||
-rw-r--r-- | content/browser/download/download_file_manager.cc | 17 | ||||
-rw-r--r-- | content/browser/download/download_file_manager.h | 9 | ||||
-rw-r--r-- | content/browser/download/download_resource_handler.cc | 25 | ||||
-rw-r--r-- | content/browser/download/download_resource_handler.h | 7 | ||||
-rw-r--r-- | content/browser/download/download_types.cc | 6 | ||||
-rw-r--r-- | content/browser/download/download_types.h | 22 |
9 files changed, 292 insertions, 58 deletions
diff --git a/content/browser/download/download_buffer.cc b/content/browser/download/download_buffer.cc new file mode 100644 index 0000000..eadb60b --- /dev/null +++ b/content/browser/download/download_buffer.cc @@ -0,0 +1,71 @@ +// Copyright (c) 2011 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 "content/browser/download/download_buffer.h" + +#include "net/base/io_buffer.h" + +namespace content { + +net::IOBuffer* AssembleData(const ContentVector& contents, size_t* num_bytes) { + if (*num_bytes) + *num_bytes = 0; + + size_t data_len; + // Determine how large a buffer we need. + size_t assembly_buffer_length = 0; + for (size_t i = 0; i < contents.size(); ++i) { + data_len = contents[i].second; + assembly_buffer_length += data_len; + } + + // 0-length IOBuffers are not allowed. + if (assembly_buffer_length == 0) + return NULL; + + net::IOBuffer* assembly_buffer = new net::IOBuffer(assembly_buffer_length); + + // Copy the data into |assembly_buffer|. + size_t bytes_copied = 0; + for (size_t i = 0; i < contents.size(); ++i) { + net::IOBuffer* data = contents[i].first; + data_len = contents[i].second; + DCHECK(data != NULL); + DCHECK_LE(bytes_copied + data_len, assembly_buffer_length); + memcpy(assembly_buffer->data() + bytes_copied, data->data(), data_len); + bytes_copied += data_len; + } + DCHECK_EQ(assembly_buffer_length, bytes_copied); + + if (num_bytes) + *num_bytes = assembly_buffer_length; + + return assembly_buffer; +} + +DownloadBuffer::DownloadBuffer() { +} + +DownloadBuffer::~DownloadBuffer() { +} + +size_t DownloadBuffer::AddData(net::IOBuffer* io_buffer, size_t byte_count) { + base::AutoLock auto_lock(lock_); + contents_.push_back(std::make_pair(io_buffer, byte_count)); + return contents_.size(); +} + +ContentVector* DownloadBuffer::ReleaseContents() { + base::AutoLock auto_lock(lock_); + ContentVector* other_contents = new ContentVector; + other_contents->swap(contents_); + return other_contents; +} + +size_t DownloadBuffer::size() const { + base::AutoLock auto_lock(lock_); + return contents_.size(); +} + +} // namespace content diff --git a/content/browser/download/download_buffer.h b/content/browser/download/download_buffer.h new file mode 100644 index 0000000..acbd28a --- /dev/null +++ b/content/browser/download/download_buffer.h @@ -0,0 +1,70 @@ +// Copyright (c) 2011 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 CONTENT_BROWSER_DOWNLOAD_DOWNLOAD_BUFFER_H_ +#define CONTENT_BROWSER_DOWNLOAD_DOWNLOAD_BUFFER_H_ +#pragma once + +#include <vector> + +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" +#include "base/synchronization/lock.h" +#include "content/common/content_export.h" + +namespace net { +class IOBuffer; +} + +namespace content { + +typedef std::pair<scoped_refptr<net::IOBuffer>, size_t> ContentElement; +typedef std::vector<ContentElement> ContentVector; + +// Helper function for ContentVector. +// Assembles the data from |contents| and returns it in an |IOBuffer|. +// The number of bytes in the |IOBuffer| is returned in |*num_bytes| +// (if |num_bytes| is not NULL). +// If |contents| is empty, returns NULL as an |IOBuffer| is not allowed +// to have 0 bytes. +net::IOBuffer* AssembleData(const ContentVector& contents, size_t* num_bytes); + +// |DownloadBuffer| is a thread-safe wrapper around |ContentVector|. +// +// It is created and populated on the IO thread, and passed to the +// FILE thread for writing. In order to avoid flooding the FILE thread with +// too many small write messages, each write is appended to the +// |DownloadBuffer| while waiting for the task to run on the FILE +// thread. Access to the write buffers is synchronized via the lock. +class CONTENT_EXPORT DownloadBuffer : + public base::RefCountedThreadSafe<DownloadBuffer> { + public: + DownloadBuffer(); + + // Adds data to the buffers. + // Returns the number of |IOBuffer|s in the ContentVector. + size_t AddData(net::IOBuffer* io_buffer, size_t byte_count); + + // Retrieves the ContentVector of buffers, clearing the contents. + // The caller takes ownership. + ContentVector* ReleaseContents(); + + // Gets the number of |IOBuffers| we have. + size_t size() const; + + private: + friend class base::RefCountedThreadSafe<DownloadBuffer>; + + // Do not allow explicit destruction by anyone else. + ~DownloadBuffer(); + + mutable base::Lock lock_; + ContentVector contents_; + + DISALLOW_COPY_AND_ASSIGN(DownloadBuffer); +}; + +} // namespace content + +#endif // CONTENT_BROWSER_DOWNLOAD_DOWNLOAD_BUFFER_H_ diff --git a/content/browser/download/download_buffer_unittest.cc b/content/browser/download/download_buffer_unittest.cc new file mode 100644 index 0000000..137e612 --- /dev/null +++ b/content/browser/download/download_buffer_unittest.cc @@ -0,0 +1,123 @@ +// Copyright (c) 2011 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 "content/browser/download/download_buffer.h" + +#include <string.h> + +#include "net/base/io_buffer.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace content { + +namespace { + +const char* kTestData[] = { + "Four score and twenty years ago", + "Our four (or five) fathers", + "Danced and sang in the woods" +}; +const size_t kTestDataQty = arraysize(kTestData); + +class DownloadBufferTest : public testing::Test { + public: + DownloadBufferTest() { + } + ~DownloadBufferTest() { + } + + void CreateBuffer() { + EXPECT_EQ(0u, content_buffer_.size()); + + for (size_t i = 0; i < kTestDataQty; ++i) { + net::StringIOBuffer* io_buffer = new net::StringIOBuffer(kTestData[i]); + content_buffer_.push_back(std::make_pair(io_buffer, io_buffer->size())); + EXPECT_EQ(i + 1, content_buffer_.size()); + } + } + + ContentVector* content_buffer() { + return &content_buffer_; + } + + private: + ContentVector content_buffer_; +}; + +TEST_F(DownloadBufferTest, CreateContentVector) { + CreateBuffer(); + + ContentVector* contents = content_buffer(); + + EXPECT_EQ(kTestDataQty, content_buffer()->size()); + EXPECT_EQ(kTestDataQty, contents->size()); + + for (size_t i = 0; i < kTestDataQty; ++i) { + size_t io_buffer_size = strlen(kTestData[i]); + EXPECT_STREQ(kTestData[i], (*contents)[i].first->data()); + EXPECT_EQ(io_buffer_size, (*contents)[i].second); + } +} + +TEST_F(DownloadBufferTest, CreateDownloadBuffer) { + scoped_refptr<DownloadBuffer> content_buffer(new DownloadBuffer); + EXPECT_EQ(0u, content_buffer->size()); + + for (size_t i = 0; i < kTestDataQty; ++i) { + net::StringIOBuffer* io_buffer = new net::StringIOBuffer(kTestData[i]); + EXPECT_EQ(i + 1, content_buffer->AddData(io_buffer, io_buffer->size())); + } + scoped_ptr<ContentVector> contents(content_buffer->ReleaseContents()); + + EXPECT_EQ(0u, content_buffer->size()); + EXPECT_EQ(kTestDataQty, contents->size()); + + for (size_t i = 0; i < kTestDataQty; ++i) { + size_t io_buffer_size = strlen(kTestData[i]); + EXPECT_STREQ(kTestData[i], (*contents)[i].first->data()); + EXPECT_EQ(io_buffer_size, (*contents)[i].second); + } +} + +TEST_F(DownloadBufferTest, AssembleData) { + CreateBuffer(); + + size_t assembled_bytes = 0; + scoped_refptr<net::IOBuffer> assembled_buffer = + AssembleData(*content_buffer(), &assembled_bytes); + + // Did not change the content buffer. + EXPECT_EQ(kTestDataQty, content_buffer()->size()); + + // Verify the data. + size_t total_size = 0; + for (size_t i = 0; i < kTestDataQty; ++i) { + size_t len = strlen(kTestData[i]); + // assembled_buffer->data() may not be NULL-terminated, so we can't use + // EXPECT_STREQ(). + EXPECT_EQ( + 0, memcmp(kTestData[i], assembled_buffer->data() + total_size, len)); + total_size += len; + } + // Verify the data length. + EXPECT_EQ(total_size, assembled_bytes); +} + +TEST_F(DownloadBufferTest, AssembleDataWithEmptyBuffer) { + ContentVector buffer; + EXPECT_EQ(0u, buffer.size()); + + size_t assembled_bytes = 0; + scoped_refptr<net::IOBuffer> assembled_buffer = + AssembleData(buffer, &assembled_bytes); + + // Did not change the content buffer. + EXPECT_EQ(0u, buffer.size()); + EXPECT_EQ(0u, assembled_bytes); + EXPECT_TRUE(NULL == assembled_buffer.get()); +} + +} // namespace + +} // namespace content diff --git a/content/browser/download/download_file_manager.cc b/content/browser/download/download_file_manager.cc index 1444db3..e5a7285 100644 --- a/content/browser/download/download_file_manager.cc +++ b/content/browser/download/download_file_manager.cc @@ -10,6 +10,7 @@ #include "base/task.h" #include "base/utf_string_conversions.h" #include "content/browser/browser_thread.h" +#include "content/browser/download/download_buffer.h" #include "content/browser/download/download_file.h" #include "content/browser/download/download_create_info.h" #include "content/browser/download/download_manager.h" @@ -143,19 +144,15 @@ void DownloadFileManager::StartDownload(DownloadCreateInfo* info) { // thread gets the cancel message: we just delete the data since the // DownloadFile has been deleted. void DownloadFileManager::UpdateDownload( - DownloadId global_id, DownloadBuffer* buffer) { + DownloadId global_id, content::DownloadBuffer* buffer) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); - std::vector<DownloadBuffer::Contents> contents; - { - base::AutoLock auto_lock(buffer->lock); - contents.swap(buffer->contents); - } + scoped_ptr<content::ContentVector> contents(buffer->ReleaseContents()); DownloadFile* download_file = GetDownloadFile(global_id); bool had_error = false; - for (size_t i = 0; i < contents.size(); ++i) { - net::IOBuffer* data = contents[i].first; - const int data_len = contents[i].second; + for (size_t i = 0; i < contents->size(); ++i) { + net::IOBuffer* data = (*contents)[i].first; + const int data_len = (*contents)[i].second; if (!had_error && download_file) { net::Error write_result = download_file->AppendDataToFile(data->data(), data_len); @@ -192,14 +189,12 @@ void DownloadFileManager::UpdateDownload( void DownloadFileManager::OnResponseCompleted( DownloadId global_id, - DownloadBuffer* buffer, net::Error net_error, const std::string& security_info) { VLOG(20) << __FUNCTION__ << "()" << " id = " << global_id << " net_error = " << net_error << " security_info = \"" << security_info << "\""; DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); - delete buffer; DownloadFile* download_file = GetDownloadFile(global_id); if (!download_file) return; diff --git a/content/browser/download/download_file_manager.h b/content/browser/download/download_file_manager.h index 20a2b6a..715d791 100644 --- a/content/browser/download/download_file_manager.h +++ b/content/browser/download/download_file_manager.h @@ -54,7 +54,6 @@ #include "net/base/net_errors.h" #include "ui/gfx/native_widget_types.h" -struct DownloadBuffer; struct DownloadCreateInfo; struct DownloadSaveInfo; class DownloadFile; @@ -63,6 +62,10 @@ class FilePath; class GURL; class ResourceDispatcherHost; +namespace content { +class DownloadBuffer; +} + namespace net { class URLRequestContextGetter; } @@ -81,14 +84,14 @@ class CONTENT_EXPORT DownloadFileManager // Handlers for notifications sent from the IO thread and run on the // FILE thread. - void UpdateDownload(DownloadId global_id, DownloadBuffer* buffer); + void UpdateDownload(DownloadId global_id, content::DownloadBuffer* buffer); + // |net_error| is 0 for normal completions, and non-0 for errors. // |security_info| contains SSL information (cert_id, cert_status, // security_bits, ssl_connection_status), which can be used to // fine-tune the error message. It is empty if the transaction // was not performed securely. void OnResponseCompleted(DownloadId global_id, - DownloadBuffer* buffer, net::Error net_error, const std::string& security_info); diff --git a/content/browser/download/download_resource_handler.cc b/content/browser/download/download_resource_handler.cc index e726bfb..8e98cb7 100644 --- a/content/browser/download/download_resource_handler.cc +++ b/content/browser/download/download_resource_handler.cc @@ -11,6 +11,7 @@ #include "base/metrics/stats_counters.h" #include "base/stringprintf.h" #include "content/browser/browser_thread.h" +#include "content/browser/download/download_buffer.h" #include "content/browser/download/download_create_info.h" #include "content/browser/download/download_file_manager.h" #include "content/browser/download/download_item.h" @@ -46,7 +47,7 @@ DownloadResourceHandler::DownloadResourceHandler( save_as_(save_as), started_cb_(started_cb), save_info_(save_info), - buffer_(new DownloadBuffer), + buffer_(new content::DownloadBuffer), rdh_(rdh), is_paused_(false) { DCHECK(dl_id.IsValid()); @@ -162,25 +163,25 @@ bool DownloadResourceHandler::OnReadCompleted(int request_id, int* bytes_read) { if (!*bytes_read) return true; DCHECK(read_buffer_); - base::AutoLock auto_lock(buffer_->lock); - bool need_update = buffer_->contents.empty(); + // Swap the data. + net::IOBuffer* io_buffer = NULL; + read_buffer_.swap(&io_buffer); + size_t vector_size = buffer_->AddData(io_buffer, *bytes_read); + bool need_update = (vector_size == 1); // Buffer was empty. // We are passing ownership of this buffer to the download file manager. - net::IOBuffer* buffer = NULL; - read_buffer_.swap(&buffer); - buffer_->contents.push_back(std::make_pair(buffer, *bytes_read)); if (need_update) { BrowserThread::PostTask( BrowserThread::FILE, FROM_HERE, NewRunnableMethod(download_file_manager_, &DownloadFileManager::UpdateDownload, download_id_, - buffer_.get())); + buffer_)); } // We schedule a pause outside of the read loop if there is too much file // writing work to do. - if (buffer_->contents.size() > kLoadsToWrite) + if (vector_size > kLoadsToWrite) StartPauseTimer(); return true; @@ -206,9 +207,9 @@ bool DownloadResourceHandler::OnResponseCompleted( NewRunnableMethod(download_file_manager_, &DownloadFileManager::OnResponseCompleted, download_id_, - buffer_.release(), error_code, security_info)); + buffer_ = NULL; // The buffer is longer needed by |DownloadResourceHandler|. read_buffer_ = NULL; return true; } @@ -236,11 +237,7 @@ void DownloadResourceHandler::CheckWriteProgress() { if (!buffer_.get()) return; // The download completed while we were waiting to run. - size_t contents_size; - { - base::AutoLock lock(buffer_->lock); - contents_size = buffer_->contents.size(); - } + size_t contents_size = buffer_->size(); bool should_pause = contents_size > kLoadsToWrite; diff --git a/content/browser/download/download_resource_handler.h b/content/browser/download/download_resource_handler.h index ef3ec8f..a7dc77c 100644 --- a/content/browser/download/download_resource_handler.h +++ b/content/browser/download/download_resource_handler.h @@ -18,7 +18,10 @@ class DownloadFileManager; class ResourceDispatcherHost; -struct DownloadBuffer; + +namespace content { +class DownloadBuffer; +} namespace net { class URLRequest; @@ -95,7 +98,7 @@ class DownloadResourceHandler : public ResourceHandler { bool save_as_; // Request was initiated via "Save As" by the user. OnStartedCallback started_cb_; DownloadSaveInfo save_info_; - scoped_ptr<DownloadBuffer> buffer_; + scoped_refptr<content::DownloadBuffer> buffer_; ResourceDispatcherHost* rdh_; bool is_paused_; base::OneShotTimer<DownloadResourceHandler> pause_timer_; diff --git a/content/browser/download/download_types.cc b/content/browser/download/download_types.cc index b54d7f2..007ca56 100644 --- a/content/browser/download/download_types.cc +++ b/content/browser/download/download_types.cc @@ -4,12 +4,6 @@ #include "content/browser/download/download_types.h" -DownloadBuffer::DownloadBuffer() { -} - -DownloadBuffer::~DownloadBuffer() { -} - DownloadSaveInfo::DownloadSaveInfo() { } diff --git a/content/browser/download/download_types.h b/content/browser/download/download_types.h index 7ee8043..f0153a5 100644 --- a/content/browser/download/download_types.h +++ b/content/browser/download/download_types.h @@ -6,33 +6,11 @@ #define CONTENT_BROWSER_DOWNLOAD_DOWNLOAD_TYPES_H_ #pragma once -#include <vector> - #include "base/file_path.h" #include "base/memory/linked_ptr.h" -#include "base/synchronization/lock.h" #include "content/common/content_export.h" #include "net/base/file_stream.h" -namespace net { -class IOBuffer; -} - -// DownloadBuffer is created and populated on the IO thread, and passed to the -// file thread for writing. In order to avoid flooding the file thread with too -// many small write messages, each write is appended to the DownloadBuffer while -// waiting for the task to run on the file thread. Access to the write buffers -// is synchronized via the lock. Each entry in 'contents' represents one data -// buffer and its size in bytes. -struct CONTENT_EXPORT DownloadBuffer { - DownloadBuffer(); - ~DownloadBuffer(); - - base::Lock lock; - typedef std::pair<net::IOBuffer*, int> Contents; - std::vector<Contents> contents; -}; - // Holds the information about how to save a download file. struct CONTENT_EXPORT DownloadSaveInfo { DownloadSaveInfo(); |