Skip to content

Commit 3f18e7a

Browse files
committed
steamwebrtc: Let resolver threads exit after the lookup
1 parent a424b7d commit 3f18e7a

1 file changed

Lines changed: 185 additions & 1 deletion

File tree

‎src/external/steamwebrtc/ice_session.cpp‎

Lines changed: 185 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,22 @@
55
#include <mutex>
66
#include <string.h>
77

8+
#include <atomic>
9+
#include <memory>
810
#include <string>
11+
#include <thread>
912

1013
#include <absl/types/optional.h>
1114
#include <absl/memory/memory.h>
1215
#include <api/async_resolver_factory.h>
1316
#include <api/turn_customizer.h>
1417
#include <rtc_base/network_route.h>
18+
#include <rtc_base/async_resolver_interface.h>
1519
#include <rtc_base/bind.h>
20+
#include <rtc_base/ip_address.h>
1621
#include <rtc_base/physical_socket_server.h>
1722
#include <rtc_base/ssl_adapter.h>
23+
#include <rtc_base/task_utils/to_queued_task.h>
1824

1925
#include <api/jsep.h>
2026
#include <p2p/base/p2p_transport_channel.h>
@@ -55,6 +61,182 @@ extern "C"
5561
STEAMWEBRTC_DECLSPEC IICESession *CreateWebRTCICESession( const ICESessionConfig &cfg, IICESessionDelegate *pDelegate, int nInterfaceVersion );
5662
}
5763

64+
namespace rtc
65+
{
66+
// Defined in net_helpers.cc, but not declared in a header.
67+
int ResolveHostname( const std::string &hostname, int family, std::vector<IPAddress> *addresses );
68+
}
69+
70+
namespace {
71+
72+
struct AsyncResolverState
73+
{
74+
std::mutex mutex;
75+
bool alive = true; // Guarded by mutex
76+
std::atomic<bool> finished{ false };
77+
};
78+
79+
using ResolverThreads = std::vector< std::pair< std::thread, std::shared_ptr<AsyncResolverState> > >;
80+
81+
// Leaked on purpose: joinable threads would terminate at exit.
82+
std::mutex s_mutexResolverThreads;
83+
ResolverThreads &s_vecResolverThreads = *new ResolverThreads;
84+
85+
void AbandonResolverThread( std::thread thread, std::shared_ptr<AsyncResolverState> state )
86+
{
87+
std::lock_guard<std::mutex> lock( s_mutexResolverThreads );
88+
for ( auto it = s_vecResolverThreads.begin(); it != s_vecResolverThreads.end(); )
89+
{
90+
if ( it->second->finished )
91+
{
92+
it->first.join();
93+
it = s_vecResolverThreads.erase( it );
94+
}
95+
else
96+
{
97+
++it;
98+
}
99+
}
100+
s_vecResolverThreads.emplace_back( std::move( thread ), std::move( state ) );
101+
}
102+
103+
void JoinResolverThreads()
104+
{
105+
ResolverThreads threads;
106+
{
107+
std::lock_guard<std::mutex> lock( s_mutexResolverThreads );
108+
threads.swap( s_vecResolverThreads );
109+
}
110+
for ( auto &thread : threads )
111+
thread.first.join();
112+
}
113+
114+
//-----------------------------------------------------------------------------
115+
// rtc::AsyncResolver's thread idles until the resolver is destroyed (webrtc:12659)
116+
//-----------------------------------------------------------------------------
117+
class CAsyncResolver final : public rtc::AsyncResolverInterface
118+
{
119+
public:
120+
void Start( const rtc::SocketAddress &addr ) override
121+
{
122+
Assert( !worker_.joinable() );
123+
addr_ = addr;
124+
125+
thread_ = rtc::Thread::Current();
126+
Assert( thread_ != nullptr );
127+
128+
CAsyncResolver *pResolver = this;
129+
std::shared_ptr<AsyncResolverState> state = state_;
130+
rtc::Thread *pCallingThread = thread_;
131+
std::string hostname = addr.hostname();
132+
int family = addr.family();
133+
134+
worker_ = std::thread( [pResolver, state, pCallingThread, hostname, family]()
135+
{
136+
std::vector<rtc::IPAddress> addresses;
137+
int error = rtc::ResolveHostname( hostname, family, &addresses );
138+
139+
// The calling thread may be gone once Destroy() has run.
140+
std::lock_guard<std::mutex> lock( state->mutex );
141+
state->finished = true;
142+
if ( !state->alive )
143+
return;
144+
pCallingThread->PostTask( webrtc::ToQueuedTask(
145+
[pResolver, state, addresses = std::move( addresses ), error]() mutable
146+
{
147+
{
148+
std::lock_guard<std::mutex> lock( state->mutex );
149+
if ( !state->alive )
150+
return;
151+
}
152+
pResolver->ResolveDone( std::move( addresses ), error );
153+
} ) );
154+
} );
155+
}
156+
157+
bool GetResolvedAddress( int family, rtc::SocketAddress *addr ) const override
158+
{
159+
if ( error_ != 0 || addresses_.empty() )
160+
return false;
161+
162+
*addr = addr_;
163+
for ( const rtc::IPAddress &ip : addresses_ )
164+
{
165+
if ( family == ip.family() )
166+
{
167+
addr->SetResolvedIP( ip );
168+
return true;
169+
}
170+
}
171+
return false;
172+
}
173+
174+
int GetError() const override { return error_; }
175+
176+
void Destroy( bool wait ) override
177+
{
178+
// The posted result runs on this thread too, so it can't race with Destroy().
179+
Assert( thread_ == nullptr || thread_->IsCurrent() );
180+
181+
{
182+
std::lock_guard<std::mutex> lock( state_->mutex );
183+
state_->alive = false;
184+
}
185+
186+
if ( worker_.joinable() )
187+
{
188+
if ( wait )
189+
worker_.join();
190+
else
191+
AbandonResolverThread( std::move( worker_ ), state_ );
192+
}
193+
194+
// Called from a SignalDone handler: ResolveDone() deletes us after the signal.
195+
if ( in_signal_ )
196+
{
197+
destroy_pending_ = true;
198+
return;
199+
}
200+
delete this;
201+
}
202+
203+
private:
204+
~CAsyncResolver() override = default;
205+
206+
void ResolveDone( std::vector<rtc::IPAddress> addresses, int error )
207+
{
208+
addresses_ = std::move( addresses );
209+
error_ = error;
210+
in_signal_ = true;
211+
SignalDone( this );
212+
in_signal_ = false;
213+
if ( destroy_pending_ )
214+
delete this;
215+
}
216+
217+
rtc::SocketAddress addr_;
218+
rtc::Thread *thread_ = nullptr;
219+
std::vector<rtc::IPAddress> addresses_;
220+
int error_ = -1;
221+
bool in_signal_ = false;
222+
bool destroy_pending_ = false;
223+
std::thread worker_;
224+
std::shared_ptr<AsyncResolverState> state_ = std::make_shared<AsyncResolverState>();
225+
};
226+
227+
class CPacketSocketFactory final : public rtc::BasicPacketSocketFactory
228+
{
229+
public:
230+
explicit CPacketSocketFactory( rtc::Thread *thread ) : rtc::BasicPacketSocketFactory( thread ) {}
231+
232+
rtc::AsyncResolverInterface *CreateAsyncResolver() override
233+
{
234+
return new CAsyncResolver();
235+
}
236+
};
237+
238+
} // namespace <anonymous>
239+
58240
//-----------------------------------------------------------------------------
59241
// Class to represent an ICE connection
60242
//-----------------------------------------------------------------------------
@@ -201,6 +383,8 @@ CICESession::~CICESession()
201383
delete s_pSocketServer;
202384
s_pSocketServer = nullptr;
203385

386+
JoinResolverThreads();
387+
204388
rtc::CleanupSSL();
205389
}
206390
s_mutex.unlock();
@@ -240,7 +424,7 @@ bool CICESession::BInitializeOnSocketThread( const ICESessionConfig &cfg )
240424

241425
default_network_manager_.reset(new rtc::BasicNetworkManager());
242426
default_socket_factory_.reset(
243-
new rtc::BasicPacketSocketFactory( s_pSocketThread ));
427+
new CPacketSocketFactory( s_pSocketThread ));
244428

245429
webrtc::TurnCustomizer *turn_customizer = nullptr;
246430

0 commit comments

Comments
 (0)