From b382cdb022180bca18631dda21e6fa74a61db169 Mon Sep 17 00:00:00 2001 From: Edwin Wu Date: Sun, 5 Apr 2026 07:50:13 -0700 Subject: [PATCH] [Nearby] Optimize NWFramework socket reads and writes for performance and memory. III PiperOrigin-RevId: 894930585 --- .../Mediums/WiFiCommon/GNCNWFrameworkError.h | 1 + .../Mediums/WiFiCommon/GNCNWFrameworkSocket.h | 11 +++++ .../WiFiCommon/GNCNWFrameworkSocket.mm | 46 ++++++++++++++++++- .../Tests/GNCNWFrameworkSocketTest.mm | 23 ++++++++++ 4 files changed, 80 insertions(+), 1 deletion(-) diff --git a/internal/platform/implementation/apple/Mediums/WiFiCommon/GNCNWFrameworkError.h b/internal/platform/implementation/apple/Mediums/WiFiCommon/GNCNWFrameworkError.h index 1893dbd6..df429dd8 100644 --- a/internal/platform/implementation/apple/Mediums/WiFiCommon/GNCNWFrameworkError.h +++ b/internal/platform/implementation/apple/Mediums/WiFiCommon/GNCNWFrameworkError.h @@ -26,4 +26,5 @@ typedef NS_ERROR_ENUM(GNCNWFrameworkErrorDomain, GNCNWFrameworkError){ GNCNWFrameworkErrorUnknown, GNCNWFrameworkErrorTimedOut, GNCNWFrameworkErrorDuplicateDiscovererForServiceType, + GNCNWFrameworkErrorNotConnected, }; diff --git a/internal/platform/implementation/apple/Mediums/WiFiCommon/GNCNWFrameworkSocket.h b/internal/platform/implementation/apple/Mediums/WiFiCommon/GNCNWFrameworkSocket.h index 5ce0e67e..a13b2774 100644 --- a/internal/platform/implementation/apple/Mediums/WiFiCommon/GNCNWFrameworkSocket.h +++ b/internal/platform/implementation/apple/Mediums/WiFiCommon/GNCNWFrameworkSocket.h @@ -75,6 +75,17 @@ */ - (BOOL)write:(NSData *)data error:(NSError **_Nullable)error; +/** + * Writes raw bytes to the connection. + * + * @param bytes The buffer to write. + * @param length The number of bytes to write. + * @param error Error that will be populated on failure. + */ +- (BOOL)writeBytes:(const void *)bytes + length:(NSUInteger)length + error:(NSError **_Nullable)error; + /** * Gracefully closes the connection to remote endpoint. * diff --git a/internal/platform/implementation/apple/Mediums/WiFiCommon/GNCNWFrameworkSocket.mm b/internal/platform/implementation/apple/Mediums/WiFiCommon/GNCNWFrameworkSocket.mm index d9c2ed3c..001fe2e0 100644 --- a/internal/platform/implementation/apple/Mediums/WiFiCommon/GNCNWFrameworkSocket.mm +++ b/internal/platform/implementation/apple/Mediums/WiFiCommon/GNCNWFrameworkSocket.mm @@ -92,7 +92,7 @@ static const NSTimeInterval kConnectionWriteTimeout = 5.0; // 5 seconds timeout if (!self.connection) { if (error) { *error = [NSError errorWithDomain:GNCNWFrameworkErrorDomain - code:GNCNWFrameworkErrorUnknown + code:GNCNWFrameworkErrorNotConnected userInfo:nil]; } return std::nullopt; @@ -185,6 +185,50 @@ static const NSTimeInterval kConnectionWriteTimeout = 5.0; // 5 seconds timeout return signaled && blockSuccess; } +- (BOOL)writeBytes:(const void *)bytes length:(NSUInteger)length error:(NSError **)error { + if (!self.connection) { + if (error) { + *error = [NSError errorWithDomain:GNCNWFrameworkErrorDomain + code:GNCNWFrameworkErrorNotConnected + userInfo:nil]; + } + return NO; + } + + dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); + + __block NSError *blockError = nil; + + // OPTIMIZATION: Use DISPATCH_DATA_DESTRUCTOR_DEFAULT to perform a + // single copy into a GCD-managed buffer. No NSData required. + // TODO: edwinwu - Investigate to see if it is worth to make it zero-copy by replacing + // DISPATCH_DATA_DESTRUCTOR_DEFAULT with a custom empty destructor: + // dispatch_data_t dispatchData = dispatch_data_create(bytes, length, nil, ^{ + // // Zero-copy: ownership remains with the caller. + // }); + dispatch_data_t dispatchData = + dispatch_data_create(bytes, length, nil, DISPATCH_DATA_DESTRUCTOR_DEFAULT); + + [self.connection sendData:dispatchData + context:NW_CONNECTION_DEFAULT_MESSAGE_CONTEXT + isComplete:NO + completionHandler:^(nw_error_t _Nullable sendError) { + if (sendError) { + blockError = (__bridge_transfer NSError *)nw_error_copy_cf_error(sendError); + } + dispatch_semaphore_signal(semaphore); + }]; + + // Wait until signaled or the 5-second timeout passes + intptr_t waitResult = dispatch_semaphore_wait( + semaphore, + dispatch_time(DISPATCH_TIME_NOW, (int64_t)(kConnectionWriteTimeout * NSEC_PER_SEC))); + if (error != nil) { + *error = blockError; + } + return (waitResult == 0) && (blockError == nil); +} + - (void)close { [_connection cancel]; _connection = nil; diff --git a/internal/platform/implementation/apple/Mediums/WiFiCommon/Tests/GNCNWFrameworkSocketTest.mm b/internal/platform/implementation/apple/Mediums/WiFiCommon/Tests/GNCNWFrameworkSocketTest.mm index a7856bd5..bce7847b 100644 --- a/internal/platform/implementation/apple/Mediums/WiFiCommon/Tests/GNCNWFrameworkSocketTest.mm +++ b/internal/platform/implementation/apple/Mediums/WiFiCommon/Tests/GNCNWFrameworkSocketTest.mm @@ -132,6 +132,28 @@ NS_ASSUME_NONNULL_BEGIN XCTAssertFalse(result); } +- (void)testWriteBytes_Success { + NSError *error = nil; + NSString *testString = @"testData"; + NSData *testData = [testString dataUsingEncoding:NSUTF8StringEncoding]; + + BOOL result = [_socket writeBytes:testData.bytes length:testData.length error:&error]; + + XCTAssertTrue(result); + XCTAssertNil(error); +} + +- (void)testWriteBytes_Error { + NSError *error = nil; + NSString *testString = @"testData"; + NSData *testData = [testString dataUsingEncoding:NSUTF8StringEncoding]; + _fakeConnection.simulateSendFailure = YES; + + BOOL result = [_socket writeBytes:testData.bytes length:testData.length error:&error]; + + XCTAssertFalse(result); +} + - (void)testClose { XCTAssertFalse(_fakeConnection.cancelCalled); [_socket close]; @@ -140,6 +162,7 @@ NS_ASSUME_NONNULL_BEGIN NSError *error = nil; XCTAssertNil([_socket readMaxLength:10 error:&error]); XCTAssertFalse([_socket write:[NSData data] error:&error]); + XCTAssertFalse([_socket writeBytes:"test" length:4 error:&error]); } @end