-
Notifications
You must be signed in to change notification settings - Fork 6k
Advertise iOS Observatory port over mDNS/Bonjour #6533
Conversation
chinmaygarde
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Concerns primarily around thread safety. A few other nits but nothing else major.
| int64_t _nextTextureId; | ||
| BOOL _initialized; | ||
|
|
||
| FlutterObservatoryPublisher* _publisher; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: The convention seems to be to use a scoped_nsobject here. Consider using the smart pointer for consistency.
| #include "flutter/runtime/dart_service_isolate.h" | ||
|
|
||
| @implementation FlutterObservatoryPublisher { | ||
| NSNetService* netService; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: fml::scoped_nsobject here will dry up your cleanup code. You can stop the netservice once in dealloc.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: ivars have leading underscores in Objective-C. See style guide here.
|
|
||
| NSRunLoop* currentLoop = [NSRunLoop currentRunLoop]; | ||
| blink::DartServiceIsolate::ObservatoryServerStateCallback callback = | ||
| fml::MakeCopyable([self, currentLoop](const std::string& uri) mutable { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Capture of this object via a raw pointer makes you susceptible to lifecycle issues when this object is collected before the lambda is used. Admittedly, this is unlikely to happen because RemoveServerStatusCallback is called in dealloc. But you are relying on the assumption that that call releases the last reference to the lambda. You can avoid being susceptible to vagaries in the behavior of that method by just copying a scoped_nsobject reference to the net service as well as the any other objects you'd need (like the runloop).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See the concern about thread safety above. I think it is better to move this to an instance method and invoke it after a weak pointer check in the callback after the task has been posted onto the correct task runner.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The problem is, I need access to self to set the delegate, although I suppose I could get around that by just creating another class to be the delegate in the lambda. I had tried that at one point and it wasn't seeming to work, but I also had some threading issues unresolved at that point - I'll try again.
| // uri comes in as something like 'http://127.0.0.1:XXXXX/' where XXXXX is the port number. | ||
| auto colonPosition = uri.find_last_of(":"); | ||
| auto portSubstring = uri.substr(colonPosition + 1); | ||
| auto port = std::stoi(portSubstring); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's not reinvent [NSURL port]
| } | ||
|
|
||
| - (void)netService:(NSNetService*)sender didNotPublish:(NSDictionary*)errorDict { | ||
| FML_DLOG(FATAL) << "Could not register as server for FlutterObservatoryPublisher. Check your " |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems like a user facing message. You need to make this a FML_LOG message as the DLOG variants will be stripped in opt builds. Also, there is no need to kill the process. An ERROR severity will suffice.
| } | ||
|
|
||
| - (void)dealloc { | ||
| #if FLUTTER_RUNTIME_MODE != FLUTTER_RUNTIME_MODE_RELEASE && \ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can dry up the repeated instances of this long preprocessor guard by just creating an empty class once with a real implementation in the else clause.
runtime/dart_service_isolate.h
Outdated
| static void Shutdown(Dart_NativeArguments args); | ||
|
|
||
| static int handle_count_; | ||
| static std::vector<std::pair<int, ObservatoryServerStateCallback>> callbacks_; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can just stick the callback in a unique pointer and put that in a set. Then return the pointer as the handle to callers. You wont have to perform the linear search for the handle while removing.
runtime/dart_service_isolate.h
Outdated
| static void Shutdown(Dart_NativeArguments args); | ||
|
|
||
| static int handle_count_; | ||
| static std::vector<std::pair<int, ObservatoryServerStateCallback>> callbacks_; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The NotifyServerState call probably comes on the thread for the service isolate while you are adding and removing callbacks into this collection on the platform thread. This is not thread safe and you need a mutex around the access to the collection.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think I've covered all the accesses now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm a bit concerned that this could deadlock though. If callback code tries to add or remove a callback (or remove itself). Maybe a compare and swap would work instead of the mutexes, or maybe I should be looking to implement this on the dart side instead.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added a scope around the lock so that I don't have to hold it while making the callback, I guess that should avoid the potential deadlock.
runtime/dart_service_isolate.cc
Outdated
| for (auto it = callbacks_.begin(); it != callbacks_.end(); it++) { | ||
| std::pair<int, DartServiceIsolate::ObservatoryServerStateCallback> pair = | ||
| *it; | ||
| pair.second(uri); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This pattern is susceptible to mutation while enumeration. Collect the callbacks in a separate collection here and cycle over the items in that collection to invoke the callbacks.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See that note later about thread safety and the need for a lock guard here. You'll have to invoke the callback after the guard ends.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Move the collection, added mutex
| NSAssert(self, @"Super must not return null on init."); | ||
|
|
||
| NSRunLoop* currentLoop = [NSRunLoop currentRunLoop]; | ||
| blink::DartServiceIsolate::ObservatoryServerStateCallback callback = |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since this callback will likely happen on the thread used by the service isolate, you'll have to capture a weak pointer and a task runner reference and then post a task back on that task runner to perform the work described here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When you say a weak pointer and a task runner, what's the weak pointer to? The task runner?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I thought I could avoid the need for this by telling the NSNetService to run on the currentRunLoop from the calling (platform) thread. Is that not really safe?
|
fyi @shrike69, who was looking at adding a similar callback for advertising the service port to Fuchsia's hub. |
|
@rmacnak-google - I've created dart-lang/sdk#34782 to explore doing this on the SDK side rather than on individual consumer sides. It seems like something that could be handled in a |
chinmaygarde
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Issues with callback unregistration now. Please add a unit test to the runtime_unittests target.
runtime/dart_service_isolate.cc
Outdated
| } | ||
| for (auto it = callbacks.begin(); it != callbacks.end(); it++) { | ||
| auto callback = std::move(**it); | ||
| callback(uri); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: Not sure why the local variable is necessary. Shouldn't (*it)(uri) suffice? Not a big deal. Just curious.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(**it) works. It's a convention I picked up from elsewhere, although I guess it doesn't make as much sense when I'm just using auto anyway. Will change.
runtime/dart_service_isolate.cc
Outdated
| callbacks; | ||
| { | ||
| std::lock_guard<std::mutex> lock(callbacks_mutex_); | ||
| callbacks = std::move(callbacks_); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Did you mean to clear the current set of callbacks already registered? I thought callers were supposed to manually unregister their callbacks based on the handle by calling RemoveServerStatusCallback.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, I'm using move incorrectly here.
| #include "flutter/shell/platform/darwin/ios/framework/Source/platform_message_response_darwin.h" | ||
| #include "flutter/shell/platform/darwin/ios/platform_view_ios.h" | ||
|
|
||
| #include "flutter/shell/platform/darwin/ios/framework/Source/FlutterObservatoryPublisher.h" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: The space between these set of includes is not necessary.
runtime/dart_service_isolate.cc
Outdated
| if (!observatory_uri_.empty()) { | ||
| callback(observatory_uri_); | ||
| } | ||
| return callback_pointer; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is going to return a smart pointer with a null pointer member because you moved it away. This makes it so that the callback can never be removed.
|
CC @mklim |
flutter/engine@08272ee...2cd79d1 git log 08272ee..2cd79d1 --no-merges --oneline 2cd79d1 Roll src/third_party/skia 4b7b2ceb4ad9..b3067e8e1b6e (1 commits) (flutter/engine#6551) 62cd86c Advertise iOS Observatory port over mDNS/Bonjour (flutter/engine#6533) 26f437f Fix crash when removeViewImmediate invoked from platform view (flutter/engine#6266) aed6b8c Roll Dart to ac6d4f7e653deba11d4836768376537893a9e9d6. (flutter/engine#6549) 3ba6270 Roll src/third_party/skia 921ec976556c..4b7b2ceb4ad9 (14 commits) (flutter/engine#6550) The AutoRoll server is located here: https://autoroll.skia.org/r/flutter-engine-flutter-autoroll Documentation for the AutoRoller is here: https://skia.googlesource.com/buildbot/+/master/autoroll/README.md If the roll is causing failures, please contact the current sheriff, who should be CC'd on the roll, and stop the roller if necessary.
flutter/engine@08272ee...126e61d git log 08272ee..126e61d --no-merges --oneline 126e61d Roll src/third_party/skia b3067e8e1b6e..198b87e5be20 (2 commits) (flutter/engine#6553) 2cd79d1 Roll src/third_party/skia 4b7b2ceb4ad9..b3067e8e1b6e (1 commits) (flutter/engine#6551) 62cd86c Advertise iOS Observatory port over mDNS/Bonjour (flutter/engine#6533) 26f437f Fix crash when removeViewImmediate invoked from platform view (flutter/engine#6266) aed6b8c Roll Dart to ac6d4f7e653deba11d4836768376537893a9e9d6. (flutter/engine#6549) 3ba6270 Roll src/third_party/skia 921ec976556c..4b7b2ceb4ad9 (14 commits) (flutter/engine#6550) The AutoRoll server is located here: https://autoroll.skia.org/r/flutter-engine-flutter-autoroll Documentation for the AutoRoller is here: https://skia.googlesource.com/buildbot/+/master/autoroll/README.md If the roll is causing failures, please contact the current sheriff, who should be CC'd on the roll, and stop the roller if necessary.
flutter/engine@08272ee...2dbbb2a git log 08272ee..2dbbb2a --no-merges --oneline 2dbbb2a Roll src/third_party/skia 198b87e5be20..a583a06f831e (5 commits) (flutter/engine#6554) 126e61d Roll src/third_party/skia b3067e8e1b6e..198b87e5be20 (2 commits) (flutter/engine#6553) 2cd79d1 Roll src/third_party/skia 4b7b2ceb4ad9..b3067e8e1b6e (1 commits) (flutter/engine#6551) 62cd86c Advertise iOS Observatory port over mDNS/Bonjour (flutter/engine#6533) 26f437f Fix crash when removeViewImmediate invoked from platform view (flutter/engine#6266) aed6b8c Roll Dart to ac6d4f7e653deba11d4836768376537893a9e9d6. (flutter/engine#6549) 3ba6270 Roll src/third_party/skia 921ec976556c..4b7b2ceb4ad9 (14 commits) (flutter/engine#6550) The AutoRoll server is located here: https://autoroll.skia.org/r/flutter-engine-flutter-autoroll Documentation for the AutoRoller is here: https://skia.googlesource.com/buildbot/+/master/autoroll/README.md If the roll is causing failures, please contact the current sheriff, who should be CC'd on the roll, and stop the roller if necessary.
flutter/engine@08272ee...fc376dd git log 08272ee..fc376dd --no-merges --oneline fc376dd Roll src/third_party/skia a583a06f831e..1349ec4de78c (1 commits) (flutter/engine#6555) 2dbbb2a Roll src/third_party/skia 198b87e5be20..a583a06f831e (5 commits) (flutter/engine#6554) 126e61d Roll src/third_party/skia b3067e8e1b6e..198b87e5be20 (2 commits) (flutter/engine#6553) 2cd79d1 Roll src/third_party/skia 4b7b2ceb4ad9..b3067e8e1b6e (1 commits) (flutter/engine#6551) 62cd86c Advertise iOS Observatory port over mDNS/Bonjour (flutter/engine#6533) 26f437f Fix crash when removeViewImmediate invoked from platform view (flutter/engine#6266) aed6b8c Roll Dart to ac6d4f7e653deba11d4836768376537893a9e9d6. (flutter/engine#6549) 3ba6270 Roll src/third_party/skia 921ec976556c..4b7b2ceb4ad9 (14 commits) (flutter/engine#6550) The AutoRoll server is located here: https://autoroll.skia.org/r/flutter-engine-flutter-autoroll Documentation for the AutoRoller is here: https://skia.googlesource.com/buildbot/+/master/autoroll/README.md If the roll is causing failures, please contact the current sheriff, who should be CC'd on the roll, and stop the roller if necessary.
flutter/engine@08272ee...215c146 git log 08272ee..215c146 --no-merges --oneline 215c146 Roll src/third_party/skia 1349ec4de78c..3d99b1e347be (3 commits) (flutter/engine#6556) fc376dd Roll src/third_party/skia a583a06f831e..1349ec4de78c (1 commits) (flutter/engine#6555) 2dbbb2a Roll src/third_party/skia 198b87e5be20..a583a06f831e (5 commits) (flutter/engine#6554) 126e61d Roll src/third_party/skia b3067e8e1b6e..198b87e5be20 (2 commits) (flutter/engine#6553) 2cd79d1 Roll src/third_party/skia 4b7b2ceb4ad9..b3067e8e1b6e (1 commits) (flutter/engine#6551) 62cd86c Advertise iOS Observatory port over mDNS/Bonjour (flutter/engine#6533) 26f437f Fix crash when removeViewImmediate invoked from platform view (flutter/engine#6266) aed6b8c Roll Dart to ac6d4f7e653deba11d4836768376537893a9e9d6. (flutter/engine#6549) 3ba6270 Roll src/third_party/skia 921ec976556c..4b7b2ceb4ad9 (14 commits) (flutter/engine#6550) The AutoRoll server is located here: https://autoroll.skia.org/r/flutter-engine-flutter-autoroll Documentation for the AutoRoller is here: https://skia.googlesource.com/buildbot/+/master/autoroll/README.md If the roll is causing failures, please contact the current sheriff, who should be CC'd on the roll, and stop the roller if necessary.
flutter/engine@08272ee...df5f420 git log 08272ee..df5f420 --no-merges --oneline df5f420 Roll src/third_party/skia 3d99b1e347be..c9092eb46754 (10 commits) (flutter/engine#6559) 215c146 Roll src/third_party/skia 1349ec4de78c..3d99b1e347be (3 commits) (flutter/engine#6556) fc376dd Roll src/third_party/skia a583a06f831e..1349ec4de78c (1 commits) (flutter/engine#6555) 2dbbb2a Roll src/third_party/skia 198b87e5be20..a583a06f831e (5 commits) (flutter/engine#6554) 126e61d Roll src/third_party/skia b3067e8e1b6e..198b87e5be20 (2 commits) (flutter/engine#6553) 2cd79d1 Roll src/third_party/skia 4b7b2ceb4ad9..b3067e8e1b6e (1 commits) (flutter/engine#6551) 62cd86c Advertise iOS Observatory port over mDNS/Bonjour (flutter/engine#6533) 26f437f Fix crash when removeViewImmediate invoked from platform view (flutter/engine#6266) aed6b8c Roll Dart to ac6d4f7e653deba11d4836768376537893a9e9d6. (flutter/engine#6549) 3ba6270 Roll src/third_party/skia 921ec976556c..4b7b2ceb4ad9 (14 commits) (flutter/engine#6550) The AutoRoll server is located here: https://autoroll.skia.org/r/flutter-engine-flutter-autoroll Documentation for the AutoRoller is here: https://skia.googlesource.com/buildbot/+/master/autoroll/README.md If the roll is causing failures, please contact the current sheriff, who should be CC'd on the roll, and stop the roller if necessary.
Thank you for flagging this - it looks very useful! |
flutter/engine@08272ee...04c860f git log 08272ee..04c860f --no-merges --oneline 04c860f Revert "Roll Dart to ac6d4f7e653deba11d4836768376537893a9e9d6. (flutter#6549)" (flutter/engine#6568) ef2ea12 Roll src/third_party/skia 5b90b84085cf..cb65ce7f77c9 (8 commits) (flutter/engine#6567) 065246e Roll src/third_party/skia 483536c242f3..5b90b84085cf (3 commits) (flutter/engine#6566) b0008ca Roll src/third_party/skia 583161c09a3b..483536c242f3 (2 commits) (flutter/engine#6565) e22da7c Roll src/third_party/skia 0a8faf3f557d..583161c09a3b (1 commits) (flutter/engine#6564) 915f5e0 Roll src/third_party/skia f831b64ed40e..0a8faf3f557d (5 commits) (flutter/engine#6563) ad0fbd5 Roll src/third_party/skia c9092eb46754..f831b64ed40e (14 commits) (flutter/engine#6561) 6697d9d Update @animation dartdoc directives to current api (flutter/engine#6552) 10f9cab Ensure that the platform view is created and destroyed when running the shell unittests. (flutter/engine#6560) df5f420 Roll src/third_party/skia 3d99b1e347be..c9092eb46754 (10 commits) (flutter/engine#6559) 215c146 Roll src/third_party/skia 1349ec4de78c..3d99b1e347be (3 commits) (flutter/engine#6556) fc376dd Roll src/third_party/skia a583a06f831e..1349ec4de78c (1 commits) (flutter/engine#6555) 2dbbb2a Roll src/third_party/skia 198b87e5be20..a583a06f831e (5 commits) (flutter/engine#6554) 126e61d Roll src/third_party/skia b3067e8e1b6e..198b87e5be20 (2 commits) (flutter/engine#6553) 2cd79d1 Roll src/third_party/skia 4b7b2ceb4ad9..b3067e8e1b6e (1 commits) (flutter/engine#6551) 62cd86c Advertise iOS Observatory port over mDNS/Bonjour (flutter/engine#6533) 26f437f Fix crash when removeViewImmediate invoked from platform view (flutter/engine#6266) aed6b8c Roll Dart to ac6d4f7e653deba11d4836768376537893a9e9d6. (flutter/engine#6549) 3ba6270 Roll src/third_party/skia 921ec976556c..4b7b2ceb4ad9 (14 commits) (flutter/engine#6550) The AutoRoll server is located here: https://autoroll.skia.org/r/flutter-engine-flutter-autoroll Documentation for the AutoRoller is here: https://skia.googlesource.com/buildbot/+/master/autoroll/README.md If the roll is causing failures, please contact the current sheriff, who should be CC'd on the roll, and stop the roller if necessary.
flutter/engine@08272ee...04c860f git log 08272ee..04c860f --no-merges --oneline 04c860f Revert "Roll Dart to ac6d4f7e653deba11d4836768376537893a9e9d6. (#6549)" (flutter/engine#6568) ef2ea12 Roll src/third_party/skia 5b90b84085cf..cb65ce7f77c9 (8 commits) (flutter/engine#6567) 065246e Roll src/third_party/skia 483536c242f3..5b90b84085cf (3 commits) (flutter/engine#6566) b0008ca Roll src/third_party/skia 583161c09a3b..483536c242f3 (2 commits) (flutter/engine#6565) e22da7c Roll src/third_party/skia 0a8faf3f557d..583161c09a3b (1 commits) (flutter/engine#6564) 915f5e0 Roll src/third_party/skia f831b64ed40e..0a8faf3f557d (5 commits) (flutter/engine#6563) ad0fbd5 Roll src/third_party/skia c9092eb46754..f831b64ed40e (14 commits) (flutter/engine#6561) 6697d9d Update @animation dartdoc directives to current api (flutter/engine#6552) 10f9cab Ensure that the platform view is created and destroyed when running the shell unittests. (flutter/engine#6560) df5f420 Roll src/third_party/skia 3d99b1e347be..c9092eb46754 (10 commits) (flutter/engine#6559) 215c146 Roll src/third_party/skia 1349ec4de78c..3d99b1e347be (3 commits) (flutter/engine#6556) fc376dd Roll src/third_party/skia a583a06f831e..1349ec4de78c (1 commits) (flutter/engine#6555) 2dbbb2a Roll src/third_party/skia 198b87e5be20..a583a06f831e (5 commits) (flutter/engine#6554) 126e61d Roll src/third_party/skia b3067e8e1b6e..198b87e5be20 (2 commits) (flutter/engine#6553) 2cd79d1 Roll src/third_party/skia 4b7b2ceb4ad9..b3067e8e1b6e (1 commits) (flutter/engine#6551) 62cd86c Advertise iOS Observatory port over mDNS/Bonjour (flutter/engine#6533) 26f437f Fix crash when removeViewImmediate invoked from platform view (flutter/engine#6266) aed6b8c Roll Dart to ac6d4f7e653deba11d4836768376537893a9e9d6. (flutter/engine#6549) 3ba6270 Roll src/third_party/skia 921ec976556c..4b7b2ceb4ad9 (14 commits) (flutter/engine#6550) The AutoRoll server is located here: https://autoroll.skia.org/r/flutter-engine-flutter-autoroll Documentation for the AutoRoller is here: https://skia.googlesource.com/buildbot/+/master/autoroll/README.md If the roll is causing failures, please contact the current sheriff, who should be CC'd on the roll, and stop the roller if necessary.
This PR does two things:
This will enable
flutter attachto work better for Add2App scenarios (where it can't check for logs that have already happened).This will have to be sync'ed up with #6447, but can work independently of however that one ends up going.
/cc @chinmaygarde @GaryQian @cbracken @mit-mit @matthew-carroll