Skip to content

Commit 0b589ff

Browse files
committed
Stop using IOChannel in NetworkCache
https://bugs.webkit.org/show_bug.cgi?id=290211 rdar://142645876 Reviewed by Per Arne Vollan. This is a speculative fix for a hard-to-repro issue where page loads sometimes hang for minutes while the system is under heavy load. What we see in logs is that the disk network cache lookup ends up on a pri 4 default overcommit dispatch queue. That thread is parked in a pread and gets preempted for minutes while other higher pri work is occurring. The thing that is enqueuing the pread on to that dispatch queue is `dispatch_io_read`, which enqueues that block on to a queue that it controls. We think that probably something is going wrong with the priority propagation to that queue sometimes, as the network cache lookup hops from main thread => network cache I/O queue => dispatch_io private queue and back. As a speculative fix: - Stop using `dispatch_io` / `IOChannel` altogether. It buys us nothing other than adding extra thread hops, making things harder to debug, and perhaps exposing some latent bug in priority propagation. Instead, just do the I/O directly on the network cache's I/O queues. - Change the priority of the read-only I/O queue from default to UserInteractive, since it's on the blocking path for resource fetching. - Change the priority of the write I/O queues from Background to Utility, since Background is very punitive (capped at max pri 4), and if we get anything wrong, getting capped at max pri 4 can cause minutes of starvation. WriteOperations also had an completion handler that received an integer errno as an argument. This was unused, so I removed it. * Source/WebKit/NetworkProcess/cache/NetworkCacheData.cpp: (WebKit::NetworkCache::Data::Data): * Source/WebKit/NetworkProcess/cache/NetworkCacheData.h: * Source/WebKit/NetworkProcess/cache/NetworkCacheDataCocoa.mm: (WebKit::NetworkCache::Data::Data): * Source/WebKit/NetworkProcess/cache/NetworkCacheStorage.cpp: (WebKit::NetworkCache::Storage::WriteOperation::WriteOperation): (WebKit::NetworkCache::Storage::WriteOperation::~WriteOperation): (WebKit::NetworkCache::Storage::Storage): (WebKit::NetworkCache::Storage::dispatchReadOperation): (WebKit::NetworkCache::Storage::readRecordFromData): (WebKit::NetworkCache::Storage::dispatchWriteOperation): (WebKit::NetworkCache::Storage::finishWriteOperationActivity): (WebKit::NetworkCache::Storage::store): (WebKit::NetworkCache::Storage::WriteOperation::invokeCompletionHandler): Deleted. * Source/WebKit/NetworkProcess/cache/NetworkCacheStorage.h: (WebKit::NetworkCache::Storage::store): Deleted. Canonical link: https://commits.webkit.org/292576@main
1 parent baef96f commit 0b589ff

5 files changed

Lines changed: 40 additions & 39 deletions

File tree

Source/WebKit/NetworkProcess/cache/NetworkCacheData.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,18 @@
3939
namespace WebKit {
4040
namespace NetworkCache {
4141

42+
#if !USE(GLIB) && USE(CURL)
43+
Data::Data(Vector<uint8_t>&& data)
44+
: Data(std::variant<Vector<uint8_t>, FileSystem::MappedFileData> { WTFMove(data) })
45+
{
46+
}
47+
#elif !PLATFORM(COCOA)
48+
Data::Data(Vector<uint8_t>&& data)
49+
: Data(data.span())
50+
{
51+
}
52+
#endif
53+
4254
Data Data::mapToFile(const String& path) const
4355
{
4456
FileSystem::FileHandle handle;

Source/WebKit/NetworkProcess/cache/NetworkCacheData.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
#include <wtf/MappedFileData.h>
3333
#include <wtf/SHA1.h>
3434
#include <wtf/ThreadSafeRefCounted.h>
35+
#include <wtf/Vector.h>
3536
#include <wtf/text/WTFString.h>
3637

3738
#if PLATFORM(COCOA)
@@ -59,6 +60,7 @@ class Data {
5960
public:
6061
Data() { }
6162
Data(std::span<const uint8_t>);
63+
Data(Vector<uint8_t>&& data);
6264

6365
~Data() { }
6466

@@ -73,7 +75,6 @@ class Data {
7375
Data(GRefPtr<GBytes>&&, FileSystem::FileHandle&& = { });
7476
#elif USE(CURL)
7577
Data(std::variant<Vector<uint8_t>, FileSystem::MappedFileData>&&);
76-
Data(Vector<uint8_t>&& data) : Data(std::variant<Vector<uint8_t>, FileSystem::MappedFileData> { WTFMove(data) }) { }
7778
#endif
7879
bool isNull() const;
7980
bool isEmpty() const { return !size(); }

Source/WebKit/NetworkProcess/cache/NetworkCacheDataCocoa.mm

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
#import <sys/stat.h>
3333
#import <wtf/FileHandle.h>
3434
#import <wtf/cocoa/SpanCocoa.h>
35+
#import <wtf/cocoa/VectorCocoa.h>
3536

3637
namespace WebKit {
3738
namespace NetworkCache {
@@ -47,6 +48,11 @@
4748
{
4849
}
4950

51+
Data::Data(Vector<uint8_t>&& data)
52+
: Data(makeDispatchData(WTFMove(data)).get(), Backing::Buffer)
53+
{
54+
}
55+
5056
Data Data::empty()
5157
{
5258
return { OSObjectPtr<dispatch_data_t> { dispatch_data_empty } };

Source/WebKit/NetworkProcess/cache/NetworkCacheStorage.cpp

Lines changed: 17 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -206,32 +206,27 @@ void Storage::ReadOperation::finishReadBlob(BlobStorage::Blob&& blob, MonotonicT
206206
class Storage::WriteOperation {
207207
WTF_MAKE_TZONE_ALLOCATED(Storage::WriteOperation);
208208
public:
209-
WriteOperation(const Record& record, MappedBodyHandler&& mappedBodyHandler, CompletionHandler<void(int)>&& completionHandler)
209+
WriteOperation(const Record& record, MappedBodyHandler&& mappedBodyHandler)
210210
: m_identifier(Storage::WriteOperationIdentifier::generate())
211211
, m_record(record)
212212
, m_mappedBodyHandler(WTFMove(mappedBodyHandler))
213-
, m_completionHandler(WTFMove(completionHandler))
214213
{
215214
ASSERT(isMainRunLoop());
216215
}
217216

218217
~WriteOperation()
219218
{
220219
ASSERT(isMainRunLoop());
221-
if (m_completionHandler)
222-
m_completionHandler(0);
223220
}
224221

225222
Storage::WriteOperationIdentifier identifier() const { return m_identifier; }
226223
const Record& record() const WTF_REQUIRES_CAPABILITY(mainThread) { return m_record; }
227224
void invokeMappedBodyHandler(const Data&);
228-
void invokeCompletionHandler(int);
229225

230226
private:
231227
Storage::WriteOperationIdentifier m_identifier;
232228
const Record m_record;
233229
const MappedBodyHandler m_mappedBodyHandler;
234-
CompletionHandler<void(int)> m_completionHandler;
235230
};
236231

237232
WTF_MAKE_TZONE_ALLOCATED_IMPL(Storage::WriteOperation);
@@ -242,12 +237,6 @@ void Storage::WriteOperation::invokeMappedBodyHandler(const Data& data)
242237
m_mappedBodyHandler(data);
243238
}
244239

245-
void Storage::WriteOperation::invokeCompletionHandler(int result)
246-
{
247-
if (m_completionHandler)
248-
m_completionHandler(result);
249-
}
250-
251240
class TraverseOperation final : public ThreadSafeRefCounted<TraverseOperation, WTF::DestructionThread::MainRunLoop> {
252241
public:
253242
static Ref<TraverseOperation> create(Storage::TraverseHandler&& handler)
@@ -414,9 +403,9 @@ Storage::Storage(const String& baseDirectoryPath, Mode mode, Salt salt, size_t c
414403
, m_capacity(capacity)
415404
, m_readOperationTimeoutTimer(*this, &Storage::cancelAllReadOperations)
416405
, m_writeOperationDispatchTimer(*this, &Storage::dispatchPendingWriteOperations)
417-
, m_ioQueue(ConcurrentWorkQueue::create("com.apple.WebKit.Cache.Storage"_s))
418-
, m_backgroundIOQueue(ConcurrentWorkQueue::create("com.apple.WebKit.Cache.Storage.background"_s, WorkQueue::QOS::Background))
419-
, m_serialBackgroundIOQueue(WorkQueue::create("com.apple.WebKit.Cache.Storage.serialBackground"_s, WorkQueue::QOS::Background))
406+
, m_ioQueue(ConcurrentWorkQueue::create("com.apple.WebKit.Cache.Storage"_s, WorkQueue::QOS::UserInteractive))
407+
, m_backgroundIOQueue(ConcurrentWorkQueue::create("com.apple.WebKit.Cache.Storage.background"_s, WorkQueue::QOS::Utility))
408+
, m_serialBackgroundIOQueue(WorkQueue::create("com.apple.WebKit.Cache.Storage.serialBackground"_s, WorkQueue::QOS::Utility))
420409
, m_blobStorage(makeBlobDirectoryPath(baseDirectoryPath), m_salt)
421410
{
422411
ASSERT(RunLoop::isMain());
@@ -884,21 +873,16 @@ void Storage::dispatchReadOperation(std::unique_ptr<ReadOperation> readOperation
884873
}
885874

886875
ioQueue().dispatch([this, protectedThis = Ref { *this }, identifier, recordPath = crossThreadCopy(WTFMove(recordPath)), blobPath = crossThreadCopy(WTFMove(blobPath))]() mutable {
887-
auto recordIOStartTime = MonotonicTime::now();
888-
auto channel = IOChannel::open(recordPath, IOChannel::Type::Read);
889-
channel->read(0, std::numeric_limits<size_t>::max(), ioQueue(), [this, protectedThis = Ref { *this }, identifier, recordIOStartTime](auto fileData, int error) mutable {
890-
readRecordFromData(identifier, recordIOStartTime, WTFMove(fileData), error);
891-
});
892-
876+
readRecordFromData(identifier, MonotonicTime::now(), FileSystem::readEntireFile(recordPath));
893877
readBlobIfNecessary(identifier, blobPath);
894878
});
895879
}
896880

897-
void Storage::readRecordFromData(Storage::ReadOperationIdentifier identifier, MonotonicTime recordIOStartTime, Data&& data, int error)
881+
void Storage::readRecordFromData(Storage::ReadOperationIdentifier identifier, MonotonicTime recordIOStartTime, std::optional<Vector<uint8_t>>&& data)
898882
{
899883
Record record;
900-
if (!error)
901-
record = readRecord(data);
884+
if (data)
885+
record = readRecord(WTFMove(*data));
902886

903887
auto recordIOEndTime = MonotonicTime::now();
904888
RunLoop::protectedMain()->dispatch([this, protectedThis = Ref { *this }, identifier, recordIOStartTime, recordIOEndTime, record = crossThreadCopy(WTFMove(record))]() mutable {
@@ -1039,15 +1023,14 @@ void Storage::dispatchWriteOperation(std::unique_ptr<WriteOperation> writeOperat
10391023
bool shouldStoreAsBlob = shouldStoreBodyAsBlob(record.body);
10401024
auto blob = shouldStoreAsBlob ? storeBodyAsBlob(identifier, record) : std::nullopt;
10411025
auto recordData = encodeRecord(record, blob);
1026+
auto recordSize = recordData.size();
10421027

1043-
auto channel = IOChannel::open(WTFMove(recordPath), IOChannel::Type::Create);
1044-
size_t recordSize = recordData.size();
1045-
channel->write(0, recordData, WorkQueue::main(), [this, protectedThis = Ref { *this }, identifier, recordSize](int error) {
1046-
// On error the entry still stays in the contents filter until next synchronization.
1047-
m_approximateRecordsSize += recordSize;
1048-
finishWriteOperationActivity(identifier, error);
1028+
if (!FileSystem::overwriteEntireFile(recordPath, recordData.span()))
1029+
RELEASE_LOG_ERROR(NetworkCacheStorage, "Failed to write %zu bytes of network cache record data to %" PUBLIC_LOG_STRING, recordSize, recordPath.utf8().data());
10491030

1050-
LOG(NetworkCacheStorage, "(NetworkProcess) write complete error=%d", error);
1031+
RunLoop::protectedMain()->dispatch([this, protectedThis = Ref { *this }, identifier, recordSize]() mutable {
1032+
m_approximateRecordsSize += recordSize;
1033+
finishWriteOperationActivity(identifier);
10511034
});
10521035
});
10531036
}
@@ -1065,15 +1048,14 @@ bool Storage::removeWriteOperationActivity(WriteOperationIdentifier identifier)
10651048
return m_writeOperationActivities.remove(identifier);
10661049
}
10671050

1068-
void Storage::finishWriteOperationActivity(WriteOperationIdentifier identifier, int error)
1051+
void Storage::finishWriteOperationActivity(WriteOperationIdentifier identifier)
10691052
{
10701053
ASSERT(RunLoop::isMain());
10711054
if (!removeWriteOperationActivity(identifier))
10721055
return;
10731056

10741057
auto writeOperation = m_activeWriteOperations.take(identifier);
10751058
RELEASE_ASSERT(writeOperation);
1076-
writeOperation->invokeCompletionHandler(error);
10771059

10781060
dispatchPendingWriteOperations();
10791061

@@ -1108,15 +1090,15 @@ void Storage::retrieve(const Key& key, unsigned priority, RetrieveCompletionHand
11081090
dispatchPendingReadOperations();
11091091
}
11101092

1111-
void Storage::store(const Record& record, MappedBodyHandler&& mappedBodyHandler, CompletionHandler<void(int)>&& completionHandler)
1093+
void Storage::store(const Record& record, MappedBodyHandler&& mappedBodyHandler)
11121094
{
11131095
ASSERT(RunLoop::isMain());
11141096
ASSERT(!record.key.isNull());
11151097

11161098
if (!m_capacity)
11171099
return;
11181100

1119-
auto writeOperation = makeUnique<WriteOperation>(record, WTFMove(mappedBodyHandler), WTFMove(completionHandler));
1101+
auto writeOperation = makeUnique<WriteOperation>(record, WTFMove(mappedBodyHandler));
11201102
m_pendingWriteOperations.prepend(WTFMove(writeOperation));
11211103

11221104
// Add key to the filter already here as we do lookups from the pending operations too.

Source/WebKit/NetworkProcess/cache/NetworkCacheStorage.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ class Storage : public ThreadSafeRefCountedAndCanMakeThreadSafeWeakPtr<Storage,
103103
void retrieve(const Key&, unsigned priority, RetrieveCompletionHandler&&);
104104

105105
using MappedBodyHandler = Function<void (const Data& mappedBody)>;
106-
void store(const Record&, MappedBodyHandler&&, CompletionHandler<void(int)>&& = { });
106+
void store(const Record&, MappedBodyHandler&&);
107107

108108
void remove(const Key&);
109109
void remove(const Vector<Key>&, CompletionHandler<void()>&&);
@@ -166,13 +166,13 @@ class Storage : public ThreadSafeRefCountedAndCanMakeThreadSafeWeakPtr<Storage,
166166
void dispatchPendingWriteOperations();
167167
void addWriteOperationActivity(WriteOperationIdentifier);
168168
bool removeWriteOperationActivity(WriteOperationIdentifier);
169-
void finishWriteOperationActivity(WriteOperationIdentifier, int error = 0);
169+
void finishWriteOperationActivity(WriteOperationIdentifier);
170170

171171
bool shouldStoreBodyAsBlob(const Data& bodyData);
172172
std::optional<BlobStorage::Blob> storeBodyAsBlob(WriteOperationIdentifier, const Storage::Record&);
173173
Data encodeRecord(const Record&, std::optional<BlobStorage::Blob>);
174174
Record readRecord(const Data&);
175-
void readRecordFromData(Storage::ReadOperationIdentifier, MonotonicTime, Data&&, int error);
175+
void readRecordFromData(Storage::ReadOperationIdentifier, MonotonicTime, std::optional<Vector<uint8_t>>&&);
176176
void readBlobIfNecessary(Storage::ReadOperationIdentifier, const String& blobPath);
177177

178178
void updateFileModificationTime(String&& path);

0 commit comments

Comments
 (0)