From 8cf6d201c492e2d5910226a890e2635b27e3ae90 Mon Sep 17 00:00:00 2001 From: Joe George Date: Fri, 19 Jul 2024 11:00:01 -0400 Subject: [PATCH] Port Ice Swift src to use async/await --- cpp/src/slice2swift/Gen.cpp | 56 ++++---- cpp/src/slice2swift/SwiftUtil.cpp | 49 ++++--- cpp/src/slice2swift/SwiftUtil.h | 2 +- swift/src/Ice/AdminFacetFactory.swift | 15 ++- swift/src/Ice/Blobject.swift | 14 +- swift/src/Ice/BlobjectAsync.swift | 18 +-- swift/src/Ice/Communicator.swift | 5 +- swift/src/Ice/CommunicatorI.swift | 8 +- swift/src/Ice/Connection.swift | 4 +- swift/src/Ice/ConnectionI.swift | 8 +- swift/src/Ice/Dispatcher.swift | 6 +- swift/src/Ice/IceSwift.h | 5 - swift/src/Ice/Object.swift | 73 ++++------- swift/src/Ice/ObjectAdapterI.swift | 14 +- swift/src/Ice/Proxy.swift | 138 +++++++++----------- swift/src/Ice/ServantManager.swift | 45 +++---- swift/src/IceImpl/DispatchAdapter.mm | 15 ++- swift/src/IceImpl/ObjectPrx.mm | 12 +- swift/src/IceImpl/include/DispatchAdapter.h | 27 ++-- swift/src/IceImpl/include/ObjectPrx.h | 2 +- 20 files changed, 241 insertions(+), 275 deletions(-) delete mode 100644 swift/src/Ice/IceSwift.h diff --git a/cpp/src/slice2swift/Gen.cpp b/cpp/src/slice2swift/Gen.cpp index 6b50948c472..545919ceb6a 100644 --- a/cpp/src/slice2swift/Gen.cpp +++ b/cpp/src/slice2swift/Gen.cpp @@ -133,14 +133,6 @@ Gen::ImportVisitor::visitModuleStart(const ModulePtr& p) } } - // - // Add PromiseKit import for interfaces and local interfaces which contain "async-oneway" metadata - // - if (p->contains()) - { - addImport("PromiseKit"); - } - return true; } @@ -1470,45 +1462,61 @@ Gen::ObjectVisitor::visitInterfaceDefStart(const InterfaceDefPtr& p) const OperationList allOps = p->allOperations(); - StringList allOpNames; + vector> allOpNamesAndAmdPairs; transform( allOps.begin(), allOps.end(), - back_inserter(allOpNames), - [](const ContainedPtr& it) { return it->name(); }); - - allOpNames.push_back("ice_id"); - allOpNames.push_back("ice_ids"); - allOpNames.push_back("ice_isA"); - allOpNames.push_back("ice_ping"); - allOpNames.sort(); - allOpNames.unique(); + back_inserter(allOpNamesAndAmdPairs), + [](const OperationPtr& it) { return make_tuple(it->name(), operationIsAmd(it)); }); + + allOpNamesAndAmdPairs.emplace_back("ice_id", false); + allOpNamesAndAmdPairs.emplace_back("ice_ids", false); + allOpNamesAndAmdPairs.emplace_back("ice_isA", false); + allOpNamesAndAmdPairs.emplace_back("ice_ping", false); + + // Sort the operations by name + sort( + allOpNamesAndAmdPairs.begin(), + allOpNamesAndAmdPairs.end(), + [](const auto& a, const auto& b) { return a.first < b.first; }); + + // Remove duplicates (we only need to check the name) + allOpNamesAndAmdPairs.erase( + unique( + allOpNamesAndAmdPairs.begin(), + allOpNamesAndAmdPairs.end(), + [](const auto& a, const auto& b) { return a.first == b.first; }), + allOpNamesAndAmdPairs.end()); out << sp; out << nl; - out << "public func dispatch(_ request: Ice.IncomingRequest) -> PromiseKit.Promise"; + out << "public func dispatch(_ request: Ice.IncomingRequest) async throws -> Ice.OutgoingResponse"; out << sb; out << nl; out << "switch request.current.operation"; out << sb; out.dec(); // to align case with switch - for (const auto& opName : allOpNames) + for (const auto& [opName, isAmd] : allOpNamesAndAmdPairs) { out << nl << "case \"" << opName << "\":"; out.inc(); if (opName == "ice_id" || opName == "ice_ids" || opName == "ice_isA" || opName == "ice_ping") { - out << nl << "(servant as? Ice.Object ?? " << disp << ".defaultObject)._iceD_" << opName << "(request)"; + out << nl << "try (servant as? Ice.Object ?? " << disp << ".defaultObject)._iceD_" << opName << "(request)"; + } + else if (isAmd) + { + out << nl << "try await servant._iceD_" << opName << "(request)"; } else { - out << nl << "servant._iceD_" << opName << "(request)"; + out << nl << "try servant._iceD_" << opName << "(request)"; } out.dec(); } out << nl << "default:"; out.inc(); - out << nl << "PromiseKit.Promise(error: Ice.OperationNotExistException())"; + out << nl << "throw Ice.OperationNotExistException()"; // missing dec to compensate for the extra dec after switch sb out << eb; out << eb; @@ -1591,7 +1599,7 @@ Gen::ObjectVisitor::visitOperation(const OperationPtr& op) if (isAmd) { - out << " -> PromiseKit.Promise<" << (allOutParams.size() > 0 ? operationReturnType(op) : "Swift.Void") << ">"; + out << " async throws -> " << (allOutParams.size() > 0 ? operationReturnType(op) : "Swift.Void"); } else { diff --git a/cpp/src/slice2swift/SwiftUtil.cpp b/cpp/src/slice2swift/SwiftUtil.cpp index 860422b7628..e549a30c503 100644 --- a/cpp/src/slice2swift/SwiftUtil.cpp +++ b/cpp/src/slice2swift/SwiftUtil.cpp @@ -725,8 +725,7 @@ SwiftGenerator::writeOpDocSummary(IceInternal::Output& out, const OperationPtr& } out << nl << "///"; - out << nl << "/// - returns: `PromiseKit.Promise<" << operationReturnType(p, typeCtx) - << ">` - The result of the operation"; + out << nl << "/// - returns: `" << operationReturnType(p, typeCtx) << "` - The result of the operation"; } else { @@ -2510,7 +2509,7 @@ SwiftGenerator::writeProxyAsyncOperation(::IceInternal::Output& out, const Opera out << "sent: ((Swift.Bool) -> Swift.Void)? = nil"; out << epar; - out << " -> PromiseKit.Promise<"; + out << " async throws -> "; if (allOutParams.empty()) { out << "Swift.Void"; @@ -2519,7 +2518,6 @@ SwiftGenerator::writeProxyAsyncOperation(::IceInternal::Output& out, const Opera { out << operationReturnType(op); } - out << ">"; out << sb; @@ -2527,7 +2525,7 @@ SwiftGenerator::writeProxyAsyncOperation(::IceInternal::Output& out, const Opera // Invoke // out << sp; - out << nl << "return _impl._invokeAsync("; + out << nl << "return try await _impl._invokeAsync("; out.useCurrentPosAsIndent(); out << "operation: \"" << op->name() << "\","; @@ -2579,14 +2577,14 @@ SwiftGenerator::writeDispatchOperation(::IceInternal::Output& out, const Operati const string swiftModule = getSwiftModule(getTopLevelModule(dynamic_pointer_cast(op))); - out << sp; - out << nl << "public func _iceD_" << opName - << "(_ request: Ice.IncomingRequest) -> PromiseKit.Promise"; + const bool isAmd = operationIsAmd(op); - out << sb; + out << sp; + out << nl << "public func _iceD_" << opName << "(_ request: Ice.IncomingRequest)" << (isAmd ? " async" : "") + << " throws -> Ice.OutgoingResponse"; - out << nl << "do"; out << sb; + out << nl; // TODO: check operation mode @@ -2603,30 +2601,33 @@ SwiftGenerator::writeDispatchOperation(::IceInternal::Output& out, const Operati if (operationIsAmd(op)) { - out << nl << "return self." << opName << "Async("; + out << nl; + if (!outParams.empty()) + { + out << "let result = "; + } + + out << "try await self." << opName << "Async("; out << nl << " "; // inc/dec doesn't work for an unknown reason for (const auto& q : inParams) { out << q.name << ": iceP_" << q.name << ", "; } - out << "current: request.current"; - out << nl; - out << ").map(on: nil)"; - out << sb; + out << "current: request.current)"; + if (outParams.empty()) { - out << nl << "request.current.makeEmptyOutgoingResponse()"; + out << nl << "return request.current.makeEmptyOutgoingResponse()"; } else { - out << " result in "; - out << nl << "request.current.makeOutgoingResponse(result, formatType:" << opFormatTypeToString(op) << ")"; + out << nl << "return request.current.makeOutgoingResponse(result, formatType:" << opFormatTypeToString(op) + << ")"; out << sb; out << " ostr, value in "; writeMarshalAsyncOutParams(out, op); out << eb; } - out << eb; } else { @@ -2647,7 +2648,7 @@ SwiftGenerator::writeDispatchOperation(::IceInternal::Output& out, const Operati if (outParams.empty()) { - out << nl << "return PromiseKit.Promise.value(request.current.makeEmptyOutgoingResponse())"; + out << nl << "return request.current.makeEmptyOutgoingResponse()"; } else { @@ -2657,14 +2658,10 @@ SwiftGenerator::writeDispatchOperation(::IceInternal::Output& out, const Operati << ")"; writeMarshalOutParams(out, op); out << nl << "ostr.endEncapsulation()"; - out << nl << "return PromiseKit.Promise.value(Ice.OutgoingResponse(ostr))"; + out << nl << "return Ice.OutgoingResponse(ostr)"; } } - out << eb; - out << " catch"; - out << sb; - out << nl << "return PromiseKit.Promise(error: error)"; - out << eb; + out << eb; } diff --git a/cpp/src/slice2swift/SwiftUtil.h b/cpp/src/slice2swift/SwiftUtil.h index e4f9ae999e8..be36be98436 100644 --- a/cpp/src/slice2swift/SwiftUtil.h +++ b/cpp/src/slice2swift/SwiftUtil.h @@ -83,7 +83,7 @@ namespace Slice std::string operationReturnDeclaration(const OperationPtr&); std::string operationInParamsDeclaration(const OperationPtr&); - bool operationIsAmd(const OperationPtr&); + static bool operationIsAmd(const OperationPtr&); ParamInfoList getAllInParams(const OperationPtr&, int = 0); void getInParams(const OperationPtr&, ParamInfoList&, ParamInfoList&); diff --git a/swift/src/Ice/AdminFacetFactory.swift b/swift/src/Ice/AdminFacetFactory.swift index a8eb22860c3..0dd456bf930 100644 --- a/swift/src/Ice/AdminFacetFactory.swift +++ b/swift/src/Ice/AdminFacetFactory.swift @@ -25,8 +25,8 @@ class AdminFacetFacade: ICEDispatchAdapter { requestId: Int32, encodingMajor: UInt8, encodingMinor: UInt8, - completionHandler: @escaping ICEOutgoingResponse - ) { + outgoingResponseHandler: @escaping ICEOutgoingResponse + ) async { let objectAdapter = adapter.getSwiftObject(ObjectAdapterI.self) { let oa = ObjectAdapterI(handle: adapter, communicator: communicator) @@ -58,20 +58,21 @@ class AdminFacetFacade: ICEDispatchAdapter { let request = IncomingRequest(current: current, inputStream: istr) - // Dispatch directly to the servant. - dispatcher.dispatch(request).map { response in + do { + // Dispatch directly to the servant. + let response = try await dispatcher.dispatch(request) response.outputStream.finished().withUnsafeBytes { - completionHandler( + outgoingResponseHandler( response.replyStatus.rawValue, response.exceptionId, response.exceptionMessage, $0.baseAddress!, $0.count) } - }.catch { error in + } catch { let response = current.makeOutgoingResponse(error: error) response.outputStream.finished().withUnsafeBytes { - completionHandler( + outgoingResponseHandler( response.replyStatus.rawValue, response.exceptionId, response.exceptionMessage, diff --git a/swift/src/Ice/Blobject.swift b/swift/src/Ice/Blobject.swift index 568cf1debf3..0bad909d3f3 100644 --- a/swift/src/Ice/Blobject.swift +++ b/swift/src/Ice/Blobject.swift @@ -1,7 +1,6 @@ // Copyright (c) ZeroC, Inc. import Foundation -import PromiseKit /// Base protocol for dynamic dispatch servants. public protocol Blobject { @@ -31,14 +30,9 @@ public struct BlobjectDisp: Dispatcher { self.servant = servant } - public func dispatch(_ request: IncomingRequest) -> Promise { - do { - let (inEncaps, _) = try request.inputStream.readEncapsulation() - let result = try servant.ice_invoke(inEncaps: inEncaps, current: request.current) - return Promise.value( - request.current.makeOutgoingResponse(ok: result.ok, encapsulation: result.outParams)) - } catch { - return Promise(error: error) - } + public func dispatch(_ request: IncomingRequest) async throws -> OutgoingResponse { + let (inEncaps, _) = try request.inputStream.readEncapsulation() + let result = try servant.ice_invoke(inEncaps: inEncaps, current: request.current) + return request.current.makeOutgoingResponse(ok: result.ok, encapsulation: result.outParams) } } diff --git a/swift/src/Ice/BlobjectAsync.swift b/swift/src/Ice/BlobjectAsync.swift index dccfcb4dabc..6398c02073d 100644 --- a/swift/src/Ice/BlobjectAsync.swift +++ b/swift/src/Ice/BlobjectAsync.swift @@ -1,7 +1,6 @@ // Copyright (c) ZeroC, Inc. import Foundation -import PromiseKit /// Base protocol for dynamic asynchronous dispatch servants. public protocol BlobjectAsync { @@ -11,7 +10,7 @@ public protocol BlobjectAsync { /// /// - parameter current: `Ice.Current` - The Current object to pass to the operation. /// - /// - returns: `PromiseKit.Promise<(ok: Bool, outParams: Data)>` - The result of the operation. + /// - returns: `(ok: Bool, outParams: Data)` - The result of the operation. /// /// - ok: `Bool` - True if the operation completed successfully, false if /// the operation raised a user exception (in this case, outParams @@ -20,7 +19,7 @@ public protocol BlobjectAsync { /// /// - outParams: `Data` - The encoded out-parameters and return value /// for the operation. The return value follows any out-parameters. - func ice_invokeAsync(inEncaps: Data, current: Current) -> Promise<(ok: Bool, outParams: Data)> + func ice_invokeAsync(inEncaps: Data, current: Current) async throws -> (ok: Bool, outParams: Data) } /// Request dispatcher for BlobjectAsync servants. @@ -31,14 +30,9 @@ public struct BlobjectAsyncDisp: Dispatcher { self.servant = servant } - public func dispatch(_ request: IncomingRequest) -> Promise { - do { - let (inEncaps, _) = try request.inputStream.readEncapsulation() - return servant.ice_invokeAsync(inEncaps: inEncaps, current: request.current).map(on: nil) { result in - request.current.makeOutgoingResponse(ok: result.ok, encapsulation: result.outParams) - } - } catch { - return Promise(error: error) - } + public func dispatch(_ request: IncomingRequest) async throws -> OutgoingResponse { + let (inEncaps, _) = try request.inputStream.readEncapsulation() + let result = try await servant.ice_invokeAsync(inEncaps: inEncaps, current: request.current) + return request.current.makeOutgoingResponse(ok: result.ok, encapsulation: result.outParams) } } diff --git a/swift/src/Ice/Communicator.swift b/swift/src/Ice/Communicator.swift index d5d6726a09a..803e3e67d5d 100644 --- a/swift/src/Ice/Communicator.swift +++ b/swift/src/Ice/Communicator.swift @@ -1,7 +1,6 @@ // Copyright (c) ZeroC, Inc. import Foundation -import PromiseKit /// The central object in Ice. One or more communicators can be instantiated for an Ice application. Communicator /// instantiation is language-specific, and not specified in Slice code. @@ -183,14 +182,12 @@ public protocol Communicator: AnyObject { /// to dispatch the sent callback /// /// - parameter sent: `((Bool) -> Void)` - Optional sent callback. - /// - /// - returns: `PromiseKit.Promise<>` - The result of the operation func flushBatchRequestsAsync( _ compress: CompressBatch, sentOn: Dispatch.DispatchQueue?, sentFlags: Dispatch.DispatchWorkItemFlags?, sent: ((Bool) -> Void)? - ) -> PromiseKit.Promise + ) async throws /// Add the Admin object with all its facets to the provided object adapter. If Ice.Admin.ServerId is /// set and the provided object adapter has a Locator, createAdmin registers the Admin's Process facet with diff --git a/swift/src/Ice/CommunicatorI.swift b/swift/src/Ice/CommunicatorI.swift index bf287edf20f..5521bac56e3 100644 --- a/swift/src/Ice/CommunicatorI.swift +++ b/swift/src/Ice/CommunicatorI.swift @@ -275,15 +275,15 @@ extension Communicator { sentOn: DispatchQueue? = nil, sentFlags: DispatchWorkItemFlags? = nil, sent: ((Bool) -> Void)? = nil - ) -> Promise { + ) async throws { let impl = self as! CommunicatorI let sentCB = createSentCallback(sentOn: sentOn, sentFlags: sentFlags, sent: sent) - return Promise { seal in + return try await withCheckedThrowingContinuation { continuation in impl.handle.flushBatchRequestsAsync( compress.rawValue, - exception: { seal.reject($0) }, + exception: { continuation.resume(throwing: $0) }, sent: { - seal.fulfill(()) + continuation.resume(returning: ()) if let sentCB = sentCB { sentCB($0) } diff --git a/swift/src/Ice/Connection.swift b/swift/src/Ice/Connection.swift index 741c2153fdb..e951ffe5b22 100644 --- a/swift/src/Ice/Connection.swift +++ b/swift/src/Ice/Connection.swift @@ -207,14 +207,12 @@ public protocol Connection: AnyObject, CustomStringConvertible { /// to dispatch the sent callback /// /// - parameter sent: `((Bool) -> Void)` - Optional sent callback. - /// - /// - returns: `PromiseKit.Promise<>` - The result of the operation func flushBatchRequestsAsync( _ compress: CompressBatch, sentOn: Dispatch.DispatchQueue?, sentFlags: Dispatch.DispatchWorkItemFlags?, sent: ((Bool) -> Void)? - ) -> PromiseKit.Promise + ) async throws /// Set a close callback on the connection. The callback is called by the connection when it's closed. The callback /// is called from the Ice thread pool associated with the connection. If the callback needs more information about diff --git a/swift/src/Ice/ConnectionI.swift b/swift/src/Ice/ConnectionI.swift index ce6da433c88..34678a1d490 100644 --- a/swift/src/Ice/ConnectionI.swift +++ b/swift/src/Ice/ConnectionI.swift @@ -9,15 +9,15 @@ extension Connection { sentOn: DispatchQueue? = nil, sentFlags: DispatchWorkItemFlags? = nil, sent: ((Bool) -> Void)? = nil - ) -> Promise { + ) async throws { let impl = self as! ConnectionI let sentCB = createSentCallback(sentOn: sentOn, sentFlags: sentFlags, sent: sent) - return Promise { seal in + return try await withCheckedThrowingContinuation { continuation in impl.handle.flushBatchRequestsAsync( compress.rawValue, - exception: { error in seal.reject(error) }, + exception: { error in continuation.resume(throwing: error) }, sent: { - seal.fulfill(()) + continuation.resume(returning: ()) if let sentCB = sentCB { sentCB($0) } diff --git a/swift/src/Ice/Dispatcher.swift b/swift/src/Ice/Dispatcher.swift index 3db1c813d42..b9ceea6cd0a 100644 --- a/swift/src/Ice/Dispatcher.swift +++ b/swift/src/Ice/Dispatcher.swift @@ -1,13 +1,11 @@ // Copyright (c) ZeroC, Inc. -import PromiseKit - /// A dispatcher accepts incoming requests and returns outgoing responses. public protocol Dispatcher { /// Dispatches an incoming request and returns the corresponding outgoing response. /// - Parameter request: The incoming request. - /// - Returns: The outgoing response, wrapped in a Promise. - func dispatch(_ request: IncomingRequest) -> Promise + /// - Returns: The outgoing response. + func dispatch(_ request: IncomingRequest) async throws -> OutgoingResponse } @available(*, deprecated, renamed: "Dispatcher") diff --git a/swift/src/Ice/IceSwift.h b/swift/src/Ice/IceSwift.h deleted file mode 100644 index 34c3d84db40..00000000000 --- a/swift/src/Ice/IceSwift.h +++ /dev/null @@ -1,5 +0,0 @@ -// Copyright (c) ZeroC, Inc. -#import - -FOUNDATION_EXPORT double IceVersionNumber; -FOUNDATION_EXPORT const unsigned char IceVersionString[]; diff --git a/swift/src/Ice/Object.swift b/swift/src/Ice/Object.swift index b124adf8fc9..fa7ce454cb6 100644 --- a/swift/src/Ice/Object.swift +++ b/swift/src/Ice/Object.swift @@ -45,55 +45,36 @@ public protocol Object { } extension Object { - public func _iceD_ice_id(_ request: IncomingRequest) -> Promise { - do { - _ = try request.inputStream.skipEmptyEncapsulation() - let returnValue = try ice_id(current: request.current) - return Promise.value( - request.current.makeOutgoingResponse(returnValue, formatType: .DefaultFormat) { ostr, value in - ostr.write(value) - }) - } catch { - return Promise(error: error) + public func _iceD_ice_id(_ request: IncomingRequest) throws -> OutgoingResponse { + _ = try request.inputStream.skipEmptyEncapsulation() + let returnValue = try ice_id(current: request.current) + return request.current.makeOutgoingResponse(returnValue, formatType: .DefaultFormat) { ostr, value in + ostr.write(value) } } - public func _iceD_ice_ids(_ request: IncomingRequest) -> Promise { - do { - _ = try request.inputStream.skipEmptyEncapsulation() - let returnValue = try ice_ids(current: request.current) - return Promise.value( - request.current.makeOutgoingResponse(returnValue, formatType: .DefaultFormat) { ostr, value in - ostr.write(value) - }) - } catch { - return Promise(error: error) + public func _iceD_ice_ids(_ request: IncomingRequest) throws -> OutgoingResponse { + _ = try request.inputStream.skipEmptyEncapsulation() + let returnValue = try ice_ids(current: request.current) + return request.current.makeOutgoingResponse(returnValue, formatType: .DefaultFormat) { ostr, value in + ostr.write(value) } } - public func _iceD_ice_isA(_ request: IncomingRequest) -> Promise { - do { - let istr = request.inputStream - _ = try istr.startEncapsulation() - let identity: String = try istr.read() - let returnValue = try ice_isA(id: identity, current: request.current) - return Promise.value( - request.current.makeOutgoingResponse(returnValue, formatType: .DefaultFormat) { ostr, value in - ostr.write(value) - }) - } catch { - return Promise(error: error) + public func _iceD_ice_isA(_ request: IncomingRequest) throws -> OutgoingResponse { + let istr = request.inputStream + _ = try istr.startEncapsulation() + let identity: String = try istr.read() + let returnValue = try ice_isA(id: identity, current: request.current) + return request.current.makeOutgoingResponse(returnValue, formatType: .DefaultFormat) { ostr, value in + ostr.write(value) } } - public func _iceD_ice_ping(_ request: IncomingRequest) -> Promise { - do { - _ = try request.inputStream.skipEmptyEncapsulation() - try ice_ping(current: request.current) - return Promise.value(request.current.makeEmptyOutgoingResponse()) - } catch { - return Promise(error: error) - } + public func _iceD_ice_ping(_ request: IncomingRequest) throws -> OutgoingResponse { + _ = try request.inputStream.skipEmptyEncapsulation() + try ice_ping(current: request.current) + return request.current.makeEmptyOutgoingResponse() } } @@ -133,18 +114,18 @@ public struct ObjectDisp: Dispatcher { self.servant = servant } - public func dispatch(_ request: IncomingRequest) -> Promise { + public func dispatch(_ request: IncomingRequest) async throws -> OutgoingResponse { switch request.current.operation { case "ice_id": - servant._iceD_ice_id(request) + try servant._iceD_ice_id(request) case "ice_ids": - servant._iceD_ice_ids(request) + try servant._iceD_ice_ids(request) case "ice_isA": - servant._iceD_ice_isA(request) + try servant._iceD_ice_isA(request) case "ice_ping": - servant._iceD_ice_ping(request) + try servant._iceD_ice_ping(request) default: - Promise(error: OperationNotExistException()) + throw OperationNotExistException() } } } diff --git a/swift/src/Ice/ObjectAdapterI.swift b/swift/src/Ice/ObjectAdapterI.swift index 704dd009397..f84f0f647b9 100644 --- a/swift/src/Ice/ObjectAdapterI.swift +++ b/swift/src/Ice/ObjectAdapterI.swift @@ -238,8 +238,8 @@ class ObjectAdapterI: LocalObject, ObjectAdapter, ICEDispatchA requestId: Int32, encodingMajor: UInt8, encodingMinor: UInt8, - completionHandler: @escaping ICEOutgoingResponse - ) { + outgoingResponseHandler: @escaping ICEOutgoingResponse + ) async { precondition(handle == adapter) let connection = con?.getSwiftObject(ConnectionI.self) { ConnectionI(handle: con!) } @@ -263,19 +263,21 @@ class ObjectAdapterI: LocalObject, ObjectAdapter, ICEDispatchA let request = IncomingRequest(current: current, inputStream: istr) - dispatchPipeline.dispatch(request).map { response in + do { + let response = try await dispatchPipeline.dispatch(request) response.outputStream.finished().withUnsafeBytes { - completionHandler( + outgoingResponseHandler( response.replyStatus.rawValue, response.exceptionId, response.exceptionMessage, $0.baseAddress!, $0.count) } - }.catch { error in + + } catch { let response = current.makeOutgoingResponse(error: error) response.outputStream.finished().withUnsafeBytes { - completionHandler( + outgoingResponseHandler( response.replyStatus.rawValue, response.exceptionId, response.exceptionMessage, diff --git a/swift/src/Ice/Proxy.swift b/swift/src/Ice/Proxy.swift index fea9e2182ab..3c3bc369a43 100644 --- a/swift/src/Ice/Proxy.swift +++ b/swift/src/Ice/Proxy.swift @@ -2,7 +2,6 @@ import Foundation import IceImpl -import PromiseKit /// The base protocol for all Ice proxies. public protocol ObjectPrx: CustomStringConvertible, AnyObject { @@ -413,16 +412,13 @@ extension ObjectPrx { /// dispatch sent callback /// /// - parameter sent: `((Bool) -> Void)` - Optional sent callback. - /// - /// - returns: `PromiseKit.Promise` - A promise object that will be resolved with - /// the result of the invocation. public func ice_pingAsync( context: Context? = nil, sentOn: DispatchQueue? = nil, sentFlags: DispatchWorkItemFlags? = nil, sent: ((Bool) -> Void)? = nil - ) -> Promise { - return _impl._invokeAsync( + ) async throws { + return try await _impl._invokeAsync( operation: "ice_ping", mode: .Idempotent, context: context, @@ -464,15 +460,14 @@ extension ObjectPrx { /// /// - parameter sent: `((Bool) -> Void)` - Optional sent callback. /// - /// - returns: `PromiseKit.Promise` - A promise object that will be resolved with - /// the result of the invocation. + /// - returns: `Bool` - The result of the invocation. public func ice_isAAsync( id: String, context: Context? = nil, sentOn: DispatchQueue? = nil, sentFlags: DispatchWorkItemFlags? = nil, sent: ((Bool) -> Void)? = nil - ) -> Promise { - return _impl._invokeAsync( + ) async throws -> Bool { + return try await _impl._invokeAsync( operation: "ice_isA", mode: .Idempotent, write: { ostr in @@ -510,15 +505,14 @@ extension ObjectPrx { /// /// - parameter sent: `((Bool) -> Void)` - Optional sent callback. /// - /// - returns: `PromiseKit.Promise` A promise object that will be resolved with - /// the result of the invocation. + /// - returns: `String` The result of the invocation. public func ice_idAsync( context: Context? = nil, sentOn: DispatchQueue? = nil, sentFlags: DispatchWorkItemFlags? = nil, sent: ((Bool) -> Void)? = nil - ) -> Promise { - return _impl._invokeAsync( + ) async throws -> String { + return try await _impl._invokeAsync( operation: "ice_id", mode: .Idempotent, read: { istr in try istr.read() as String }, @@ -554,15 +548,14 @@ extension ObjectPrx { /// /// - parameter sent: `((Bool) -> Void)` - Optional sent callback. /// - /// - returns: `PromiseKit.Promise` - A promise object that will be resolved with - /// the result of the invocation. + /// - returns: `Ice.StringSeq` - The result of the invocation. public func ice_idsAsync( context: Context? = nil, sentOn: DispatchQueue? = nil, sentFlags: DispatchWorkItemFlags? = nil, sent: ((Bool) -> Void)? = nil - ) -> Promise { - return _impl._invokeAsync( + ) async throws -> StringSeq { + return try await _impl._invokeAsync( operation: "ice_ids", mode: .Idempotent, read: { istr in try istr.read() as StringSeq }, @@ -630,8 +623,7 @@ extension ObjectPrx { /// /// - parameter sent: `((Bool) -> Void)` - Optional sent callback. /// - /// - returns: `PromiseKit.Promise<(ok: Bool, outEncaps: Data)>` - A promise object that will be - //// resolved with the result of the invocation. + /// - returns: `(ok: Bool, outEncaps: Data)` - The result of the invocation. public func ice_invokeAsync( operation: String, mode: OperationMode, @@ -640,9 +632,10 @@ extension ObjectPrx { sentOn: DispatchQueue? = nil, sentFlags: DispatchWorkItemFlags? = nil, sent: ((Bool) -> Void)? = nil - ) -> Promise<(ok: Bool, outEncaps: Data)> { + ) async throws -> (ok: Bool, outEncaps: Data) { + if _impl.isTwoway { - return Promise<(ok: Bool, outEncaps: Data)> { seal in + return try await withCheckedThrowingContinuation { continuation in _impl.handle.invokeAsync( operation, mode: mode.rawValue, @@ -655,13 +648,13 @@ extension ObjectPrx { communicator: self._impl.communicator, encoding: self._impl.encoding, bytes: Data(bytes: bytes, count: count)) // make a copy - try seal.fulfill((ok, istr.readEncapsulation().bytes)) + try continuation.resume(returning: (ok, istr.readEncapsulation().bytes)) } catch { - seal.reject(error) + continuation.resume(throwing: error) } }, exception: { error in - seal.reject(error) + continuation.resume(throwing: error) }, sent: createSentCallback( sentOn: sentOn, @@ -670,20 +663,20 @@ extension ObjectPrx { } } else { let sentCB = createSentCallback(sentOn: sentOn, sentFlags: sentFlags, sent: sent) - return Promise<(ok: Bool, outEncaps: Data)> { seal in + return try await withCheckedThrowingContinuation { continuation in _impl.handle.invokeAsync( operation, mode: mode.rawValue, inParams: inEncaps, context: context, response: { _, _, _ in - fatalError("Unexpected response") + fatalError("unexpected response") }, exception: { error in - seal.reject(error) + continuation.resume(throwing: error) }, sent: { - seal.fulfill((true, Data())) + continuation.resume(returning: (true, Data())) if let sentCB = sentCB { sentCB($0) } @@ -711,17 +704,11 @@ extension ObjectPrx { /// Returns the connection for this proxy. If the proxy does not yet have an established connection, /// it first attempts to create a connection. /// - /// - returns: `PromiseKit.Promise` - A promise object that will be resolved with - /// the result of the invocation. - public func ice_getConnectionAsync() -> Promise { - return Promise { seal in - self._impl.handle.ice_getConnectionAsync( - { conn in - seal.fulfill( - conn?.getSwiftObject(ConnectionI.self) { - ConnectionI(handle: conn!) - }) - }, exception: { ex in seal.reject(ex) }) + /// - returns: `Ice.Connection?` - The result of the invocation. + public func ice_getConnectionAsync() async throws -> Connection? { + let conn = try await self._impl.handle.ice_getConnectionAsync() + return conn.getSwiftObject(ConnectionI.self) { + ConnectionI(handle: conn) } } @@ -741,22 +728,19 @@ extension ObjectPrx { /// dispatch the sent callback. /// /// - parameter sent: `((Bool) -> Void)` - Optional sent callback. - /// - /// - returns: `PromiseKit.Promise - A promise object that will be resolved when - /// the flush is complete. public func ice_flushBatchRequestsAsync( sentOn: DispatchQueue? = nil, sentFlags: DispatchWorkItemFlags? = nil.self, sent: ((Bool) -> Void)? = nil - ) -> Promise { + ) async throws { let sentCB = createSentCallback(sentOn: sentOn, sentFlags: sentFlags, sent: sent) - return Promise { seal in + return try await withCheckedThrowingContinuation { continuation in _impl.handle.ice_flushBatchRequestsAsync( exception: { - seal.reject($0) + continuation.resume(throwing: $0) }, sent: { - seal.fulfill(()) + continuation.resume(returning: ()) if let sentCB = sentCB { sentCB($0) } @@ -1280,9 +1264,10 @@ open class ObjectPrxI: ObjectPrx { sentOn: DispatchQueue? = nil, sentFlags: DispatchWorkItemFlags? = nil, sent: ((Bool) -> Void)? = nil - ) -> Promise { + ) async throws { + if userException != nil, !isTwoway { - return Promise(error: TwowayOnlyException(operation: operation)) + throw TwowayOnlyException(operation: operation) } let ostr = OutputStream(communicator: communicator) if let write = write { @@ -1291,7 +1276,7 @@ open class ObjectPrxI: ObjectPrx { ostr.endEncapsulation() } if isTwoway { - return Promise { seal in + return try await withCheckedThrowingContinuation { continuation in handle.invokeAsync( operation, mode: mode.rawValue, @@ -1311,31 +1296,36 @@ open class ObjectPrxI: ObjectPrx { userException: userException) } try istr.skipEmptyEncapsulation() - seal.fulfill(()) + continuation.resume(returning: ()) } catch { - seal.reject(error) + continuation.resume(throwing: error) } }, exception: { error in - seal.reject(error) + continuation.resume(throwing: error) }, sent: createSentCallback(sentOn: sentOn, sentFlags: sentFlags, sent: sent)) } } else { if ice_isBatchOneway() || ice_isBatchDatagram() { - return Promise { seal in - try autoreleasepool { - try handle.onewayInvoke( - operation, - mode: mode.rawValue, - inParams: ostr.finished(), - context: context) - - seal.fulfill(()) + return try await withCheckedThrowingContinuation { continuation in + do { + try autoreleasepool { + try handle.onewayInvoke( + operation, + mode: mode.rawValue, + inParams: ostr.finished(), + context: context) + + continuation.resume(returning: ()) + } + } catch { + continuation.resume(throwing: error) } + } } else { - return Promise { seal in + return try await withCheckedThrowingContinuation { continuation in let sentCB = createSentCallback(sentOn: sentOn, sentFlags: sentFlags, sent: sent) handle.invokeAsync( operation, @@ -1343,13 +1333,13 @@ open class ObjectPrxI: ObjectPrx { inParams: ostr.finished(), context: context, response: { _, _, _ in - fatalError("Unexpected response") + fatalError("unexpected response") }, exception: { error in - seal.reject(error) + continuation.resume(throwing: error) }, sent: { - seal.fulfill(()) + continuation.resume(returning: ()) if let sentCB = sentCB { sentCB($0) } @@ -1370,17 +1360,19 @@ open class ObjectPrxI: ObjectPrx { sentOn: DispatchQueue? = nil, sentFlags: DispatchWorkItemFlags? = nil, sent: ((Bool) -> Void)? = nil - ) -> Promise { + ) async throws -> T { if !isTwoway { - return Promise(error: TwowayOnlyException(operation: operation)) + throw TwowayOnlyException(operation: operation) } + let ostr = OutputStream(communicator: communicator) if let write = write { ostr.startEncapsulation(encoding: encoding, format: format) write(ostr) ostr.endEncapsulation() } - return Promise { seal in + + return try await withCheckedThrowingContinuation { continuation in handle.invokeAsync( operation, mode: mode.rawValue, @@ -1402,13 +1394,13 @@ open class ObjectPrxI: ObjectPrx { try istr.startEncapsulation() let l = try read(istr) try istr.endEncapsulation() - seal.fulfill(l) + continuation.resume(returning: l) } catch { - seal.reject(error) + continuation.resume(throwing: error) } }, exception: { error in - seal.reject(error) + continuation.resume(throwing: error) }, sent: createSentCallback(sentOn: sentOn, sentFlags: sentFlags, sent: sent)) } @@ -1427,7 +1419,7 @@ open class ObjectPrxI: ObjectPrx { } throw UnknownUserException(badTypeId: error.ice_id()) } - fatalError("Failed to throw user exception") + fatalError("failed to throw user exception") } public static func checkedCast( diff --git a/swift/src/Ice/ServantManager.swift b/swift/src/Ice/ServantManager.swift index d6a13378788..754b34e7128 100644 --- a/swift/src/Ice/ServantManager.swift +++ b/swift/src/Ice/ServantManager.swift @@ -1,7 +1,5 @@ // Copyright (c) ZeroC, Inc. -import PromiseKit - class ServantManager: Dispatcher { private let adapterName: String private let communicator: Communicator @@ -178,13 +176,14 @@ class ServantManager: Dispatcher { } } - func dispatch(_ request: IncomingRequest) -> Promise { + func dispatch(_ request: IncomingRequest) async throws -> OutgoingResponse { + let current = request.current var servant = findServant(id: current.id, facet: current.facet) if let servant = servant { // the simple, common path - return servant.dispatch(request) + return try await servant.dispatch(request) } // Else, check servant locators @@ -195,36 +194,34 @@ class ServantManager: Dispatcher { } if let locator = locator { - do { - var cookie: AnyObject? - (servant, cookie) = try locator.locate(current) + var cookie: AnyObject? + (servant, cookie) = try locator.locate(current) - if let servant = servant { + if let servant = servant { + do { // If locator returned a servant, we must execute finished once no matter what. - return servant.dispatch(request).map(on: nil) { response in - do { - try locator.finished(curr: current, servant: servant, cookie: cookie) - } catch { - // Can't return a rejected promise here; otherwise recover will execute finished a second - // time. - return current.makeOutgoingResponse(error: error) - } - return response - }.recover(on: nil) { error in - // This can throw and return a rejected promise. + let response = try await servant.dispatch(request) + + do { try locator.finished(curr: current, servant: servant, cookie: cookie) - return Promise(error: error) + } catch { + // Can't return a rejected promise here; otherwise recover will execute finished a second + // time. + return current.makeOutgoingResponse(error: error) } + + return response + } catch { + try locator.finished(curr: current, servant: servant, cookie: cookie) + throw error } - } catch { - return Promise(error: error) } } if hasServant(id: current.id) || isAdminId(current.id) { - return Promise(error: FacetNotExistException()) + throw FacetNotExistException() } else { - return Promise(error: ObjectNotExistException()) + throw ObjectNotExistException() } } } diff --git a/swift/src/IceImpl/DispatchAdapter.mm b/swift/src/IceImpl/DispatchAdapter.mm index 4551067f135..3f4406d79b0 100644 --- a/swift/src/IceImpl/DispatchAdapter.mm +++ b/swift/src/IceImpl/DispatchAdapter.mm @@ -26,9 +26,18 @@ current}); }; + // Create a new InputStream and swap it with the one from the request. + // When dispatch completes, the InputStream will be deleted. + Ice::InputStream* dispatchInputStream = new Ice::InputStream(); + dispatchInputStream->swap(request.inputStream()); + + void (^completion)(void) = ^{ + delete dispatchInputStream; + }; + int32_t sz; const std::byte* inEncaps; - request.inputStream().readEncapsulation(inEncaps, sz); + dispatchInputStream->readEncapsulation(inEncaps, sz); ICEObjectAdapter* adapter = [ICEObjectAdapter getHandle:current.adapter]; ICEConnection* con = [ICEConnection getHandle:current.con]; @@ -48,6 +57,8 @@ requestId:current.requestId encodingMajor:current.encoding.major encodingMinor:current.encoding.minor - completionHandler:outgoingResponse]; + outgoingResponseHandler:outgoingResponse + completion:completion + ]; } } diff --git a/swift/src/IceImpl/ObjectPrx.mm b/swift/src/IceImpl/ObjectPrx.mm index 3aa49affa5b..3444aa7e071 100644 --- a/swift/src/IceImpl/ObjectPrx.mm +++ b/swift/src/IceImpl/ObjectPrx.mm @@ -434,23 +434,23 @@ - (id)ice_getConnection:(NSError**)error } } -- (void)ice_getConnectionAsync:(void (^)(ICEConnection* _Nullable))response exception:(void (^)(NSError*))exception +- (void)ice_getConnectionAsyncWithCompletion:(void (^)(ICEConnection* _Nullable, NSError*))completion { try { _prx->ice_getConnectionAsync( - [response](std::shared_ptr cppConnection) + [completion](std::shared_ptr cppConnection) { @autoreleasepool { - response([ICEConnection getHandle:cppConnection]); + completion([ICEConnection getHandle:cppConnection], nullptr); } }, - [exception](std::exception_ptr e) + [completion](std::exception_ptr e) { @autoreleasepool { - exception(convertException(e)); + completion(nullptr, convertException(e)); } }); } @@ -459,7 +459,7 @@ - (void)ice_getConnectionAsync:(void (^)(ICEConnection* _Nullable))response exce // Typically CommunicatorDestroyedException. Note that the callback is called on the // thread making the invocation, which is fine since we only use it to fulfill the // PromiseKit promise. - exception(convertException(std::current_exception())); + completion(nullptr, convertException(std::current_exception())); } } diff --git a/swift/src/IceImpl/include/DispatchAdapter.h b/swift/src/IceImpl/include/DispatchAdapter.h index 1da9d53bc2e..f5454fd5c92 100644 --- a/swift/src/IceImpl/include/DispatchAdapter.h +++ b/swift/src/IceImpl/include/DispatchAdapter.h @@ -13,19 +13,20 @@ typedef void (^ICEOutgoingResponse)(uint8_t, NSString* _Nullable, NSString* _Nul // The implementation must call the completion handler exactly once. ICEIMPL_API @protocol ICEDispatchAdapter - (void)dispatch:(ICEObjectAdapter*)adapter - inEncapsBytes:(void*)inEncapsBytes - inEncapsCount:(long)inEncapsCount - con:(ICEConnection* _Nullable)con - name:(NSString*)name - category:(NSString*)category - facet:(NSString*)facet - operation:(NSString*)operation - mode:(uint8_t)mode - context:(NSDictionary*)context - requestId:(int32_t)requestId - encodingMajor:(uint8_t)encodingMajor - encodingMinor:(uint8_t)encodingMinor - completionHandler:(ICEOutgoingResponse)completionHandler; + inEncapsBytes:(void*)inEncapsBytes + inEncapsCount:(long)inEncapsCount + con:(ICEConnection* _Nullable)con + name:(NSString*)name + category:(NSString*)category + facet:(NSString*)facet + operation:(NSString*)operation + mode:(uint8_t)mode + context:(NSDictionary*)context + requestId:(int32_t)requestId + encodingMajor:(uint8_t)encodingMajor + encodingMinor:(uint8_t)encodingMinor + outgoingResponseHandler:(ICEOutgoingResponse)outgoingResponseHandler + completion:(void (^)(void))completion; - (void)complete; @end diff --git a/swift/src/IceImpl/include/ObjectPrx.h b/swift/src/IceImpl/include/ObjectPrx.h index b8e867321f9..fc2494068f7 100644 --- a/swift/src/IceImpl/include/ObjectPrx.h +++ b/swift/src/IceImpl/include/ObjectPrx.h @@ -66,7 +66,7 @@ ICEIMPL_API @interface ICEObjectPrx : NSObject - (nullable instancetype)ice_fixed:(ICEConnection*)connection error:(NSError* _Nullable* _Nullable)error; - (bool)ice_isFixed; - (nullable id)ice_getConnection:(NSError* _Nullable* _Nullable)error; // Either NSNull or ICEConnection -- (void)ice_getConnectionAsync:(void (^)(ICEConnection* _Nullable))response exception:(void (^)(NSError*))exception; +- (void)ice_getConnectionAsyncWithCompletion:(void (^)(ICEConnection* _Nullable, NSError* _Nullable))completion; - (nullable ICEConnection*)ice_getCachedConnection; - (BOOL)ice_flushBatchRequests:(NSError* _Nullable* _Nullable)error; - (void)ice_flushBatchRequestsAsync:(void (^)(NSError*))exception