diff options
Diffstat (limited to 'Source/WebCore/Modules/indexeddb/client/TransactionOperation.h')
-rw-r--r-- | Source/WebCore/Modules/indexeddb/client/TransactionOperation.h | 270 |
1 files changed, 270 insertions, 0 deletions
diff --git a/Source/WebCore/Modules/indexeddb/client/TransactionOperation.h b/Source/WebCore/Modules/indexeddb/client/TransactionOperation.h new file mode 100644 index 000000000..cd95befd5 --- /dev/null +++ b/Source/WebCore/Modules/indexeddb/client/TransactionOperation.h @@ -0,0 +1,270 @@ +/* + * Copyright (C) 2015 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#if ENABLE(INDEXED_DATABASE) + +#include "IDBRequest.h" +#include "IDBRequestData.h" +#include "IDBResourceIdentifier.h" +#include "IDBResultData.h" +#include "IDBTransaction.h" +#include <wtf/MainThread.h> +#include <wtf/Threading.h> + +namespace WebCore { + +class IDBResultData; + +namespace IndexedDB { +enum class IndexRecordType; +} + +namespace IDBClient { + +class TransactionOperation : public ThreadSafeRefCounted<TransactionOperation> { + friend IDBRequestData::IDBRequestData(TransactionOperation&); +public: + virtual ~TransactionOperation() + { + ASSERT(m_originThreadID == currentThread()); + } + + void perform() + { + ASSERT(m_originThreadID == currentThread()); + ASSERT(m_performFunction); + m_performFunction(); + m_performFunction = { }; + } + + void transitionToCompleteOnThisThread(const IDBResultData& data) + { + ASSERT(m_originThreadID == currentThread()); + m_transaction->operationCompletedOnServer(data, *this); + } + + void transitionToComplete(const IDBResultData& data, RefPtr<TransactionOperation>&& lastRef) + { + ASSERT(isMainThread()); + + if (m_originThreadID == currentThread()) + transitionToCompleteOnThisThread(data); + else { + m_transaction->performCallbackOnOriginThread(*this, &TransactionOperation::transitionToCompleteOnThisThread, data); + m_transaction->callFunctionOnOriginThread([lastRef = WTFMove(lastRef)]() { + }); + } + } + + void doComplete(const IDBResultData& data) + { + ASSERT(m_originThreadID == currentThread()); + + // Due to race conditions between the server sending an "operation complete" message and the client + // forcefully aborting an operation, it's unavoidable that this method might be called twice. + // It's okay to handle that gracefully with an early return. + if (!m_completeFunction) + return; + + m_completeFunction(data); + m_transaction->operationCompletedOnClient(*this); + + // m_completeFunction might be holding the last ref to this TransactionOperation, + // so we need to do this trick to null it out without first destroying it. + std::function<void (const IDBResultData&)> oldCompleteFunction; + std::swap(m_completeFunction, oldCompleteFunction); + } + + const IDBResourceIdentifier& identifier() const { return m_identifier; } + + ThreadIdentifier originThreadID() const { return m_originThreadID; } + + IDBRequest* idbRequest() { return m_idbRequest.get(); } + + bool nextRequestCanGoToServer() const { return m_nextRequestCanGoToServer && m_idbRequest; } + void setNextRequestCanGoToServer(bool nextRequestCanGoToServer) { m_nextRequestCanGoToServer = nextRequestCanGoToServer; } + +protected: + TransactionOperation(IDBTransaction& transaction) + : m_transaction(transaction) + , m_identifier(transaction.connectionProxy()) + { + } + + TransactionOperation(IDBTransaction&, IDBRequest&); + + Ref<IDBTransaction> m_transaction; + IDBResourceIdentifier m_identifier; + uint64_t m_objectStoreIdentifier { 0 }; + uint64_t m_indexIdentifier { 0 }; + std::unique_ptr<IDBResourceIdentifier> m_cursorIdentifier; + IndexedDB::IndexRecordType m_indexRecordType; + std::function<void ()> m_performFunction; + std::function<void (const IDBResultData&)> m_completeFunction; + +private: + IDBResourceIdentifier transactionIdentifier() const { return m_transaction->info().identifier(); } + uint64_t objectStoreIdentifier() const { return m_objectStoreIdentifier; } + uint64_t indexIdentifier() const { return m_indexIdentifier; } + IDBResourceIdentifier* cursorIdentifier() const { return m_cursorIdentifier.get(); } + IDBTransaction& transaction() { return m_transaction.get(); } + IndexedDB::IndexRecordType indexRecordType() const { return m_indexRecordType; } + + ThreadIdentifier m_originThreadID { currentThread() }; + RefPtr<IDBRequest> m_idbRequest; + bool m_nextRequestCanGoToServer { true }; +}; + +template <typename... Arguments> +class TransactionOperationImpl final : public TransactionOperation { +public: + TransactionOperationImpl(IDBTransaction& transaction, void (IDBTransaction::*completeMethod)(const IDBResultData&), void (IDBTransaction::*performMethod)(TransactionOperation&, Arguments...), Arguments&&... arguments) + : TransactionOperation(transaction) + { + RefPtr<TransactionOperation> protectedThis(this); + + ASSERT(performMethod); + m_performFunction = [protectedThis, this, performMethod, arguments...] { + (&m_transaction.get()->*performMethod)(*this, arguments...); + }; + + if (completeMethod) { + m_completeFunction = [protectedThis, this, completeMethod](const IDBResultData& resultData) { + if (completeMethod) + (&m_transaction.get()->*completeMethod)(resultData); + }; + } + } + + TransactionOperationImpl(IDBTransaction& transaction, IDBRequest& request, void (IDBTransaction::*completeMethod)(IDBRequest&, const IDBResultData&), void (IDBTransaction::*performMethod)(TransactionOperation&, Arguments...), Arguments&&... arguments) + : TransactionOperation(transaction, request) + { + RefPtr<TransactionOperation> protectedThis(this); + + ASSERT(performMethod); + m_performFunction = [protectedThis, this, performMethod, arguments...] { + (&m_transaction.get()->*performMethod)(*this, arguments...); + }; + + if (completeMethod) { + RefPtr<IDBRequest> refRequest(&request); + m_completeFunction = [protectedThis, this, refRequest, completeMethod](const IDBResultData& resultData) { + if (completeMethod) + (&m_transaction.get()->*completeMethod)(*refRequest, resultData); + }; + } + } +}; + +inline RefPtr<TransactionOperation> createTransactionOperation( + IDBTransaction& transaction, + void (IDBTransaction::*complete)(const IDBResultData&), + void (IDBTransaction::*perform)(TransactionOperation&)) +{ + auto operation = new TransactionOperationImpl<>(transaction, complete, perform); + return adoptRef(operation); +} + +template<typename MP1, typename P1> +RefPtr<TransactionOperation> createTransactionOperation( + IDBTransaction& transaction, + void (IDBTransaction::*complete)(const IDBResultData&), + void (IDBTransaction::*perform)(TransactionOperation&, MP1), + const P1& parameter1) +{ + auto operation = new TransactionOperationImpl<MP1>(transaction, complete, perform, parameter1); + return adoptRef(operation); +} + +template<typename MP1, typename P1, typename MP2, typename P2> +RefPtr<TransactionOperation> createTransactionOperation( + IDBTransaction& transaction, + void (IDBTransaction::*complete)(const IDBResultData&), + void (IDBTransaction::*perform)(TransactionOperation&, MP1, MP2), + const P1& parameter1, + const P2& parameter2) +{ + auto operation = new TransactionOperationImpl<MP1, MP2>(transaction, complete, perform, parameter1, parameter2); + return adoptRef(operation); +} + +template<typename MP1, typename P1, typename MP2, typename P2, typename MP3, typename P3> +RefPtr<TransactionOperation> createTransactionOperation( + IDBTransaction& transaction, + void (IDBTransaction::*complete)(const IDBResultData&), + void (IDBTransaction::*perform)(TransactionOperation&, MP1, MP2, MP3), + const P1& parameter1, + const P2& parameter2, + const P3& parameter3) +{ + auto operation = new TransactionOperationImpl<MP1, MP2, MP3>(transaction, complete, perform, parameter1, parameter2, parameter3); + return adoptRef(operation); +} + +template<typename MP1, typename P1> +RefPtr<TransactionOperation> createTransactionOperation( + IDBTransaction& transaction, + IDBRequest& request, + void (IDBTransaction::*complete)(IDBRequest&, const IDBResultData&), + void (IDBTransaction::*perform)(TransactionOperation&, MP1), + const P1& parameter1) +{ + auto operation = new TransactionOperationImpl<MP1>(transaction, request, complete, perform, parameter1); + return adoptRef(operation); +} + +template<typename MP1, typename P1, typename MP2, typename P2> +RefPtr<TransactionOperation> createTransactionOperation( + IDBTransaction& transaction, + IDBRequest& request, + void (IDBTransaction::*complete)(IDBRequest&, const IDBResultData&), + void (IDBTransaction::*perform)(TransactionOperation&, MP1, MP2), + const P1& parameter1, + const P2& parameter2) +{ + auto operation = new TransactionOperationImpl<MP1, MP2>(transaction, request, complete, perform, parameter1, parameter2); + return adoptRef(operation); +} + +template<typename MP1, typename MP2, typename MP3, typename P1, typename P2, typename P3> +RefPtr<TransactionOperation> createTransactionOperation( + IDBTransaction& transaction, + IDBRequest& request, + void (IDBTransaction::*complete)(IDBRequest&, const IDBResultData&), + void (IDBTransaction::*perform)(TransactionOperation&, MP1, MP2, MP3), + const P1& parameter1, + const P2& parameter2, + const P3& parameter3) +{ + auto operation = new TransactionOperationImpl<MP1, MP2, MP3>(transaction, request, complete, perform, parameter1, parameter2, parameter3); + return adoptRef(operation); +} + +} // namespace IDBClient +} // namespace WebCore + +#endif // ENABLE(INDEXED_DATABASE) |