Skip to content
This repository has been archived by the owner on Aug 8, 2023. It is now read-only.

Silently ignore removal of nonexistent style layer or source #7962

Merged
merged 7 commits into from
Feb 23, 2017
Merged

Silently ignore removal of nonexistent style layer or source #7962

merged 7 commits into from
Feb 23, 2017

Conversation

eimantas
Copy link
Contributor

@eimantas eimantas commented Feb 7, 2017

In case of exception:

  • log a warning to console;
  • mark style as nonmutated;
  • return nullptr;

@mention-bot
Copy link

@eimantas, thanks for your PR! By analyzing this pull request, we identified @jfirebaugh, @1ec5 and @incanus to be potential reviewers.

@tobrun tobrun added iOS Mapbox Maps SDK for iOS macOS Mapbox Maps SDK for macOS Core The cross-platform C++ core, aka mbgl and removed iOS Mapbox Maps SDK for iOS macOS Mapbox Maps SDK for macOS labels Feb 7, 2017
kkaefer
kkaefer previously requested changes Feb 7, 2017
impl->onUpdate(Update::Classes);
return removedLayer;
} catch (const std::runtime_error& err) {
impl->styleMutated = false;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe this is incorrect. It always sets to false, even when the style was mutated before the call to remove a layer.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should I remove this statement? Since a couple of lines above there's impl->styleMutated = true; assuming the layer will be removed. And since this is in a catch block - the layer is not removed and the style is not mutated.

@kkaefer
Copy link
Member

kkaefer commented Feb 7, 2017

@eimantas Thank you for your contribution! Are you able to make the changes?

@eimantas
Copy link
Contributor Author

eimantas commented Feb 7, 2017

@kkaefer - change implemented.

@eimantas
Copy link
Contributor Author

eimantas commented Feb 7, 2017

Since this is "Core GL" fix, should I also add the entry to the Android SDK changelog?

@kkaefer
Copy link
Member

kkaefer commented Feb 7, 2017

@eimantas The fix is to not mark the style as mutated until the change was completed successfully, so that a failure to remove still leaves the style in unmutated state

@eimantas
Copy link
Contributor Author

eimantas commented Feb 7, 2017

Please see the change now.

@boundsj boundsj added this to the ios-v3.5.0 milestone Feb 7, 2017
@boundsj
Copy link
Contributor

boundsj commented Feb 7, 2017

Noting that this addresses #7900

@1ec5
Copy link
Contributor

1ec5 commented Feb 7, 2017

Would this change officially sanction a pattern of removing potentially nonexistent layers? That’s reasonable, but then why issue a warning? The developer might want to remove layers indiscriminately without spewing onto the console.

On the other hand, if we still consider the removal of a nonexistent layer to be programmer error, we should continue to throw an assertion. However, -[MGLStyle removeLayer:] (in the iOS/macOS SDK) could catch the C++ exception and rethrow it as an NSException, offering pure Objective-C code the opportunity to catch the exception if the removal was intentional.

@eimantas
Copy link
Contributor Author

eimantas commented Feb 8, 2017

That’s reasonable, but then why issue a warning?

So that a developer can fix his code the check whether the layer exists before removing it.

However if you decide to transfer this into Cocoa/Android land with NSException and related classes, I believe there should be a convenience function for checking whether a layer exists before removing it (something like bool hasLayer(const std::string& id) or bool layerExists(const std::string& id). This would encourage developers to use defensive style of programming. I understand there's findLayer function, but that is not really its purpose. Is it?

@eimantas eimantas changed the title Catch exception when removing nonexistent layer [core] Catch exception when removing nonexistent layer Feb 8, 2017
@1ec5
Copy link
Contributor

1ec5 commented Feb 8, 2017

I believe there should be a convenience function for checking whether a layer exists before removing it (something like bool hasLayer(const std::string& id) or bool layerExists(const std::string& id). This would encourage developers to use defensive style of programming. I understand there's findLayer function, but that is not really its purpose. Is it?

Sure, that's an intended use of -[MGLStyle layerWithIdentifier:] and its Android equivalent. In fact, on iOS and macOS, it's impossible to remove a layer without obtaining an MGLStyleLayer object first. So the only time this error comes up is when you obtain the MGLStyleLayer by creating one anew or hanging onto a stale MGLStyleLayer.

There should be negligible performance impact to always checking for the existence of a layer before removing it. On the other hand, there should also be negligible performance impact to attempting to remove a nonexistent layer. So if that's the only programming mistake we're trying to head off with the warning, I'd lean towards silencing the warning. What do others here think?

@jfirebaugh
Copy link
Contributor

I think we should change the removal method in core and in SDK bindings to return a null pointer and not log any message.

@eimantas
Copy link
Contributor Author

eimantas commented Feb 9, 2017

If everyone is fine with silently ignoring the removal of nonexistent layer, I can implement these changes.

Copy link
Contributor

@jfirebaugh jfirebaugh left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rather than throwing an exception which is caught by Map::removeLayer, Style::removeLayer itself should return nullptr.

@eimantas eimantas changed the title [core] Catch exception when removing nonexistent layer [core] Silently ignore removal of nonexistent style layer Feb 12, 2017
@eimantas
Copy link
Contributor Author

Updates are done.

Copy link
Contributor

@jfirebaugh jfirebaugh left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you update the CHANGELOG entries now that it doesn't log a warning?

@ivovandongen Do you want to change the Android bindings to follow this behavior (no error, return null)?

@ivovandongen
Copy link
Contributor

Do you want to change the Android bindings to follow this behavior (no error, return null)?

@jfirebaugh Sure, but don't we want to make this consistent for sources than as well? Eg here

@1ec5
Copy link
Contributor

1ec5 commented Feb 13, 2017

On iOS/macOS, -[MGL*StyleLayer removeFromMapView:] clears its internal state before telling mbgl to remove the layer from the style. If the layer isn’t part of the style, -removeFromMapView: will bail, resulting in an unusable MGLStyleLayer object. The following code would crash:

MGLBackgroundStyleLayer *fillLayer = [[MGLBackgroundStyleLayer alloc] initWithIdentifier:@"bg"];
[style removeLayer:fillLayer]; // no-op
//
[style addLayer:fillLayer]; // MGLRedundantLayerException

I think the correct behavior would be to ensure that _pendingLayer ends up with whatever either _pendingLayer or rawLayer had at the beginning of this method.

/cc @boundsj

@boundsj
Copy link
Contributor

boundsj commented Feb 14, 2017

@1ec5 I think the correct behavior would be to ensure that _pendingLayer ends up with whatever either _pendingLayer or rawLayer had at the beginning of this method.

I agree. Instead of setting this variable and property to nullptr in the first place, I think we can create a mbgl::style::Layer pointer and use it to set self.rawLayer just like we do in initWithIdentifier:source: but we can use self.identifier.UTF8String and self.sourceIdentifier.UTF8String to create the layer (since we will have those strings at this point).

I think we should do this in between the two early returns, resetting _pendingLayer and self.rawLayer if mbglMap->removeLayer() succeeds but before the dynamic_cast check. I think this would allow the layer to keep the original values if the remove operations fails and still have a valid value if somehow the wrong type of layer was removed and the cast check causes an early return.

I can create an issue for this and make a separate PR.

@boundsj
Copy link
Contributor

boundsj commented Feb 16, 2017

@jfirebaugh @kkaefer can this change be merged now? Once this core change lands and the behavior is changed, I'd like to add Darwin platform tests for the change in #8055.

@jfirebaugh
Copy link
Contributor

don't we want to make this consistent for sources than as well

Yes, we should.

The Android bindings also need to be adjusted (e.g. here), to either test for a nullptr result (if we want to keep the existing behavior), or to pass on a null pointer.

Eimantas Vaiciunas and others added 5 commits February 20, 2017 15:06
In case of exception, silently ignore removal of nonexistent layer
and return nullptr
This reverts a previous change that recreated the pending and raw
layer pointers if an identifier match caused a layer to be removed
but the removed layer was of a different type than the layer triggering
the removal

This refactors the pointer replacement to use a simpler solution that
returns early if the layer does not have a raw pointer loaded in the
mbgl map instance.
ivovandongen and others added 2 commits February 22, 2017 10:25
This refactors the source removal methods to make them consistent
with the way layers are removed. This makes removal of nonexistent
sources and removal of sources of a different type but same
identifier as a previously added source a no-ops.

As with layers, the check at the top of the method to ensure that the
raw pointer is the same as the one in mbgl for the same
identifier string should make it impossible to attempt to remove
a source of a different type than the one in mbgl for the same
identifier. However, for consistency with the layer implementation,
the reinterpret_cast has been replaced with a dynamic_cast and check
for nullptr.
@boundsj boundsj changed the title [core] Silently ignore removal of nonexistent style layer Silently ignore removal of nonexistent style layer or source Feb 23, 2017
@boundsj boundsj added Android Mapbox Maps SDK for Android crash iOS Mapbox Maps SDK for iOS macOS Mapbox Maps SDK for macOS labels Feb 23, 2017
@boundsj
Copy link
Contributor

boundsj commented Feb 23, 2017

@jfirebaugh @kkaefer @1ec5: @ivovandongen and I made the change to make nonexistent source and layer removal consistent and updated the platform implementations (including taking care of #8053). I think this is ready for re-review.

Copy link
Contributor

@1ec5 1ec5 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Darwin/iOS/macOS changes look good.

@kkaefer kkaefer dismissed their stale review February 23, 2017 10:06

outdated

@boundsj boundsj merged commit 7d90180 into mapbox:master Feb 23, 2017
JesseCrocker pushed a commit to trailbehind/mapbox-gl-native that referenced this pull request Mar 6, 2017
JesseCrocker pushed a commit to trailbehind/mapbox-gl-native that referenced this pull request Mar 6, 2017
@eimantas eimantas deleted the 7900-remove-nonexistent-layer branch March 22, 2017 10:19
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Android Mapbox Maps SDK for Android Core The cross-platform C++ core, aka mbgl crash iOS Mapbox Maps SDK for iOS macOS Mapbox Maps SDK for macOS
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants