1/*
2 * Copyright (C) 2010 Google Inc. All rights reserved.
3 * Copyright (C) 2012, 2014 Apple Inc. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
7 * met:
8 *
9 * * Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * * Redistributions in binary form must reproduce the above
12 * copyright notice, this list of conditions and the following disclaimer
13 * in the documentation and/or other materials provided with the
14 * distribution.
15 * * Neither the name of Google Inc. nor the names of its
16 * contributors may be used to endorse or promote products derived from
17 * this software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 */
31
32#include "config.h"
33#include "AsyncFileStream.h"
34
35#include "FileStream.h"
36#include "FileStreamClient.h"
37#include <mutex>
38#include <wtf/AutodrainedPool.h>
39#include <wtf/Function.h>
40#include <wtf/MainThread.h>
41#include <wtf/MessageQueue.h>
42#include <wtf/NeverDestroyed.h>
43#include <wtf/Threading.h>
44#include <wtf/URL.h>
45
46namespace WebCore {
47
48struct AsyncFileStream::Internals {
49 WTF_MAKE_STRUCT_FAST_ALLOCATED;
50
51 explicit Internals(FileStreamClient&);
52
53 FileStream stream;
54 FileStreamClient& client;
55#if !COMPILER(MSVC)
56 std::atomic_bool destroyed { false };
57#else
58 std::atomic_bool destroyed;
59#endif
60};
61
62inline AsyncFileStream::Internals::Internals(FileStreamClient& client)
63 : client(client)
64{
65#if COMPILER(MSVC)
66 // Work around a bug that prevents the default value above from compiling.
67 atomic_init(&destroyed, false);
68#endif
69}
70
71static void callOnFileThread(Function<void ()>&& function)
72{
73 ASSERT(isMainThread());
74 ASSERT(function);
75
76 static NeverDestroyed<MessageQueue<Function<void ()>>> queue;
77
78 static std::once_flag createFileThreadOnce;
79 std::call_once(createFileThreadOnce, [] {
80 Thread::create("WebCore: AsyncFileStream", [] {
81 for (;;) {
82 AutodrainedPool pool;
83
84 auto function = queue.get().waitForMessage();
85
86 // This can never be null because we never kill the MessageQueue.
87 ASSERT(function);
88
89 // This can bever be null because we never queue a function that is null.
90 ASSERT(*function);
91
92 (*function)();
93 }
94 });
95 });
96
97 queue.get().append(std::make_unique<Function<void ()>>(WTFMove(function)));
98}
99
100AsyncFileStream::AsyncFileStream(FileStreamClient& client)
101 : m_internals(std::make_unique<Internals>(client))
102{
103 ASSERT(isMainThread());
104}
105
106AsyncFileStream::~AsyncFileStream()
107{
108 ASSERT(isMainThread());
109
110 // Set flag to prevent client callbacks and also prevent queued operations from starting.
111 m_internals->destroyed = true;
112
113 // Call through file thread and back to main thread to make sure deletion happens
114 // after all file thread functions and all main thread functions called from them.
115 callOnFileThread([internals = WTFMove(m_internals)]() mutable {
116 callOnMainThread([internals = WTFMove(internals)] {
117 });
118 });
119}
120
121void AsyncFileStream::perform(WTF::Function<WTF::Function<void(FileStreamClient&)>(FileStream&)>&& operation)
122{
123 auto& internals = *m_internals;
124 callOnFileThread([&internals, operation = WTFMove(operation)] {
125 // Don't do the operation if stop was already called on the main thread. Note that there is
126 // a race here, but since skipping the operation is an optimization it's OK that we can't
127 // guarantee exactly which operations are skipped. Note that this is also the only reason
128 // we use an atomic_bool rather than just a bool for destroyed.
129 if (internals.destroyed)
130 return;
131 callOnMainThread([&internals, mainThreadWork = operation(internals.stream)] {
132 if (internals.destroyed)
133 return;
134 mainThreadWork(internals.client);
135 });
136 });
137}
138
139void AsyncFileStream::getSize(const String& path, Optional<WallTime> expectedModificationTime)
140{
141 // FIXME: Explicit return type here and in all the other cases like this below is a workaround for a deficiency
142 // in the Windows compiler at the time of this writing. Could remove it if that is resolved.
143 perform([path = path.isolatedCopy(), expectedModificationTime](FileStream& stream) -> WTF::Function<void(FileStreamClient&)> {
144 long long size = stream.getSize(path, expectedModificationTime);
145 return [size](FileStreamClient& client) {
146 client.didGetSize(size);
147 };
148 });
149}
150
151void AsyncFileStream::openForRead(const String& path, long long offset, long long length)
152{
153 // FIXME: Explicit return type here is a workaround for a deficiency in the Windows compiler at the time of this writing.
154 perform([path = path.isolatedCopy(), offset, length](FileStream& stream) -> WTF::Function<void(FileStreamClient&)> {
155 bool success = stream.openForRead(path, offset, length);
156 return [success](FileStreamClient& client) {
157 client.didOpen(success);
158 };
159 });
160}
161
162void AsyncFileStream::close()
163{
164 auto& internals = *m_internals;
165 callOnFileThread([&internals] {
166 internals.stream.close();
167 });
168}
169
170void AsyncFileStream::read(char* buffer, int length)
171{
172 perform([buffer, length](FileStream& stream) -> WTF::Function<void(FileStreamClient&)> {
173 int bytesRead = stream.read(buffer, length);
174 return [bytesRead](FileStreamClient& client) {
175 client.didRead(bytesRead);
176 };
177 });
178}
179
180} // namespace WebCore
181