Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Calling a method on a class for the first time on a secondary thread fails. #190

Closed
jwhpryor opened this issue Sep 6, 2023 · 6 comments
Closed
Assignees
Labels
bug Something isn't working Release
Milestone

Comments

@jwhpryor
Copy link
Collaborator

jwhpryor commented Sep 6, 2023

This seems to be an issue with caching not occurring properly, despite a ThreadGuard being held.

To workaround, call any method on a class from the main thread and all subsequent calls will work.

@jwhpryor jwhpryor self-assigned this Sep 6, 2023
@jwhpryor jwhpryor added the bug Something isn't working label Sep 6, 2023
@jwhpryor jwhpryor added this to the Release 1.0 milestone Sep 6, 2023
@jwhpryor
Copy link
Collaborator Author

I started looking at this and I've discovered that this is inconsistent behavior between Android and vanilla java. Curiously, I can write a test that only fails on Android but the identical test fails with vanilla Java.

Updates to follow (and if folks have seen this outside of Android I'm keen to know).

@jwhpryor
Copy link
Collaborator Author

I believe this relates to the problem:
https://groups.google.com/g/android-ndk/c/eYL9tYkvefs

In fact, there are a couple threads posted which seem to be this specific Android issue.

I have a couple ideas I can try. I think if I cache the loader during the OnLoad call, it hopefully will work. I need to make sure this wouldn't induce breaking changes elsewhere (and, I presume it would work, but the devil is in the details).

@jwhpryor
Copy link
Collaborator Author

@jwhpryor
Copy link
Collaborator Author

So, I've invested a non-zero amount of time trying to resolve this, and I think this cannot be resolved without breaking or confusing behaviour for end users .

The issue is that any JNI call is effectively bound to the loader of the class that initiated the call. For Android, this means the application class loader, but when new threads are spun up, they do not have the same classloader, and fail to find the user defined class.

The only workaround is to call loadClass ahead of time, but JNI Bind doesn't know what class names are going to be used, so it actually can't. I think I could go to complicated lengths to work around this (e.g. having some type of callback mechanism), but I think it would be pretty unexpected for an end user to have this happen (and could cause side effects).

I'll try add a syntax like so to workaround, but at the end of the day I think there's nothing I can do unfortunately.

JvmRef jvm_ref {vm};
jvm_ref.Warm<kClassUsedOnOtherThread>()

@jwhpryor
Copy link
Collaborator Author

jwhpryor commented Jan 3, 2024

So, I continued to plug away at this, and it turns out the mistake was partly a cacheing mistake. TL;DR: I managed to get a fallback class loader to work for classes that fail lookup.

The unfortunate part is that it requires some extra setup from the caller, however, it's fairly minor. Basically the caller just has to provide a single instance of the host activity so that it can cache the application cache loader (you won't have to spell out each class type which would be easy to forget).

copybara-service bot pushed a commit that referenced this issue Jan 4, 2024
On Android, secondary threads do not have access to the application loader and there is no way to recover it.

Callers can now prime a loader (i.e. whatever loader is used by the `Activity` or `Application`). This loader can be used as a fallback when looking for classes.

This CL should resolve #190.

PiperOrigin-RevId: 594851933
copybara-service bot pushed a commit that referenced this issue Jan 4, 2024
On Android, secondary threads do not have access to the application loader and there is no way to recover it.

Callers can now prime a loader (i.e. whatever loader is used by the `Activity` or `Application`). This loader can be used as a fallback when looking for classes.

This CL should resolve #190.

PiperOrigin-RevId: 594851933
copybara-service bot pushed a commit that referenced this issue Jan 4, 2024
On Android, secondary threads do not have access to the application loader and there is no way to recover it.

Callers can now prime a loader (i.e. whatever loader is used by the `Activity` or `Application`). This loader can be used as a fallback when looking for classes.

This CL should resolve #190.

PiperOrigin-RevId: 594851933
copybara-service bot pushed a commit that referenced this issue Jan 4, 2024
On Android, secondary threads do not have access to the application loader and there is no way to recover it.

Callers can now prime a loader (i.e. whatever loader is used by the `Activity` or `Application`). This loader can be used as a fallback when looking for classes.

This CL should resolve #190.

PiperOrigin-RevId: 594851933
copybara-service bot pushed a commit that referenced this issue Jan 4, 2024
On Android, secondary threads do not have access to the application loader and there is no way to recover it.

Callers can now prime a loader (i.e. whatever loader is used by the `Activity` or `Application`). This loader can be used as a fallback when looking for classes.

This CL should resolve #190.

PiperOrigin-RevId: 594851933
copybara-service bot pushed a commit that referenced this issue Jan 4, 2024
On Android, secondary threads do not have access to the application loader and there is no way to recover it.

Callers can now prime a loader (i.e. whatever loader is used by the `Activity` or `Application`). This loader can be used as a fallback when looking for classes.

This CL should resolve #190.

PiperOrigin-RevId: 594851933
copybara-service bot pushed a commit that referenced this issue Jan 4, 2024
On Android, secondary threads do not have access to the application loader and there is no way to recover it.

Callers can now prime a loader (i.e. whatever loader is used by the `Activity` or `Application`). This loader can be used as a fallback when looking for classes.

This CL should resolve #190.

PiperOrigin-RevId: 594851933
copybara-service bot pushed a commit that referenced this issue Jan 4, 2024
On Android, secondary threads do not have access to the application loader and there is no way to recover it.

Callers can now prime a loader (i.e. whatever loader is used by the `Activity` or `Application`). This loader can be used as a fallback when looking for classes.

This CL should resolve #190.

PiperOrigin-RevId: 594851933
copybara-service bot pushed a commit that referenced this issue Jan 4, 2024
On Android, secondary threads do not have access to the application loader and there is no way to recover it.

Callers can now prime a loader (i.e. whatever loader is used by the `Activity` or `Application`). This loader can be used as a fallback when looking for classes.

This CL should resolve #190.

PiperOrigin-RevId: 594851933
copybara-service bot pushed a commit that referenced this issue Jan 4, 2024
On Android, secondary threads do not have access to the application loader and there is no way to recover it.

Callers can now prime a loader (i.e. whatever loader is used by the `Activity` or `Application`). This loader can be used as a fallback when looking for classes.

This CL should resolve #190.

PiperOrigin-RevId: 594851933
copybara-service bot pushed a commit that referenced this issue Jan 4, 2024
On Android, secondary threads do not have access to the application loader and there is no way to recover it.

Callers can now prime a loader (i.e. whatever loader is used by the `Activity` or `Application`). This loader can be used as a fallback when looking for classes.

This CL should resolve #190.

PiperOrigin-RevId: 594851933
copybara-service bot pushed a commit that referenced this issue Jan 5, 2024
On Android, secondary threads do not have access to the application loader and there is no way to recover it.

Callers can now prime a loader (i.e. whatever loader is used by the `Activity` or `Application`). This loader can be used as a fallback when looking for classes.

This CL should resolve #190.

PiperOrigin-RevId: 594851933
copybara-service bot pushed a commit that referenced this issue Jan 5, 2024
On Android, secondary threads do not have access to the application loader and there is no way to recover it.

Callers can now prime a loader (i.e. whatever loader is used by the `Activity` or `Application`). This loader can be used as a fallback when looking for classes.

This CL should resolve #190.

PiperOrigin-RevId: 594851933
copybara-service bot pushed a commit that referenced this issue Jan 5, 2024
On Android, secondary threads do not have access to the application loader and there is no way to recover it.

Callers can now prime a loader (i.e. whatever loader is used by the `Activity` or `Application`). This loader can be used as a fallback when looking for classes.

This CL should resolve #190.

PiperOrigin-RevId: 594851933
copybara-service bot pushed a commit that referenced this issue Jan 5, 2024
On Android, secondary threads do not have access to the application loader and there is no way to recover it.

Callers can now prime a loader (i.e. whatever loader is used by the `Activity` or `Application`). This loader can be used as a fallback when looking for classes.

This CL should resolve #190.

PiperOrigin-RevId: 594851933
copybara-service bot pushed a commit that referenced this issue Jan 5, 2024
On Android, secondary threads do not have access to the application loader and there is no way to recover it.

Callers can now prime a loader (i.e. whatever loader is used by the `Activity` or `Application`). This loader can be used as a fallback when looking for classes.

This CL should resolve #190.

PiperOrigin-RevId: 594851933
copybara-service bot pushed a commit that referenced this issue Jan 5, 2024
On Android, secondary threads do not have access to the application loader and there is no way to recover it.

Callers can now prime a loader (i.e. whatever loader is used by the `Activity` or `Application`). This loader can be used as a fallback when looking for classes.

This CL should resolve #190.

PiperOrigin-RevId: 594851933
copybara-service bot pushed a commit that referenced this issue Jan 8, 2024
On Android, secondary threads do not have access to the application loader and there is no way to recover it.

Callers can now prime a loader (i.e. whatever loader is used by the `Activity` or `Application`). This loader can be used as a fallback when looking for classes.

This CL should resolve #190.

PiperOrigin-RevId: 594851933
copybara-service bot pushed a commit that referenced this issue Jan 8, 2024
On Android, secondary threads do not have access to the application loader and there is no way to recover it.

Callers can now prime a loader (i.e. whatever loader is used by the `Activity` or `Application`). This loader can be used as a fallback when looking for classes.

This CL should resolve #190.

PiperOrigin-RevId: 594851933
@jwhpryor
Copy link
Collaborator Author

jwhpryor commented Jan 9, 2024

FYI I uploaded the Android test that I used to test this fix here.

Sadly, I haven't managed to get these tests running under Bazel, but it at least shows the correct flow.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working Release
Projects
None yet
Development

Successfully merging a pull request may close this issue.

1 participant