Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/netwerk/ipc/ChannelEventQueue.cpp
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2
 * vim: set sw=2 ts=8 et tw=80 :
3
 */
4
/* This Source Code Form is subject to the terms of the Mozilla Public
5
 * License, v. 2.0. If a copy of the MPL was not distributed with this
6
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7
8
#include "ChannelEventQueue.h"
9
10
#include "mozilla/Assertions.h"
11
#include "mozilla/Unused.h"
12
#include "nsISupports.h"
13
#include "nsThreadUtils.h"
14
15
namespace mozilla {
16
namespace net {
17
18
ChannelEvent*
19
ChannelEventQueue::TakeEvent()
20
0
{
21
0
  mMutex.AssertCurrentThreadOwns();
22
0
  MOZ_ASSERT(mFlushing);
23
0
24
0
  if (mSuspended || mEventQueue.IsEmpty()) {
25
0
    return nullptr;
26
0
  }
27
0
28
0
  UniquePtr<ChannelEvent> event(std::move(mEventQueue[0]));
29
0
  mEventQueue.RemoveElementAt(0);
30
0
31
0
  return event.release();
32
0
}
33
34
void
35
ChannelEventQueue::FlushQueue()
36
0
{
37
0
  // Events flushed could include destruction of channel (and our own
38
0
  // destructor) unless we make sure its refcount doesn't drop to 0 while this
39
0
  // method is running.
40
0
  nsCOMPtr<nsISupports> kungFuDeathGrip(mOwner);
41
0
  mozilla::Unused << kungFuDeathGrip; // Not used in this function
42
0
43
#ifdef DEBUG
44
  {
45
    MutexAutoLock lock(mMutex);
46
    MOZ_ASSERT(mFlushing);
47
  }
48
#endif // DEBUG
49
50
0
  bool needResumeOnOtherThread = false;
51
0
52
0
  while (true) {
53
0
    UniquePtr<ChannelEvent> event;
54
0
    {
55
0
      MutexAutoLock lock(mMutex);
56
0
      event.reset(TakeEvent());
57
0
      if (!event) {
58
0
        MOZ_ASSERT(mFlushing);
59
0
        mFlushing = false;
60
0
        MOZ_ASSERT(mEventQueue.IsEmpty() || (mSuspended || !!mForcedCount));
61
0
        break;
62
0
      }
63
0
    }
64
0
65
0
    nsCOMPtr<nsIEventTarget> target = event->GetEventTarget();
66
0
    MOZ_ASSERT(target);
67
0
68
0
    bool isCurrentThread = false;
69
0
    nsresult rv = target->IsOnCurrentThread(&isCurrentThread);
70
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
71
0
      // Simply run this event on current thread if we are not sure about it
72
0
      // in release channel, or assert in Aurora/Nightly channel.
73
0
      MOZ_DIAGNOSTIC_ASSERT(false);
74
0
      isCurrentThread = true;
75
0
    }
76
0
77
0
    if (!isCurrentThread) {
78
0
      // Next event needs to run on another thread. Put it back to
79
0
      // the front of the queue can try resume on that thread.
80
0
      Suspend();
81
0
      PrependEvent(event);
82
0
83
0
      needResumeOnOtherThread = true;
84
0
      {
85
0
        MutexAutoLock lock(mMutex);
86
0
        MOZ_ASSERT(mFlushing);
87
0
        mFlushing = false;
88
0
        MOZ_ASSERT(!mEventQueue.IsEmpty());
89
0
      }
90
0
      break;
91
0
    }
92
0
93
0
    event->Run();
94
0
  } // end of while(true)
95
0
96
0
  // The flush procedure is aborted because next event cannot be run on current
97
0
  // thread. We need to resume the event processing right after flush procedure
98
0
  // is finished.
99
0
  // Note: we cannot call Resume() while "mFlushing == true" because
100
0
  // CompleteResume will not trigger FlushQueue while there is an ongoing flush.
101
0
  if (needResumeOnOtherThread) {
102
0
    Resume();
103
0
  }
104
0
}
105
106
void
107
ChannelEventQueue::Suspend()
108
0
{
109
0
  MutexAutoLock lock(mMutex);
110
0
  SuspendInternal();
111
0
}
112
113
void
114
ChannelEventQueue::SuspendInternal()
115
0
{
116
0
  mMutex.AssertCurrentThreadOwns();
117
0
118
0
  mSuspended = true;
119
0
  mSuspendCount++;
120
0
}
121
122
void ChannelEventQueue::Resume()
123
0
{
124
0
  MutexAutoLock lock(mMutex);
125
0
  ResumeInternal();
126
0
}
127
128
void
129
ChannelEventQueue::ResumeInternal()
130
0
{
131
0
  mMutex.AssertCurrentThreadOwns();
132
0
133
0
  // Resuming w/o suspend: error in debug mode, ignore in build
134
0
  MOZ_ASSERT(mSuspendCount > 0);
135
0
  if (mSuspendCount <= 0) {
136
0
    return;
137
0
  }
138
0
139
0
  if (!--mSuspendCount) {
140
0
    if (mEventQueue.IsEmpty() || !!mForcedCount) {
141
0
      // Nothing in queue to flush or waiting for AutoEventEnqueuer to
142
0
      // finish the force enqueue period, simply clear the flag.
143
0
      mSuspended = false;
144
0
      return;
145
0
    }
146
0
147
0
    // Hold a strong reference of mOwner to avoid the channel release
148
0
    // before CompleteResume was executed.
149
0
    class CompleteResumeRunnable : public CancelableRunnable
150
0
    {
151
0
    public:
152
0
      explicit CompleteResumeRunnable(ChannelEventQueue* aQueue, nsISupports* aOwner)
153
0
        : CancelableRunnable("CompleteResumeRunnable")
154
0
        , mQueue(aQueue)
155
0
        , mOwner(aOwner)
156
0
      {
157
0
      }
158
0
159
0
      NS_IMETHOD Run() override
160
0
      {
161
0
        mQueue->CompleteResume();
162
0
        return NS_OK;
163
0
      }
164
0
165
0
    private:
166
0
      virtual ~CompleteResumeRunnable() = default;
167
0
168
0
      RefPtr<ChannelEventQueue> mQueue;
169
0
      nsCOMPtr<nsISupports> mOwner;
170
0
    };
171
0
172
0
    // Worker thread requires a CancelableRunnable.
173
0
    RefPtr<Runnable> event = new CompleteResumeRunnable(this, mOwner);
174
0
175
0
    nsCOMPtr<nsIEventTarget> target;
176
0
      target = mEventQueue[0]->GetEventTarget();
177
0
    MOZ_ASSERT(target);
178
0
179
0
    Unused << NS_WARN_IF(NS_FAILED(target->Dispatch(event.forget(),
180
0
                                                    NS_DISPATCH_NORMAL)));
181
0
  }
182
0
}
183
184
} // namespace net
185
} // namespace mozilla