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

[core] Immutable style Impls #8929

Merged
merged 5 commits into from
May 12, 2017
Merged

[core] Immutable style Impls #8929

merged 5 commits into from
May 12, 2017

Conversation

jfirebaugh
Copy link
Contributor

Implements immutability for:

  • Light::Impl
  • Source::Impl and subclasses
  • Layer::Impl and subclasses

Refs #8820

== std::tie(rhs->id, lhs->type);
};

longest_common_subsequence(a.begin(), a.end(), b.begin(), b.end(), std::back_inserter(lcs), eq);
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This LCS implementation is the unrefined O(ND) space algorithm from the classic paper. I'm not sure if we need a full LCS, versus a more basic unordered set difference -- it probably depends if we want to implement a "move layer" operation rather than the current "remove and re-add" implementation (see mapbox/mapbox-gl-js#4142).

If we do end up keeping LCS, we should probably implement the O(N) space refinement presented in the paper. I haven't yet invested the time to understand it or write a high-quality implementation.

@ivovandongen
Copy link
Contributor

Starting to look good! One thing I'm wondering about is wether we should add the option to mutate multiple properties at once (in layers for example) to avoid unnecessary copies of the implementations.

@@ -41,7 +41,7 @@ void main() {


float dist = length(v_pos - gl_FragCoord.xy);
float alpha = smoothstep(1.0, 0.0, dist);
float alpha = 1.0 - smoothstep(0.0, 1.0, dist);
Copy link
Contributor

Choose a reason for hiding this comment

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

Did these snuck in by accident?

Copy link
Contributor

Choose a reason for hiding this comment

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

See #8751 and #8760

@jfirebaugh jfirebaugh force-pushed the immutable branch 3 times, most recently from a696800 to bac2476 Compare May 9, 2017 21:32
@kkaefer kkaefer added the Core The cross-platform C++ core, aka mbgl label May 10, 2017
@ivovandongen ivovandongen mentioned this pull request May 10, 2017
28 tasks
@jfirebaugh
Copy link
Contributor Author

This is ready for review. I checked for conflicts with #8937; they are minimal (added a header file in the same place).

}

if (evaluate || layer->hasTransition()) {
layer->evaluate(evaluationParameters);
if (classesChanged || layerAdded || layerChanged || layer.hasTransition()) {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

zoomChanged is missing here, and none of our regression tests caught it. Will investigate.

Copy link
Contributor

Choose a reason for hiding this comment

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

Nice catch 👍

Copy link
Contributor

@ivovandongen ivovandongen left a comment

Choose a reason for hiding this comment

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

Looks good!

  • Ran the Android integration tests as well, no regressions. Although the run time (locally) increased from ~5:30min to ~8:00min. No immediately obvious cause for this, it is spread fairly evenly over the style tests.
  • Ran through all the Android test app activities manually, no regressions I could spot.


// Private implementation
const std::unique_ptr<Impl> baseImpl;
class Impl;
Immutable<Impl> baseImpl;
Copy link
Contributor

Choose a reason for hiding this comment

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

I realize this has been public before, but is there a way we can make it private now?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Style at a minimum would need to be friended. GeometryTile also uses it; it's possible that use could be eliminated. I'd like to leave this for a followup change though.

@@ -44,6 +44,13 @@ public:
<% } -%>

<% } -%>
// Visibility
void setVisibility(VisibilityType) final;
Copy link
Contributor

Choose a reason for hiding this comment

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

Nit: Can we add override to be consistent with the rest of the codebase?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

You want to write override final or final override? Neither of those appear elsewhere in the codebase.

Copy link
Contributor

Choose a reason for hiding this comment

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

I'm specifically interested in override (final override reads better), to make it fully clear that this is a virtual function override.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Let's discuss making that style change separately. We already have quite a few uses of final without override.

<%- camelize(type) %>Layer(const <%- camelize(type) %>Layer&) = delete;
Mutable<Impl> mutableImpl() const;
<%- camelize(type) %>Layer(Immutable<Impl>);
std::unique_ptr<Layer> cloneRef(const std::string& id) const final;
Copy link
Contributor

Choose a reason for hiding this comment

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

Same note about privatization here. I know they've been public before, so this may be a follow-up change.

@@ -98,20 +91,30 @@ class Layer : public mbgl::util::noncopyable {
}
}

LayerType getType() const;
const std::string& getID() const;
Copy link
Contributor

Choose a reason for hiding this comment

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

This returns a reference to a string, which is stored in the immutable implementation. Now that we're switching this frequently, it means that the reference can become invalid when you do something like this:

auto& id = layer->getID();
layer->setMaxZoom(2);
std::cout << id;

Instead of returning a reference to objects inside the frequently-changing Immutable, we should return the ID as a value.


template <class S> friend class Immutable;
template <class S, class... Args> friend Mutable<S> makeMutable(Args&&...);
};
Copy link
Contributor

Choose a reason for hiding this comment

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

Mutable/Immutable is vulnerable to the following scenario: Contained type inherits from std::enable_shared_from_this. It's now possible to obtain a shared_ptr from the Mutable object, convert that to an Immutable, and continue to mutate the object with the previously obtained shared_ptr handle. The same goes for EnableImmutableFromThis. It seems like a promise that it can't keep...

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Well, sure. But you could also just hold onto the raw pointer from Mutable::get(), a reference from Mutable::operator*, const_cast, .... There's no way to 100% prevent misuse in C++; that would require language support in the style of Rust borrow checking.

Copy link
Contributor

Choose a reason for hiding this comment

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

Yup, but shared_ptr indicates shared ownership, whereas the others don't.

@@ -30,24 +27,17 @@ namespace style {
* Members that are public in `FooLayer::Impl` are part of the internal API for "foo" layers.
* Members that are private in `FooLayer::Impl` are internal to "foo" layers.
*/
class Layer::Impl {
class Layer::Impl : public EnableImmutableFromThis<Layer::Impl> {
Copy link
Contributor

Choose a reason for hiding this comment

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

This allows converting an existing object to an Immutable, but this object has many mutable fields that cut be mutated from the original handle.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

EnableImmutableFromThis is required only for Source::Impl::createRenderSource and Layer::Impl::createRenderLayer. It's probably a good idea to change how those operations are performed so that EnableImmutableFromThis isn't necessary. I see two options:

  • Require passing an Immutable<Impl> self reference to those methods (3e7ce6ba27b31a73a9242e4e14e8eaacefe42a5e). I'm not crazy about this because it allows the possibility of passing some other reference that doesn't match this.
  • Replace the virtual dispatch with a static dispatch -- switch statement or Layer::accept.

return longest_common_subsequence(a, endA, b, endB, outIt, std::equal_to<>());
}

}
Copy link
Contributor

Choose a reason for hiding this comment

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

This file could use a lot more documentation

@jfirebaugh jfirebaugh force-pushed the immutable branch 2 times, most recently from 3db6ac8 to 38d1677 Compare May 11, 2017 20:07
@jfirebaugh
Copy link
Contributor Author

Bizarre link error on linux builds:

[3/3] Linking CXX executable mbgl-offline
FAILED: mbgl-offline 
: && /usr/bin/ccache  clang++-3.9 -Qunused-arguments -fcolor-diagnostics  -std=c++14 -ftemplate-depth=1024 -Wall -Wextra -Wshadow -Werror -Wno-variadic-macros -Wno-unknown-pragmas -g   CMakeFiles/mbgl-offline.dir/bin/offline.cpp.o CMakeFiles/mbgl-offline.dir/platform/default/mbgl/util/default_styles.cpp.o  -o mbgl-offline  -rdynamic libmbgl-core.a ../../../mason_packages/linux-x86_64/boost_libprogram_options/1.62.0/lib/libboost_program_options.a -L/home/travis/build/mapbox/mapbox-gl-native/mason_packages/linux-x86_64/boost_libprogram_options/1.62.0/lib -lboost_program_options libmbgl-loop-uv.a -L/home/travis/build/mapbox/mapbox-gl-native/mason_packages/linux-x86_64/mesa/13.0.4/lib -lGLESv2 -lEGL -lgbm ../../../mason_packages/linux-x86_64/sqlite/3.14.2/lib/libsqlite3.a -ldl -lpthread ../../../mason_packages/linux-x86_64/nunicode/1.7.1/lib/libnu.a ../../../mason_packages/linux-x86_64/libpng/1.6.25/lib/libpng.a -lz -lm ../../../mason_packages/linux-x86_64/libjpeg-turbo/1.5.0/lib/libjpeg.a ../../../mason_packages/linux-x86_64/webp/0.5.1/lib/libwebp.a -pthread -lm ../../../mason_packages/linux-x86_64/icu/58.1-min-size/lib/libicuuc.a -lz -lcurl ../../../mason_packages/linux-x86_64/libuv/1.9.1/lib/libuv.a -lrt -lpthread -lnsl -ldl && :
libmbgl-loop-uv.a(run_loop.cpp.o): In function `operator()':
/home/travis/build/mapbox/mapbox-gl-native/build/linux-x86_64/Debug/../../../include/mbgl/util/run_loop.hpp:86: undefined reference to `mbgl::Mailbox::maybeReceive(std::weak_ptr<mbgl::Mailbox>)'

Seems totally unrelated to any of the changes in cebc5f11cd834a6cf478b267df5f436ab5fd7362.

Avoid dangling references in the following sequence:

    auto& id = layer->getID();
    layer->setMaxZoom(2);
    std::cout << id;

The reference would be dangling because mutating the layer allocates a new Immutable impl, and there may be no references to the prior impl, which held the id.
…create

* Eliminates the need for EnableImmutableFromThis
* Eliminates the dependency of {Source,Layer}::Impl on corresponding Render class (circular dependency)
@jfirebaugh jfirebaugh force-pushed the immutable branch 2 times, most recently from 9fffcd3 to e859904 Compare May 12, 2017 17:40
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Core The cross-platform C++ core, aka mbgl
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants