summaryrefslogtreecommitdiffstats
path: root/media/mtransport/transportflow.h
diff options
context:
space:
mode:
Diffstat (limited to 'media/mtransport/transportflow.h')
-rw-r--r--media/mtransport/transportflow.h143
1 files changed, 143 insertions, 0 deletions
diff --git a/media/mtransport/transportflow.h b/media/mtransport/transportflow.h
new file mode 100644
index 000000000..b21764046
--- /dev/null
+++ b/media/mtransport/transportflow.h
@@ -0,0 +1,143 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// Original author: ekr@rtfm.com
+
+#ifndef transportflow_h__
+#define transportflow_h__
+
+#include <deque>
+#include <queue>
+#include <string>
+
+#include "nscore.h"
+#include "nsISupportsImpl.h"
+#include "mozilla/UniquePtr.h"
+#include "transportlayer.h"
+#include "m_cpp_utils.h"
+#include "nsAutoPtr.h"
+
+// A stack of transport layers acts as a flow.
+// Generally, one reads and writes to the top layer.
+
+// This code has a confusing hybrid threading model which
+// probably needs some eventual refactoring.
+// TODO(ekr@rtfm.com): Bug 844891
+//
+// TransportFlows are not inherently bound to a thread *but*
+// TransportLayers can be. If any layer in a flow is bound
+// to a given thread, then all layers in the flow MUST be
+// bound to that thread and you can only manipulate the
+// flow (push layers, write, etc.) on that thread.
+//
+// The sole official exception to this is that you are
+// allowed to *destroy* a flow off the bound thread provided
+// that there are no listeners on its signals. This exception
+// is designed to allow idioms where you create the flow
+// and then something goes wrong and you destroy it and
+// you don't want to bother with a thread dispatch.
+//
+// Eventually we hope to relax the "no listeners"
+// restriction by thread-locking the signals, but previous
+// attempts have caused deadlocks.
+//
+// Most of these invariants are enforced by hard asserts
+// (i.e., those which fire even in production builds).
+
+namespace mozilla {
+
+class TransportFlow final : public nsISupports,
+ public sigslot::has_slots<> {
+ public:
+ TransportFlow()
+ : id_("(anonymous)"),
+ state_(TransportLayer::TS_NONE),
+ layers_(new std::deque<TransportLayer *>) {}
+ explicit TransportFlow(const std::string id)
+ : id_(id),
+ state_(TransportLayer::TS_NONE),
+ layers_(new std::deque<TransportLayer *>) {}
+
+ const std::string& id() const { return id_; }
+
+ // Layer management. Note PushLayer() is not thread protected, so
+ // either:
+ // (a) Do it in the thread handling the I/O
+ // (b) Do it before you activate the I/O system
+ //
+ // The flow takes ownership of the layers after a successful
+ // push.
+ nsresult PushLayer(TransportLayer *layer);
+
+ // Convenience function to push multiple layers on. Layers
+ // are pushed on in the order that they are in the queue.
+ // Any failures cause the flow to become inoperable and
+ // destroys all the layers including those already pushed.
+ // TODO(ekr@rtfm.com): Change layers to be ref-counted.
+ nsresult PushLayers(nsAutoPtr<std::queue<TransportLayer *> > layers);
+
+ TransportLayer *top() const;
+ TransportLayer *GetLayer(const std::string& id) const;
+
+ // Wrappers for whatever TLayer happens to be the top layer
+ // at the time. This way you don't need to do top()->Foo().
+ TransportLayer::State state(); // Current state
+ TransportResult SendPacket(const unsigned char *data, size_t len);
+
+ // State has changed. Reflects the top flow.
+ sigslot::signal2<TransportFlow *, TransportLayer::State>
+ SignalStateChange;
+
+ // Data received on the flow
+ sigslot::signal3<TransportFlow*, const unsigned char *, size_t>
+ SignalPacketReceived;
+
+ bool Contains(TransportLayer *layer) const;
+
+ NS_DECL_THREADSAFE_ISUPPORTS
+
+ private:
+ ~TransportFlow();
+
+ DISALLOW_COPY_ASSIGN(TransportFlow);
+
+ // Check if we are on the right thread
+ void CheckThread() const {
+ if (!CheckThreadInt())
+ MOZ_CRASH();
+ }
+
+ bool CheckThreadInt() const {
+ bool on;
+
+ if (!target_) // OK if no thread set.
+ return true;
+ if (NS_FAILED(target_->IsOnCurrentThread(&on)))
+ return false;
+
+ return on;
+ }
+
+ void EnsureSameThread(TransportLayer *layer);
+
+ void StateChange(TransportLayer *layer, TransportLayer::State state);
+ void StateChangeInt(TransportLayer::State state);
+ void PacketReceived(TransportLayer* layer, const unsigned char *data,
+ size_t len);
+ static void DestroyFinal(nsAutoPtr<std::deque<TransportLayer *> > layers);
+
+ // Overload needed because we use deque internally and queue externally.
+ static void ClearLayers(std::deque<TransportLayer *>* layers);
+ static void ClearLayers(std::queue<TransportLayer *>* layers);
+
+ std::string id_;
+ TransportLayer::State state_;
+ UniquePtr<std::deque<TransportLayer *>> layers_;
+ nsCOMPtr<nsIEventTarget> target_;
+};
+
+} // close namespace
+#endif