Skip to content

Commit d3aa74a

Browse files
rmacnak-googleCommit Queue
authored andcommitted
[dart:io] Create one IOService port per process instead of per isolate.
Nothing cleaned up this port when the creating isolate died, so the port itself and the last unjoined thread in the corresponding thread pool were leaked until VM shutdown. Cf. 42d796f TEST=testandalone/io/leak_io_service_test Change-Id: Icea27804baf9175b1ff5eb9c50970e0f8d07de2c Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/437160 Reviewed-by: Brian Quinlan <[email protected]> Commit-Queue: Ryan Macnak <[email protected]>
1 parent f856d0b commit d3aa74a

File tree

4 files changed

+48
-2
lines changed

4 files changed

+48
-2
lines changed

runtime/bin/dart_embedder_api_impl.cc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
#include "bin/dartutils.h"
88
#include "bin/eventhandler.h"
9+
#include "bin/io_service.h"
910
#include "bin/isolate_data.h"
1011
#include "bin/process.h"
1112
#include "bin/secure_socket_filter.h"
@@ -54,6 +55,7 @@ void Cleanup() {
5455
bin::SSLFilter::Cleanup();
5556
#endif
5657
bin::Process::Cleanup();
58+
bin::IOService::Cleanup();
5759
}
5860

5961
Dart_Isolate CreateKernelServiceIsolate(const IsolateCreationData& data,

runtime/bin/io_service.cc

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,10 +55,22 @@ void IOServiceCallback(Dart_Port dest_port_id, Dart_CObject* message) {
5555
}
5656

5757
intptr_t IOService::max_concurrency_ = 32;
58+
std::atomic<Dart_Port> IOService::port_ = ILLEGAL_PORT;
5859

5960
Dart_Port IOService::GetServicePort() {
60-
return Dart_NewConcurrentNativePort("IOService", IOServiceCallback,
61-
max_concurrency_);
61+
Dart_Port port = port_;
62+
if (port == ILLEGAL_PORT) {
63+
port = Dart_NewConcurrentNativePort("IOService", IOServiceCallback,
64+
max_concurrency_);
65+
Dart_Port expected = ILLEGAL_PORT;
66+
if (!port_.compare_exchange_strong(expected, port)) {
67+
// Lost the initialization race. Use the winner's port and close our port.
68+
// The winner's port is eventually implicitly closed by VM shutdown.
69+
Dart_CloseNativePort(port);
70+
return expected;
71+
}
72+
}
73+
return port;
6274
}
6375

6476
void FUNCTION_NAME(IOService_NewServicePort)(Dart_NativeArguments args) {

runtime/bin/io_service.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
#error "io_service.h can only be included on builds with IO and SSL enabled"
1010
#endif
1111

12+
#include <atomic>
13+
1214
#include "bin/builtin.h"
1315
#include "bin/utils.h"
1416

@@ -69,12 +71,14 @@ class IOService {
6971
enum { IO_SERVICE_REQUEST_LIST(DECLARE_REQUEST) };
7072

7173
static Dart_Port GetServicePort();
74+
static void Cleanup() { port_ = ILLEGAL_PORT; }
7275

7376
static intptr_t max_concurrency() { return max_concurrency_; }
7477
static void set_max_concurrency(intptr_t value) { max_concurrency_ = value; }
7578

7679
private:
7780
static intptr_t max_concurrency_;
81+
static std::atomic<Dart_Port> port_;
7882

7983
DISALLOW_ALLOCATION();
8084
DISALLOW_IMPLICIT_CONSTRUCTORS(IOService);
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
import "dart:async";
6+
import "dart:io";
7+
import "dart:isolate";
8+
9+
child(replyPort) async {
10+
var ops = <Future>[];
11+
for (var i = 0; i < 32; i++) {
12+
ops.add(File(Platform.executable).stat()); // Uses the IO Service.
13+
}
14+
await Future.wait(ops);
15+
replyPort.send(null);
16+
}
17+
18+
main() {
19+
var pending = 1000;
20+
var port = new RawReceivePort();
21+
port.handler = (_) {
22+
pending--;
23+
if (pending == 0) port.close();
24+
};
25+
for (var i = 0; i < pending; i++) {
26+
Isolate.spawn(child, port.sendPort);
27+
}
28+
}

0 commit comments

Comments
 (0)