Skip to content

Commit 0b815bb

Browse files
committed
Fix CoreSimulator accessibility FD leaks
1 parent 030f3d9 commit 0b815bb

13 files changed

Lines changed: 301 additions & 137 deletions

cli/XCWAccessibilityBridge.m

Lines changed: 57 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@
99
static NSString * const XCWCoreSimulatorPath = @"/Library/Developer/PrivateFrameworks/CoreSimulator.framework/CoreSimulator";
1010
static NSString * const XCWAccessibilityPlatformTranslationPath = @"/System/Library/PrivateFrameworks/AccessibilityPlatformTranslation.framework/AccessibilityPlatformTranslation";
1111
static const NSUInteger XCWAXMaxDepth = 80;
12+
static NSObject *XCWAXDeviceCacheLock = nil;
13+
static id XCWAXCachedServiceContext = nil;
14+
static id XCWAXCachedDeviceSet = nil;
15+
static NSMutableDictionary<NSString *, id> *XCWAXCachedDevicesByUDID = nil;
1216

1317
typedef id _Nullable (^XCWAXTranslationCallback)(id request);
1418

@@ -50,6 +54,19 @@ static BOOL XCWAXLoadPrivateFrameworks(NSError **error) {
5054
}
5155

5256
static id XCWAXDeviceForUDID(NSString *udid, NSError **error) {
57+
static dispatch_once_t cacheOnceToken;
58+
dispatch_once(&cacheOnceToken, ^{
59+
XCWAXDeviceCacheLock = [NSObject new];
60+
XCWAXCachedDevicesByUDID = [NSMutableDictionary dictionary];
61+
});
62+
63+
@synchronized (XCWAXDeviceCacheLock) {
64+
id cachedDevice = XCWAXCachedDevicesByUDID[udid];
65+
if (cachedDevice != nil) {
66+
return cachedDevice;
67+
}
68+
}
69+
5370
Class serviceContextClass = NSClassFromString(@"SimServiceContext");
5471
if (serviceContextClass == Nil) {
5572
if (error != NULL) {
@@ -58,35 +75,50 @@ static id XCWAXDeviceForUDID(NSString *udid, NSError **error) {
5875
return nil;
5976
}
6077

61-
NSError *serviceError = nil;
62-
id contextAlloc = ((id(*)(id, SEL))objc_msgSend)(serviceContextClass, sel_registerName("alloc"));
63-
id serviceContext = ((id(*)(id, SEL, id, long long, NSError **))objc_msgSend)(
64-
contextAlloc,
65-
sel_registerName("initWithDeveloperDir:connectionType:error:"),
66-
nil,
67-
0LL,
68-
&serviceError
69-
);
70-
if (serviceContext == nil) {
71-
if (error != NULL) {
72-
*error = serviceError ?: XCWAXError(4, @"Unable to create a CoreSimulator service context.");
78+
@synchronized (XCWAXDeviceCacheLock) {
79+
if (XCWAXCachedDeviceSet == nil) {
80+
NSError *serviceError = nil;
81+
id contextAlloc = ((id(*)(id, SEL))objc_msgSend)(serviceContextClass, sel_registerName("alloc"));
82+
XCWAXCachedServiceContext = ((id(*)(id, SEL, id, long long, NSError **))objc_msgSend)(
83+
contextAlloc,
84+
sel_registerName("initWithDeveloperDir:connectionType:error:"),
85+
nil,
86+
0LL,
87+
&serviceError
88+
);
89+
if (XCWAXCachedServiceContext == nil) {
90+
if (error != NULL) {
91+
*error = serviceError ?: XCWAXError(4, @"Unable to create a CoreSimulator service context.");
92+
}
93+
return nil;
94+
}
95+
96+
NSError *deviceSetError = nil;
97+
XCWAXCachedDeviceSet = ((id(*)(id, SEL, NSError **))objc_msgSend)(
98+
XCWAXCachedServiceContext,
99+
sel_registerName("defaultDeviceSetWithError:"),
100+
&deviceSetError
101+
);
102+
if (XCWAXCachedDeviceSet == nil) {
103+
XCWAXCachedServiceContext = nil;
104+
if (error != NULL) {
105+
*error = deviceSetError ?: XCWAXError(5, @"Unable to access the default CoreSimulator device set.");
106+
}
107+
return nil;
108+
}
73109
}
74-
return nil;
75-
}
76110

77-
NSError *deviceSetError = nil;
78-
id deviceSet = ((id(*)(id, SEL, NSError **))objc_msgSend)(serviceContext, sel_registerName("defaultDeviceSetWithError:"), &deviceSetError);
79-
if (deviceSet == nil) {
80-
if (error != NULL) {
81-
*error = deviceSetError ?: XCWAXError(5, @"Unable to access the default CoreSimulator device set.");
111+
NSArray *devices = ((id(*)(id, SEL))objc_msgSend)(XCWAXCachedDeviceSet, sel_registerName("devices"));
112+
for (id candidate in devices) {
113+
NSString *candidateUDID = XCWAXUDIDString(candidate);
114+
if (candidateUDID.length > 0) {
115+
XCWAXCachedDevicesByUDID[candidateUDID] = candidate;
116+
}
82117
}
83-
return nil;
84-
}
85118

86-
NSArray *devices = ((id(*)(id, SEL))objc_msgSend)(deviceSet, sel_registerName("devices"));
87-
for (id candidate in devices) {
88-
if ([XCWAXUDIDString(candidate) isEqualToString:udid]) {
89-
return candidate;
119+
id device = XCWAXCachedDevicesByUDID[udid];
120+
if (device != nil) {
121+
return device;
90122
}
91123
}
92124

cli/XCWPrivateSimulatorSession.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@ typedef void (^XCWPrivateSimulatorEncodedFrameHandler)(NSData *sampleData,
2727

2828
- (BOOL)waitUntilReadyWithTimeout:(NSTimeInterval)timeout;
2929
- (BOOL)waitForFirstEncodedFrameWithTimeout:(NSTimeInterval)timeout;
30-
- (NSDictionary *)sessionInfoRepresentation;
3130
- (void)requestKeyFrameRefresh;
3231
- (id)addEncodedFrameListener:(XCWPrivateSimulatorEncodedFrameHandler)handler;
3332
- (void)removeEncodedFrameListener:(id)token;

cli/XCWPrivateSimulatorSession.m

Lines changed: 0 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,6 @@ @implementation XCWPrivateSimulatorSession {
3030
NSData *_latestKeyFrameDecoderConfig;
3131
CGSize _latestKeyFrameDimensions;
3232
NSUInteger _latestKeyFrameSequenceValue;
33-
NSUInteger _displayFrameCount;
34-
NSUInteger _manualRefreshFrameCount;
3533
BOOL _displayReadyValue;
3634
BOOL _didSignalReadiness;
3735
}
@@ -143,55 +141,6 @@ - (BOOL)waitForFirstEncodedFrameWithTimeout:(NSTimeInterval)timeout {
143141
return hasFrame;
144142
}
145143

146-
- (NSDictionary *)sessionInfoRepresentation {
147-
__block NSDictionary *representation = nil;
148-
dispatch_sync(_stateQueue, ^{
149-
NSMutableDictionary *payload = [@{
150-
@"displayReady": @(self->_displayReadyValue),
151-
@"displayStatus": self->_displayStatusValue ?: @"",
152-
@"displayWidth": @(self->_displaySizeValue.width),
153-
@"displayHeight": @(self->_displaySizeValue.height),
154-
@"frameSequence": @(self->_encodedFrameSequenceValue),
155-
@"displayFrameCount": @(self->_displayFrameCount),
156-
@"manualRefreshFrameCount": @(self->_manualRefreshFrameCount),
157-
@"encoder": [self->_videoEncoder statsRepresentation],
158-
} mutableCopy];
159-
if (self->_latestKeyFrameCodec.length > 0) {
160-
payload[@"latestKeyFrameCodec"] = self->_latestKeyFrameCodec;
161-
}
162-
if (self->_latestKeyFrameDecoderConfig.length > 0) {
163-
payload[@"latestDecoderConfigBytes"] = @(self->_latestKeyFrameDecoderConfig.length);
164-
}
165-
representation = payload;
166-
});
167-
return representation;
168-
}
169-
170-
- (nullable NSDictionary *)latestEncodedKeyFrameRepresentation {
171-
__block NSDictionary *representation = nil;
172-
dispatch_sync(_stateQueue, ^{
173-
if (self->_latestKeyFrameData.length == 0) {
174-
return;
175-
}
176-
177-
NSMutableDictionary *payload = [@{
178-
@"sampleData": self->_latestKeyFrameData,
179-
@"frameSequence": @(self->_latestKeyFrameSequenceValue),
180-
@"timestampUs": @(self->_latestKeyFrameTimestampUs),
181-
@"width": @(self->_latestKeyFrameDimensions.width),
182-
@"height": @(self->_latestKeyFrameDimensions.height),
183-
} mutableCopy];
184-
if (self->_latestKeyFrameCodec.length > 0) {
185-
payload[@"codec"] = self->_latestKeyFrameCodec;
186-
}
187-
if (self->_latestKeyFrameDecoderConfig.length > 0) {
188-
payload[@"decoderConfig"] = self->_latestKeyFrameDecoderConfig;
189-
}
190-
representation = payload;
191-
});
192-
return representation;
193-
}
194-
195144
- (void)refreshCurrentFrame {
196145
CVPixelBufferRef pixelBuffer = [_displayBridge copyPixelBuffer];
197146
if (pixelBuffer == nil) {
@@ -200,7 +149,6 @@ - (void)refreshCurrentFrame {
200149

201150
CGSize displaySize = CGSizeMake((CGFloat)CVPixelBufferGetWidth(pixelBuffer), (CGFloat)CVPixelBufferGetHeight(pixelBuffer));
202151
dispatch_async(_stateQueue, ^{
203-
self->_manualRefreshFrameCount += 1;
204152
self->_displaySizeValue = displaySize;
205153
self->_displayReadyValue = YES;
206154
self->_displayStatusValue = [NSString stringWithFormat:@"Private display ready (%.0fx%.0f)", displaySize.width, displaySize.height];
@@ -363,7 +311,6 @@ - (void)disconnect {
363311
- (void)privateSimulatorDisplayBridge:(DFPrivateSimulatorDisplayBridge *)bridge didUpdateFrame:(CVPixelBufferRef)pixelBuffer {
364312
CGSize displaySize = CGSizeMake((CGFloat)CVPixelBufferGetWidth(pixelBuffer), (CGFloat)CVPixelBufferGetHeight(pixelBuffer));
365313
dispatch_async(_stateQueue, ^{
366-
self->_displayFrameCount += 1;
367314
self->_displaySizeValue = displaySize;
368315
self->_displayReadyValue = YES;
369316
self->_displayStatusValue = [NSString stringWithFormat:@"Private display ready (%.0fx%.0f)", displaySize.width, displaySize.height];
@@ -393,7 +340,6 @@ - (void)primeStateFromBridge {
393340
if (pixelBuffer != nil) {
394341
CGSize displaySize = CGSizeMake((CGFloat)CVPixelBufferGetWidth(pixelBuffer), (CGFloat)CVPixelBufferGetHeight(pixelBuffer));
395342
dispatch_async(_stateQueue, ^{
396-
self->_manualRefreshFrameCount += 1;
397343
self->_displaySizeValue = displaySize;
398344
self->_displayReadyValue = YES;
399345
self->_displayStatusValue = [NSString stringWithFormat:@"Private display ready (%.0fx%.0f)", displaySize.width, displaySize.height];

0 commit comments

Comments
 (0)