1/* 2 * Copyright (C) 2010 Google Inc. All rights reserved. 3 * Copyright (C) 2012 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 34#if ENABLE(BLOB) 35 36#include "AsyncFileStream.h" 37 38#include "Blob.h" 39#include "FileStream.h" 40#include "FileStreamClient.h" 41#include "FileThread.h" 42#include "FileThreadTask.h" 43#include "MainThreadTask.h" 44#include <wtf/MainThread.h> 45#include <wtf/text/WTFString.h> 46 47namespace WebCore { 48 49static PassRefPtr<FileThread> createFileThread() 50{ 51 RefPtr<FileThread> thread = FileThread::create(); 52 if (!thread->start()) 53 return 0; 54 return thread.release(); 55} 56 57static FileThread* fileThread() 58{ 59 ASSERT(isMainThread()); 60 static FileThread* thread = createFileThread().leakRef(); 61 return thread; 62} 63 64inline AsyncFileStream::AsyncFileStream(FileStreamClient* client) 65 : m_stream(FileStream::create()) 66 , m_client(client) 67{ 68 ASSERT(isMainThread()); 69} 70 71PassRefPtr<AsyncFileStream> AsyncFileStream::create(FileStreamClient* client) 72{ 73 RefPtr<AsyncFileStream> proxy = adoptRef(new AsyncFileStream(client)); 74 75 // Hold a reference so that the instance will not get deleted while there are tasks on the file thread. 76 // This is balanced by the deref in derefProxyOnContext below. 77 proxy->ref(); 78 79 fileThread()->postTask(createFileThreadTask(proxy.get(), &AsyncFileStream::startOnFileThread)); 80 81 return proxy.release(); 82} 83 84AsyncFileStream::~AsyncFileStream() 85{ 86} 87 88static void didStart(AsyncFileStream* proxy) 89{ 90 if (proxy->client()) 91 proxy->client()->didStart(); 92} 93 94void AsyncFileStream::startOnFileThread() 95{ 96 // FIXME: It is not correct to check m_client from a secondary thread - stop() could be racing with this check. 97 if (!m_client) 98 return; 99 m_stream->start(); 100 callOnMainThread(didStart, AllowCrossThreadAccess(this)); 101} 102 103void AsyncFileStream::stop() 104{ 105 // Clear the client so that we won't be invoking callbacks on the client. 106 setClient(0); 107 108 fileThread()->unscheduleTasks(m_stream.get()); 109 fileThread()->postTask(createFileThreadTask(this, &AsyncFileStream::stopOnFileThread)); 110} 111 112static void derefProxyOnMainThread(AsyncFileStream* proxy) 113{ 114 ASSERT(proxy->hasOneRef()); 115 proxy->deref(); 116} 117 118void AsyncFileStream::stopOnFileThread() 119{ 120 m_stream->stop(); 121 callOnMainThread(derefProxyOnMainThread, AllowCrossThreadAccess(this)); 122} 123 124static void didGetSize(AsyncFileStream* proxy, long long size) 125{ 126 if (proxy->client()) 127 proxy->client()->didGetSize(size); 128} 129 130void AsyncFileStream::getSize(const String& path, double expectedModificationTime) 131{ 132 fileThread()->postTask(createFileThreadTask(this, &AsyncFileStream::getSizeOnFileThread, path, expectedModificationTime)); 133} 134 135void AsyncFileStream::getSizeOnFileThread(const String& path, double expectedModificationTime) 136{ 137 long long size = m_stream->getSize(path, expectedModificationTime); 138 callOnMainThread(didGetSize, AllowCrossThreadAccess(this), size); 139} 140 141static void didOpen(AsyncFileStream* proxy, bool success) 142{ 143 if (proxy->client()) 144 proxy->client()->didOpen(success); 145} 146 147void AsyncFileStream::openForRead(const String& path, long long offset, long long length) 148{ 149 fileThread()->postTask(createFileThreadTask(this, &AsyncFileStream::openForReadOnFileThread, path, offset, length)); 150} 151 152void AsyncFileStream::openForReadOnFileThread(const String& path, long long offset, long long length) 153{ 154 bool success = m_stream->openForRead(path, offset, length); 155 callOnMainThread(didOpen, AllowCrossThreadAccess(this), success); 156} 157 158void AsyncFileStream::openForWrite(const String& path) 159{ 160 fileThread()->postTask( 161 createFileThreadTask(this, 162 &AsyncFileStream::openForWriteOnFileThread, path)); 163} 164 165void AsyncFileStream::openForWriteOnFileThread(const String& path) 166{ 167 bool success = m_stream->openForWrite(path); 168 callOnMainThread(didOpen, AllowCrossThreadAccess(this), success); 169} 170 171void AsyncFileStream::close() 172{ 173 fileThread()->postTask(createFileThreadTask(this, &AsyncFileStream::closeOnFileThread)); 174} 175 176void AsyncFileStream::closeOnFileThread() 177{ 178 m_stream->close(); 179} 180 181static void didRead(AsyncFileStream* proxy, int bytesRead) 182{ 183 if (proxy->client()) 184 proxy->client()->didRead(bytesRead); 185} 186 187void AsyncFileStream::read(char* buffer, int length) 188{ 189 fileThread()->postTask( 190 createFileThreadTask(this, &AsyncFileStream::readOnFileThread, 191 AllowCrossThreadAccess(buffer), length)); 192} 193 194void AsyncFileStream::readOnFileThread(char* buffer, int length) 195{ 196 int bytesRead = m_stream->read(buffer, length); 197 callOnMainThread(didRead, AllowCrossThreadAccess(this), bytesRead); 198} 199 200static void didWrite(AsyncFileStream* proxy, int bytesWritten) 201{ 202 if (proxy->client()) 203 proxy->client()->didWrite(bytesWritten); 204} 205 206void AsyncFileStream::write(const KURL& blobURL, long long position, int length) 207{ 208 fileThread()->postTask(createFileThreadTask(this, &AsyncFileStream::writeOnFileThread, blobURL, position, length)); 209} 210 211void AsyncFileStream::writeOnFileThread(const KURL& blobURL, long long position, int length) 212{ 213 int bytesWritten = m_stream->write(blobURL, position, length); 214 callOnMainThread(didWrite, AllowCrossThreadAccess(this), bytesWritten); 215} 216 217static void didTruncate(AsyncFileStream* proxy, bool success) 218{ 219 if (proxy->client()) 220 proxy->client()->didTruncate(success); 221} 222 223void AsyncFileStream::truncate(long long position) 224{ 225 fileThread()->postTask(createFileThreadTask(this, &AsyncFileStream::truncateOnFileThread, position)); 226} 227 228void AsyncFileStream::truncateOnFileThread(long long position) 229{ 230 bool success = m_stream->truncate(position); 231 callOnMainThread(didTruncate, AllowCrossThreadAccess(this), success); 232} 233 234} // namespace WebCore 235 236#endif // ENABLE(BLOB) 237