OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2013 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/nacl_host/pnacl_translation_cache.h" |
| 6 |
| 7 #include <string> |
| 8 |
| 9 #include "base/files/file_path.h" |
| 10 #include "base/logging.h" |
| 11 #include "base/threading/thread_checker.h" |
| 12 #include "chrome/common/chrome_paths.h" |
| 13 #include "content/public/browser/browser_thread.h" |
| 14 #include "net/base/io_buffer.h" |
| 15 #include "net/base/net_errors.h" |
| 16 #include "net/disk_cache/disk_cache.h" |
| 17 |
| 18 using content::BrowserThread; |
| 19 |
| 20 static const base::FilePath::CharType kDiskCacheDirectoryName[] = |
| 21 FILE_PATH_LITERAL("PNaClTranslationCache"); |
| 22 |
| 23 namespace { |
| 24 |
| 25 void CloseDiskCacheEntry(disk_cache::Entry* entry) { entry->Close(); } |
| 26 |
| 27 } // namespace |
| 28 |
| 29 namespace pnacl_cache { |
| 30 // These are in pnacl_cache namespace instead of static so they can be used |
| 31 // by the unit test. |
| 32 const int kMaxDiskCacheSize = 1000 * 1024 * 1024; |
| 33 const int kMaxMemCacheSize = 100 * 1024 * 1024; |
| 34 |
| 35 ////////////////////////////////////////////////////////////////////// |
| 36 // Handle Storing to Cache. |
| 37 |
| 38 // PNaClTranslationCacheWriteEntry is a shim that provides storage for the |
| 39 // 'key' and 'data' strings as the disk_cache is performing various async |
| 40 // operations. It also tracks the open disk_cache::Entry |
| 41 // and ensures that the entry is closed. |
| 42 class PNaClTranslationCacheWriteEntry |
| 43 : public base::RefCounted<PNaClTranslationCacheWriteEntry> { |
| 44 public: |
| 45 PNaClTranslationCacheWriteEntry(base::WeakPtr<PNaClTranslationCache> cache, |
| 46 const std::string& key, |
| 47 const std::string& nexe, |
| 48 const net::CompletionCallback& callback); |
| 49 |
| 50 void Cache(); |
| 51 |
| 52 // --- |
| 53 // v | |
| 54 // Cache -> Open Existing --------------> Write ---> Close |
| 55 // \ ^ |
| 56 // \ / |
| 57 // --> Create -- |
| 58 enum CacheStep { |
| 59 UNINITIALIZED, |
| 60 OPEN_ENTRY, |
| 61 CREATE_ENTRY, |
| 62 WRITE_ENTRY, |
| 63 CLOSE_ENTRY |
| 64 }; |
| 65 |
| 66 private: |
| 67 friend class base::RefCounted<PNaClTranslationCacheWriteEntry>; |
| 68 ~PNaClTranslationCacheWriteEntry(); |
| 69 |
| 70 void CreateEntry(); |
| 71 |
| 72 void OpenEntry(); |
| 73 |
| 74 void WriteEntry(int bytes_to_skip); |
| 75 |
| 76 void CloseEntry(int rv); |
| 77 |
| 78 void DispatchNext(int rv); |
| 79 |
| 80 base::WeakPtr<PNaClTranslationCache> cache_; |
| 81 |
| 82 std::string key_; |
| 83 std::string nexe_; |
| 84 disk_cache::Entry* entry_; |
| 85 CacheStep step_; |
| 86 CompletionCallback finish_callback_; |
| 87 base::ThreadChecker thread_checker_; |
| 88 DISALLOW_COPY_AND_ASSIGN(PNaClTranslationCacheWriteEntry); |
| 89 }; |
| 90 |
| 91 PNaClTranslationCacheWriteEntry::PNaClTranslationCacheWriteEntry( |
| 92 base::WeakPtr<PNaClTranslationCache> cache, |
| 93 const std::string& key, |
| 94 const std::string& nexe, |
| 95 const net::CompletionCallback& callback) |
| 96 : cache_(cache), |
| 97 key_(key), |
| 98 nexe_(nexe), |
| 99 entry_(NULL), |
| 100 step_(UNINITIALIZED), |
| 101 finish_callback_(callback) {} |
| 102 |
| 103 PNaClTranslationCacheWriteEntry::~PNaClTranslationCacheWriteEntry() { |
| 104 if (entry_) |
| 105 BrowserThread::PostTask( |
| 106 BrowserThread::IO, FROM_HERE, base::Bind(&CloseDiskCacheEntry, entry_)); |
| 107 } |
| 108 |
| 109 void PNaClTranslationCacheWriteEntry::Cache() { |
| 110 DCHECK(thread_checker_.CalledOnValidThread()); |
| 111 step_ = OPEN_ENTRY; |
| 112 OpenEntry(); |
| 113 } |
| 114 |
| 115 // OpenEntry, CreateEntry, WriteEntry, and CloseEntry are only called from |
| 116 // DispatchNext, so they know that cache_ is still valid. |
| 117 void PNaClTranslationCacheWriteEntry::OpenEntry() { |
| 118 int rv = cache_->backend()->OpenEntry( |
| 119 key_, |
| 120 &entry_, |
| 121 base::Bind(&PNaClTranslationCacheWriteEntry::DispatchNext, this)); |
| 122 if (rv != net::ERR_IO_PENDING) |
| 123 DispatchNext(rv); |
| 124 } |
| 125 |
| 126 void PNaClTranslationCacheWriteEntry::CreateEntry() { |
| 127 int rv = cache_->backend()->CreateEntry( |
| 128 key_, |
| 129 &entry_, |
| 130 base::Bind(&PNaClTranslationCacheWriteEntry::DispatchNext, this)); |
| 131 if (rv != net::ERR_IO_PENDING) |
| 132 DispatchNext(rv); |
| 133 } |
| 134 |
| 135 void PNaClTranslationCacheWriteEntry::WriteEntry(int bytes_to_skip) { |
| 136 nexe_ = nexe_.substr(bytes_to_skip); |
| 137 scoped_refptr<net::StringIOBuffer> io_buf = new net::StringIOBuffer(nexe_); |
| 138 int rv = entry_->WriteData( |
| 139 1, |
| 140 0, |
| 141 io_buf, |
| 142 nexe_.length(), |
| 143 base::Bind(&PNaClTranslationCacheWriteEntry::DispatchNext, this), |
| 144 false); |
| 145 if (rv != net::ERR_IO_PENDING) |
| 146 DispatchNext(rv); |
| 147 } |
| 148 |
| 149 void PNaClTranslationCacheWriteEntry::CloseEntry(int rv) { |
| 150 if (rv < 0) |
| 151 entry_->Doom(); |
| 152 if (!finish_callback_.is_null()) { |
| 153 finish_callback_.Run(rv); |
| 154 finish_callback_.Reset(); |
| 155 } |
| 156 cache_->WriteComplete(this); |
| 157 } |
| 158 |
| 159 void PNaClTranslationCacheWriteEntry::DispatchNext(int rv) { |
| 160 DCHECK(thread_checker_.CalledOnValidThread()); |
| 161 if (!cache_) |
| 162 return; |
| 163 |
| 164 switch (step_) { |
| 165 case UNINITIALIZED: |
| 166 LOG(ERROR) << "Unexpected step in DispatchNext"; |
| 167 break; |
| 168 |
| 169 case OPEN_ENTRY: |
| 170 if (rv == net::OK) { |
| 171 step_ = WRITE_ENTRY; |
| 172 WriteEntry(0); |
| 173 } else { |
| 174 step_ = CREATE_ENTRY; |
| 175 CreateEntry(); |
| 176 } |
| 177 break; |
| 178 |
| 179 case CREATE_ENTRY: |
| 180 if (rv == net::OK) { |
| 181 step_ = WRITE_ENTRY; |
| 182 WriteEntry(0); |
| 183 } else { |
| 184 LOG(ERROR) << "Failed to Open/Create a PNaCl Translation Cache Entry"; |
| 185 CloseEntry(rv); |
| 186 } |
| 187 break; |
| 188 |
| 189 case WRITE_ENTRY: |
| 190 if (rv < 0) { |
| 191 // We do not call DispatchNext directly if WriteEntry returns |
| 192 // ERR_IO_PENDING, and the callback should not return that value either. |
| 193 LOG(ERROR) |
| 194 << "Failed to complete write to PNaCl Translation Cache Entry: " |
| 195 << rv; |
| 196 step_ = CLOSE_ENTRY; |
| 197 CloseEntry(rv); |
| 198 break; |
| 199 } |
| 200 if (rv == 0) { |
| 201 step_ = CLOSE_ENTRY; |
| 202 CloseEntry(rv); |
| 203 break; |
| 204 } |
| 205 // rv bytes were written; call WriteEntry again to skip them and try to |
| 206 // write the rest. |
| 207 WriteEntry(rv); |
| 208 break; |
| 209 |
| 210 case CLOSE_ENTRY: |
| 211 step_ = UNINITIALIZED; |
| 212 break; |
| 213 } |
| 214 } |
| 215 |
| 216 ////////////////////////////////////////////////////////////////////// |
| 217 void PNaClTranslationCache::WriteComplete( |
| 218 PNaClTranslationCacheWriteEntry* entry) { |
| 219 write_entries_.erase(entry); |
| 220 } |
| 221 |
| 222 ////////////////////////////////////////////////////////////////////// |
| 223 // Construction and cache backend initialization |
| 224 PNaClTranslationCache::PNaClTranslationCache() |
| 225 : disk_cache_(NULL), in_memory_(false) {} |
| 226 |
| 227 PNaClTranslationCache::~PNaClTranslationCache() {} |
| 228 |
| 229 int PNaClTranslationCache::InitWithDiskBackend( |
| 230 const base::FilePath& cache_dir, |
| 231 int cache_size, |
| 232 const net::CompletionCallback& callback) { |
| 233 return Init(net::DISK_CACHE, cache_dir, cache_size, callback); |
| 234 } |
| 235 |
| 236 int PNaClTranslationCache::InitWithMemBackend( |
| 237 int cache_size, |
| 238 const net::CompletionCallback& callback) { |
| 239 return Init(net::MEMORY_CACHE, base::FilePath(), cache_size, callback); |
| 240 } |
| 241 |
| 242 int PNaClTranslationCache::Init(net::CacheType cache_type, |
| 243 const base::FilePath& cache_dir, |
| 244 int cache_size, |
| 245 const net::CompletionCallback& callback) { |
| 246 int rv = disk_cache::CreateCacheBackend( |
| 247 cache_type, |
| 248 net::CACHE_BACKEND_DEFAULT, |
| 249 cache_dir, |
| 250 cache_size, |
| 251 true /* force_initialize */, |
| 252 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::CACHE), |
| 253 NULL, /* dummy net log */ |
| 254 &disk_cache_, |
| 255 base::Bind(&PNaClTranslationCache::OnCreateBackendComplete, AsWeakPtr())); |
| 256 init_callback_ = callback; |
| 257 if (rv != net::ERR_IO_PENDING) { |
| 258 OnCreateBackendComplete(rv); |
| 259 } |
| 260 return rv; |
| 261 } |
| 262 |
| 263 void PNaClTranslationCache::OnCreateBackendComplete(int rv) { |
| 264 // Invoke our client's callback function. |
| 265 if (!init_callback_.is_null()) { |
| 266 init_callback_.Run(rv); |
| 267 init_callback_.Reset(); |
| 268 } |
| 269 } |
| 270 |
| 271 ////////////////////////////////////////////////////////////////////// |
| 272 // High-level API |
| 273 |
| 274 // TODO(dschuff): Surely there must be a way to just create a null callback? |
| 275 static void NullCallback(int ignored) {} |
| 276 |
| 277 void PNaClTranslationCache::StoreNexe(const std::string& key, |
| 278 const std::string& nexe) { |
| 279 StoreNexe(key, nexe, base::Bind(NullCallback)); |
| 280 } |
| 281 |
| 282 void PNaClTranslationCache::StoreNexe(const std::string& key, |
| 283 const std::string& nexe, |
| 284 const net::CompletionCallback& callback) { |
| 285 PNaClTranslationCacheWriteEntry* entry = |
| 286 new PNaClTranslationCacheWriteEntry(AsWeakPtr(), key, nexe, callback); |
| 287 write_entries_[entry] = entry; |
| 288 entry->Cache(); |
| 289 } |
| 290 |
| 291 int PNaClTranslationCache::GetNexe(const std::string& key, |
| 292 std::string* nexe, |
| 293 const net::CompletionCallback& callback) { |
| 294 // TODO(dschuff): Actually find the entry, and do the right thing. |
| 295 // Shader cache ended up making a separate ReadHelper, analogous |
| 296 // to the PNaClTranslationCacheWriteEntry. |
| 297 return net::OK; |
| 298 } |
| 299 |
| 300 int PNaClTranslationCache::InitCache(const base::FilePath& cache_directory, |
| 301 bool in_memory, |
| 302 const net::CompletionCallback& callback) { |
| 303 int rv; |
| 304 in_memory_ = in_memory; |
| 305 if (in_memory_) { |
| 306 rv = InitWithMemBackend(kMaxMemCacheSize, callback); |
| 307 } else { |
| 308 rv = InitWithDiskBackend(cache_directory.Append(kDiskCacheDirectoryName), |
| 309 kMaxDiskCacheSize, |
| 310 callback); |
| 311 } |
| 312 |
| 313 return rv; |
| 314 } |
| 315 |
| 316 int PNaClTranslationCache::Size() { |
| 317 if (!disk_cache_) |
| 318 return -1; |
| 319 return disk_cache_->GetEntryCount(); |
| 320 } |
| 321 |
| 322 } // namespace nacl_cache |
OLD | NEW |