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