Skip to content

Commit 303e2fd

Browse files
bnoordhuisaddaleax
authored andcommittedFeb 13, 2020
src: wrap HostPort in ExclusiveAccess
I found it exceedingly hard to figure out if there is a race condition where one thread reads the inspector agent's HostPort's properties while another modifies them concurrently. I think the answer is "no, there isn't" but with this commit use sites are forced to unwrap the object (and acquire the mutex in the process), making it a great deal easier to reason about correctness. PR-URL: nodejs#31717 Reviewed-By: James M Snell <[email protected]> Reviewed-By: Colin Ihrig <[email protected]> Reviewed-By: Anna Henningsen <[email protected]> Reviewed-By: Tobias Nießen <[email protected]> Reviewed-By: David Carlier <[email protected]>
1 parent b32fa7b commit 303e2fd

9 files changed

+36
-21
lines changed
 

‎src/env-inl.h

+2-1
Original file line numberDiff line numberDiff line change
@@ -743,7 +743,8 @@ inline uint64_t Environment::heap_prof_interval() const {
743743

744744
#endif // HAVE_INSPECTOR
745745

746-
inline std::shared_ptr<HostPort> Environment::inspector_host_port() {
746+
inline
747+
std::shared_ptr<ExclusiveAccess<HostPort>> Environment::inspector_host_port() {
747748
return inspector_host_port_;
748749
}
749750

‎src/env.cc

+2-1
Original file line numberDiff line numberDiff line change
@@ -321,7 +321,8 @@ Environment::Environment(IsolateData* isolate_data,
321321
// part of the per-Isolate option set, for which in turn the defaults are
322322
// part of the per-process option set.
323323
options_.reset(new EnvironmentOptions(*isolate_data->options()->per_env));
324-
inspector_host_port_.reset(new HostPort(options_->debug_options().host_port));
324+
inspector_host_port_.reset(
325+
new ExclusiveAccess<HostPort>(options_->debug_options().host_port));
325326

326327
#if HAVE_INSPECTOR
327328
// We can only create the inspector agent after having cloned the options.

‎src/env.h

+2-2
Original file line numberDiff line numberDiff line change
@@ -1227,7 +1227,7 @@ class Environment : public MemoryRetainer {
12271227
void* data);
12281228

12291229
inline std::shared_ptr<EnvironmentOptions> options();
1230-
inline std::shared_ptr<HostPort> inspector_host_port();
1230+
inline std::shared_ptr<ExclusiveAccess<HostPort>> inspector_host_port();
12311231

12321232
// The BaseObject count is a debugging helper that makes sure that there are
12331233
// no memory leaks caused by BaseObjects staying alive longer than expected
@@ -1326,7 +1326,7 @@ class Environment : public MemoryRetainer {
13261326
// server starts listening), but when the inspector server starts
13271327
// the inspector_host_port_->port() will be the actual port being
13281328
// used.
1329-
std::shared_ptr<HostPort> inspector_host_port_;
1329+
std::shared_ptr<ExclusiveAccess<HostPort>> inspector_host_port_;
13301330
std::vector<std::string> exec_argv_;
13311331
std::vector<std::string> argv_;
13321332
std::string exec_path_;

‎src/inspector_agent.cc

+1-1
Original file line numberDiff line numberDiff line change
@@ -757,7 +757,7 @@ Agent::~Agent() {}
757757

758758
bool Agent::Start(const std::string& path,
759759
const DebugOptions& options,
760-
std::shared_ptr<HostPort> host_port,
760+
std::shared_ptr<ExclusiveAccess<HostPort>> host_port,
761761
bool is_main) {
762762
path_ = path;
763763
debug_options_ = options;

‎src/inspector_agent.h

+3-3
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ class Agent {
4848
// Create client_, may create io_ if option enabled
4949
bool Start(const std::string& path,
5050
const DebugOptions& options,
51-
std::shared_ptr<HostPort> host_port,
51+
std::shared_ptr<ExclusiveAccess<HostPort>> host_port,
5252
bool is_main);
5353
// Stop and destroy io_
5454
void Stop();
@@ -110,7 +110,7 @@ class Agent {
110110
void RequestIoThreadStart();
111111

112112
const DebugOptions& options() { return debug_options_; }
113-
std::shared_ptr<HostPort> host_port() { return host_port_; }
113+
std::shared_ptr<ExclusiveAccess<HostPort>> host_port() { return host_port_; }
114114
void ContextCreated(v8::Local<v8::Context> context, const ContextInfo& info);
115115

116116
// Interface for interacting with inspectors in worker threads
@@ -133,7 +133,7 @@ class Agent {
133133
// pointer which is meant to store the actual host and port of the inspector
134134
// server.
135135
DebugOptions debug_options_;
136-
std::shared_ptr<HostPort> host_port_;
136+
std::shared_ptr<ExclusiveAccess<HostPort>> host_port_;
137137

138138
bool pending_enable_async_hook_ = false;
139139
bool pending_disable_async_hook_ = false;

‎src/inspector_io.cc

+15-6
Original file line numberDiff line numberDiff line change
@@ -239,7 +239,7 @@ class InspectorIoDelegate: public node::inspector::SocketServerDelegate {
239239
std::unique_ptr<InspectorIo> InspectorIo::Start(
240240
std::shared_ptr<MainThreadHandle> main_thread,
241241
const std::string& path,
242-
std::shared_ptr<HostPort> host_port,
242+
std::shared_ptr<ExclusiveAccess<HostPort>> host_port,
243243
const InspectPublishUid& inspect_publish_uid) {
244244
auto io = std::unique_ptr<InspectorIo>(
245245
new InspectorIo(main_thread,
@@ -254,7 +254,7 @@ std::unique_ptr<InspectorIo> InspectorIo::Start(
254254

255255
InspectorIo::InspectorIo(std::shared_ptr<MainThreadHandle> main_thread,
256256
const std::string& path,
257-
std::shared_ptr<HostPort> host_port,
257+
std::shared_ptr<ExclusiveAccess<HostPort>> host_port,
258258
const InspectPublishUid& inspect_publish_uid)
259259
: main_thread_(main_thread),
260260
host_port_(host_port),
@@ -293,18 +293,26 @@ void InspectorIo::ThreadMain() {
293293
std::unique_ptr<InspectorIoDelegate> delegate(
294294
new InspectorIoDelegate(queue, main_thread_, id_,
295295
script_path, script_name_));
296+
std::string host;
297+
int port;
298+
{
299+
ExclusiveAccess<HostPort>::Scoped host_port(host_port_);
300+
host = host_port->host();
301+
port = host_port->port();
302+
}
296303
InspectorSocketServer server(std::move(delegate),
297304
&loop,
298-
host_port_->host(),
299-
host_port_->port(),
305+
std::move(host),
306+
port,
300307
inspect_publish_uid_);
301308
request_queue_ = queue->handle();
302309
// Its lifetime is now that of the server delegate
303310
queue.reset();
304311
{
305312
Mutex::ScopedLock scoped_lock(thread_start_lock_);
306313
if (server.Start()) {
307-
host_port_->set_port(server.Port());
314+
ExclusiveAccess<HostPort>::Scoped host_port(host_port_);
315+
host_port->set_port(server.Port());
308316
}
309317
thread_start_condition_.Broadcast(scoped_lock);
310318
}
@@ -313,7 +321,8 @@ void InspectorIo::ThreadMain() {
313321
}
314322

315323
std::string InspectorIo::GetWsUrl() const {
316-
return FormatWsAddress(host_port_->host(), host_port_->port(), id_, true);
324+
ExclusiveAccess<HostPort>::Scoped host_port(host_port_);
325+
return FormatWsAddress(host_port->host(), host_port->port(), id_, true);
317326
}
318327

319328
InspectorIoDelegate::InspectorIoDelegate(

‎src/inspector_io.h

+3-3
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ class InspectorIo {
3030
static std::unique_ptr<InspectorIo> Start(
3131
std::shared_ptr<MainThreadHandle> main_thread,
3232
const std::string& path,
33-
std::shared_ptr<HostPort> host_port,
33+
std::shared_ptr<ExclusiveAccess<HostPort>> host_port,
3434
const InspectPublishUid& inspect_publish_uid);
3535

3636
// Will block till the transport thread shuts down
@@ -42,7 +42,7 @@ class InspectorIo {
4242
private:
4343
InspectorIo(std::shared_ptr<MainThreadHandle> handle,
4444
const std::string& path,
45-
std::shared_ptr<HostPort> host_port,
45+
std::shared_ptr<ExclusiveAccess<HostPort>> host_port,
4646
const InspectPublishUid& inspect_publish_uid);
4747

4848
// Wrapper for agent->ThreadMain()
@@ -57,7 +57,7 @@ class InspectorIo {
5757
// Used to post on a frontend interface thread, lives while the server is
5858
// running
5959
std::shared_ptr<RequestQueue> request_queue_;
60-
std::shared_ptr<HostPort> host_port_;
60+
std::shared_ptr<ExclusiveAccess<HostPort>> host_port_;
6161
InspectPublishUid inspect_publish_uid_;
6262

6363
// The IO thread runs its own uv_loop to implement the TCP server off

‎src/inspector_js_api.cc

+4-2
Original file line numberDiff line numberDiff line change
@@ -278,12 +278,14 @@ void Open(const FunctionCallbackInfo<Value>& args) {
278278

279279
if (args.Length() > 0 && args[0]->IsUint32()) {
280280
uint32_t port = args[0].As<Uint32>()->Value();
281-
agent->host_port()->set_port(static_cast<int>(port));
281+
ExclusiveAccess<HostPort>::Scoped host_port(agent->host_port());
282+
host_port->set_port(static_cast<int>(port));
282283
}
283284

284285
if (args.Length() > 1 && args[1]->IsString()) {
285286
Utf8Value host(env->isolate(), args[1].As<String>());
286-
agent->host_port()->set_host(*host);
287+
ExclusiveAccess<HostPort>::Scoped host_port(agent->host_port());
288+
host_port->set_host(*host);
287289
}
288290

289291
agent->StartIoThread();

‎src/node_process_object.cc

+4-2
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,8 @@ static void ProcessTitleSetter(Local<Name> property,
5151
static void DebugPortGetter(Local<Name> property,
5252
const PropertyCallbackInfo<Value>& info) {
5353
Environment* env = Environment::GetCurrent(info);
54-
int port = env->inspector_host_port()->port();
54+
ExclusiveAccess<HostPort>::Scoped host_port(env->inspector_host_port());
55+
int port = host_port->port();
5556
info.GetReturnValue().Set(port);
5657
}
5758

@@ -60,7 +61,8 @@ static void DebugPortSetter(Local<Name> property,
6061
const PropertyCallbackInfo<void>& info) {
6162
Environment* env = Environment::GetCurrent(info);
6263
int32_t port = value->Int32Value(env->context()).FromMaybe(0);
63-
env->inspector_host_port()->set_port(static_cast<int>(port));
64+
ExclusiveAccess<HostPort>::Scoped host_port(env->inspector_host_port());
65+
host_port->set_port(static_cast<int>(port));
6466
}
6567

6668
static void GetParentProcessId(Local<Name> property,

0 commit comments

Comments
 (0)
Please sign in to comment.