RequestCallbackManager.h (4393B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #ifndef mozilla_dom_RequestCallbackManager_h 8 #define mozilla_dom_RequestCallbackManager_h 9 10 #include <limits> 11 12 #include "mozilla/RefPtr.h" 13 #include "nsTArray.h" 14 #include "nsThreadUtils.h" 15 16 namespace mozilla::dom { 17 18 template <typename RequestCallback> 19 struct RequestCallbackEntry { 20 RequestCallbackEntry(RequestCallback& aCallback, uint32_t aHandle) 21 : mCallback(&aCallback), mHandle(aHandle) { 22 LogTaskBase<RequestCallback>::LogDispatch(mCallback); 23 } 24 25 ~RequestCallbackEntry() = default; 26 27 // Comparator operators to allow RemoveElementSorted with an 28 // integer argument on arrays of RequestCallback 29 bool operator==(uint32_t aHandle) const { return mHandle == aHandle; } 30 bool operator<(uint32_t aHandle) const { return mHandle < aHandle; } 31 32 RefPtr<RequestCallback> mCallback; 33 const uint32_t mHandle; 34 bool mCancelled = false; 35 }; 36 37 template <typename RequestCallback> 38 class RequestCallbackManager { 39 public: 40 RequestCallbackManager() = default; 41 ~RequestCallbackManager() = default; 42 43 using CallbackList = nsTArray<RequestCallbackEntry<RequestCallback>>; 44 45 nsresult Schedule(RequestCallback& aCallback, uint32_t* aHandle) { 46 if (mCallbackCounter == std::numeric_limits<uint32_t>::max()) { 47 // Can't increment without overflowing; bail out 48 return NS_ERROR_NOT_AVAILABLE; 49 } 50 uint32_t newHandle = ++mCallbackCounter; 51 52 mCallbacks.AppendElement(RequestCallbackEntry(aCallback, newHandle)); 53 54 *aHandle = newHandle; 55 return NS_OK; 56 } 57 58 bool Cancel(uint32_t aHandle) { 59 // mCallbacks is stored sorted by handle 60 if (mCallbacks.RemoveElementSorted(aHandle)) { 61 return true; 62 } 63 for (auto* callbacks : mFiringCallbacksOnStack) { 64 auto index = callbacks->mList.BinaryIndexOf(aHandle); 65 if (index != CallbackList::NoIndex) { 66 callbacks->mList.ElementAt(index).mCancelled = true; 67 } 68 } 69 return false; 70 } 71 72 bool IsEmpty() const { return mCallbacks.IsEmpty(); } 73 74 // FiringCallbacks takes care of: 75 // * Stealing (and thus "freezing") the current callback list, in preparation 76 // for firing them. 77 // * Registering and unregistering in mFiringCallbacksOnStack, to deal with 78 // cancellation of in-flight callbacks in cases like the first callback on 79 // the list calling cancelAnimationFrame(secondCallbackId) or so. 80 // mList is guaranteed not to reallocate once stolen. Instead if a callback is 81 // cancelled mid-firing, the mCancelled bit is set, see Cancel(). 82 struct MOZ_NON_MEMMOVABLE MOZ_STACK_CLASS FiringCallbacks { 83 explicit FiringCallbacks(RequestCallbackManager& aManager) 84 : mManager(aManager) { 85 mList = std::move(aManager.mCallbacks); 86 aManager.mFiringCallbacksOnStack.AppendElement(this); 87 } 88 89 ~FiringCallbacks() { 90 MOZ_ASSERT(mManager.mFiringCallbacksOnStack.LastElement() == this); 91 mManager.mFiringCallbacksOnStack.RemoveLastElement(); 92 } 93 94 RequestCallbackManager& mManager; 95 CallbackList mList; 96 }; 97 98 void Unlink() { mCallbacks.Clear(); } 99 100 void Traverse(nsCycleCollectionTraversalCallback& aCB) { 101 for (auto& i : mCallbacks) { 102 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME( 103 aCB, "RequestCallbackManager::mCallbacks[i]"); 104 aCB.NoteXPCOMChild(i.mCallback); 105 } 106 } 107 108 private: 109 CallbackList mCallbacks; 110 111 // The current lists of callbacks that are executing. Used to deal with 112 // cancellation within the same frame. Note this is a list to deal reasonably 113 // with event loop spinning. 114 AutoTArray<FiringCallbacks*, 1> mFiringCallbacksOnStack; 115 116 // The current frame request callback handle. 117 uint32_t mCallbackCounter = 0; 118 }; 119 120 template <class RequestCallback> 121 inline void ImplCycleCollectionUnlink( 122 RequestCallbackManager<RequestCallback>& aField) { 123 aField.Unlink(); 124 } 125 126 template <class RequestCallback> 127 inline void ImplCycleCollectionTraverse( 128 nsCycleCollectionTraversalCallback& aCallback, 129 RequestCallbackManager<RequestCallback>& aField, const char* aName, 130 uint32_t aFlags) { 131 aField.Traverse(aCallback); 132 } 133 134 } // namespace mozilla::dom 135 136 #endif