Implement ObjectArena and ArenaRef to solve JNI Global Reference Issue#1176
Implement ObjectArena and ArenaRef to solve JNI Global Reference Issue#1176cherylEnkidu wants to merge 17 commits intomainfrom
Conversation
Integration test with FLAKINESS (succeeded after retry)Requested by @cherylEnkidu on commit 2ce5b8d
Add flaky tests to go/fpl-cpp-flake-tracker |
69db5b3 to
8975580
Compare
✅ Integration test succeeded!Requested by @cherylEnkidu on commit 7889019 |
firestore/integration_test_internal/src/android/arena_ref_android_test.cc
Outdated
Show resolved
Hide resolved
firestore/integration_test_internal/src/android/arena_ref_android_test.cc
Outdated
Show resolved
Hide resolved
firestore/integration_test_internal/src/android/arena_ref_android_test.cc
Outdated
Show resolved
Hide resolved
|
I just realized something: the ArenaRef has a concurrency bug. If ArenaRefs are created/copied concurrently from multiple threads then there is a race condition that will cause bugs. This is because the multiple threads can be accessing the Java HashMap at the same time without any synchronization. This leads to undefined behavior, and could be causing that weird bug you're seeing in CI that you can't reproduce locally. One fix would be to protect |
4c9e2a6 to
54c0826
Compare
…ad ignoring exception and only printing description
firestore/integration_test_internal/src/android/arena_ref_android_test.cc
Outdated
Show resolved
Hide resolved
firestore/integration_test_internal/src/android/arena_ref_android_test.cc
Outdated
Show resolved
Hide resolved
firestore/integration_test_internal/src/android/arena_ref_android_test.cc
Outdated
Show resolved
Hide resolved
firestore/integration_test_internal/src/android/arena_ref_android_test.cc
Outdated
Show resolved
Hide resolved
firestore/integration_test_internal/src/android/arena_ref_android_test.cc
Outdated
Show resolved
Hide resolved
|
Any update, please? Games crash rates are too high due to this issue, and Play Console's bad behavior threshold is crossed. |
Please see firebase/firebase-unity-sdk#569. tl;dr The fix is not yet ready but we'll let you know when it is. |
|
|
||
| ArenaRef& ArenaRef::operator=(ArenaRef&& other) { | ||
| if (this != &other) { | ||
| std::swap(key_, other.key_); |
There was a problem hiding this comment.
Please do the using std::swap thing here in the move assignment operator, just like you did for the move constructor (#1176 (comment))
| EXPECT_EQ("Foo", result); | ||
| } | ||
|
|
||
| TEST_F(EnvTest, IsInstanceOfChecksValidArenaRef) { |
There was a problem hiding this comment.
Augment the IsInstanceOfChecksValidArenaRef test to also call IsInstanceOf with a different class (e.g. java.lang.Integer) such that it returns false.
| // clang-format on | ||
| } | ||
|
|
||
| TEST_F(FieldValueTest, TestArenaRefMinimunLimit) { |
There was a problem hiding this comment.
Please add a comment to TestArenaRefMinimunLimit to mention the global refs issue that it's checking and include a link to the issue that it's verifying doesn't regress (i.e. firebase/firebase-unity-sdk#569)
| HashMap* gArenaRefHashMap = nullptr; | ||
| jclass gLongClass = nullptr; | ||
| jmethodID gLongConstructor = nullptr; | ||
| std::mutex mutex_; |
There was a problem hiding this comment.
nit: Move std::mutex mutex_ to be defined above the variable that it is protecting (i.e. above the definition of gArenaRefHashMap). Although it's not super relevant in this case, you always want to make sure that the mutex outlives the variable(s) that it is protecting.
| HashMap* gArenaRefHashMap = nullptr; | ||
| jclass gLongClass = nullptr; | ||
| jmethodID gLongConstructor = nullptr; | ||
| std::mutex mutex_; |
There was a problem hiding this comment.
Rename mutex_ to match the naming convention of global variables. Also name it to indicate which variable it is protecting. e.g. gArenaRefHashMapMutex.
| jmethodID ctor = | ||
| env().GetMethodId(clazz, "<init>", "(Ljava/lang/String;)V"); | ||
|
|
||
| Local<String> message = env().NewStringUtf("Testing throw"); |
There was a problem hiding this comment.
I think you can just get rid of these throwException() and clearExceptionOccurred() helper methods.
throwException() can be replaced by
env.Throw(CreateException(env, "Test Exception"));and clearExceptionOccurred() can be replaced by
env.ExceptionClear();Admittedly, this leads to a tiny bit of code duplciation between test cases but that's acceptable because, IMO, it improves readabililty of the test cases.
See go/tott/598 about "DAMP" tests rather than the "DRY" principle that is common in non-testing code.
| TEST_F(ArenaRefTestAndroid, MoveConstructsFromValid) { | ||
| Local<String> string = env().NewStringUtf("hello world"); | ||
|
|
||
| ArenaRef arena_ref2(env(), string); |
There was a problem hiding this comment.
nit: Rename arena_ref2 and arena_ref3 back to arena_ref1 and arena_ref2, respectively.
|
|
||
| TEST_F(ArenaRefTestAndroid, ThrowBeforeConstruct) { | ||
| Local<String> string = env().NewStringUtf("hello world"); | ||
| EXPECT_EQ(string.ToString(env()).size(), 11U); |
There was a problem hiding this comment.
Remove these checks for the length of the string returned from NewStringUtf(). You can just assume that this is true. Applies here and elsewhere in this file.
| EXPECT_TRUE(env().IsSameObject(arena_ref1.get(env()), string1)); | ||
| } | ||
|
|
||
| TEST_F(ArenaRefTestAndroid, ThrowBeforeConstruct) { |
There was a problem hiding this comment.
nit: Can you rename the "ThrowBeforeXXX" test names to "XXXWithPendingException" or something like that? I think using the term "with pending exception" more clearly conveys what scenario the test is testing.
| clearExceptionOccurred(); | ||
| EXPECT_TRUE(env().IsSameObject(arena_ref2.get(env()), string)); | ||
| } | ||
|
|
There was a problem hiding this comment.
Add a test for calling get() with a pending exception.
Description
Testing
Type of Change
Place an
xthe applicable box:Notes
Release Notessection ofrelease_build_files/readme.md.