Add RefCounting for TransportProxies

BUG=1574
R=pthatcher@webrtc.org

Review URL: https://webrtc-codereview.appspot.com/37869004

Cr-Commit-Position: refs/heads/master@{#8238}
git-svn-id: http://webrtc.googlecode.com/svn/trunk@8238 4adac7df-926f-26a2-2b94-8c16560cd09d
This commit is contained in:
decurtis@webrtc.org 2015-02-03 23:18:39 +00:00
parent af01d93aa2
commit e2506670a4
5 changed files with 143 additions and 40 deletions

View File

@ -1482,7 +1482,7 @@ void WebRtcSession::RemoveUnusedChannelsAndTransports(
SignalVideoChannelDestroyed(); SignalVideoChannelDestroyed();
const std::string content_name = video_channel_->content_name(); const std::string content_name = video_channel_->content_name();
channel_manager_->DestroyVideoChannel(video_channel_.release()); channel_manager_->DestroyVideoChannel(video_channel_.release());
DestroyTransportProxy(content_name); DestroyTransportProxyWhenUnused(content_name);
} }
const cricket::ContentInfo* voice_info = const cricket::ContentInfo* voice_info =
@ -1492,7 +1492,7 @@ void WebRtcSession::RemoveUnusedChannelsAndTransports(
SignalVoiceChannelDestroyed(); SignalVoiceChannelDestroyed();
const std::string content_name = voice_channel_->content_name(); const std::string content_name = voice_channel_->content_name();
channel_manager_->DestroyVoiceChannel(voice_channel_.release()); channel_manager_->DestroyVoiceChannel(voice_channel_.release());
DestroyTransportProxy(content_name); DestroyTransportProxyWhenUnused(content_name);
} }
const cricket::ContentInfo* data_info = const cricket::ContentInfo* data_info =
@ -1502,7 +1502,7 @@ void WebRtcSession::RemoveUnusedChannelsAndTransports(
SignalDataChannelDestroyed(); SignalDataChannelDestroyed();
const std::string content_name = data_channel_->content_name(); const std::string content_name = data_channel_->content_name();
channel_manager_->DestroyDataChannel(data_channel_.release()); channel_manager_->DestroyDataChannel(data_channel_.release());
DestroyTransportProxy(content_name); DestroyTransportProxyWhenUnused(content_name);
} }
} }

View File

@ -30,11 +30,12 @@ namespace cricket {
using rtc::Bind; using rtc::Bind;
TransportProxy::~TransportProxy() { TransportProxy::~TransportProxy() {
for (ChannelMap::iterator iter = channels_.begin(); ASSERT(channels_.empty());
iter != channels_.end(); ++iter) {
iter->second->SignalDestroyed(iter->second);
delete iter->second;
} }
bool TransportProxy::HasChannels() const {
ASSERT(rtc::Thread::Current() == worker_thread_);
return !channels_.empty();
} }
const std::string& TransportProxy::type() const { const std::string& TransportProxy::type() const {
@ -49,14 +50,21 @@ TransportChannel* TransportProxy::GetChannel(int component) {
TransportChannel* TransportProxy::CreateChannel(const std::string& name, TransportChannel* TransportProxy::CreateChannel(const std::string& name,
int component) { int component) {
ASSERT(rtc::Thread::Current() == worker_thread_); ASSERT(rtc::Thread::Current() == worker_thread_);
ASSERT(GetChannel(component) == NULL);
ASSERT(!transport_->get()->HasChannel(component));
// We always create a proxy in case we need to change out the transport later. TransportChannelProxyRef* channel_proxy;
TransportChannelProxy* channel_proxy = if (channels_.find(component) == channels_.end()) {
new TransportChannelProxy(content_name(), name, component); channel_proxy =
new TransportChannelProxyRef(content_name(), name, component);
channels_[component] = channel_proxy; channels_[component] = channel_proxy;
// TransportProxy maintains its own reference
// here. RefCountedObject automatically deletes the pointer when
// the refcount hits 0. This prevents RefCountedObject from
// deleting the object when all *external* references are
// gone. Things need to be done in DestroyChannel prior to the
// proxy being deleted.
channel_proxy->AddRef();
// If we're already negotiated, create an impl and hook it up to the proxy // If we're already negotiated, create an impl and hook it up to the proxy
// channel. If we're connecting, create an impl but don't hook it up yet. // channel. If we're connecting, create an impl but don't hook it up yet.
if (negotiated_) { if (negotiated_) {
@ -65,6 +73,12 @@ TransportChannel* TransportProxy::CreateChannel(const std::string& name,
} else if (connecting_) { } else if (connecting_) {
CreateChannelImpl_w(component); CreateChannelImpl_w(component);
} }
} else {
channel_proxy = channels_[component];
}
channel_proxy->AddRef();
return channel_proxy; return channel_proxy;
} }
@ -74,8 +88,22 @@ bool TransportProxy::HasChannel(int component) {
void TransportProxy::DestroyChannel(int component) { void TransportProxy::DestroyChannel(int component) {
ASSERT(rtc::Thread::Current() == worker_thread_); ASSERT(rtc::Thread::Current() == worker_thread_);
TransportChannelProxy* channel_proxy = GetChannelProxy(component);
if (channel_proxy) { ChannelMap::const_iterator iter = channels_.find(component);
if (iter == channels_.end()) {
return;
}
TransportChannelProxyRef* channel_proxy = iter->second;
int ref_count = channel_proxy->Release();
if (ref_count > 1) {
return;
}
// TransportProxy owns the last reference on the TransportChannelProxy.
// It should *never* be the case that ref_count is less than one
// here but this makes me sleep better at night.
ASSERT(ref_count == 1);
// If the state of TransportProxy is not NEGOTIATED then // If the state of TransportProxy is not NEGOTIATED then
// TransportChannelProxy and its impl are not connected. Both must // TransportChannelProxy and its impl are not connected. Both must
// be connected before deletion. // be connected before deletion.
@ -88,8 +116,9 @@ void TransportProxy::DestroyChannel(int component) {
channels_.erase(component); channels_.erase(component);
channel_proxy->SignalDestroyed(channel_proxy); channel_proxy->SignalDestroyed(channel_proxy);
delete channel_proxy;
} // Implicitly deletes the object since ref_count is now 0.
channel_proxy->Release();
} }
void TransportProxy::ConnectChannels() { void TransportProxy::ConnectChannels() {
@ -590,8 +619,15 @@ TransportProxy* BaseSession::GetFirstTransportProxy() {
return transports_.begin()->second; return transports_.begin()->second;
} }
void BaseSession::DestroyTransportProxy( void BaseSession::DestroyTransportProxyWhenUnused(
const std::string& content_name) { const std::string& content_name) {
TransportProxy *tp = GetTransportProxy(content_name);
if(tp && !tp->HasChannels()) {
DestroyTransportProxy(content_name);
}
}
void BaseSession::DestroyTransportProxy(const std::string& content_name) {
TransportMap::iterator iter = transports_.find(content_name); TransportMap::iterator iter = transports_.find(content_name);
if (iter != transports_.end()) { if (iter != transports_.end()) {
delete iter->second; delete iter->second;

View File

@ -19,6 +19,7 @@
#include "webrtc/p2p/base/candidate.h" #include "webrtc/p2p/base/candidate.h"
#include "webrtc/p2p/base/port.h" #include "webrtc/p2p/base/port.h"
#include "webrtc/p2p/base/transport.h" #include "webrtc/p2p/base/transport.h"
#include "webrtc/p2p/base/transportchannelproxy.h"
#include "webrtc/base/refcount.h" #include "webrtc/base/refcount.h"
#include "webrtc/base/scoped_ptr.h" #include "webrtc/base/scoped_ptr.h"
#include "webrtc/base/scoped_ref_ptr.h" #include "webrtc/base/scoped_ref_ptr.h"
@ -30,11 +31,12 @@ class BaseSession;
class P2PTransportChannel; class P2PTransportChannel;
class Transport; class Transport;
class TransportChannel; class TransportChannel;
class TransportChannelProxy;
class TransportChannelImpl; class TransportChannelImpl;
typedef rtc::RefCountedObject<rtc::scoped_ptr<Transport> > // Transport and TransportChannelProxy are incomplete types. Thus we
TransportWrapper; // wrap them in a scoped_ptr.
typedef rtc::RefCountedObject<rtc::scoped_ptr<Transport> > TransportWrapper;
typedef rtc::RefCountedObject<TransportChannelProxy> TransportChannelProxyRef;
// Bundles a Transport and ChannelMap together. ChannelMap is used to // Bundles a Transport and ChannelMap together. ChannelMap is used to
// create transport channels before receiving or sending a session // create transport channels before receiving or sending a session
@ -42,7 +44,7 @@ TransportWrapper;
// session had one ChannelMap and transport. Now, with multiple // session had one ChannelMap and transport. Now, with multiple
// transports per session, we need multiple ChannelMaps as well. // transports per session, we need multiple ChannelMaps as well.
typedef std::map<int, TransportChannelProxy*> ChannelMap; typedef std::map<int, TransportChannelProxyRef*> ChannelMap;
class TransportProxy : public sigslot::has_slots<>, class TransportProxy : public sigslot::has_slots<>,
public CandidateTranslator { public CandidateTranslator {
@ -82,9 +84,14 @@ class TransportProxy : public sigslot::has_slots<>,
} }
TransportChannel* GetChannel(int component); TransportChannel* GetChannel(int component);
// Returns the channel for component. This channel may be used by
// others and should only be deleted by calling DestroyChannel.
TransportChannel* CreateChannel(const std::string& channel_name, TransportChannel* CreateChannel(const std::string& channel_name,
int component); int component);
bool HasChannel(int component); bool HasChannel(int component);
// Destroy the channel for component.
void DestroyChannel(int component); void DestroyChannel(int component);
void AddSentCandidates(const Candidates& candidates); void AddSentCandidates(const Candidates& candidates);
@ -118,6 +125,9 @@ class TransportProxy : public sigslot::has_slots<>,
virtual bool GetComponentFromChannelName( virtual bool GetComponentFromChannelName(
const std::string& channel_name, int* component) const; const std::string& channel_name, int* component) const;
// Determine if TransportProxy has any active channels.
bool HasChannels() const;
// Called when a transport signals that it has new candidates. // Called when a transport signals that it has new candidates.
void OnTransportCandidatesReady(cricket::Transport* transport, void OnTransportCandidatesReady(cricket::Transport* transport,
const Candidates& candidates) { const Candidates& candidates) {
@ -346,6 +356,8 @@ class BaseSession : public sigslot::has_slots<>,
TransportProxy* GetTransportProxy(const Transport* transport); TransportProxy* GetTransportProxy(const Transport* transport);
TransportProxy* GetFirstTransportProxy(); TransportProxy* GetFirstTransportProxy();
void DestroyTransportProxy(const std::string& content_name); void DestroyTransportProxy(const std::string& content_name);
// Calls DestroyTransportProxy if the TransportProxy is not used.
void DestroyTransportProxyWhenUnused(const std::string& content_name);
// TransportProxy is owned by session. Return proxy just for convenience. // TransportProxy is owned by session. Return proxy just for convenience.
TransportProxy* GetOrCreateTransportProxy(const std::string& content_name); TransportProxy* GetOrCreateTransportProxy(const std::string& content_name);
// Creates the actual transport object. Overridable for testing. // Creates the actual transport object. Overridable for testing.

View File

@ -0,0 +1,54 @@
/*
* Copyright 2004 The WebRTC Project Authors. All rights reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#include "webrtc/p2p/base/session.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace cricket {
class BaseSessionTest : public testing::Test, public BaseSession {
protected:
BaseSessionTest() :
BaseSession(rtc::Thread::Current(), rtc::Thread::Current(),
NULL, "sid", "video?", true) {
}
~BaseSessionTest() {}
};
// Tests that channels are not being deleted until all refcounts are
// used and that the TransportProxy is not removed unless all channels
// are removed from the proxy.
TEST_F(BaseSessionTest, TransportChannelProxyRefCounter) {
std::string content_name = "no matter";
int component = 10;
TransportChannel* channel = CreateChannel(content_name, "", component);
TransportChannel* channel_again = CreateChannel(content_name, "", component);
EXPECT_EQ(channel, channel_again);
EXPECT_EQ(channel, GetChannel(content_name, component));
DestroyChannel(content_name, component);
EXPECT_EQ(channel, GetChannel(content_name, component));
// Try to destroy a non-existant content name.
DestroyTransportProxyWhenUnused("other content");
EXPECT_TRUE(GetTransportProxy(content_name) != NULL);
DestroyChannel(content_name, component);
EXPECT_EQ(NULL, GetChannel(content_name, component));
EXPECT_TRUE(GetTransportProxy(content_name) != NULL);
DestroyTransportProxyWhenUnused(content_name);
EXPECT_TRUE(GetTransportProxy(content_name) == NULL);
}
} // namespace cricket

View File

@ -22,6 +22,7 @@
'base/pseudotcp_unittest.cc', 'base/pseudotcp_unittest.cc',
'base/relayport_unittest.cc', 'base/relayport_unittest.cc',
'base/relayserver_unittest.cc', 'base/relayserver_unittest.cc',
'base/session_unittest.cc',
'base/stun_unittest.cc', 'base/stun_unittest.cc',
'base/stunport_unittest.cc', 'base/stunport_unittest.cc',
'base/stunrequest_unittest.cc', 'base/stunrequest_unittest.cc',