diff --git a/runtime/include/dart_api.h b/runtime/include/dart_api.h index 040887b793b..1f2c0c4de3c 100644 --- a/runtime/include/dart_api.h +++ b/runtime/include/dart_api.h @@ -1522,6 +1522,10 @@ DART_EXPORT DART_WARN_UNUSED_RESULT char* Dart_IsolateMakeRunnable( * 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 @@ DART_EXPORT bool Dart_Post(Dart_Port port_id, Dart_Handle object); /** * 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 @@ -1780,6 +1788,16 @@ DART_EXPORT bool Dart_Post(Dart_Port port_id, Dart_Handle object); */ 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. @@ -1790,6 +1808,15 @@ DART_EXPORT Dart_Handle Dart_NewSendPort(Dart_Port port_id); 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 5088b4337c3..2b4c8d4fdfa 100644 --- a/runtime/include/dart_api_dl.h +++ b/runtime/include/dart_api_dl.h @@ -38,6 +38,10 @@ DART_EXPORT intptr_t Dart_InitializeApiDL(void* data); // 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 @@ typedef void (*Dart_NativeMessageHandler_DL)(Dart_Port_DL dest_port_id, /* 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 cb343c0d3f1..5ca0b683c7e 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 708a80e2007..c16130e1866 100644 --- a/runtime/tests/vm/dart/exported_symbols_test.dart +++ b/runtime/tests/vm/dart/exported_symbols_test.dart @@ -248,6 +248,7 @@ main() { "Dart_NewNativePort", "Dart_NewPersistentHandle", "Dart_NewSendPort", + "Dart_NewSendPortEx", "Dart_NewStringFromCString", "Dart_NewStringFromUTF16", "Dart_NewStringFromUTF32", @@ -280,6 +281,7 @@ main() { "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 00000000000..2df80056ecb --- /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(symbol: 'Dart_NewSendPort') +external SendPort newSendPort(int obj); + +@Native)>(symbol: 'Dart_SendPortGetId') +external Object sendPortGetId(Object sendPort, Pointer outId); + +final class PortEx extends Struct { + @Int64() + external int portId; + @Int64() + external int originId; +} + +@Native(symbol: 'Dart_NewSendPortEx') +external SendPort newSendPortEx(PortEx portEx); + +@Native)>( + symbol: 'Dart_SendPortGetIdEx') +external Object sendPortGetIdEx(Object sendPort, Pointer outPortExId); + +class A {} + +main() async { + asyncStart(); + + final rp = ReceivePort(); + rp.close(); + + { + Pointer portId = calloc(); + sendPortGetId(rp.sendPort, portId); + final sendPortThroughAPI = newSendPort(portId.value); + sendPortThroughAPI.send('A'); + Expect.throwsArgumentError(() { + sendPortThroughAPI.send(A()); + }); + calloc.free(portId); + } + + { + Pointer 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 001bb8236ae..b8b7f25632b 100644 --- a/runtime/vm/dart_api_impl.cc +++ b/runtime/vm/dart_api_impl.cc @@ -2157,6 +2157,17 @@ DART_EXPORT Dart_Handle Dart_NewSendPort(Dart_Port port_id) { 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 @@ DART_EXPORT Dart_Handle Dart_SendPortGetId(Dart_Handle port, 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);