netwerk/ipc/ChannelEventQueue.h

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/netwerk/ipc/ChannelEventQueue.h	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,181 @@
     1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
     1.5 + * vim: set sw=2 ts=8 et tw=80 :
     1.6 + */
     1.7 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.8 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.9 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
    1.10 +
    1.11 +#ifndef mozilla_net_ChannelEventQueue_h
    1.12 +#define mozilla_net_ChannelEventQueue_h
    1.13 +
    1.14 +#include <nsTArray.h>
    1.15 +#include <nsAutoPtr.h>
    1.16 +
    1.17 +class nsISupports;
    1.18 +class nsIEventTarget;
    1.19 +class nsIThread;
    1.20 +
    1.21 +namespace mozilla {
    1.22 +namespace net {
    1.23 +
    1.24 +class ChannelEvent
    1.25 +{
    1.26 + public:
    1.27 +  ChannelEvent() { MOZ_COUNT_CTOR(ChannelEvent); }
    1.28 +  virtual ~ChannelEvent() { MOZ_COUNT_DTOR(ChannelEvent); }
    1.29 +  virtual void Run() = 0;
    1.30 +};
    1.31 +
    1.32 +// Workaround for Necko re-entrancy dangers. We buffer IPDL messages in a
    1.33 +// queue if still dispatching previous one(s) to listeners/observers.
    1.34 +// Otherwise synchronous XMLHttpRequests and/or other code that spins the
    1.35 +// event loop (ex: IPDL rpc) could cause listener->OnDataAvailable (for
    1.36 +// instance) to be dispatched and called before mListener->OnStartRequest has
    1.37 +// completed.
    1.38 +
    1.39 +class AutoEventEnqueuerBase;
    1.40 +
    1.41 +class ChannelEventQueue MOZ_FINAL
    1.42 +{
    1.43 +  NS_INLINE_DECL_REFCOUNTING(ChannelEventQueue)
    1.44 +
    1.45 + public:
    1.46 +  ChannelEventQueue(nsISupports *owner)
    1.47 +    : mSuspendCount(0)
    1.48 +    , mSuspended(false)
    1.49 +    , mForced(false)
    1.50 +    , mFlushing(false)
    1.51 +    , mOwner(owner) {}
    1.52 +
    1.53 +  // Checks to determine if an IPDL-generated channel event can be processed
    1.54 +  // immediately, or needs to be queued using Enqueue().
    1.55 +  inline bool ShouldEnqueue();
    1.56 +
    1.57 +  // Puts IPDL-generated channel event into queue, to be run later
    1.58 +  // automatically when EndForcedQueueing and/or Resume is called.
    1.59 +  inline void Enqueue(ChannelEvent* callback);
    1.60 +
    1.61 +  // After StartForcedQueueing is called, ShouldEnqueue() will return true and
    1.62 +  // no events will be run/flushed until EndForcedQueueing is called.
    1.63 +  // - Note: queueing may still be required after EndForcedQueueing() (if the
    1.64 +  //   queue is suspended, etc):  always call ShouldEnqueue() to determine
    1.65 +  //   whether queueing is needed.
    1.66 +  inline void StartForcedQueueing();
    1.67 +  inline void EndForcedQueueing();
    1.68 +
    1.69 +  // Suspend/resume event queue.  ShouldEnqueue() will return true and no events
    1.70 +  // will be run/flushed until resume is called.  These should be called when
    1.71 +  // the channel owning the event queue is suspended/resumed.
    1.72 +  inline void Suspend();
    1.73 +  // Resume flushes the queue asynchronously, i.e. items in queue will be
    1.74 +  // dispatched in a new event on the current thread.
    1.75 +  void Resume();
    1.76 +
    1.77 +  // Retargets delivery of events to the target thread specified.
    1.78 +  nsresult RetargetDeliveryTo(nsIEventTarget* aTargetThread);
    1.79 +
    1.80 + private:
    1.81 +  // Private destructor, to discourage deletion outside of Release():
    1.82 +  ~ChannelEventQueue()
    1.83 +  {
    1.84 +  }
    1.85 +
    1.86 +  inline void MaybeFlushQueue();
    1.87 +  void FlushQueue();
    1.88 +  inline void CompleteResume();
    1.89 +
    1.90 +  nsTArray<nsAutoPtr<ChannelEvent> > mEventQueue;
    1.91 +
    1.92 +  uint32_t mSuspendCount;
    1.93 +  bool     mSuspended;
    1.94 +  bool mForced;
    1.95 +  bool mFlushing;
    1.96 +
    1.97 +  // Keep ptr to avoid refcount cycle: only grab ref during flushing.
    1.98 +  nsISupports *mOwner;
    1.99 +
   1.100 +  // Target thread for delivery of events.
   1.101 +  nsCOMPtr<nsIThread> mTargetThread;
   1.102 +
   1.103 +  friend class AutoEventEnqueuer;
   1.104 +};
   1.105 +
   1.106 +inline bool
   1.107 +ChannelEventQueue::ShouldEnqueue()
   1.108 +{
   1.109 +  bool answer =  mForced || mSuspended || mFlushing;
   1.110 +
   1.111 +  NS_ABORT_IF_FALSE(answer == true || mEventQueue.IsEmpty(),
   1.112 +                    "Should always enqueue if ChannelEventQueue not empty");
   1.113 +
   1.114 +  return answer;
   1.115 +}
   1.116 +
   1.117 +inline void
   1.118 +ChannelEventQueue::Enqueue(ChannelEvent* callback)
   1.119 +{
   1.120 +  mEventQueue.AppendElement(callback);
   1.121 +}
   1.122 +
   1.123 +inline void
   1.124 +ChannelEventQueue::StartForcedQueueing()
   1.125 +{
   1.126 +  mForced = true;
   1.127 +}
   1.128 +
   1.129 +inline void
   1.130 +ChannelEventQueue::EndForcedQueueing()
   1.131 +{
   1.132 +  mForced = false;
   1.133 +  MaybeFlushQueue();
   1.134 +}
   1.135 +
   1.136 +inline void
   1.137 +ChannelEventQueue::Suspend()
   1.138 +{
   1.139 +  mSuspended = true;
   1.140 +  mSuspendCount++;
   1.141 +}
   1.142 +
   1.143 +inline void
   1.144 +ChannelEventQueue::CompleteResume()
   1.145 +{
   1.146 +  // channel may have been suspended again since Resume fired event to call this.
   1.147 +  if (!mSuspendCount) {
   1.148 +    // we need to remain logically suspended (for purposes of queuing incoming
   1.149 +    // messages) until this point, else new incoming messages could run before
   1.150 +    // queued ones.
   1.151 +    mSuspended = false;
   1.152 +    MaybeFlushQueue();
   1.153 +  }
   1.154 +}
   1.155 +
   1.156 +inline void
   1.157 +ChannelEventQueue::MaybeFlushQueue()
   1.158 +{
   1.159 +  // Don't flush if forced queuing on, we're already being flushed, or
   1.160 +  // suspended, or there's nothing to flush
   1.161 +  if (!mForced && !mFlushing && !mSuspended && !mEventQueue.IsEmpty())
   1.162 +    FlushQueue();
   1.163 +}
   1.164 +
   1.165 +// Ensures that ShouldEnqueue() will be true during its lifetime (letting
   1.166 +// caller know incoming IPDL msgs should be queued). Flushes the queue when it
   1.167 +// goes out of scope.
   1.168 +class AutoEventEnqueuer
   1.169 +{
   1.170 + public:
   1.171 +  AutoEventEnqueuer(ChannelEventQueue *queue) : mEventQueue(queue) {
   1.172 +    mEventQueue->StartForcedQueueing();
   1.173 +  }
   1.174 +  ~AutoEventEnqueuer() {
   1.175 +    mEventQueue->EndForcedQueueing();
   1.176 +  }
   1.177 + private:
   1.178 +  ChannelEventQueue* mEventQueue;
   1.179 +};
   1.180 +
   1.181 +}
   1.182 +}
   1.183 +
   1.184 +#endif

mercurial