1 // Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
2 // This source code is licensed under both the GPLv2 (found in the
3 // COPYING file in the root directory) and Apache 2.0 License
4 // (found in the LICENSE.Apache file in the root directory).
6 // This file implements the callback "bridge" between Java and C++ for
7 // rocksdb::Comparator.
9 #include "rocksjni/comparatorjnicallback.h"
10 #include "rocksjni/portal.h"
13 BaseComparatorJniCallback::BaseComparatorJniCallback(
14 JNIEnv
* env
, jobject jComparator
,
15 const ComparatorJniCallbackOptions
* copt
)
16 : JniCallback(env
, jComparator
),
17 mtx_compare(new port::Mutex(copt
->use_adaptive_mutex
)),
18 mtx_findShortestSeparator(new port::Mutex(copt
->use_adaptive_mutex
)) {
20 // Note: The name of a Comparator will not change during it's lifetime,
21 // so we cache it in a global var
22 jmethodID jNameMethodId
= AbstractComparatorJni::getNameMethodId(env
);
23 if(jNameMethodId
== nullptr) {
24 // exception thrown: NoSuchMethodException or OutOfMemoryError
27 jstring jsName
= (jstring
)env
->CallObjectMethod(m_jcallback_obj
, jNameMethodId
);
28 if(env
->ExceptionCheck()) {
32 jboolean has_exception
= JNI_FALSE
;
33 m_name
= JniUtil::copyString(env
, jsName
,
34 &has_exception
); // also releases jsName
35 if (has_exception
== JNI_TRUE
) {
40 m_jCompareMethodId
= AbstractComparatorJni::getCompareMethodId(env
);
41 if(m_jCompareMethodId
== nullptr) {
42 // exception thrown: NoSuchMethodException or OutOfMemoryError
46 m_jFindShortestSeparatorMethodId
=
47 AbstractComparatorJni::getFindShortestSeparatorMethodId(env
);
48 if(m_jFindShortestSeparatorMethodId
== nullptr) {
49 // exception thrown: NoSuchMethodException or OutOfMemoryError
53 m_jFindShortSuccessorMethodId
=
54 AbstractComparatorJni::getFindShortSuccessorMethodId(env
);
55 if(m_jFindShortSuccessorMethodId
== nullptr) {
56 // exception thrown: NoSuchMethodException or OutOfMemoryError
61 const char* BaseComparatorJniCallback::Name() const {
65 int BaseComparatorJniCallback::Compare(const Slice
& a
, const Slice
& b
) const {
66 jboolean attached_thread
= JNI_FALSE
;
67 JNIEnv
* env
= getJniEnv(&attached_thread
);
68 assert(env
!= nullptr);
70 // TODO(adamretter): slice objects can potentially be cached using thread
71 // local variables to avoid locking. Could make this configurable depending on
73 mtx_compare
.get()->Lock();
75 bool pending_exception
=
76 AbstractSliceJni::setHandle(env
, m_jSliceA
, &a
, JNI_FALSE
);
77 if(pending_exception
) {
78 if(env
->ExceptionCheck()) {
79 // exception thrown from setHandle or descendant
80 env
->ExceptionDescribe(); // print out exception to stderr
82 releaseJniEnv(attached_thread
);
87 AbstractSliceJni::setHandle(env
, m_jSliceB
, &b
, JNI_FALSE
);
88 if(pending_exception
) {
89 if(env
->ExceptionCheck()) {
90 // exception thrown from setHandle or descendant
91 env
->ExceptionDescribe(); // print out exception to stderr
93 releaseJniEnv(attached_thread
);
98 env
->CallIntMethod(m_jcallback_obj
, m_jCompareMethodId
, m_jSliceA
,
101 mtx_compare
.get()->Unlock();
103 if(env
->ExceptionCheck()) {
104 // exception thrown from CallIntMethod
105 env
->ExceptionDescribe(); // print out exception to stderr
106 result
= 0; // we could not get a result from java callback so use 0
109 releaseJniEnv(attached_thread
);
114 void BaseComparatorJniCallback::FindShortestSeparator(
115 std::string
* start
, const Slice
& limit
) const {
116 if (start
== nullptr) {
120 jboolean attached_thread
= JNI_FALSE
;
121 JNIEnv
* env
= getJniEnv(&attached_thread
);
122 assert(env
!= nullptr);
124 const char* startUtf
= start
->c_str();
125 jstring jsStart
= env
->NewStringUTF(startUtf
);
126 if(jsStart
== nullptr) {
127 // unable to construct string
128 if(env
->ExceptionCheck()) {
129 env
->ExceptionDescribe(); // print out exception to stderr
131 releaseJniEnv(attached_thread
);
134 if(env
->ExceptionCheck()) {
135 // exception thrown: OutOfMemoryError
136 env
->ExceptionDescribe(); // print out exception to stderr
137 env
->DeleteLocalRef(jsStart
);
138 releaseJniEnv(attached_thread
);
142 // TODO(adamretter): slice object can potentially be cached using thread local
143 // variable to avoid locking. Could make this configurable depending on
145 mtx_findShortestSeparator
.get()->Lock();
147 bool pending_exception
=
148 AbstractSliceJni::setHandle(env
, m_jSliceLimit
, &limit
, JNI_FALSE
);
149 if(pending_exception
) {
150 if(env
->ExceptionCheck()) {
151 // exception thrown from setHandle or descendant
152 env
->ExceptionDescribe(); // print out exception to stderr
154 if(jsStart
!= nullptr) {
155 env
->DeleteLocalRef(jsStart
);
157 releaseJniEnv(attached_thread
);
161 jstring jsResultStart
=
162 (jstring
)env
->CallObjectMethod(m_jcallback_obj
,
163 m_jFindShortestSeparatorMethodId
, jsStart
, m_jSliceLimit
);
165 mtx_findShortestSeparator
.get()->Unlock();
167 if(env
->ExceptionCheck()) {
168 // exception thrown from CallObjectMethod
169 env
->ExceptionDescribe(); // print out exception to stderr
170 env
->DeleteLocalRef(jsStart
);
171 releaseJniEnv(attached_thread
);
175 env
->DeleteLocalRef(jsStart
);
177 if (jsResultStart
!= nullptr) {
178 // update start with result
179 jboolean has_exception
= JNI_FALSE
;
180 std::unique_ptr
<const char[]> result_start
= JniUtil::copyString(env
, jsResultStart
,
181 &has_exception
); // also releases jsResultStart
182 if (has_exception
== JNI_TRUE
) {
183 if (env
->ExceptionCheck()) {
184 env
->ExceptionDescribe(); // print out exception to stderr
186 releaseJniEnv(attached_thread
);
190 start
->assign(result_start
.get());
192 releaseJniEnv(attached_thread
);
195 void BaseComparatorJniCallback::FindShortSuccessor(
196 std::string
* key
) const {
197 if (key
== nullptr) {
201 jboolean attached_thread
= JNI_FALSE
;
202 JNIEnv
* env
= getJniEnv(&attached_thread
);
203 assert(env
!= nullptr);
205 const char* keyUtf
= key
->c_str();
206 jstring jsKey
= env
->NewStringUTF(keyUtf
);
207 if(jsKey
== nullptr) {
208 // unable to construct string
209 if(env
->ExceptionCheck()) {
210 env
->ExceptionDescribe(); // print out exception to stderr
212 releaseJniEnv(attached_thread
);
214 } else if(env
->ExceptionCheck()) {
215 // exception thrown: OutOfMemoryError
216 env
->ExceptionDescribe(); // print out exception to stderr
217 env
->DeleteLocalRef(jsKey
);
218 releaseJniEnv(attached_thread
);
222 jstring jsResultKey
=
223 (jstring
)env
->CallObjectMethod(m_jcallback_obj
,
224 m_jFindShortSuccessorMethodId
, jsKey
);
226 if(env
->ExceptionCheck()) {
227 // exception thrown from CallObjectMethod
228 env
->ExceptionDescribe(); // print out exception to stderr
229 env
->DeleteLocalRef(jsKey
);
230 releaseJniEnv(attached_thread
);
234 env
->DeleteLocalRef(jsKey
);
236 if (jsResultKey
!= nullptr) {
237 // updates key with result, also releases jsResultKey.
238 jboolean has_exception
= JNI_FALSE
;
239 std::unique_ptr
<const char[]> result_key
= JniUtil::copyString(env
, jsResultKey
,
240 &has_exception
); // also releases jsResultKey
241 if (has_exception
== JNI_TRUE
) {
242 if (env
->ExceptionCheck()) {
243 env
->ExceptionDescribe(); // print out exception to stderr
245 releaseJniEnv(attached_thread
);
249 key
->assign(result_key
.get());
252 releaseJniEnv(attached_thread
);
255 ComparatorJniCallback::ComparatorJniCallback(
256 JNIEnv
* env
, jobject jComparator
,
257 const ComparatorJniCallbackOptions
* copt
) :
258 BaseComparatorJniCallback(env
, jComparator
, copt
) {
259 m_jSliceA
= env
->NewGlobalRef(SliceJni::construct0(env
));
260 if(m_jSliceA
== nullptr) {
261 // exception thrown: OutOfMemoryError
265 m_jSliceB
= env
->NewGlobalRef(SliceJni::construct0(env
));
266 if(m_jSliceB
== nullptr) {
267 // exception thrown: OutOfMemoryError
271 m_jSliceLimit
= env
->NewGlobalRef(SliceJni::construct0(env
));
272 if(m_jSliceLimit
== nullptr) {
273 // exception thrown: OutOfMemoryError
278 ComparatorJniCallback::~ComparatorJniCallback() {
279 jboolean attached_thread
= JNI_FALSE
;
280 JNIEnv
* env
= getJniEnv(&attached_thread
);
281 assert(env
!= nullptr);
283 if(m_jSliceA
!= nullptr) {
284 env
->DeleteGlobalRef(m_jSliceA
);
287 if(m_jSliceB
!= nullptr) {
288 env
->DeleteGlobalRef(m_jSliceB
);
291 if(m_jSliceLimit
!= nullptr) {
292 env
->DeleteGlobalRef(m_jSliceLimit
);
295 releaseJniEnv(attached_thread
);
298 DirectComparatorJniCallback::DirectComparatorJniCallback(
299 JNIEnv
* env
, jobject jComparator
,
300 const ComparatorJniCallbackOptions
* copt
) :
301 BaseComparatorJniCallback(env
, jComparator
, copt
) {
302 m_jSliceA
= env
->NewGlobalRef(DirectSliceJni::construct0(env
));
303 if(m_jSliceA
== nullptr) {
304 // exception thrown: OutOfMemoryError
308 m_jSliceB
= env
->NewGlobalRef(DirectSliceJni::construct0(env
));
309 if(m_jSliceB
== nullptr) {
310 // exception thrown: OutOfMemoryError
314 m_jSliceLimit
= env
->NewGlobalRef(DirectSliceJni::construct0(env
));
315 if(m_jSliceLimit
== nullptr) {
316 // exception thrown: OutOfMemoryError
321 DirectComparatorJniCallback::~DirectComparatorJniCallback() {
322 jboolean attached_thread
= JNI_FALSE
;
323 JNIEnv
* env
= getJniEnv(&attached_thread
);
324 assert(env
!= nullptr);
326 if(m_jSliceA
!= nullptr) {
327 env
->DeleteGlobalRef(m_jSliceA
);
330 if(m_jSliceB
!= nullptr) {
331 env
->DeleteGlobalRef(m_jSliceB
);
334 if(m_jSliceLimit
!= nullptr) {
335 env
->DeleteGlobalRef(m_jSliceLimit
);
338 releaseJniEnv(attached_thread
);
340 } // namespace rocksdb