1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=2 et sw=2 tw=80: */
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 file,
5 * You can obtain one at http://mozilla.org/MPL/2.0/. */
7 // Original author: ekr@rtfm.com
11 #include "runnable_utils.h"
12 #include "transportflow.h"
13 #include "transportlayer.h"
17 MOZ_MTLOG_MODULE("mtransport")
19 NS_IMPL_ISUPPORTS0(TransportFlow
)
21 // There are some hacks here to allow destruction off of
23 TransportFlow::~TransportFlow() {
24 // Make sure that if we are off the right thread, we have
25 // no more attached signals.
26 if (!CheckThreadInt()) {
27 MOZ_ASSERT(SignalStateChange
.is_empty());
28 MOZ_ASSERT(SignalPacketReceived
.is_empty());
31 // Push the destruction onto the STS thread. Note that there
32 // is still some possibility that someone is accessing this
33 // object simultaneously, but as long as smart pointer discipline
34 // is maintained, it shouldn't be possible to access and
35 // destroy it simultaneously. The conversion to an nsAutoPtr
36 // ensures automatic destruction of the queue at exit of
38 nsAutoPtr
<std::deque
<TransportLayer
*> > layers_tmp(layers_
.forget());
39 RUN_ON_THREAD(target_
,
40 WrapRunnableNM(&TransportFlow::DestroyFinal
, layers_tmp
),
44 void TransportFlow::DestroyFinal(nsAutoPtr
<std::deque
<TransportLayer
*> > layers
) {
48 void TransportFlow::ClearLayers(std::queue
<TransportLayer
*>* layers
) {
49 while (!layers
->empty()) {
50 delete layers
->front();
55 void TransportFlow::ClearLayers(std::deque
<TransportLayer
*>* layers
) {
56 while (!layers
->empty()) {
57 delete layers
->front();
62 nsresult
TransportFlow::PushLayer(TransportLayer
*layer
) {
64 ScopedDeletePtr
<TransportLayer
> layer_tmp(layer
); // Destroy on failure.
66 // Don't allow pushes once we are in error state.
67 if (state_
== TransportLayer::TS_ERROR
) {
68 MOZ_MTLOG(ML_ERROR
, id_
+ ": Can't call PushLayer in error state for flow");
69 return NS_ERROR_FAILURE
;
72 nsresult rv
= layer
->Init();
73 if (!NS_SUCCEEDED(rv
)) {
74 // Destroy the rest of the flow, because it's no longer in an acceptable
76 ClearLayers(layers_
.get());
78 // Set ourselves to have failed.
79 MOZ_MTLOG(ML_ERROR
, id_
<< ": Layer initialization failed; invalidating");
80 StateChangeInt(TransportLayer::TS_ERROR
);
84 EnsureSameThread(layer
);
86 TransportLayer
*old_layer
= layers_
->empty() ? nullptr : layers_
->front();
88 // Re-target my signals to the new layer
90 old_layer
->SignalStateChange
.disconnect(this);
91 old_layer
->SignalPacketReceived
.disconnect(this);
93 layers_
->push_front(layer_tmp
.forget());
94 layer
->Inserted(this, old_layer
);
96 layer
->SignalStateChange
.connect(this, &TransportFlow::StateChange
);
97 layer
->SignalPacketReceived
.connect(this, &TransportFlow::PacketReceived
);
98 StateChangeInt(layer
->state());
103 // This is all-or-nothing.
104 nsresult
TransportFlow::PushLayers(nsAutoPtr
<std::queue
<TransportLayer
*> > layers
) {
107 MOZ_ASSERT(!layers
->empty());
108 if (layers
->empty()) {
109 MOZ_MTLOG(ML_ERROR
, id_
<< ": Can't call PushLayers with empty layers");
110 return NS_ERROR_INVALID_ARG
;
113 // Don't allow pushes once we are in error state.
114 if (state_
== TransportLayer::TS_ERROR
) {
116 id_
<< ": Can't call PushLayers in error state for flow ");
117 ClearLayers(layers
.get());
118 return NS_ERROR_FAILURE
;
123 // Disconnect all the old signals.
126 TransportLayer
*layer
;
128 while (!layers
->empty()) {
129 TransportLayer
*old_layer
= layers_
->empty() ? nullptr : layers_
->front();
130 layer
= layers
->front();
135 id_
<< ": Layer initialization failed; invalidating flow ");
139 EnsureSameThread(layer
);
141 // Push the layer onto the queue.
142 layers_
->push_front(layer
);
144 layer
->Inserted(this, old_layer
);
148 // Destroy any layers we could not push.
151 // Now destroy the rest of the flow, because it's no longer
152 // in an acceptable state.
153 ClearLayers(layers_
);
155 // Set ourselves to have failed.
156 StateChangeInt(TransportLayer::TS_ERROR
);
162 // Finally, attach ourselves to the top layer.
163 layer
->SignalStateChange
.connect(this, &TransportFlow::StateChange
);
164 layer
->SignalPacketReceived
.connect(this, &TransportFlow::PacketReceived
);
165 StateChangeInt(layer
->state()); // Signals if the state changes.
170 TransportLayer
*TransportFlow::top() const {
173 return layers_
->empty() ? nullptr : layers_
->front();
176 TransportLayer
*TransportFlow::GetLayer(const std::string
& id
) const {
179 for (std::deque
<TransportLayer
*>::const_iterator it
= layers_
->begin();
180 it
!= layers_
->end(); ++it
) {
181 if ((*it
)->id() == id
)
188 TransportLayer::State
TransportFlow::state() {
194 TransportResult
TransportFlow::SendPacket(const unsigned char *data
,
198 if (state_
!= TransportLayer::TS_OPEN
) {
201 return top() ? top()->SendPacket(data
, len
) : TE_ERROR
;
204 bool TransportFlow::Contains(TransportLayer
*layer
) const {
206 for (auto l
= layers_
->begin(); l
!= layers_
->end(); ++l
) {
215 void TransportFlow::EnsureSameThread(TransportLayer
*layer
) {
216 // Enforce that if any of the layers have a thread binding,
217 // they all have the same binding.
219 const nsCOMPtr
<nsIEventTarget
>& lthread
= layer
->GetThread();
221 if (lthread
&& (lthread
!= target_
))
225 target_
= layer
->GetThread();
229 void TransportFlow::StateChangeInt(TransportLayer::State state
) {
232 if (state
== state_
) {
237 SignalStateChange(this, state_
);
240 void TransportFlow::StateChange(TransportLayer
*layer
,
241 TransportLayer::State state
) {
244 StateChangeInt(state
);
247 void TransportFlow::PacketReceived(TransportLayer
* layer
,
248 const unsigned char *data
,
252 SignalPacketReceived(this, data
, len
);