| Index: chrome/browser/nacl_host/pnacl_translation_cache.cc
 | 
| diff --git a/chrome/browser/nacl_host/pnacl_translation_cache.cc b/chrome/browser/nacl_host/pnacl_translation_cache.cc
 | 
| new file mode 100644
 | 
| index 0000000000000000000000000000000000000000..5d6081f91298ffd879fac0e70034d68371db1005
 | 
| --- /dev/null
 | 
| +++ b/chrome/browser/nacl_host/pnacl_translation_cache.cc
 | 
| @@ -0,0 +1,322 @@
 | 
| +// Copyright (c) 2013 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/nacl_host/pnacl_translation_cache.h"
 | 
| +
 | 
| +#include <string>
 | 
| +
 | 
| +#include "base/files/file_path.h"
 | 
| +#include "base/logging.h"
 | 
| +#include "base/threading/thread_checker.h"
 | 
| +#include "chrome/common/chrome_paths.h"
 | 
| +#include "content/public/browser/browser_thread.h"
 | 
| +#include "net/base/io_buffer.h"
 | 
| +#include "net/base/net_errors.h"
 | 
| +#include "net/disk_cache/disk_cache.h"
 | 
| +
 | 
| +using content::BrowserThread;
 | 
| +
 | 
| +static const base::FilePath::CharType kDiskCacheDirectoryName[] =
 | 
| +    FILE_PATH_LITERAL("PNaClTranslationCache");
 | 
| +
 | 
| +namespace {
 | 
| +
 | 
| +void CloseDiskCacheEntry(disk_cache::Entry* entry) { entry->Close(); }
 | 
| +
 | 
| +}  // namespace
 | 
| +
 | 
| +namespace pnacl_cache {
 | 
| +// These are in pnacl_cache namespace instead of static so they can be used
 | 
| +// by the unit test.
 | 
| +const int kMaxDiskCacheSize = 1000 * 1024 * 1024;
 | 
| +const int kMaxMemCacheSize = 100 * 1024 * 1024;
 | 
| +
 | 
| +//////////////////////////////////////////////////////////////////////
 | 
| +// Handle Storing to Cache.
 | 
| +
 | 
| +// PNaClTranslationCacheWriteEntry is a shim that provides storage for the
 | 
| +// 'key' and 'data' strings as the disk_cache is performing various async
 | 
| +// operations. It also tracks the open disk_cache::Entry
 | 
| +// and ensures that the entry is closed.
 | 
| +class PNaClTranslationCacheWriteEntry
 | 
| +    : public base::RefCounted<PNaClTranslationCacheWriteEntry> {
 | 
| + public:
 | 
| +  PNaClTranslationCacheWriteEntry(base::WeakPtr<PNaClTranslationCache> cache,
 | 
| +                                  const std::string& key,
 | 
| +                                  const std::string& nexe,
 | 
| +                                  const net::CompletionCallback& callback);
 | 
| +
 | 
| +  void Cache();
 | 
| +
 | 
| +  //                                        ---
 | 
| +  //                                        v  |
 | 
| +  // Cache -> Open Existing --------------> Write ---> Close
 | 
| +  //                          \              ^
 | 
| +  //                           \             /
 | 
| +  //                            --> Create --
 | 
| +  enum CacheStep {
 | 
| +    UNINITIALIZED,
 | 
| +    OPEN_ENTRY,
 | 
| +    CREATE_ENTRY,
 | 
| +    WRITE_ENTRY,
 | 
| +    CLOSE_ENTRY
 | 
| +  };
 | 
| +
 | 
| + private:
 | 
| +  friend class base::RefCounted<PNaClTranslationCacheWriteEntry>;
 | 
| +  ~PNaClTranslationCacheWriteEntry();
 | 
| +
 | 
| +  void CreateEntry();
 | 
| +
 | 
| +  void OpenEntry();
 | 
| +
 | 
| +  void WriteEntry(int bytes_to_skip);
 | 
| +
 | 
| +  void CloseEntry(int rv);
 | 
| +
 | 
| +  void DispatchNext(int rv);
 | 
| +
 | 
| +  base::WeakPtr<PNaClTranslationCache> cache_;
 | 
| +
 | 
| +  std::string key_;
 | 
| +  std::string nexe_;
 | 
| +  disk_cache::Entry* entry_;
 | 
| +  CacheStep step_;
 | 
| +  CompletionCallback finish_callback_;
 | 
| +  base::ThreadChecker thread_checker_;
 | 
| +  DISALLOW_COPY_AND_ASSIGN(PNaClTranslationCacheWriteEntry);
 | 
| +};
 | 
| +
 | 
| +PNaClTranslationCacheWriteEntry::PNaClTranslationCacheWriteEntry(
 | 
| +    base::WeakPtr<PNaClTranslationCache> cache,
 | 
| +    const std::string& key,
 | 
| +    const std::string& nexe,
 | 
| +    const net::CompletionCallback& callback)
 | 
| +    : cache_(cache),
 | 
| +      key_(key),
 | 
| +      nexe_(nexe),
 | 
| +      entry_(NULL),
 | 
| +      step_(UNINITIALIZED),
 | 
| +      finish_callback_(callback) {}
 | 
| +
 | 
| +PNaClTranslationCacheWriteEntry::~PNaClTranslationCacheWriteEntry() {
 | 
| +  if (entry_)
 | 
| +    BrowserThread::PostTask(
 | 
| +        BrowserThread::IO, FROM_HERE, base::Bind(&CloseDiskCacheEntry, entry_));
 | 
| +}
 | 
| +
 | 
| +void PNaClTranslationCacheWriteEntry::Cache() {
 | 
| +  DCHECK(thread_checker_.CalledOnValidThread());
 | 
| +  step_ = OPEN_ENTRY;
 | 
| +  OpenEntry();
 | 
| +}
 | 
| +
 | 
| +// OpenEntry, CreateEntry, WriteEntry, and CloseEntry are only called from
 | 
| +// DispatchNext, so they know that cache_ is still valid.
 | 
| +void PNaClTranslationCacheWriteEntry::OpenEntry() {
 | 
| +  int rv = cache_->backend()->OpenEntry(
 | 
| +      key_,
 | 
| +      &entry_,
 | 
| +      base::Bind(&PNaClTranslationCacheWriteEntry::DispatchNext, this));
 | 
| +  if (rv != net::ERR_IO_PENDING)
 | 
| +    DispatchNext(rv);
 | 
| +}
 | 
| +
 | 
| +void PNaClTranslationCacheWriteEntry::CreateEntry() {
 | 
| +  int rv = cache_->backend()->CreateEntry(
 | 
| +      key_,
 | 
| +      &entry_,
 | 
| +      base::Bind(&PNaClTranslationCacheWriteEntry::DispatchNext, this));
 | 
| +  if (rv != net::ERR_IO_PENDING)
 | 
| +    DispatchNext(rv);
 | 
| +}
 | 
| +
 | 
| +void PNaClTranslationCacheWriteEntry::WriteEntry(int bytes_to_skip) {
 | 
| +  nexe_ = nexe_.substr(bytes_to_skip);
 | 
| +  scoped_refptr<net::StringIOBuffer> io_buf = new net::StringIOBuffer(nexe_);
 | 
| +  int rv = entry_->WriteData(
 | 
| +      1,
 | 
| +      0,
 | 
| +      io_buf,
 | 
| +      nexe_.length(),
 | 
| +      base::Bind(&PNaClTranslationCacheWriteEntry::DispatchNext, this),
 | 
| +      false);
 | 
| +  if (rv != net::ERR_IO_PENDING)
 | 
| +    DispatchNext(rv);
 | 
| +}
 | 
| +
 | 
| +void PNaClTranslationCacheWriteEntry::CloseEntry(int rv) {
 | 
| +  if (rv < 0)
 | 
| +    entry_->Doom();
 | 
| +  if (!finish_callback_.is_null()) {
 | 
| +    finish_callback_.Run(rv);
 | 
| +    finish_callback_.Reset();
 | 
| +  }
 | 
| +  cache_->WriteComplete(this);
 | 
| +}
 | 
| +
 | 
| +void PNaClTranslationCacheWriteEntry::DispatchNext(int rv) {
 | 
| +  DCHECK(thread_checker_.CalledOnValidThread());
 | 
| +  if (!cache_)
 | 
| +    return;
 | 
| +
 | 
| +  switch (step_) {
 | 
| +    case UNINITIALIZED:
 | 
| +      LOG(ERROR) << "Unexpected step in DispatchNext";
 | 
| +      break;
 | 
| +
 | 
| +    case OPEN_ENTRY:
 | 
| +      if (rv == net::OK) {
 | 
| +        step_ = WRITE_ENTRY;
 | 
| +        WriteEntry(0);
 | 
| +      } else {
 | 
| +        step_ = CREATE_ENTRY;
 | 
| +        CreateEntry();
 | 
| +      }
 | 
| +      break;
 | 
| +
 | 
| +    case CREATE_ENTRY:
 | 
| +      if (rv == net::OK) {
 | 
| +        step_ = WRITE_ENTRY;
 | 
| +        WriteEntry(0);
 | 
| +      } else {
 | 
| +        LOG(ERROR) << "Failed to Open/Create a PNaCl Translation Cache Entry";
 | 
| +        CloseEntry(rv);
 | 
| +      }
 | 
| +      break;
 | 
| +
 | 
| +    case WRITE_ENTRY:
 | 
| +      if (rv < 0) {
 | 
| +        // We do not call DispatchNext directly if WriteEntry returns
 | 
| +        // ERR_IO_PENDING, and the callback should not return that value either.
 | 
| +        LOG(ERROR)
 | 
| +            << "Failed to complete write to PNaCl Translation Cache Entry: "
 | 
| +            << rv;
 | 
| +        step_ = CLOSE_ENTRY;
 | 
| +        CloseEntry(rv);
 | 
| +        break;
 | 
| +      }
 | 
| +      if (rv == 0) {
 | 
| +        step_ = CLOSE_ENTRY;
 | 
| +        CloseEntry(rv);
 | 
| +        break;
 | 
| +      }
 | 
| +      // rv bytes were written; call WriteEntry again to skip them and try to
 | 
| +      // write the rest.
 | 
| +      WriteEntry(rv);
 | 
| +      break;
 | 
| +
 | 
| +    case CLOSE_ENTRY:
 | 
| +      step_ = UNINITIALIZED;
 | 
| +      break;
 | 
| +  }
 | 
| +}
 | 
| +
 | 
| +//////////////////////////////////////////////////////////////////////
 | 
| +void PNaClTranslationCache::WriteComplete(
 | 
| +    PNaClTranslationCacheWriteEntry* entry) {
 | 
| +  write_entries_.erase(entry);
 | 
| +}
 | 
| +
 | 
| +//////////////////////////////////////////////////////////////////////
 | 
| +// Construction and cache backend initialization
 | 
| +PNaClTranslationCache::PNaClTranslationCache()
 | 
| +    : disk_cache_(NULL), in_memory_(false) {}
 | 
| +
 | 
| +PNaClTranslationCache::~PNaClTranslationCache() {}
 | 
| +
 | 
| +int PNaClTranslationCache::InitWithDiskBackend(
 | 
| +    const base::FilePath& cache_dir,
 | 
| +    int cache_size,
 | 
| +    const net::CompletionCallback& callback) {
 | 
| +  return Init(net::DISK_CACHE, cache_dir, cache_size, callback);
 | 
| +}
 | 
| +
 | 
| +int PNaClTranslationCache::InitWithMemBackend(
 | 
| +    int cache_size,
 | 
| +    const net::CompletionCallback& callback) {
 | 
| +  return Init(net::MEMORY_CACHE, base::FilePath(), cache_size, callback);
 | 
| +}
 | 
| +
 | 
| +int PNaClTranslationCache::Init(net::CacheType cache_type,
 | 
| +                                const base::FilePath& cache_dir,
 | 
| +                                int cache_size,
 | 
| +                                const net::CompletionCallback& callback) {
 | 
| +  int rv = disk_cache::CreateCacheBackend(
 | 
| +      cache_type,
 | 
| +      net::CACHE_BACKEND_DEFAULT,
 | 
| +      cache_dir,
 | 
| +      cache_size,
 | 
| +      true /* force_initialize */,
 | 
| +      BrowserThread::GetMessageLoopProxyForThread(BrowserThread::CACHE),
 | 
| +      NULL, /* dummy net log */
 | 
| +      &disk_cache_,
 | 
| +      base::Bind(&PNaClTranslationCache::OnCreateBackendComplete, AsWeakPtr()));
 | 
| +  init_callback_ = callback;
 | 
| +  if (rv != net::ERR_IO_PENDING) {
 | 
| +    OnCreateBackendComplete(rv);
 | 
| +  }
 | 
| +  return rv;
 | 
| +}
 | 
| +
 | 
| +void PNaClTranslationCache::OnCreateBackendComplete(int rv) {
 | 
| +  // Invoke our client's callback function.
 | 
| +  if (!init_callback_.is_null()) {
 | 
| +    init_callback_.Run(rv);
 | 
| +    init_callback_.Reset();
 | 
| +  }
 | 
| +}
 | 
| +
 | 
| +//////////////////////////////////////////////////////////////////////
 | 
| +// High-level API
 | 
| +
 | 
| +// TODO(dschuff): Surely there must be a way to just create a null callback?
 | 
| +static void NullCallback(int ignored) {}
 | 
| +
 | 
| +void PNaClTranslationCache::StoreNexe(const std::string& key,
 | 
| +                                      const std::string& nexe) {
 | 
| +  StoreNexe(key, nexe, base::Bind(NullCallback));
 | 
| +}
 | 
| +
 | 
| +void PNaClTranslationCache::StoreNexe(const std::string& key,
 | 
| +                                      const std::string& nexe,
 | 
| +                                      const net::CompletionCallback& callback) {
 | 
| +  PNaClTranslationCacheWriteEntry* entry =
 | 
| +      new PNaClTranslationCacheWriteEntry(AsWeakPtr(), key, nexe, callback);
 | 
| +  write_entries_[entry] = entry;
 | 
| +  entry->Cache();
 | 
| +}
 | 
| +
 | 
| +int PNaClTranslationCache::GetNexe(const std::string& key,
 | 
| +                                   std::string* nexe,
 | 
| +                                   const net::CompletionCallback& callback) {
 | 
| +  // TODO(dschuff): Actually find the entry, and do the right thing.
 | 
| +  // Shader cache ended up making a separate ReadHelper, analogous
 | 
| +  // to the PNaClTranslationCacheWriteEntry.
 | 
| +  return net::OK;
 | 
| +}
 | 
| +
 | 
| +int PNaClTranslationCache::InitCache(const base::FilePath& cache_directory,
 | 
| +                                     bool in_memory,
 | 
| +                                     const net::CompletionCallback& callback) {
 | 
| +  int rv;
 | 
| +  in_memory_ = in_memory;
 | 
| +  if (in_memory_) {
 | 
| +    rv = InitWithMemBackend(kMaxMemCacheSize, callback);
 | 
| +  } else {
 | 
| +    rv = InitWithDiskBackend(cache_directory.Append(kDiskCacheDirectoryName),
 | 
| +                             kMaxDiskCacheSize,
 | 
| +                             callback);
 | 
| +  }
 | 
| +
 | 
| +  return rv;
 | 
| +}
 | 
| +
 | 
| +int PNaClTranslationCache::Size() {
 | 
| +  if (!disk_cache_)
 | 
| +    return -1;
 | 
| +  return disk_cache_->GetEntryCount();
 | 
| +}
 | 
| +
 | 
| +}  // namespace nacl_cache
 | 
| 
 |