1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim:set ts=2 sw=2 sts=2 et cindent: */ 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 DDMediaLogs_h_ 8 #define DDMediaLogs_h_ 9 10 #include "DDLifetimes.h" 11 #include "DDMediaLog.h" 12 #include "mozilla/MozPromise.h" 13 #include "MultiWriterQueue.h" 14 15 namespace mozilla { 16 17 // Main object managing all processed logs, and yet-unprocessed messages. 18 struct DDMediaLogs { 19 public: 20 // Construct a DDMediaLogs object if possible. 21 struct ConstructionResult { 22 nsresult mRv; 23 DDMediaLogs* mMediaLogs; 24 }; 25 static ConstructionResult New(); 26 27 // If not already shutdown, performs normal end-of-life processing, and shuts 28 // down the processing thread (blocking). 29 ~DDMediaLogs(); 30 31 // Shutdown the processing thread (blocking), and free as much memory as 32 // possible. 33 void Panic(); 34 LogDDMediaLogs35 inline void Log(const char* aSubjectTypeName, const void* aSubjectPointer, 36 DDLogCategory aCategory, const char* aLabel, 37 DDLogValue&& aValue) { 38 if (mMessagesQueue.PushF( 39 [&](DDLogMessage& aMessage, MessagesQueue::Index i) { 40 aMessage.mIndex = i; 41 aMessage.mTimeStamp = DDNow(); 42 aMessage.mObject.Set(aSubjectTypeName, aSubjectPointer); 43 aMessage.mCategory = aCategory; 44 aMessage.mLabel = aLabel; 45 aMessage.mValue = std::move(aValue); 46 })) { 47 // Filled a buffer-full of messages, process it in another thread. 48 DispatchProcessLog(); 49 } 50 } 51 52 // Process the log right now; should only be used on the processing thread, 53 // or after shutdown for end-of-life log retrieval. Work includes: 54 // - Processing incoming buffers, to update object lifetimes and links; 55 // - Resolve pending promises that requested logs; 56 // - Clean-up old logs from memory. 57 void ProcessLog(); 58 59 using LogMessagesPromise = 60 MozPromise<nsCString, nsresult, /* IsExclusive = */ true>; 61 62 // Retrieve all messages associated with an HTMLMediaElement. 63 // This will trigger an async processing run (to ensure most recent messages 64 // get retrieved too), and the returned promise will be resolved with all 65 // found log messages. 66 RefPtr<LogMessagesPromise> RetrieveMessages( 67 const dom::HTMLMediaElement* aMediaElement); 68 69 size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const; 70 71 private: 72 // Constructor, takes the given thread to use for log processing. 73 explicit DDMediaLogs(nsCOMPtr<nsIThread>&& aThread); 74 75 // Shutdown the processing thread, blocks until that thread exits. 76 // If aPanic is true, just free as much memory as possible. 77 // Otherwise, perform a final processing run, output end-logs (if enabled). 78 void Shutdown(bool aPanic); 79 80 // Get the log of yet-unassociated messages. 81 DDMediaLog& LogForUnassociatedMessages(); 82 const DDMediaLog& LogForUnassociatedMessages() const; 83 84 // Get the log for the given HTMLMediaElement. Returns nullptr if there is no 85 // such log yet. 86 DDMediaLog* GetLogFor(const dom::HTMLMediaElement* aMediaElement); 87 88 // Get the log for the given HTMLMediaElement. 89 // A new log is created if that element didn't already have one. 90 DDMediaLog& LogFor(const dom::HTMLMediaElement* aMediaElement); 91 92 // Associate a lifetime, and all its already-linked lifetimes, with an 93 // HTMLMediaElement. 94 // All messages involving the modified lifetime(s) are moved to the 95 // corresponding log. 96 void SetMediaElement(DDLifetime& aLifetime, 97 const dom::HTMLMediaElement* aMediaElement); 98 99 // Find the lifetime corresponding to an object (known type and pointer) that 100 // was known to be alive at aIndex. 101 // If there is no such lifetime yet, create it with aTimeStamp as implicit 102 // construction timestamp. 103 // If the object is of type HTMLMediaElement, run SetMediaElement() on it. 104 DDLifetime& FindOrCreateLifetime(const DDLogObject& aObject, 105 DDMessageIndex aIndex, 106 const DDTimeStamp& aTimeStamp); 107 108 // Link two lifetimes together (at a given time corresponding to aIndex). 109 // If only one is associated with an HTMLMediaElement, run SetMediaElement on 110 // the other one. 111 void LinkLifetimes(DDLifetime& aParentLifetime, const char* aLinkName, 112 DDLifetime& aChildLifetime, DDMessageIndex aIndex); 113 114 // Unlink all lifetimes linked to aLifetime; only used to know when links 115 // expire, so that they won't be used after this time. 116 void UnlinkLifetime(DDLifetime& aLifetime, DDMessageIndex aIndex); 117 118 // Unlink two lifetimes; only used to know when a link expires, so that it 119 // won't be used after this time. 120 void UnlinkLifetimes(DDLifetime& aParentLifetime, DDLifetime& aChildLifetime, 121 DDMessageIndex aIndex); 122 123 // Remove all links involving aLifetime from the database. 124 void DestroyLifetimeLinks(const DDLifetime& aLifetime); 125 126 // Process all incoming log messages. 127 // This will create the appropriate DDLifetime and links objects, and then 128 // move processed messages to logs associated with different 129 // HTMLMediaElements. 130 void ProcessBuffer(); 131 132 // Pending promises (added by RetrieveMessages) are resolved with all new 133 // log messages corresponding to requested HTMLMediaElements -- These 134 // messages are removed from our logs. 135 void FulfillPromises(); 136 137 // Remove processed messages that have a low chance of being requested, 138 // based on the assumption that users/scripts will regularly call 139 // RetrieveMessages for HTMLMediaElements they are interested in. 140 void CleanUpLogs(); 141 142 // Request log-processing on the processing thread. Thread-safe. 143 nsresult DispatchProcessLog(); 144 145 // Request log-processing on the processing thread. 146 nsresult DispatchProcessLog(const MutexAutoLock& aProofOfLock); 147 148 using MessagesQueue = 149 MultiWriterQueue<DDLogMessage, MultiWriterQueueDefaultBufferSize, 150 MultiWriterQueueReaderLocking_None>; 151 MessagesQueue mMessagesQueue; 152 153 DDLifetimes mLifetimes; 154 155 // mMediaLogs[0] contains unsorted message (with mMediaElement=nullptr). 156 // mMediaLogs[1+] contains sorted messages for each media element. 157 nsTArray<DDMediaLog> mMediaLogs; 158 159 struct DDObjectLink { 160 const DDLogObject mParent; 161 const DDLogObject mChild; 162 const char* const mLinkName; 163 const DDMessageIndex mLinkingIndex; 164 Maybe<DDMessageIndex> mUnlinkingIndex; 165 DDObjectLinkDDMediaLogs::DDObjectLink166 DDObjectLink(DDLogObject aParent, DDLogObject aChild, const char* aLinkName, 167 DDMessageIndex aLinkingIndex) 168 : mParent(aParent), 169 mChild(aChild), 170 mLinkName(aLinkName), 171 mLinkingIndex(aLinkingIndex), 172 mUnlinkingIndex(Nothing{}) {} 173 }; 174 // Links between live objects, updated while messages are processed. 175 nsTArray<DDObjectLink> mObjectLinks; 176 177 // Protects members below. 178 Mutex mMutex; 179 180 // Processing thread. 181 nsCOMPtr<nsIThread> mThread; 182 183 struct PendingPromise { 184 MozPromiseHolder<LogMessagesPromise> mPromiseHolder; 185 const dom::HTMLMediaElement* mMediaElement; 186 }; 187 // Most cases should have 1 media panel requesting 1 promise at a time. 188 AutoTArray<PendingPromise, 2> mPendingPromises; 189 }; 190 191 } // namespace mozilla 192 193 #endif // DDMediaLogs_h_ 194