From 2a438e68a71e906cb2c1c45de627d9a138fe2ab8 Mon Sep 17 00:00:00 2001 From: "Hedden, Kyle Matthew" Date: Fri, 20 Oct 2023 12:04:28 -0400 Subject: [PATCH 1/2] Improve MVMCoreActionUtility.perform signature. Remove odd default. Use TimeInterval for better typing and to allow split seconds. --- .../Client Parameters/ClientParameterHandler.swift | 2 +- .../Client Parameters/ClientParameterProtocol.swift | 8 +++----- .../Utility/Helpers/MVMCoreActionUtility+Extension.swift | 4 ++-- 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/MVMCore/MVMCore/Models/ActionType/Client Parameters/ClientParameterHandler.swift b/MVMCore/MVMCore/Models/ActionType/Client Parameters/ClientParameterHandler.swift index bd1db37..18748f9 100644 --- a/MVMCore/MVMCore/Models/ActionType/Client Parameters/ClientParameterHandler.swift +++ b/MVMCore/MVMCore/Models/ActionType/Client Parameters/ClientParameterHandler.swift @@ -106,7 +106,7 @@ parametersWorkQueue.async { if (returnedList[index] != nil) { MVMCoreLoggingHandler.addError(toLog: MVMCoreErrorObject(title: nil, message: "Client parameter \(parameterType) has already executed. The completion handler should only be called once!", code: ErrorCode.default.rawValue, domain: ErrorDomainNative, location: String(describing: ClientParameterHandler.self))!) - } else { + } else { MVMCoreLoggingHandler.shared()?.logCoreEvent(.clientParameterFetchComplete(name: parameterType, uuid: requestUUID[index], actionId: actionId)) returnedList[index] = receivedParameter group.leave() // Leaving is only done after setup (barriered). diff --git a/MVMCore/MVMCore/Models/ActionType/Client Parameters/ClientParameterProtocol.swift b/MVMCore/MVMCore/Models/ActionType/Client Parameters/ClientParameterProtocol.swift index 7f52778..e55fd15 100644 --- a/MVMCore/MVMCore/Models/ActionType/Client Parameters/ClientParameterProtocol.swift +++ b/MVMCore/MVMCore/Models/ActionType/Client Parameters/ClientParameterProtocol.swift @@ -15,7 +15,7 @@ public protocol ClientParameterProtocol: ModelHandlerProtocol { var clientParameterModel: ClientParameterModelProtocol { get set } - func fetchClientParameters(requestParameters: [String: Any], timingOutIn timeout: Double, completionHandler:@escaping ([String: AnyHashable]?) -> ()) + func fetchClientParameters(requestParameters: [String: Any], timingOutIn timeout: TimeInterval, completionHandler:@escaping ([String: AnyHashable]?) -> ()) /// Default parameter for timeout scenarios. It will use the protocol extension method bydefault. Can override to send custom values. func valueOnTimeout() -> [String: AnyHashable] @@ -28,10 +28,8 @@ public extension ClientParameterProtocol { } /// The handler should call this method to pass the parameter back to the caller. - func returnParameters(_ isFlatMap: Bool, _ parameter: [String: AnyHashable]?, completionHandler: @escaping ([String: AnyHashable]?) -> ()) { - guard let parameter = parameter else { - return completionHandler(nil) - } + /// If using isFlatMap, you must provide at least 1 element in parameters or it will result in triggering a timeout. + func returnParameters(_ isFlatMap: Bool, _ parameter: [String: AnyHashable], completionHandler: @escaping ([String: AnyHashable]?) -> ()) { if isFlatMap { completionHandler(parameter) } else { diff --git a/MVMCore/MVMCore/Utility/Helpers/MVMCoreActionUtility+Extension.swift b/MVMCore/MVMCore/Utility/Helpers/MVMCoreActionUtility+Extension.swift index 10299bb..bb6e590 100644 --- a/MVMCore/MVMCore/Utility/Helpers/MVMCoreActionUtility+Extension.swift +++ b/MVMCore/MVMCore/Utility/Helpers/MVMCoreActionUtility+Extension.swift @@ -27,7 +27,7 @@ public extension MVMCoreActionUtility { - Parameter timemout: the time the operation has to finish before throwing a timeout error. - Parameter operation: the operation to perform. */ - static func perform(with timeout: Int = 1, operation: @escaping @Sendable () async throws -> T) async throws -> T { + static func perform(withTimeout timeout: TimeInterval, operation: @escaping @Sendable () async throws -> T) async throws -> T { return try await withCheckedThrowingContinuation { continuation in Task { await withThrowingTaskGroup(of: T.self) { group in @@ -40,7 +40,7 @@ public extension MVMCoreActionUtility { // Task for time out. group.addTask { try Task.checkCancellation() - try await Task.sleep(nanoseconds: UInt64(timeout) * NSEC_PER_SEC) + try await Task.sleep(nanoseconds: UInt64(timeout * TimeInterval(NSEC_PER_SEC))) throw MVMCoreActionUtilityError.timeOut } From f7ca8da8f94c21d97656be45eb6516ce5efa4c45 Mon Sep 17 00:00:00 2001 From: "Hedden, Kyle Matthew" Date: Fri, 20 Oct 2023 13:38:39 -0400 Subject: [PATCH 2/2] Add additional task completion guard to suppress late callbacks. --- .../ClientParameterHandler.swift | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/MVMCore/MVMCore/Models/ActionType/Client Parameters/ClientParameterHandler.swift b/MVMCore/MVMCore/Models/ActionType/Client Parameters/ClientParameterHandler.swift index 18748f9..6f8c7e0 100644 --- a/MVMCore/MVMCore/Models/ActionType/Client Parameters/ClientParameterHandler.swift +++ b/MVMCore/MVMCore/Models/ActionType/Client Parameters/ClientParameterHandler.swift @@ -104,13 +104,17 @@ timingOutIn: timeout) { (receivedParameter) in // Queue the results for merge. parametersWorkQueue.async { - if (returnedList[index] != nil) { - MVMCoreLoggingHandler.addError(toLog: MVMCoreErrorObject(title: nil, message: "Client parameter \(parameterType) has already executed. The completion handler should only be called once!", code: ErrorCode.default.rawValue, domain: ErrorDomainNative, location: String(describing: ClientParameterHandler.self))!) - } else { - MVMCoreLoggingHandler.shared()?.logCoreEvent(.clientParameterFetchComplete(name: parameterType, uuid: requestUUID[index], actionId: actionId)) - returnedList[index] = receivedParameter - group.leave() // Leaving is only done after setup (barriered). + guard !complete else { + MVMCoreLoggingHandler.logDebugMessage(withDelegate: "Client \(parameterType) responded after task completion.") + return } + guard returnedList[index] == nil else { + MVMCoreLoggingHandler.addError(toLog: MVMCoreErrorObject(title: nil, message: "Client parameter \(parameterType) has already executed. The completion handler should only be called once!", code: ErrorCode.default.rawValue, domain: ErrorDomainNative, location: String(describing: ClientParameterHandler.self))!) + return + } + MVMCoreLoggingHandler.shared()?.logCoreEvent(.clientParameterFetchComplete(name: parameterType, uuid: requestUUID[index], actionId: actionId)) + returnedList[index] = receivedParameter + group.leave() // Leaving is only done after setup (barriered). } } }