-
Notifications
You must be signed in to change notification settings - Fork 6k
Embedder Asset Provider #29802
Embedder Asset Provider #29802
Conversation
|
|
54ba55a to
769c6cf
Compare
|
This PR is ready for review. |
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.
The public API is prone to misuse in the following cases:
- If the embedder creates and caches mapping objects (something that is very reasonable for embedders to do), the second instance of the engine asking the embedder to fetch the asset will lead to the mapping being held by two unique pointers (in the asset resolver). A release by the first instance will lead to an double free.
- If the mapping is created by the embedder but the embedder never returns it via a callback, they have no way of collecting the handle and it will be leaked.
In a C API, it's usually suspicious when a Create call is not balanced by a subsequent Collect. I suggest adding the two with a handle to a struct that has shared ownership of the mapping being returned to the embedder. Then, in the resolver, the engine can just ref the handle.
Other construction issues and nits added inline. Thanks and sorry about the delayed review.
The documentation on the API specifies that the embedder should always return fresh mapping objects from the callback. Otherwise yes they will encounter a double-free. If the embedder wants to share the underlying data with multiple mappings, they should maintain some sort of reference count. Should the documentation outline this more explicitly? As a quick example of usage (from Rust for brevity, also possible with std::shared_ptr): let my_shared_data: Arc<[u8; N]> = Arc::new(b"my file bytes here");
// Creating mappings for the shared data:
let mut create_info = FlutterEngineMappingCreateInfo::new();
create_info.data = my_shared_data.as_ptr();
create_info.data_size = my_shared_data.len();
// Increment the reference count and turn into a raw ptr
create_info.user_data = Arc::into_raw(Arc::clone(my_shared_data));
unsafe extern "C" fn destroy(user_data: *mut c_void) {
// Turn raw ptr into the associated Arc and drop to decrement reference count
Arc::from_raw(user_data as *const [u8; N]);
}
create_info.destruction_callback = destroy;
let mapping = FlutterEngineCreateMapping(&create_info);
Good call, I'll add a destroy method for the mappings. It also makes sense if we start handing mappings from the engine -> embedder. Would it be worthwhile to also add data + size getters to fully flesh out the API? |
|
I've updated the I was debating whether it's worthwhile to change the interface some more to be more explicit about the ownership requirements. For example using a different opaque struct for Engine-owned mappings vs Embedder-owned mappings, and having a call to "give ownership" of an Embedder mapping to the Engine. Something like: typedef struct FlutterEngineOwnedMapping* FlutterEngineOwnedMapping;
typedef struct FlutterEmbedderOwnedMapping* FlutterEmbedderOwnedMapping;
// Releases an embedder-owned mapping to the engine. This is used to give the mapping to the engine inside of
// AssetResolver->get_asset.
// Internally just a pointer cast, but may do some additional debug-only book-keeping in the future.
FlutterEngineOwnedMapping FlutterReleaseMappingOwnership(FlutterEmbedderOwnedMapping mapping);
void FlutterCreateMapping(..., FlutterEmbedderOwnedMapping* out);
void FlutterDestroyMapping(FlutterEmbedderOwnedMapping mapping);
const uint8_t* FlutterGetMappingData(FlutterEmbedderOwnedMapping mapping, ...);If we want to also have an engine-provided shared mapping, I think it should have a different interface. e.g. typedef struct FlutterSharedMapping* FlutterSharedMapping;
void FlutterCreateSharedMapping(const FlutterSharedMappingCreateInfo* create_info, FlutterSharedMapping* out);
// Increases refcount, can be directly returned from AssetResolver->get_asset
FlutterEngineOwnedMapping FlutterMakeEngineMappingFromShared(FlutterSharedMapping mapping);
// The mapping may stay alive if there are any mappings owned by the engine.
// New mappings cannot be created from the shared mapping after this point.
void FlutterDestroySharedMapping(FlutterSharedMapping mapping);However, I think this is tangential to this PR and can be implemented by embedders with the current API. |
|
This pull request executed golden file tests, but it has not been updated in a while (20+ days). Test results from Gold expire after as many days, so this pull request will need to be updated with a fresh commit in order to get results from Gold. |
|
What is the status of this PR? |
|
@zanderso Currently on the backburner. Focus has changed on my side somewhat, but I can spend a bit of time solving the merge conflict. Other than that I think there's still some open discussion from @chinmaygarde about how FlutterMapping is exposed to the Embedder. |
|
Marking this WIP, but cc @iskakaushik regarding an issue from @akbiggs in flutter/flutter#100640. |
|
I'm going to close this one for now per @ds84182; they have the branch locally and will submit a new PR when ready. |
New embedder API for allowing embedders to handle asset requests. This is working towards the option for File I/O-less Engine builds. Some embedders may have custom filesystem APIs that are incompatible with fml's File & Directory APIs. Instead of trying to put a square peg in a round hole, allow embedders to handle I/O instead.
flutter/flutter#93838
Pre-launch Checklist
writing and running engine tests.
///).If you need help, consider asking for advice on the #hackers-new channel on Discord.