Reverts d5aff71 (#11948) and implements an alternative solution.
Even with the previous fix applied, we were calling `renderFinished()` directly when the render was canceled. This led to the following scenario (all on the main thread):
- `Map::renderStill()` callback is called, `uv_async_send` schedules a call to `renderFinished()`.
- `NodeMap::cancel()` destroys the map object, and calls `renderFinished()` directly, which cleans up the request
- `NodeMap::startRender()` is called to schedule a _new_ render, which means that a new callback will be set
- The `uv_async_t` fires and calls `renderFinished` again:
- We don't return early as intended by the fix in #11948 since a new callback/`req` object is now available.
- Since neither error nor image has been set, we'll see a `Didn't get an image` error message.
- We call the callback with the error. In scenario where we're reusing Map objects, we will start a new render, which will now trigger the `Map is currently rendering an image` error in `Map::renderStill`, and the map object enters a circle in which it will now fail repeatedly with this error.
This commit fixes the issue by completely removing the existing `uv_async_t` handle that could have a scheduled call, and replacing it with a fresh instance of an `uv_async_t` handle.
* Change error message with invalid render usage
This change disambiguates this error condition with a different error with the same error message at 789baf4c1f/src/mbgl/map/map.cpp (L157-L160)
* fix test expectation
Issue #10714
- Each expression stores its operator as a string, and default serialization is [operator, serialize(child1), ...]
- Custom implementations of `serialize` for Expression types that don't follow the pattern
- expression::Value -> mbgl::Value converter
- node_expression bindings to expose `serialize`
Avoids a potential crash if garbage collection happens in the middle of a call to NodeMap::request from a map that's eligible for GC.
Fixes issue #11281
This adds a `convertJSON` template, to be used like:
Error error
optional<Foo> foo = convertJSON<Foo>(string, error);
Internally, it parses the string with RapidJSON and then calls `convert<Foo>(parsed, error)`.
While here, rationalize GeoJSON converters and fix error handling for Tileset conversion in OfflineDownload.