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