Skip to content

Commit b599bfa

Browse files
committed
Address feedback
1 parent 84cbd19 commit b599bfa

File tree

7 files changed

+83
-127
lines changed

7 files changed

+83
-127
lines changed

firestore/integration_test_internal/CMakeLists.txt

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -121,8 +121,6 @@ set(FIREBASE_INTEGRATION_TEST_PORTABLE_TEST_SRCS
121121

122122
# These sources contain the actual tests that run on Android only.
123123
set(FIREBASE_INTEGRATION_TEST_ANDROID_TEST_SRCS
124-
src/android/arena_ref_android_test.cc
125-
src/android/env_android_test.cc
126124
src/android/field_path_portable_test.cc
127125
src/android/firestore_integration_test_android_test.cc
128126
src/android/geo_point_android_test.cc
@@ -133,6 +131,7 @@ set(FIREBASE_INTEGRATION_TEST_ANDROID_TEST_SRCS
133131
src/android/snapshot_metadata_android_test.cc
134132
src/android/timestamp_android_test.cc
135133
src/android/transaction_options_android_test.cc
134+
src/jni/arena_ref_test.cc
136135
src/jni/declaration_test.cc
137136
src/jni/env_test.cc
138137
src/jni/object_test.cc

firestore/integration_test_internal/src/android/env_android_test.cc

Lines changed: 0 additions & 68 deletions
This file was deleted.

firestore/integration_test_internal/src/android/arena_ref_android_test.cc renamed to firestore/integration_test_internal/src/jni/arena_ref_test.cc

Lines changed: 12 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,11 @@
1414
* limitations under the License.
1515
*/
1616

17-
#include "app/src/util_android.h"
18-
#include "firestore/src/android/firestore_android.h"
1917
#include "firestore/src/jni/arena_ref.h"
18+
#include "firestore/src/android/firestore_android.h"
2019
#include "firestore/src/jni/long.h"
2120

22-
#include "firestore_integration_test_android.h"
21+
#include "firestore_integration_test.h"
2322

2423
#include "gtest/gtest.h"
2524

@@ -31,29 +30,29 @@ namespace {
3130
using ArenaRefTestAndroid = FirestoreIntegrationTest;
3231

3332
TEST_F(ArenaRefTestAndroid, DefaultConstructorCreatesReferenceToNull) {
34-
Env env = FirestoreInternal::GetEnv();
33+
Env env;
3534
ArenaRef arena_ref;
3635
EXPECT_EQ(arena_ref.get(env).get(), nullptr);
3736
}
3837

3938
TEST_F(ArenaRefTestAndroid, ConstructFromEnvAndObject) {
40-
Env env = FirestoreInternal::GetEnv();
39+
Env env;
4140
Local<String> string = env.NewStringUtf("hello world");
4241

4342
ArenaRef arena_ref(env, string);
4443
EXPECT_TRUE(arena_ref.get(env).Equals(env, string));
4544
}
4645

4746
TEST_F(ArenaRefTestAndroid, CopysReferenceToNull) {
48-
Env env = FirestoreInternal::GetEnv();
47+
Env env;
4948

5049
ArenaRef arena_ref1;
5150
ArenaRef arena_ref2(arena_ref1);
5251
EXPECT_EQ(arena_ref2.get(env).get(), nullptr);
5352
}
5453

5554
TEST_F(ArenaRefTestAndroid, CopysReferenceToValidObject) {
56-
Env env = FirestoreInternal::GetEnv();
55+
Env env;
5756

5857
Local<String> string = env.NewStringUtf("hello world");
5958

@@ -65,7 +64,7 @@ TEST_F(ArenaRefTestAndroid, CopysReferenceToValidObject) {
6564
}
6665

6766
TEST_F(ArenaRefTestAndroid, CopyAssignsReferenceToNull) {
68-
Env env = FirestoreInternal::GetEnv();
67+
Env env;
6968

7069
ArenaRef arena_ref1, arena_ref2;
7170
arena_ref2 = arena_ref1;
@@ -74,7 +73,7 @@ TEST_F(ArenaRefTestAndroid, CopyAssignsReferenceToNull) {
7473
}
7574

7675
TEST_F(ArenaRefTestAndroid, CopyAssignsReferenceToValidObject) {
77-
Env env = FirestoreInternal::GetEnv();
76+
Env env;
7877

7978
Local<String> string1 = env.NewStringUtf("hello world");
8079
Local<String> string2 = env.NewStringUtf("hello earth");
@@ -94,36 +93,33 @@ TEST_F(ArenaRefTestAndroid, CopyAssignsReferenceToValidObject) {
9493
}
9594

9695
TEST_F(ArenaRefTestAndroid, MovesReferenceToNull) {
97-
Env env = FirestoreInternal::GetEnv();
96+
Env env;
9897

9998
ArenaRef arena_ref1;
10099
ArenaRef arena_ref2(std::move(arena_ref1));
101-
EXPECT_EQ(arena_ref1.get(env).get(), nullptr);
102100
EXPECT_EQ(arena_ref2.get(env).get(), nullptr);
103101
}
104102

105103
TEST_F(ArenaRefTestAndroid, MovesReferenceToValidObject) {
106-
Env env = FirestoreInternal::GetEnv();
104+
Env env;
107105

108106
Local<String> string = env.NewStringUtf("hello world");
109107

110108
ArenaRef arena_ref2(env, string);
111109
ArenaRef arena_ref3(std::move(arena_ref2));
112-
EXPECT_EQ(arena_ref2.get(env).get(), nullptr);
113110
EXPECT_TRUE(arena_ref3.get(env).Equals(env, string));
114111
}
115112

116113
TEST_F(ArenaRefTestAndroid, MoveAssignsReferenceToNull) {
117-
Env env = FirestoreInternal::GetEnv();
114+
Env env;
118115

119116
ArenaRef arena_ref1, arena_ref2;
120117
arena_ref2 = std::move(arena_ref1);
121-
EXPECT_EQ(arena_ref1.get(env).get(), nullptr);
122118
EXPECT_EQ(arena_ref2.get(env).get(), nullptr);
123119
}
124120

125121
TEST_F(ArenaRefTestAndroid, MoveAssignsReferenceToValidObject) {
126-
Env env = FirestoreInternal::GetEnv();
122+
Env env;
127123

128124
Local<String> string1 = env.NewStringUtf("hello world");
129125
Local<String> string2 = env.NewStringUtf("hello earth");
@@ -135,7 +131,6 @@ TEST_F(ArenaRefTestAndroid, MoveAssignsReferenceToValidObject) {
135131

136132
ArenaRef arena_ref3(env, string2);
137133
arena_ref3 = std::move(arena_ref2);
138-
EXPECT_EQ(arena_ref2.get(env).get(), nullptr);
139134
EXPECT_TRUE(arena_ref3.get(env).Equals(env, string1));
140135

141136
arena_ref3 = std::move(arena_ref1);

firestore/integration_test_internal/src/jni/env_test.cc

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,18 @@ TEST_F(EnvTest, CallsVoidMethods) {
128128
EXPECT_EQ(length, 42);
129129
}
130130

131+
TEST_F(EnvTest, CallsValidArenaRef) {
132+
Local<String> str = env().NewStringUtf("Foo");
133+
ArenaRef arena_ref(env(), str);
134+
135+
Local<Class> clazz = env().FindClass("java/lang/String");
136+
jmethodID to_lower_case =
137+
env().GetMethodId(clazz, "toLowerCase", "()Ljava/lang/String;");
138+
139+
Local<Object> result = env().Call(arena_ref, Method<Object>(to_lower_case));
140+
EXPECT_EQ("foo", result.ToString(env()));
141+
}
142+
131143
TEST_F(EnvTest, GetsStaticFields) {
132144
Local<Class> clazz = env().FindClass("java/lang/String");
133145
jfieldID comparator = env().GetStaticFieldId(clazz, "CASE_INSENSITIVE_ORDER",
@@ -160,6 +172,13 @@ TEST_F(EnvTest, ToString) {
160172
EXPECT_EQ("Foo", result);
161173
}
162174

175+
TEST_F(EnvTest, IsInstanceOfChecksValidArenaRef) {
176+
Local<Class> clazz = env().FindClass("java/lang/String");
177+
Local<String> str = env().NewStringUtf("Foo");
178+
ArenaRef arena_ref(env(), str);
179+
EXPECT_TRUE(env().IsInstanceOf(arena_ref, clazz));
180+
}
181+
163182
TEST_F(EnvTest, Throw) {
164183
Local<Class> clazz = env().FindClass("java/lang/Exception");
165184
jmethodID ctor = env().GetMethodId(clazz, "<init>", "(Ljava/lang/String;)V");

firestore/src/jni/arena_ref.cc

Lines changed: 44 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#include "firestore/src/jni/arena_ref.h"
1818

1919
#include <atomic>
20+
#include <utility>
2021

2122
#include "firestore/src/android/firestore_android.h"
2223
#include "firestore/src/jni/env.h"
@@ -31,6 +32,8 @@ namespace jni {
3132
namespace {
3233

3334
HashMap* gArenaRefHashMap = nullptr;
35+
jclass gLongClass = nullptr;
36+
jmethodID gLongConstructor = nullptr;
3437
std::mutex mutex_;
3538

3639
int64_t GetNextArenaRefKey() {
@@ -42,80 +45,78 @@ int64_t GetNextArenaRefKey() {
4245

4346
ArenaRef::ArenaRef(Env& env, const Object& object)
4447
: key_(GetNextArenaRefKey()) {
45-
std::unique_lock<std::mutex> lock(mutex_);
46-
gArenaRefHashMap->Put(env, key_object(env), object);
48+
Local<Long> key_ref = key_object(env);
49+
std::lock_guard<std::mutex> lock(mutex_);
50+
gArenaRefHashMap->Put(env, key_ref, object);
4751
}
4852

4953
ArenaRef::ArenaRef(const ArenaRef& other)
50-
: key_(other.key_ == -1 ? -1 : GetNextArenaRefKey()) {
51-
if (other.key_ != -1) {
54+
: key_(other.key_ == kInvalidKey ? kInvalidKey : GetNextArenaRefKey()) {
55+
if (other.key_ != kInvalidKey) {
5256
Env env = FirestoreInternal::GetEnv();
5357
Local<Object> object = other.get(env);
54-
std::unique_lock<std::mutex> lock(mutex_);
55-
gArenaRefHashMap->Put(env, key_object(env), object);
58+
Local<Long> key_ref = key_object(env);
59+
std::lock_guard<std::mutex> lock(mutex_);
60+
gArenaRefHashMap->Put(env, key_ref, object);
5661
}
5762
}
5863

5964
ArenaRef::ArenaRef(ArenaRef&& other) { std::swap(key_, other.key_); }
6065

6166
ArenaRef& ArenaRef::operator=(const ArenaRef& other) {
62-
Env env = FirestoreInternal::GetEnv();
63-
64-
if (this == &other) {
67+
if (this == &other || (key_ == kInvalidKey && other.key_ == kInvalidKey)) {
6568
return *this;
6669
}
6770

68-
{
69-
std::unique_lock<std::mutex> lock(mutex_);
70-
if (key_ != -1) {
71-
gArenaRefHashMap->Remove(env, key_object(env));
72-
key_ = -1;
73-
}
74-
75-
if (other.key_ != -1) {
71+
Env env = FirestoreInternal::GetEnv();
72+
if (other.key_ != kInvalidKey) {
73+
if (key_ == kInvalidKey) {
7674
key_ = GetNextArenaRefKey();
77-
Local<Object> object = other.get(env);
78-
gArenaRefHashMap->Put(env, key_object(env), object);
7975
}
76+
Local<Object> object = other.get(env);
77+
std::lock_guard<std::mutex> lock(mutex_);
78+
gArenaRefHashMap->Put(env, key_object(env), object);
79+
} else {
80+
{
81+
std::lock_guard<std::mutex> lock(mutex_);
82+
gArenaRefHashMap->Remove(env, key_object(env));
83+
}
84+
key_ = kInvalidKey;
8085
}
8186
return *this;
8287
}
8388

8489
ArenaRef& ArenaRef::operator=(ArenaRef&& other) {
85-
Env env = FirestoreInternal::GetEnv();
86-
87-
if (this == &other) {
88-
return *this;
89-
}
90-
91-
{
92-
std::unique_lock<std::mutex> lock(mutex_);
93-
if (key_ != -1) {
94-
gArenaRefHashMap->Remove(env, key_object(env));
95-
}
96-
key_ = other.key_;
97-
other.key_ = -1;
90+
if (this != &other) {
91+
std::swap(key_, other.key_);
9892
}
9993
return *this;
10094
}
10195

10296
ArenaRef::~ArenaRef() {
103-
if (key_ != -1) {
97+
if (key_ != kInvalidKey) {
10498
Env env;
10599
ExceptionClearGuard block(env);
106100
{
107-
std::unique_lock<std::mutex> lock(mutex_);
101+
std::lock_guard<std::mutex> lock(mutex_);
108102
gArenaRefHashMap->Remove(env, key_object(env));
109103
}
110104
if (!env.ok()) {
111-
env.ExceptionDescribe();
105+
env.RecordException();
112106
env.ExceptionClear();
113107
}
114108
}
115109
}
116110

117111
Local<Long> ArenaRef::key_object(Env& env) const {
118-
return Long::Create(env, key_);
112+
if (!env.ok()) return {};
113+
// The reason why using raw JNI calls here rather than the JNI convenience
114+
// layer is because we need to get rid of JNI convenience layer dependencies
115+
// in the ArenaRef destructor to avoid racing condition when firestore object
116+
// gets destroyed.
117+
jobject key = env.get()->NewObject(gLongClass, gLongConstructor, key_);
118+
env.RecordException();
119+
return {env.get(), key};
119120
}
120121

121122
void ArenaRef::Initialize(Env& env) {
@@ -124,6 +125,13 @@ void ArenaRef::Initialize(Env& env) {
124125
}
125126
Global<HashMap> hash_map(HashMap::Create(env));
126127
gArenaRefHashMap = new HashMap(hash_map.release());
128+
129+
auto long_class_local = env.get()->FindClass("java/lang/Long");
130+
gLongClass = static_cast<jclass>(env.get()->NewGlobalRef(long_class_local));
131+
env.get()->DeleteLocalRef(long_class_local);
132+
gLongConstructor = env.get()->GetMethodID(gLongClass, "<init>", "(J)V");
133+
134+
if (!env.ok()) return;
127135
}
128136

129137
Local<Object> ArenaRef::get(Env& env) const {

0 commit comments

Comments
 (0)