[vm/sendports] Introduce an api that can be safely used to rebuild SendPort.
Existing api is not aware of origin_id, which leads to problems when a port gets closed, origin_id can't be properly restored.
Fixes https://github.com/dart-lang/sdk/issues/55605
TEST=vm/dart/sendport_api_test
Change-Id: Ia8a5978a968f6ea643d7921c64146cebf17d2e0e
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/365120
Reviewed-by: Ryan Macnak <rmacnak@google.com>
Commit-Queue: Alexander Aprelev <aam@google.com>
diff --git a/runtime/include/dart_api.h b/runtime/include/dart_api.h
index 040887b..1f2c0c4 100644
--- a/runtime/include/dart_api.h
+++ b/runtime/include/dart_api.h
@@ -1522,6 +1522,10 @@
* A port is used to send or receive inter-isolate messages
*/
typedef int64_t Dart_Port;
+typedef struct {
+ int64_t port_id;
+ int64_t origin_id;
+} Dart_PortEx;
/**
* ILLEGAL_PORT is a port number guaranteed never to be associated with a valid
@@ -1773,6 +1777,10 @@
/**
* Returns a new SendPort with the provided port id.
*
+ * If there is a possibility of a port closing since port_id was acquired
+ * for a SendPort, one should use Dart_NewSendPortEx and
+ * Dart_SendPortGetIdEx.
+ *
* \param port_id The destination port.
*
* \return A new SendPort if no errors occurs. Otherwise returns
@@ -1781,6 +1789,16 @@
DART_EXPORT Dart_Handle Dart_NewSendPort(Dart_Port port_id);
/**
+ * Returns a new SendPort with the provided port id and origin id.
+ *
+ * \param portex_id The destination composte port id.
+ *
+ * \return A new SendPort if no errors occurs. Otherwise returns
+ * an error handle.
+ */
+DART_EXPORT Dart_Handle Dart_NewSendPortEx(Dart_PortEx portex_id);
+
+/**
* Gets the SendPort id for the provided SendPort.
* \param port A SendPort object whose id is desired.
* \param port_id Returns the id of the SendPort.
@@ -1790,6 +1808,15 @@
DART_EXPORT Dart_Handle Dart_SendPortGetId(Dart_Handle port,
Dart_Port* port_id);
+/**
+ * Gets the SendPort and Origin ids for the provided SendPort.
+ * \param port A SendPort object whose id is desired.
+ * \param portex_id Returns composite id of the SendPort.
+ * \return Success if no error occurs. Otherwise returns
+ * an error handle.
+ */
+DART_EXPORT Dart_Handle Dart_SendPortGetIdEx(Dart_Handle port,
+ Dart_PortEx* portex_id);
/*
* ======
* Scopes
diff --git a/runtime/include/dart_api_dl.h b/runtime/include/dart_api_dl.h
index 5088b433..2b4c8d4 100644
--- a/runtime/include/dart_api_dl.h
+++ b/runtime/include/dart_api_dl.h
@@ -38,6 +38,10 @@
// are typechecked nominally in C/C++, so they are not copied, instead a
// comment is added to their definition.
typedef int64_t Dart_Port_DL;
+typedef struct {
+ int64_t port_id;
+ int64_t origin_id;
+} Dart_PortEx_DL;
typedef void (*Dart_NativeMessageHandler_DL)(Dart_Port_DL dest_port_id,
Dart_CObject* message);
@@ -94,8 +98,11 @@
/* Dart_Port */ \
F(Dart_Post, bool, (Dart_Port_DL port_id, Dart_Handle object)) \
F(Dart_NewSendPort, Dart_Handle, (Dart_Port_DL port_id)) \
+ F(Dart_NewSendPortEx, Dart_Handle, (Dart_PortEx_DL portex_id)) \
F(Dart_SendPortGetId, Dart_Handle, \
(Dart_Handle port, Dart_Port_DL * port_id)) \
+ F(Dart_SendPortGetIdEx, Dart_Handle, \
+ (Dart_Handle port, Dart_PortEx_DL * portex_id)) \
/* Scopes */ \
F(Dart_EnterScope, void, (void)) \
F(Dart_ExitScope, void, (void)) \
diff --git a/runtime/include/dart_version.h b/runtime/include/dart_version.h
index cb343c0..5ca0b68 100644
--- a/runtime/include/dart_version.h
+++ b/runtime/include/dart_version.h
@@ -11,6 +11,6 @@
// On backwards compatible changes the minor version is increased.
// The versioning covers the symbols exposed in dart_api_dl.h
#define DART_API_DL_MAJOR_VERSION 2
-#define DART_API_DL_MINOR_VERSION 4
+#define DART_API_DL_MINOR_VERSION 5
#endif /* RUNTIME_INCLUDE_DART_VERSION_H_ */ /* NOLINT */
diff --git a/runtime/tests/vm/dart/exported_symbols_test.dart b/runtime/tests/vm/dart/exported_symbols_test.dart
index 708a80e..c16130e 100644
--- a/runtime/tests/vm/dart/exported_symbols_test.dart
+++ b/runtime/tests/vm/dart/exported_symbols_test.dart
@@ -248,6 +248,7 @@
"Dart_NewNativePort",
"Dart_NewPersistentHandle",
"Dart_NewSendPort",
+ "Dart_NewSendPortEx",
"Dart_NewStringFromCString",
"Dart_NewStringFromUTF16",
"Dart_NewStringFromUTF32",
@@ -280,6 +281,7 @@
"Dart_RunLoopAsync",
"Dart_ScopeAllocate",
"Dart_SendPortGetId",
+ "Dart_SendPortGetIdEx",
"Dart_ServiceSendDataEvent",
"Dart_SetBooleanReturnValue",
"Dart_SetCurrentUserTag",
diff --git a/runtime/tests/vm/dart/sendport_api_test.dart b/runtime/tests/vm/dart/sendport_api_test.dart
new file mode 100644
index 0000000..2df8005
--- /dev/null
+++ b/runtime/tests/vm/dart/sendport_api_test.dart
@@ -0,0 +1,61 @@
+// Copyright (c) 2024, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'dart:isolate';
+import 'dart:ffi';
+
+import 'package:async_helper/async_helper.dart' show asyncEnd, asyncStart;
+import 'package:expect/expect.dart';
+import 'package:ffi/ffi.dart';
+
+@Native<Handle Function(Int64 port)>(symbol: 'Dart_NewSendPort')
+external SendPort newSendPort(int obj);
+
+@Native<Handle Function(Handle, Pointer<Int64>)>(symbol: 'Dart_SendPortGetId')
+external Object sendPortGetId(Object sendPort, Pointer<Int64> outId);
+
+final class PortEx extends Struct {
+ @Int64()
+ external int portId;
+ @Int64()
+ external int originId;
+}
+
+@Native<Handle Function(PortEx portex)>(symbol: 'Dart_NewSendPortEx')
+external SendPort newSendPortEx(PortEx portEx);
+
+@Native<Handle Function(Handle, Pointer<PortEx>)>(
+ symbol: 'Dart_SendPortGetIdEx')
+external Object sendPortGetIdEx(Object sendPort, Pointer<PortEx> outPortExId);
+
+class A {}
+
+main() async {
+ asyncStart();
+
+ final rp = ReceivePort();
+ rp.close();
+
+ {
+ Pointer<Int64> portId = calloc();
+ sendPortGetId(rp.sendPort, portId);
+ final sendPortThroughAPI = newSendPort(portId.value);
+ sendPortThroughAPI.send('A');
+ Expect.throwsArgumentError(() {
+ sendPortThroughAPI.send(A());
+ });
+ calloc.free(portId);
+ }
+
+ {
+ Pointer<PortEx> portExId = calloc();
+ sendPortGetIdEx(rp.sendPort, portExId);
+ final sendPortThroughAPI = newSendPortEx(portExId[0]);
+ sendPortThroughAPI.send('A');
+ sendPortThroughAPI.send(A());
+ calloc.free(portExId);
+ }
+
+ asyncEnd();
+}
diff --git a/runtime/vm/dart_api_impl.cc b/runtime/vm/dart_api_impl.cc
index 001bb82..b8b7f25 100644
--- a/runtime/vm/dart_api_impl.cc
+++ b/runtime/vm/dart_api_impl.cc
@@ -2157,6 +2157,17 @@
return Api::NewHandle(T, SendPort::New(port_id, origin_id));
}
+DART_EXPORT Dart_Handle Dart_NewSendPortEx(Dart_PortEx portex_id) {
+ DARTSCOPE(Thread::Current());
+ CHECK_CALLBACK_STATE(T);
+ if (portex_id.port_id == ILLEGAL_PORT) {
+ return Api::NewError("%s: illegal port_id %" Pd64 ".", CURRENT_FUNC,
+ portex_id.port_id);
+ }
+ return Api::NewHandle(T,
+ SendPort::New(portex_id.port_id, portex_id.origin_id));
+}
+
DART_EXPORT Dart_Handle Dart_SendPortGetId(Dart_Handle port,
Dart_Port* port_id) {
DARTSCOPE(Thread::Current());
@@ -2173,6 +2184,23 @@
return Api::Success();
}
+DART_EXPORT Dart_Handle Dart_SendPortGetIdEx(Dart_Handle port,
+ Dart_PortEx* portex_id) {
+ DARTSCOPE(Thread::Current());
+ CHECK_CALLBACK_STATE(T);
+ API_TIMELINE_DURATION(T);
+ const SendPort& send_port = Api::UnwrapSendPortHandle(Z, port);
+ if (send_port.IsNull()) {
+ RETURN_TYPE_ERROR(Z, port, SendPort);
+ }
+ if (portex_id == nullptr) {
+ RETURN_NULL_ERROR(port_id);
+ }
+ portex_id->port_id = send_port.Id();
+ portex_id->origin_id = send_port.origin_id();
+ return Api::Success();
+}
+
DART_EXPORT Dart_Port Dart_GetMainPortId() {
Isolate* isolate = Isolate::Current();
CHECK_ISOLATE(isolate);