Changeset 179690 in webkit
- Timestamp:
- Feb 5, 2015, 6:31:07 AM (10 years ago)
- Location:
- trunk/Source/WebKit2
- Files:
-
- 4 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/Source/WebKit2/ChangeLog
r179687 r179690 1 2015-02-04 Antti Koivisto <antti@apple.com> 2 3 Avoid copying std::functions across threads in NetworkCacheStorage 4 https://bugs.webkit.org/show_bug.cgi?id=141273 5 6 Reviewed by Andreas Kling. 7 8 The current approach is risky. There is possiblity that captured variables are 9 deleted in an unexpected thread. 10 11 * NetworkProcess/cache/NetworkCache.cpp: 12 (WebKit::NetworkCache::retrieve): 13 14 The capture trick here is no longer needed. 15 16 * NetworkProcess/cache/NetworkCacheStorage.h: 17 18 For each cache operation we create Retrive/Store/UpdateOperation object kept alive by the active operation map. 19 This object captures all parameters of the operation including the lambda. When the operation completes 20 the object is removed from the map in the main thread, ensuring safe destruction. 21 22 * NetworkProcess/cache/NetworkCacheStorageCocoa.mm: 23 (WebKit::NetworkCacheStorage::dispatchRetrieveOperation): 24 (WebKit::NetworkCacheStorage::dispatchPendingRetrieveOperations): 25 (WebKit::retrieveActive): 26 27 Instead of maintaining a separate write cache we just look through the active write and update maps. 28 29 (WebKit::NetworkCacheStorage::retrieve): 30 31 Use fixed sized priority array rather than a dynamic one. Vector<Deque<std::unique_ptr>> doesn't quite work. 32 33 (WebKit::NetworkCacheStorage::store): 34 (WebKit::NetworkCacheStorage::update): 35 (WebKit::NetworkCacheStorage::clear): 36 1 37 2015-02-05 Youenn Fablet <youenn.fablet@crf.canon.fr> and Xabier Rodriguez Calvar <calvaris@igalia.com> 2 38 -
trunk/Source/WebKit2/NetworkProcess/cache/NetworkCache.cpp
r179584 r179690 227 227 228 228 auto startTime = std::chrono::system_clock::now(); 229 230 229 NetworkCacheKey storageKey = makeCacheKey(originalRequest); 231 230 unsigned priority = originalRequest.priority(); 232 231 233 // Captured data is going to be shuffled around threads. Avoid unsafe copying. 234 struct Capture { 235 WebCore::ResourceRequest originalRequest; 236 std::function<void (std::unique_ptr<Entry>)> completionHandler; 237 }; 238 // FIXME: With C++14 this could use unique_ptr and initialized lambda capture 239 auto capture = std::make_shared<Capture>(Capture { originalRequest, completionHandler }); 240 241 m_storage->retrieve(storageKey, priority, [this, capture, startTime](std::unique_ptr<NetworkCacheStorage::Entry> entry) { 232 m_storage->retrieve(storageKey, priority, [this, originalRequest, completionHandler, startTime](std::unique_ptr<NetworkCacheStorage::Entry> entry) { 242 233 if (!entry) { 243 234 LOG(NetworkCache, "(NetworkProcess) not found in storage"); 244 c apture->completionHandler(nullptr);235 completionHandler(nullptr); 245 236 return false; 246 237 } 247 auto decodedEntry = decodeStorageEntry(*entry, capture->originalRequest);238 auto decodedEntry = decodeStorageEntry(*entry, originalRequest); 248 239 bool success = !!decodedEntry; 249 240 #if !LOG_DISABLED 250 241 auto elapsedMS = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now() - startTime).count(); 251 242 #endif 252 LOG(NetworkCache, "(NetworkProcess) retrieve complete success=%d priority=%u time=%lldms", success, capture->originalRequest.priority(), elapsedMS);253 c apture->completionHandler(WTF::move(decodedEntry));243 LOG(NetworkCache, "(NetworkProcess) retrieve complete success=%d priority=%u time=%lldms", success, originalRequest.priority(), elapsedMS); 244 completionHandler(WTF::move(decodedEntry)); 254 245 return success; 255 246 }); -
trunk/Source/WebKit2/NetworkProcess/cache/NetworkCacheStorage.h
r179547 r179690 33 33 #include <wtf/BloomFilter.h> 34 34 #include <wtf/Deque.h> 35 #include <wtf/HashSet.h> 35 36 #include <wtf/text/WTFString.h> 36 37 … … 158 159 std::function<bool (std::unique_ptr<Entry>)> completionHandler; 159 160 }; 160 void dispatchRetrieveOperation( const RetrieveOperation&);161 void dispatchRetrieveOperation(std::unique_ptr<const RetrieveOperation>); 161 162 void dispatchPendingRetrieveOperations(); 163 164 struct StoreOperation { 165 NetworkCacheKey key; 166 Entry entry; 167 std::function<void (bool success)> completionHandler; 168 }; 169 170 struct UpdateOperation { 171 NetworkCacheKey key; 172 Entry entry; 173 Entry existingEntry; 174 std::function<void (bool success)> completionHandler; 175 }; 162 176 163 177 const String m_directoryPath; … … 169 183 std::atomic<bool> m_shrinkInProgress { false }; 170 184 171 Vector<Deque<RetrieveOperation>> m_pendingRetrieveOperationsByPriority; 172 unsigned m_activeRetrieveOperationCount { 0 }; 173 174 typedef std::pair<NetworkCacheKey, Entry> KeyEntryPair; 175 HashMap<NetworkCacheKey::HashType, std::shared_ptr<KeyEntryPair>, AlreadyHashed> m_writeCache; 185 static const int maximumRetrievePriority = 4; 186 Deque<std::unique_ptr<const RetrieveOperation>> m_pendingRetrieveOperationsByPriority[maximumRetrievePriority + 1]; 187 188 HashSet<std::unique_ptr<const RetrieveOperation>> m_activeRetrieveOperations; 189 HashSet<std::unique_ptr<const StoreOperation>> m_activeStoreOperations; 190 HashSet<std::unique_ptr<const UpdateOperation>> m_activeUpdateOperations; 176 191 177 192 #if PLATFORM(COCOA) -
trunk/Source/WebKit2/NetworkProcess/cache/NetworkCacheStorageCocoa.mm
r179547 r179690 353 353 } 354 354 355 void NetworkCacheStorage::dispatchRetrieveOperation(const RetrieveOperation& retrieve) 356 { 357 ASSERT(RunLoop::isMain()); 358 359 ++m_activeRetrieveOperationCount; 355 void NetworkCacheStorage::dispatchRetrieveOperation(std::unique_ptr<const RetrieveOperation> retrieveOperation) 356 { 357 ASSERT(RunLoop::isMain()); 358 359 auto& retrieve = *retrieveOperation; 360 m_activeRetrieveOperations.add(WTF::move(retrieveOperation)); 360 361 361 362 StringCapture cachePathCapture(m_directoryPath); 362 dispatch_async(m_ioQueue.get(), [this, retrieve, cachePathCapture] {363 dispatch_async(m_ioQueue.get(), [this, &retrieve, cachePathCapture] { 363 364 int fd; 364 365 auto channel = openFileForKey(retrieve.key, FileOpenType::Read, cachePathCapture.string(), fd); 365 366 366 367 bool didCallCompletionHandler = false; 367 dispatch_io_read(channel.get(), 0, std::numeric_limits<size_t>::max(), dispatch_get_main_queue(), [this, fd, retrieve, didCallCompletionHandler](bool done, dispatch_data_t fileData, int error) mutable {368 dispatch_io_read(channel.get(), 0, std::numeric_limits<size_t>::max(), dispatch_get_main_queue(), [this, fd, &retrieve, didCallCompletionHandler](bool done, dispatch_data_t fileData, int error) mutable { 368 369 if (done) { 369 ASSERT(m_activeRetrieveOperationCount); 370 --m_activeRetrieveOperationCount; 371 dispatchPendingRetrieveOperations(); 372 } 373 if (done) { 370 if (error) 371 removeEntry(retrieve.key); 372 374 373 if (!didCallCompletionHandler) 375 374 retrieve.completionHandler(nullptr); 376 if (error) 377 removeEntry(retrieve.key); 375 376 ASSERT(m_activeRetrieveOperations.contains(&retrieve)); 377 m_activeRetrieveOperations.remove(&retrieve); 378 dispatchPendingRetrieveOperations(); 378 379 return; 379 380 } … … 393 394 ASSERT(RunLoop::isMain()); 394 395 395 const unsignedmaximumActiveRetrieveOperationCount = 5;396 397 for (int priority = m _pendingRetrieveOperationsByPriority.size() - 1; priority >= 0; --priority) {398 if (m_activeRetrieveOperation Count> maximumActiveRetrieveOperationCount) {396 const int maximumActiveRetrieveOperationCount = 5; 397 398 for (int priority = maximumRetrievePriority; priority >= 0; --priority) { 399 if (m_activeRetrieveOperations.size() > maximumActiveRetrieveOperationCount) { 399 400 LOG(NetworkCacheStorage, "(NetworkProcess) limiting parallel retrieves"); 400 401 return; … … 407 408 } 408 409 410 template <class T> bool retrieveActive(const T& operations, const NetworkCacheKey& key, std::function<bool (std::unique_ptr<NetworkCacheStorage::Entry>)>& completionHandler) 411 { 412 for (auto& operation : operations) { 413 if (operation->key == key) { 414 LOG(NetworkCacheStorage, "(NetworkProcess) found store operation in progress"); 415 auto entry = operation->entry; 416 dispatch_async(dispatch_get_main_queue(), [entry, completionHandler] { 417 completionHandler(std::make_unique<NetworkCacheStorage::Entry>(entry)); 418 }); 419 return true; 420 } 421 } 422 return false; 423 } 424 409 425 void NetworkCacheStorage::retrieve(const NetworkCacheKey& key, unsigned priority, std::function<bool (std::unique_ptr<Entry>)> completionHandler) 410 426 { 411 427 ASSERT(RunLoop::isMain()); 428 ASSERT(priority <= maximumRetrievePriority); 412 429 413 430 if (!m_contentsFilter.mayContain(key.hash())) { … … 415 432 return; 416 433 } 417 418 // Write cache is a temporary memory cache used to respond to requests while a write is pending. 419 if (auto keyEntryPair = m_writeCache.get(key.hash())) { 420 if (keyEntryPair->first == key) { 421 LOG(NetworkCacheStorage, "(NetworkProcess) found from the write cache"); 422 dispatch_async(dispatch_get_main_queue(), [keyEntryPair, completionHandler] { 423 completionHandler(std::make_unique<Entry>(keyEntryPair->second)); 424 }); 425 return; 426 } 427 } 428 429 if (m_pendingRetrieveOperationsByPriority.size() < priority + 1) 430 m_pendingRetrieveOperationsByPriority.grow(priority + 1); 431 m_pendingRetrieveOperationsByPriority[priority].append(RetrieveOperation { key, completionHandler }); 432 434 // See if we have the resource in memory. 435 if (retrieveActive(m_activeStoreOperations, key, completionHandler)) 436 return; 437 if (retrieveActive(m_activeUpdateOperations, key, completionHandler)) 438 return; 439 440 // Fetch from disk. 441 m_pendingRetrieveOperationsByPriority[priority].append(std::make_unique<RetrieveOperation>(RetrieveOperation { key, completionHandler })); 433 442 dispatchPendingRetrieveOperations(); 434 443 } … … 441 450 ++m_approximateEntryCount; 442 451 443 m_writeCache.set(key.hash(), std::make_shared<KeyEntryPair>(key, entry)); 452 auto storeOperation = std::make_unique<StoreOperation>(StoreOperation { key, entry, completionHandler }); 453 auto& store = *storeOperation; 454 m_activeStoreOperations.add(WTF::move(storeOperation)); 444 455 445 456 StringCapture cachePathCapture(m_directoryPath); 446 dispatch_async(m_backgroundIOQueue.get(), [this, key, entry, cachePathCapture, completionHandler] {447 auto data = encodeEntry( key,entry);457 dispatch_async(m_backgroundIOQueue.get(), [this, &store, cachePathCapture] { 458 auto data = encodeEntry(store.key, store.entry); 448 459 449 460 int fd; 450 auto channel = openFileForKey( key, FileOpenType::Create, cachePathCapture.string(), fd);451 dispatch_io_write(channel.get(), 0, data.get(), dispatch_get_main_queue(), [this, key, completionHandler](bool done, dispatch_data_t, int error) {461 auto channel = openFileForKey(store.key, FileOpenType::Create, cachePathCapture.string(), fd); 462 dispatch_io_write(channel.get(), 0, data.get(), dispatch_get_main_queue(), [this, &store](bool done, dispatch_data_t, int error) { 452 463 ASSERT_UNUSED(done, done); 453 464 LOG(NetworkCacheStorage, "(NetworkProcess) write complete error=%d", error); 454 465 if (error) { 455 if (m_contentsFilter.mayContain( key.hash()))456 m_contentsFilter.remove( key.hash());466 if (m_contentsFilter.mayContain(store.key.hash())) 467 m_contentsFilter.remove(store.key.hash()); 457 468 if (m_approximateEntryCount) 458 469 --m_approximateEntryCount; 459 470 } 460 m_writeCache.remove(key.hash()); 461 462 completionHandler(!error); 471 472 store.completionHandler(!error); 473 474 m_activeStoreOperations.remove(&store); 463 475 }); 464 476 }); … … 477 489 } 478 490 479 m_writeCache.set(key.hash(), std::make_shared<KeyEntryPair>(key, updateEntry)); 491 auto updateOperation = std::make_unique<UpdateOperation>(UpdateOperation { key, updateEntry, existingEntry, completionHandler }); 492 auto& update = *updateOperation; 493 m_activeUpdateOperations.add(WTF::move(updateOperation)); 480 494 481 495 // Try to update the header of an existing entry. 482 496 StringCapture cachePathCapture(m_directoryPath); 483 dispatch_async(m_backgroundIOQueue.get(), [this, key, updateEntry, existingEntry, cachePathCapture, completionHandler] {484 auto headerData = encodeEntryHeader( key, updateEntry);485 auto existingHeaderData = encodeEntryHeader( key,existingEntry);497 dispatch_async(m_backgroundIOQueue.get(), [this, &update, cachePathCapture] { 498 auto headerData = encodeEntryHeader(update.key, update.entry); 499 auto existingHeaderData = encodeEntryHeader(update.key, update.existingEntry); 486 500 487 501 bool pageRoundedHeaderSizeChanged = dispatch_data_get_size(headerData.get()) != dispatch_data_get_size(existingHeaderData.get()); 488 502 if (pageRoundedHeaderSizeChanged) { 489 503 LOG(NetworkCacheStorage, "(NetworkProcess) page-rounded header size changed, storing full entry"); 490 dispatch_async(dispatch_get_main_queue(), [this, key, updateEntry, completionHandler] { 491 store(key, updateEntry, completionHandler); 504 dispatch_async(dispatch_get_main_queue(), [this, &update] { 505 store(update.key, update.entry, update.completionHandler); 506 507 ASSERT(m_activeUpdateOperations.contains(&update)); 508 m_activeUpdateOperations.remove(&update); 492 509 }); 493 510 return; … … 495 512 496 513 int fd; 497 auto channel = openFileForKey( key, FileOpenType::Write, cachePathCapture.string(), fd);498 dispatch_io_write(channel.get(), 0, headerData.get(), dispatch_get_main_queue(), [this, key, completionHandler](bool done, dispatch_data_t, int error) {514 auto channel = openFileForKey(update.key, FileOpenType::Write, cachePathCapture.string(), fd); 515 dispatch_io_write(channel.get(), 0, headerData.get(), dispatch_get_main_queue(), [this, &update](bool done, dispatch_data_t, int error) { 499 516 ASSERT_UNUSED(done, done); 500 517 LOG(NetworkCacheStorage, "(NetworkProcess) update complete error=%d", error); 501 518 502 519 if (error) 503 removeEntry(key); 504 m_writeCache.remove(key.hash()); 505 506 completionHandler(!error); 520 removeEntry(update.key); 521 522 update.completionHandler(!error); 523 524 ASSERT(m_activeUpdateOperations.contains(&update)); 525 m_activeUpdateOperations.remove(&update); 507 526 }); 508 527 }); … … 522 541 LOG(NetworkCacheStorage, "(NetworkProcess) clearing cache"); 523 542 524 m_writeCache.clear();525 543 m_contentsFilter.clear(); 526 544 m_approximateEntryCount = 0;
Note:
See TracChangeset
for help on using the changeset viewer.