This repository has been archived by the owner on Feb 22, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 9.8k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[camera]fix threading issue with thread safe types to ensure dispatch…
…ing to main thread before calling engine api
- Loading branch information
1 parent
6531740
commit 4f53674
Showing
14 changed files
with
419 additions
and
10 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
2 changes: 1 addition & 1 deletion
2
packages/camera/camera/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
44 changes: 44 additions & 0 deletions
44
packages/camera/camera/example/ios/RunnerTests/ThreadSafeEventChannelTests.m
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
// Copyright 2013 The Flutter Authors. All rights reserved. | ||
// Use of this source code is governed by a BSD-style license that can be | ||
// found in the LICENSE file. | ||
|
||
@import camera; | ||
@import XCTest; | ||
#import <OCMock/OCMock.h> | ||
|
||
@interface ThreadSafeEventChannelTests : XCTestCase | ||
@end | ||
|
||
@implementation ThreadSafeEventChannelTests { | ||
FLTThreadSafeEventChannel *_channel; | ||
XCTestExpectation *_mainThreadExpectation; | ||
} | ||
|
||
- (void)setUp { | ||
[super setUp]; | ||
id mockEventChannel = OCMClassMock([FlutterEventChannel class]); | ||
|
||
_mainThreadExpectation = | ||
[[XCTestExpectation alloc] initWithDescription:@"invokeMethod must be called in main thread"]; | ||
_channel = [[FLTThreadSafeEventChannel alloc] initWithEventChannel:mockEventChannel]; | ||
|
||
OCMStub([mockEventChannel setStreamHandler:[OCMArg any]]).andDo(^(NSInvocation *invocation) { | ||
if (NSThread.isMainThread) { | ||
[self->_mainThreadExpectation fulfill]; | ||
} | ||
}); | ||
} | ||
|
||
- (void)testSetStreamHandler_shouldStayOnMainThreadIfCalledFromMainThread { | ||
[_channel setStreamHandler:nil]; | ||
[self waitForExpectations:@[ _mainThreadExpectation ] timeout:1]; | ||
} | ||
|
||
- (void)testSetStreamHandler_shouldDispatchToMainThreadIfCalledFromBackgroundThread { | ||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ | ||
[self->_channel setStreamHandler:nil]; | ||
}); | ||
[self waitForExpectations:@[ _mainThreadExpectation ] timeout:1]; | ||
} | ||
|
||
@end |
46 changes: 46 additions & 0 deletions
46
packages/camera/camera/example/ios/RunnerTests/ThreadSafeMethodChannelTests.m
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
// Copyright 2013 The Flutter Authors. All rights reserved. | ||
// Use of this source code is governed by a BSD-style license that can be | ||
// found in the LICENSE file. | ||
|
||
@import camera; | ||
@import XCTest; | ||
#import <OCMock/OCMock.h> | ||
|
||
@interface ThreadSafeMethodChannelTests : XCTestCase | ||
@end | ||
|
||
@implementation ThreadSafeMethodChannelTests { | ||
FLTThreadSafeMethodChannel *_channel; | ||
XCTestExpectation *_mainThreadExpectation; | ||
} | ||
|
||
- (void)setUp { | ||
[super setUp]; | ||
id mockMethodChannel = OCMClassMock([FlutterMethodChannel class]); | ||
|
||
_mainThreadExpectation = | ||
[[XCTestExpectation alloc] initWithDescription:@"invokeMethod must be called in main thread"]; | ||
_channel = [[FLTThreadSafeMethodChannel alloc] initWithMethodChannel:mockMethodChannel]; | ||
|
||
OCMStub([mockMethodChannel invokeMethod:[OCMArg any] arguments:[OCMArg any]]) | ||
.andDo(^(NSInvocation *invocation) { | ||
if (NSThread.isMainThread) { | ||
[self->_mainThreadExpectation fulfill]; | ||
} | ||
}); | ||
} | ||
|
||
- (void)testInvokeMethod_shouldStayOnMainThreadIfCalledFromMainThread { | ||
[_channel invokeMethod:@"foo" arguments:nil]; | ||
|
||
[self waitForExpectations:@[ _mainThreadExpectation ] timeout:1]; | ||
} | ||
|
||
- (void)testInvokeMethod__shouldDispatchToMainThreadIfCalledFromBackgroundThread { | ||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ | ||
[self->_channel invokeMethod:@"foo" arguments:nil]; | ||
}); | ||
[self waitForExpectations:@[ _mainThreadExpectation ] timeout:1]; | ||
} | ||
|
||
@end |
74 changes: 74 additions & 0 deletions
74
packages/camera/camera/example/ios/RunnerTests/ThreadSafeTextureRegistryTests.m
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
// Copyright 2013 The Flutter Authors. All rights reserved. | ||
// Use of this source code is governed by a BSD-style license that can be | ||
// found in the LICENSE file. | ||
|
||
@import camera; | ||
@import XCTest; | ||
#import <OCMock/OCMock.h> | ||
|
||
@interface ThreadSafeTextureRegistryTests : XCTestCase | ||
@end | ||
|
||
@implementation ThreadSafeTextureRegistryTests { | ||
FLTThreadSafeTextureRegistry *_registry; | ||
XCTestExpectation *_registerTextureExpectation; | ||
XCTestExpectation *_unregisterTextureExpectation; | ||
XCTestExpectation *_textureFrameAvailableExpectation; | ||
} | ||
|
||
- (void)setUp { | ||
[super setUp]; | ||
id mockTextureRegistry = OCMProtocolMock(@protocol(FlutterTextureRegistry)); | ||
_registry = [[FLTThreadSafeTextureRegistry alloc] initWithTextureRegistry:mockTextureRegistry]; | ||
|
||
_registerTextureExpectation = [[XCTestExpectation alloc] | ||
initWithDescription:@"registerTexture must be called in main thread"]; | ||
_unregisterTextureExpectation = [[XCTestExpectation alloc] | ||
initWithDescription:@"unregisterTexture must be called in main thread"]; | ||
_textureFrameAvailableExpectation = [[XCTestExpectation alloc] | ||
initWithDescription:@"textureFrameAvailable must be called in main thread"]; | ||
|
||
OCMStub([mockTextureRegistry registerTexture:[OCMArg any]]).andDo(^(NSInvocation *invocation) { | ||
if (NSThread.isMainThread) { | ||
[self->_registerTextureExpectation fulfill]; | ||
} | ||
}); | ||
|
||
OCMStub([mockTextureRegistry unregisterTexture:0]).andDo(^(NSInvocation *invocation) { | ||
if (NSThread.isMainThread) { | ||
[self->_unregisterTextureExpectation fulfill]; | ||
} | ||
}); | ||
|
||
OCMStub([mockTextureRegistry textureFrameAvailable:0]).andDo(^(NSInvocation *invocation) { | ||
if (NSThread.isMainThread) { | ||
[self->_textureFrameAvailableExpectation fulfill]; | ||
} | ||
}); | ||
} | ||
|
||
- (void)testShouldStayOnMainThreadIfCalledFromMainThread { | ||
NSObject<FlutterTexture> *anyTexture = OCMProtocolMock(@protocol(FlutterTexture)); | ||
[_registry registerTexture:anyTexture]; | ||
[_registry textureFrameAvailable:0]; | ||
[_registry unregisterTexture:0]; | ||
[self waitForExpectations:@[ | ||
_registerTextureExpectation, _unregisterTextureExpectation, _textureFrameAvailableExpectation | ||
] | ||
timeout:1]; | ||
} | ||
|
||
- (void)testShouldDispatchToMainThreadIfCalledFromBackgroundThread { | ||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ | ||
NSObject<FlutterTexture> *anyTexture = OCMProtocolMock(@protocol(FlutterTexture)); | ||
[self->_registry registerTexture:anyTexture]; | ||
[self->_registry textureFrameAvailable:0]; | ||
[self->_registry unregisterTexture:0]; | ||
}); | ||
[self waitForExpectations:@[ | ||
_registerTextureExpectation, _unregisterTextureExpectation, _textureFrameAvailableExpectation | ||
] | ||
timeout:1]; | ||
} | ||
|
||
@end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
27 changes: 27 additions & 0 deletions
27
packages/camera/camera/ios/Classes/FLTThreadSafeEventChannel.h
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
// Copyright 2013 The Flutter Authors. All rights reserved. | ||
// Use of this source code is governed by a BSD-style license that can be | ||
// found in the LICENSE file. | ||
|
||
#import <Flutter/Flutter.h> | ||
|
||
NS_ASSUME_NONNULL_BEGIN | ||
|
||
/** | ||
* Wrapper for FlutterEventChannel that always sends events on the main thread | ||
*/ | ||
@interface FLTThreadSafeEventChannel : NSObject | ||
|
||
/** | ||
* Creates a FLTThreadSafeEventChannel by wrapping a FlutterEventChannel object. | ||
* @param channel The FlutterEventChannel object to be wrapped. | ||
*/ | ||
- (instancetype)initWithEventChannel:(FlutterEventChannel *)channel; | ||
|
||
/* | ||
* Registers a handler for stream setup requests from the Flutter side on main thread. | ||
*/ | ||
- (void)setStreamHandler:(nullable NSObject<FlutterStreamHandler> *)handler; | ||
|
||
@end | ||
|
||
NS_ASSUME_NONNULL_END |
29 changes: 29 additions & 0 deletions
29
packages/camera/camera/ios/Classes/FLTThreadSafeEventChannel.m
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
// Copyright 2013 The Flutter Authors. All rights reserved. | ||
// Use of this source code is governed by a BSD-style license that can be | ||
// found in the LICENSE file. | ||
|
||
#import "FLTThreadSafeEventChannel.h" | ||
|
||
@implementation FLTThreadSafeEventChannel { | ||
FlutterEventChannel *_channel; | ||
} | ||
|
||
- (instancetype)initWithEventChannel:(FlutterEventChannel *)channel { | ||
self = [super init]; | ||
if (self) { | ||
_channel = channel; | ||
} | ||
return self; | ||
} | ||
|
||
- (void)setStreamHandler:(NSObject<FlutterStreamHandler> *)handler { | ||
if (!NSThread.isMainThread) { | ||
dispatch_async(dispatch_get_main_queue(), ^{ | ||
[self->_channel setStreamHandler:handler]; | ||
}); | ||
} else { | ||
[_channel setStreamHandler:handler]; | ||
} | ||
} | ||
|
||
@end |
27 changes: 27 additions & 0 deletions
27
packages/camera/camera/ios/Classes/FLTThreadSafeMethodChannel.h
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
// Copyright 2013 The Flutter Authors. All rights reserved. | ||
// Use of this source code is governed by a BSD-style license that can be | ||
// found in the LICENSE file. | ||
|
||
#import <Flutter/Flutter.h> | ||
|
||
NS_ASSUME_NONNULL_BEGIN | ||
|
||
/** | ||
* Wrapper for FlutterMethodChannel that always invokes messages on the main thread | ||
*/ | ||
@interface FLTThreadSafeMethodChannel : NSObject | ||
|
||
/** | ||
* Creates a FLTThreadSafeMethodChannel by wrapping a FlutterMethodChannel object. | ||
* @param channel The FlutterMethodChannel object to be wrapped. | ||
*/ | ||
- (instancetype)initWithMethodChannel:(FlutterMethodChannel *)channel; | ||
|
||
/** | ||
* Invokes the specified flutter method with the specified arguments on main thread. | ||
*/ | ||
- (void)invokeMethod:(NSString *)method arguments:(nullable id)arguments; | ||
|
||
@end | ||
|
||
NS_ASSUME_NONNULL_END |
Oops, something went wrong.