The JUCE cross-platform C++ framework, with DISTRHO/KXStudio specific changes
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

219 lines
8.7KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2017 - ROLI Ltd.
  5. JUCE is an open source library subject to commercial or open-source
  6. licensing.
  7. By using JUCE, you agree to the terms of both the JUCE 5 End-User License
  8. Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
  9. 27th April 2017).
  10. End User License Agreement: www.juce.com/juce-5-licence
  11. Privacy Policy: www.juce.com/juce-5-privacy-policy
  12. Or: You may also use this code under the terms of the GPL v3 (see
  13. www.gnu.org/licenses).
  14. JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
  15. EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
  16. DISCLAIMED.
  17. ==============================================================================
  18. */
  19. namespace juce
  20. {
  21. //==============================================================================
  22. /**
  23. A base class for dispatching analytics events on a dedicated thread.
  24. This class is particularly useful for sending analytics events to a web
  25. server without blocking the message thread. It can also save (and restore)
  26. events that were not dispatched so no information is lost when an internet
  27. connection is absent or something else prevents successful logging.
  28. Once startAnalyticsThread is called the logBatchedEvents method is
  29. periodically invoked on an analytics thread, with the period determined by
  30. calls to setBatchPeriod. Here events are grouped together into batches, with
  31. the maximum batch size set by the implementation of getMaximumBatchSize.
  32. It's important to call stopAnalyticsThread in the destructor of your
  33. subclass (or before then) to give the analytics thread time to shut down.
  34. Calling stopAnalyticsThread will, in turn, call stopLoggingEvents, which
  35. you should use to terminate the currently running logBatchedEvents call.
  36. @see Analytics, AnalyticsDestination, AnalyticsDestination::AnalyticsEvent
  37. @tags{Analytics}
  38. */
  39. class JUCE_API ThreadedAnalyticsDestination : public AnalyticsDestination
  40. {
  41. public:
  42. //==============================================================================
  43. /**
  44. Creates a ThreadedAnalyticsDestination.
  45. @param threadName used to identify the analytics
  46. thread in debug builds
  47. */
  48. ThreadedAnalyticsDestination (const String& threadName = "Analytics thread");
  49. /** Destructor. */
  50. ~ThreadedAnalyticsDestination() override;
  51. //==============================================================================
  52. /**
  53. Override this method to provide the maximum batch size you can handle in
  54. your subclass.
  55. Calls to logBatchedEvents will contain no more than this number of events.
  56. */
  57. virtual int getMaximumBatchSize() = 0;
  58. /**
  59. This method will be called periodically on the analytics thread.
  60. If this method returns false then the subsequent call of this function will
  61. contain the same events as previous call, plus any new events that have been
  62. generated in the period between calls. The order of events will not be
  63. changed. This allows you to retry logging events until they are logged
  64. successfully.
  65. @param events a list of events to be logged
  66. @returns if the events were successfully logged
  67. */
  68. virtual bool logBatchedEvents (const Array<AnalyticsEvent>& events) = 0;
  69. /**
  70. You must always call stopAnalyticsThread in the destructor of your subclass
  71. (or before then) to give the analytics thread time to shut down.
  72. Calling stopAnalyticsThread triggers a call to this method. At this point
  73. you are guaranteed that logBatchedEvents has been called for the last time
  74. and you should make sure that the current call to logBatchedEvents finishes
  75. as quickly as possible. This method and a subsequent call to
  76. saveUnloggedEvents must both complete before the timeout supplied to
  77. stopAnalyticsThread.
  78. In a normal use case stopLoggingEvents will be called on the message thread
  79. from the destructor of your ThreadedAnalyticsDestination subclass, and must
  80. stop the logBatchedEvents method which is running on the analytics thread.
  81. @see stopAnalyticsThread
  82. */
  83. virtual void stopLoggingEvents() = 0;
  84. //==============================================================================
  85. /**
  86. Call this to set the period between logBatchedEvents invocations.
  87. This method is thread safe and can be used to implements things like
  88. exponential backoff in logBatchedEvents calls.
  89. @param newSubmissionPeriodMilliseconds the new submission period to
  90. use in milliseconds
  91. */
  92. void setBatchPeriod (int newSubmissionPeriodMilliseconds);
  93. /**
  94. Adds an event to the queue, which will ultimately be submitted to
  95. logBatchedEvents.
  96. This method is thread safe.
  97. @param event the analytics event to add to the queue
  98. */
  99. void logEvent (const AnalyticsEvent& event) override final;
  100. protected:
  101. //==============================================================================
  102. /**
  103. Starts the analytics thread, with an initial event batching period.
  104. @param initialBatchPeriodMilliseconds the initial event batching period
  105. in milliseconds
  106. */
  107. void startAnalyticsThread (int initialBatchPeriodMilliseconds);
  108. //==============================================================================
  109. /**
  110. Triggers the shutdown of the analytics thread.
  111. You must call this method in the destructor of your subclass (or before
  112. then) to give the analytics thread time to shut down.
  113. This method invokes stopLoggingEvents and you should ensure that both the
  114. analytics thread and a call to saveUnloggedEvents are able to finish before
  115. the supplied timeout. This timeout is important because on platforms like
  116. iOS an app is killed if it takes too long to shut down.
  117. @param timeoutMilliseconds the number of milliseconds before
  118. the analytics thread is forcibly
  119. terminated
  120. */
  121. void stopAnalyticsThread (int timeoutMilliseconds);
  122. private:
  123. //==============================================================================
  124. /**
  125. This method will be called when the analytics thread is shut down,
  126. giving you the chance to save any analytics events that could not be
  127. logged. Once saved these events can be put back into the queue of events
  128. when the ThreadedAnalyticsDestination is recreated via
  129. restoreUnloggedEvents.
  130. This method should return as quickly as possible, as both
  131. stopLoggingEvents and this method need to complete inside the timeout
  132. set in stopAnalyticsThread.
  133. @param eventsToSave the events that could not be logged
  134. @see stopAnalyticsThread, stopLoggingEvents, restoreUnloggedEvents
  135. */
  136. virtual void saveUnloggedEvents (const std::deque<AnalyticsEvent>& eventsToSave) = 0;
  137. /**
  138. The counterpart to saveUnloggedEvents.
  139. Events added to the event queue provided by this method will be the
  140. first events supplied to logBatchedEvents calls. Use this method to
  141. restore any unlogged events previously stored in a call to
  142. saveUnloggedEvents.
  143. This method is called on the analytics thread.
  144. @param restoredEventQueue place restored events into this queue
  145. @see saveUnloggedEvents
  146. */
  147. virtual void restoreUnloggedEvents (std::deque<AnalyticsEvent>& restoredEventQueue) = 0;
  148. struct EventDispatcher : public Thread
  149. {
  150. EventDispatcher (const String& threadName, ThreadedAnalyticsDestination&);
  151. void run() override;
  152. void addToQueue (const AnalyticsEvent&);
  153. ThreadedAnalyticsDestination& parent;
  154. std::deque<AnalyticsEvent> eventQueue;
  155. CriticalSection queueAccess;
  156. Atomic<int> batchPeriodMilliseconds { 1000 };
  157. Array<AnalyticsEvent> eventsToSend;
  158. };
  159. const String destinationName;
  160. EventDispatcher dispatcher;
  161. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ThreadedAnalyticsDestination)
  162. };
  163. } // namespace juce