You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
In order to properly represent the semantics around callbacks registered to Android via NDK bindings, there are a few things we need to know in order to map them safely, and unfortunately the NDK documentation only barely ever specifies them to us. I've reported this upstream (https://issuetracker.google.com/issues/318944941) and will backlink to this issue, which I'll use to track what we need to know, and what we know for which callbacks.
This issue also serves as a guideline when implementing future APIs that contain callbacks.
What
Is this callback only called once, or multiple times?
If called once, we can have FnOnce for move semantics and drop the Box<dyn Fn> after being called;
But what if it is never called -> we'll leak a box.
Will a NULL call invalidate a previous callback?
Allows us to drop the previous Fn;
Will a repeat ..setCallback..() overwrite a previous callback (if used with the same parameters)?
Allows us to only track one Fn and overwrite - hence drop - the previous;
Will _delete() on the object invalidate any registered callbacks?
Also check _reset() functions!
Allows us to track the Fn in the object that owns the pointer;
Impossible when the object is refcounted, or merely a reference to a (thread-local) global.
Some callbacks, such as those for ASurfaceTransaction conveniently outlive the transaction. Together with FnOnce semantics (and repeat registrations not overwriting previous callbacks), tracking the lifetimes is trivial;
On what thread is this callback called?
Important to know that it is synchronized with _delete() or ..setCallback..() in order to drop the Fn;
If called on the same thread, no marker traits are needed;
If called on one other thread, serially, only Send is needed. Implementation can be FnOnce or FnMut;
If called on multiple other threads, potentially concurrently, Send+Sync is needed. the implementation can only be FnOnce or Fn as it is unsafe to mutate captured state concurrently.
Is a previous callback cleared if the registration function returns an error?
On the side: is the object itself thread-safe? That may affect where (relatively) callbacks are invoked, and how objects should be handled when Android gives a pointer to them inside a callback.
Which callbacks
Caution
A bunch of obvious things are not yet filled out in this table. I want to post this before (trivially) testing a few things and adding more callback registration functions.
This is a strange beast. No docs and it's very easy to trip up by deleting the object or changing the callback at runtime, while a callback may be coming in. I've already locally moved the overwriting of Boxes until after the setxxxListener() call so that we can't drop them too early, but it's still finicky.
Not to mention some inconsistencies when dropping the NativeWindow, which we've previously acquired an extra reference on (docs say_release()ing should not be done, but no comment on first acquiring it...).
Also note that we don't mark the type as Send/Sync, but the both callbacks reference a to ImageReader, so it should be thead-safe?
NULL callback will register a regular non-callback event. It can be cleared via ALooper_removeFd(), but the callback might still run after removal under specific circumstances. ↩
Looper is refcounted (but what if the owning thread disappears or never calls poll()?). It can also be registered from any thread, even if poll() and callback invocations will only happen on the main thread. ↩↩2
The text was updated successfully, but these errors were encountered:
Important
This report/analysis is unfinished.
In order to properly represent the semantics around callbacks registered to Android via NDK bindings, there are a few things we need to know in order to map them safely, and unfortunately the NDK documentation only barely ever specifies them to us. I've reported this upstream (https://issuetracker.google.com/issues/318944941) and will backlink to this issue, which I'll use to track what we need to know, and what we know for which callbacks.
This issue also serves as a guideline when implementing future APIs that contain callbacks.
What
FnOnce
for move semantics and drop theBox<dyn Fn>
after being called;NULL
call invalidate a previous callback?Fn
;..setCallback..()
overwrite a previous callback (if used with the same parameters)?Fn
and overwrite - hence drop - the previous;_delete()
on the object invalidate any registered callbacks?_reset()
functions!Fn
in the object that owns the pointer;ASurfaceTransaction
conveniently outlive the transaction. Together withFnOnce
semantics (and repeat registrations not overwriting previous callbacks), tracking the lifetimes is trivial;_delete()
or..setCallback..()
in order to drop theFn
;Send
is needed. Implementation can beFnOnce
orFnMut
;Send+Sync
is needed. the implementation can only beFnOnce
orFn
as it is unsafe to mutate captured state concurrently.Which callbacks
Caution
A bunch of obvious things are not yet filled out in this table. I want to post this before (trivially) testing a few things and adding more callback registration functions.
NULL
clears callback_delete()
invalidates callbackBox
with objectAMediaCodec_setAsyncNotifyCallback()
FnMut<Send>
AMediaCodec_setOnFrameRenderedCallback()
1FnMut<Send>
AAudioStreamBuilder_setDataCallback()
andAAudioStreamBuilder_setErrorCallback()
FnMut<Send>
AndroidBitmap_compress()
FnMut
ALooper_addFd()
with a callbackLooper
thread (inferred)FnMut
forThreadLooper
,FnMut<Send>
forForeignLooper
AImageReader_setBufferRemovedListener()
andAImageReader_setImageListener()
FnMut<Send>
AThermal_registerThermalStatusListener()
mutex
unregister()
callFnMut<Send>
Drop
if user forgets to unregister?AChoreographer_postFrameCallback(Delayed)(64)()
Choreographer
Send
/Sync
)FnOnce
AChoreographer_postVsyncCallback()
Choreographer
Send
/Sync
)FnOnce
AChoreographer_registerRefreshRateCallback()
FnMut
Choreographer
is just a referenceASurfaceTransaction_setOnComplete()
FnOnce<Send>
ASurfaceTransaction_setOnCommit()
FnOnce<Send>
ImageReader
This is a strange beast. No docs and it's very easy to trip up by deleting the object or changing the callback at runtime, while a callback may be coming in. I've already locally moved the overwriting of
Box
es until after thesetxxxListener()
call so that we can't drop them too early, but it's still finicky.Not to mention some inconsistencies when dropping the
NativeWindow
, which we've previously acquired an extra reference on (docs say_release()
ing should not be done, but no comment on first acquiring it...).Also note that we don't mark the type as
Send
/Sync
, but the both callbacks reference a toImageReader
, so it should be thead-safe?References
#454
#455
Footnotes
Missing from the NDK crate ↩
NULL
callback will register a regular non-callback event. It can be cleared viaALooper_removeFd()
, but the callback might still run after removal under specific circumstances. ↩Looper
is refcounted (but what if the owning thread disappears or never callspoll()
?). It can also be registered from any thread, even ifpoll()
and callback invocations will only happen on the main thread. ↩ ↩2The text was updated successfully, but these errors were encountered: