Line data Source code
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/. */
6 :
7 : // Original author: ekr@rtfm.com
8 :
9 : #ifndef transportflow_h__
10 : #define transportflow_h__
11 :
12 : #include <deque>
13 : #include <queue>
14 : #include <string>
15 :
16 : #include "nscore.h"
17 : #include "nsISupportsImpl.h"
18 : #include "mozilla/UniquePtr.h"
19 : #include "transportlayer.h"
20 : #include "m_cpp_utils.h"
21 : #include "nsAutoPtr.h"
22 :
23 : // A stack of transport layers acts as a flow.
24 : // Generally, one reads and writes to the top layer.
25 :
26 : // This code has a confusing hybrid threading model which
27 : // probably needs some eventual refactoring.
28 : // TODO(ekr@rtfm.com): Bug 844891
29 : //
30 : // TransportFlows are not inherently bound to a thread *but*
31 : // TransportLayers can be. If any layer in a flow is bound
32 : // to a given thread, then all layers in the flow MUST be
33 : // bound to that thread and you can only manipulate the
34 : // flow (push layers, write, etc.) on that thread.
35 : //
36 : // The sole official exception to this is that you are
37 : // allowed to *destroy* a flow off the bound thread provided
38 : // that there are no listeners on its signals. This exception
39 : // is designed to allow idioms where you create the flow
40 : // and then something goes wrong and you destroy it and
41 : // you don't want to bother with a thread dispatch.
42 : //
43 : // Eventually we hope to relax the "no listeners"
44 : // restriction by thread-locking the signals, but previous
45 : // attempts have caused deadlocks.
46 : //
47 : // Most of these invariants are enforced by hard asserts
48 : // (i.e., those which fire even in production builds).
49 :
50 : namespace mozilla {
51 :
52 : class TransportFlow final : public nsISupports,
53 : public sigslot::has_slots<> {
54 : public:
55 : TransportFlow()
56 : : id_("(anonymous)"),
57 : state_(TransportLayer::TS_NONE),
58 : layers_(new std::deque<TransportLayer *>) {}
59 0 : explicit TransportFlow(const std::string id)
60 0 : : id_(id),
61 : state_(TransportLayer::TS_NONE),
62 0 : layers_(new std::deque<TransportLayer *>) {}
63 :
64 0 : const std::string& id() const { return id_; }
65 :
66 : // Layer management. Note PushLayer() is not thread protected, so
67 : // either:
68 : // (a) Do it in the thread handling the I/O
69 : // (b) Do it before you activate the I/O system
70 : //
71 : // The flow takes ownership of the layers after a successful
72 : // push.
73 : nsresult PushLayer(TransportLayer *layer);
74 :
75 : // Convenience function to push multiple layers on. Layers
76 : // are pushed on in the order that they are in the queue.
77 : // Any failures cause the flow to become inoperable and
78 : // destroys all the layers including those already pushed.
79 : // TODO(ekr@rtfm.com): Change layers to be ref-counted.
80 : nsresult PushLayers(nsAutoPtr<std::queue<TransportLayer *> > layers);
81 :
82 : TransportLayer *top() const;
83 : TransportLayer *GetLayer(const std::string& id) const;
84 :
85 : // Wrappers for whatever TLayer happens to be the top layer
86 : // at the time. This way you don't need to do top()->Foo().
87 : TransportLayer::State state(); // Current state
88 : TransportResult SendPacket(const unsigned char *data, size_t len);
89 :
90 : // State has changed. Reflects the top flow.
91 : sigslot::signal2<TransportFlow *, TransportLayer::State>
92 : SignalStateChange;
93 :
94 : // Data received on the flow
95 : sigslot::signal3<TransportFlow*, const unsigned char *, size_t>
96 : SignalPacketReceived;
97 :
98 : bool Contains(TransportLayer *layer) const;
99 :
100 : NS_DECL_THREADSAFE_ISUPPORTS
101 :
102 : private:
103 : ~TransportFlow();
104 :
105 : DISALLOW_COPY_ASSIGN(TransportFlow);
106 :
107 : // Check if we are on the right thread
108 0 : void CheckThread() const {
109 0 : if (!CheckThreadInt())
110 0 : MOZ_CRASH();
111 0 : }
112 :
113 0 : bool CheckThreadInt() const {
114 : bool on;
115 :
116 0 : if (!target_) // OK if no thread set.
117 0 : return true;
118 0 : if (NS_FAILED(target_->IsOnCurrentThread(&on)))
119 0 : return false;
120 :
121 0 : return on;
122 : }
123 :
124 : void EnsureSameThread(TransportLayer *layer);
125 :
126 : void StateChange(TransportLayer *layer, TransportLayer::State state);
127 : void StateChangeInt(TransportLayer::State state);
128 : void PacketReceived(TransportLayer* layer, const unsigned char *data,
129 : size_t len);
130 : static void DestroyFinal(nsAutoPtr<std::deque<TransportLayer *> > layers);
131 :
132 : // Overload needed because we use deque internally and queue externally.
133 : static void ClearLayers(std::deque<TransportLayer *>* layers);
134 : static void ClearLayers(std::queue<TransportLayer *>* layers);
135 :
136 : std::string id_;
137 : TransportLayer::State state_;
138 : UniquePtr<std::deque<TransportLayer *>> layers_;
139 : nsCOMPtr<nsIEventTarget> target_;
140 : };
141 :
142 : } // close namespace
143 : #endif
|