]>
Commit | Line | Data |
---|---|---|
7c673cae | 1 | // Copyright (c) 2011-present, Facebook, Inc. All rights reserved. |
11fdf7f2 TL |
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). | |
7c673cae FG |
5 | // |
6 | // This file implements the callback "bridge" between Java and C++ for | |
7 | // rocksdb::Comparator. | |
8 | ||
9 | #include "rocksjni/comparatorjnicallback.h" | |
10 | #include "rocksjni/portal.h" | |
11 | ||
12 | namespace rocksdb { | |
13 | BaseComparatorJniCallback::BaseComparatorJniCallback( | |
14 | JNIEnv* env, jobject jComparator, | |
15 | const ComparatorJniCallbackOptions* copt) | |
11fdf7f2 TL |
16 | : JniCallback(env, jComparator), |
17 | mtx_compare(new port::Mutex(copt->use_adaptive_mutex)), | |
7c673cae | 18 | mtx_findShortestSeparator(new port::Mutex(copt->use_adaptive_mutex)) { |
7c673cae FG |
19 | |
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 | |
25 | return; | |
26 | } | |
11fdf7f2 | 27 | jstring jsName = (jstring)env->CallObjectMethod(m_jcallback_obj, jNameMethodId); |
7c673cae FG |
28 | if(env->ExceptionCheck()) { |
29 | // exception thrown | |
30 | return; | |
31 | } | |
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) { | |
36 | // exception thrown | |
37 | return; | |
38 | } | |
39 | ||
40 | m_jCompareMethodId = AbstractComparatorJni::getCompareMethodId(env); | |
41 | if(m_jCompareMethodId == nullptr) { | |
42 | // exception thrown: NoSuchMethodException or OutOfMemoryError | |
43 | return; | |
44 | } | |
45 | ||
46 | m_jFindShortestSeparatorMethodId = | |
47 | AbstractComparatorJni::getFindShortestSeparatorMethodId(env); | |
48 | if(m_jFindShortestSeparatorMethodId == nullptr) { | |
49 | // exception thrown: NoSuchMethodException or OutOfMemoryError | |
50 | return; | |
51 | } | |
52 | ||
53 | m_jFindShortSuccessorMethodId = | |
54 | AbstractComparatorJni::getFindShortSuccessorMethodId(env); | |
55 | if(m_jFindShortSuccessorMethodId == nullptr) { | |
56 | // exception thrown: NoSuchMethodException or OutOfMemoryError | |
57 | return; | |
58 | } | |
59 | } | |
60 | ||
61 | const char* BaseComparatorJniCallback::Name() const { | |
11fdf7f2 | 62 | return m_name.get(); |
7c673cae FG |
63 | } |
64 | ||
65 | int BaseComparatorJniCallback::Compare(const Slice& a, const Slice& b) const { | |
66 | jboolean attached_thread = JNI_FALSE; | |
11fdf7f2 | 67 | JNIEnv* env = getJniEnv(&attached_thread); |
7c673cae FG |
68 | assert(env != nullptr); |
69 | ||
70 | // TODO(adamretter): slice objects can potentially be cached using thread | |
71 | // local variables to avoid locking. Could make this configurable depending on | |
72 | // performance. | |
11fdf7f2 | 73 | mtx_compare.get()->Lock(); |
7c673cae FG |
74 | |
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 | |
81 | } | |
11fdf7f2 | 82 | releaseJniEnv(attached_thread); |
7c673cae FG |
83 | return 0; |
84 | } | |
85 | ||
86 | pending_exception = | |
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 | |
92 | } | |
11fdf7f2 | 93 | releaseJniEnv(attached_thread); |
7c673cae FG |
94 | return 0; |
95 | } | |
11fdf7f2 | 96 | |
7c673cae | 97 | jint result = |
11fdf7f2 | 98 | env->CallIntMethod(m_jcallback_obj, m_jCompareMethodId, m_jSliceA, |
7c673cae FG |
99 | m_jSliceB); |
100 | ||
11fdf7f2 | 101 | mtx_compare.get()->Unlock(); |
7c673cae FG |
102 | |
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 | |
107 | } | |
108 | ||
11fdf7f2 | 109 | releaseJniEnv(attached_thread); |
7c673cae FG |
110 | |
111 | return result; | |
112 | } | |
113 | ||
114 | void BaseComparatorJniCallback::FindShortestSeparator( | |
11fdf7f2 | 115 | std::string* start, const Slice& limit) const { |
7c673cae FG |
116 | if (start == nullptr) { |
117 | return; | |
118 | } | |
119 | ||
120 | jboolean attached_thread = JNI_FALSE; | |
11fdf7f2 | 121 | JNIEnv* env = getJniEnv(&attached_thread); |
7c673cae FG |
122 | assert(env != nullptr); |
123 | ||
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 | |
130 | } | |
11fdf7f2 | 131 | releaseJniEnv(attached_thread); |
7c673cae FG |
132 | return; |
133 | } | |
134 | if(env->ExceptionCheck()) { | |
135 | // exception thrown: OutOfMemoryError | |
136 | env->ExceptionDescribe(); // print out exception to stderr | |
137 | env->DeleteLocalRef(jsStart); | |
11fdf7f2 | 138 | releaseJniEnv(attached_thread); |
7c673cae FG |
139 | return; |
140 | } | |
141 | ||
142 | // TODO(adamretter): slice object can potentially be cached using thread local | |
143 | // variable to avoid locking. Could make this configurable depending on | |
144 | // performance. | |
11fdf7f2 | 145 | mtx_findShortestSeparator.get()->Lock(); |
7c673cae FG |
146 | |
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 | |
153 | } | |
154 | if(jsStart != nullptr) { | |
155 | env->DeleteLocalRef(jsStart); | |
156 | } | |
11fdf7f2 | 157 | releaseJniEnv(attached_thread); |
7c673cae FG |
158 | return; |
159 | } | |
160 | ||
161 | jstring jsResultStart = | |
11fdf7f2 | 162 | (jstring)env->CallObjectMethod(m_jcallback_obj, |
7c673cae FG |
163 | m_jFindShortestSeparatorMethodId, jsStart, m_jSliceLimit); |
164 | ||
11fdf7f2 | 165 | mtx_findShortestSeparator.get()->Unlock(); |
7c673cae FG |
166 | |
167 | if(env->ExceptionCheck()) { | |
168 | // exception thrown from CallObjectMethod | |
169 | env->ExceptionDescribe(); // print out exception to stderr | |
170 | env->DeleteLocalRef(jsStart); | |
11fdf7f2 | 171 | releaseJniEnv(attached_thread); |
7c673cae FG |
172 | return; |
173 | } | |
174 | ||
175 | env->DeleteLocalRef(jsStart); | |
176 | ||
177 | if (jsResultStart != nullptr) { | |
178 | // update start with result | |
179 | jboolean has_exception = JNI_FALSE; | |
11fdf7f2 | 180 | std::unique_ptr<const char[]> result_start = JniUtil::copyString(env, jsResultStart, |
7c673cae FG |
181 | &has_exception); // also releases jsResultStart |
182 | if (has_exception == JNI_TRUE) { | |
183 | if (env->ExceptionCheck()) { | |
184 | env->ExceptionDescribe(); // print out exception to stderr | |
185 | } | |
11fdf7f2 | 186 | releaseJniEnv(attached_thread); |
7c673cae FG |
187 | return; |
188 | } | |
189 | ||
11fdf7f2 | 190 | start->assign(result_start.get()); |
7c673cae | 191 | } |
11fdf7f2 | 192 | releaseJniEnv(attached_thread); |
7c673cae FG |
193 | } |
194 | ||
11fdf7f2 TL |
195 | void BaseComparatorJniCallback::FindShortSuccessor( |
196 | std::string* key) const { | |
7c673cae FG |
197 | if (key == nullptr) { |
198 | return; | |
199 | } | |
200 | ||
201 | jboolean attached_thread = JNI_FALSE; | |
11fdf7f2 | 202 | JNIEnv* env = getJniEnv(&attached_thread); |
7c673cae FG |
203 | assert(env != nullptr); |
204 | ||
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 | |
211 | } | |
11fdf7f2 | 212 | releaseJniEnv(attached_thread); |
7c673cae FG |
213 | return; |
214 | } else if(env->ExceptionCheck()) { | |
215 | // exception thrown: OutOfMemoryError | |
216 | env->ExceptionDescribe(); // print out exception to stderr | |
217 | env->DeleteLocalRef(jsKey); | |
11fdf7f2 | 218 | releaseJniEnv(attached_thread); |
7c673cae FG |
219 | return; |
220 | } | |
221 | ||
222 | jstring jsResultKey = | |
11fdf7f2 | 223 | (jstring)env->CallObjectMethod(m_jcallback_obj, |
7c673cae FG |
224 | m_jFindShortSuccessorMethodId, jsKey); |
225 | ||
226 | if(env->ExceptionCheck()) { | |
227 | // exception thrown from CallObjectMethod | |
228 | env->ExceptionDescribe(); // print out exception to stderr | |
229 | env->DeleteLocalRef(jsKey); | |
11fdf7f2 | 230 | releaseJniEnv(attached_thread); |
7c673cae FG |
231 | return; |
232 | } | |
233 | ||
234 | env->DeleteLocalRef(jsKey); | |
235 | ||
236 | if (jsResultKey != nullptr) { | |
237 | // updates key with result, also releases jsResultKey. | |
238 | jboolean has_exception = JNI_FALSE; | |
11fdf7f2 TL |
239 | std::unique_ptr<const char[]> result_key = JniUtil::copyString(env, jsResultKey, |
240 | &has_exception); // also releases jsResultKey | |
7c673cae FG |
241 | if (has_exception == JNI_TRUE) { |
242 | if (env->ExceptionCheck()) { | |
243 | env->ExceptionDescribe(); // print out exception to stderr | |
244 | } | |
11fdf7f2 | 245 | releaseJniEnv(attached_thread); |
7c673cae FG |
246 | return; |
247 | } | |
248 | ||
11fdf7f2 | 249 | key->assign(result_key.get()); |
7c673cae FG |
250 | } |
251 | ||
11fdf7f2 | 252 | releaseJniEnv(attached_thread); |
7c673cae FG |
253 | } |
254 | ||
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 | |
262 | return; | |
263 | } | |
264 | ||
265 | m_jSliceB = env->NewGlobalRef(SliceJni::construct0(env)); | |
266 | if(m_jSliceB == nullptr) { | |
267 | // exception thrown: OutOfMemoryError | |
268 | return; | |
269 | } | |
270 | ||
271 | m_jSliceLimit = env->NewGlobalRef(SliceJni::construct0(env)); | |
272 | if(m_jSliceLimit == nullptr) { | |
273 | // exception thrown: OutOfMemoryError | |
274 | return; | |
275 | } | |
276 | } | |
277 | ||
278 | ComparatorJniCallback::~ComparatorJniCallback() { | |
279 | jboolean attached_thread = JNI_FALSE; | |
11fdf7f2 | 280 | JNIEnv* env = getJniEnv(&attached_thread); |
7c673cae FG |
281 | assert(env != nullptr); |
282 | ||
283 | if(m_jSliceA != nullptr) { | |
284 | env->DeleteGlobalRef(m_jSliceA); | |
285 | } | |
286 | ||
287 | if(m_jSliceB != nullptr) { | |
288 | env->DeleteGlobalRef(m_jSliceB); | |
289 | } | |
290 | ||
291 | if(m_jSliceLimit != nullptr) { | |
292 | env->DeleteGlobalRef(m_jSliceLimit); | |
293 | } | |
294 | ||
11fdf7f2 | 295 | releaseJniEnv(attached_thread); |
7c673cae FG |
296 | } |
297 | ||
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 | |
305 | return; | |
306 | } | |
307 | ||
308 | m_jSliceB = env->NewGlobalRef(DirectSliceJni::construct0(env)); | |
309 | if(m_jSliceB == nullptr) { | |
310 | // exception thrown: OutOfMemoryError | |
311 | return; | |
312 | } | |
313 | ||
314 | m_jSliceLimit = env->NewGlobalRef(DirectSliceJni::construct0(env)); | |
315 | if(m_jSliceLimit == nullptr) { | |
316 | // exception thrown: OutOfMemoryError | |
317 | return; | |
318 | } | |
319 | } | |
320 | ||
321 | DirectComparatorJniCallback::~DirectComparatorJniCallback() { | |
322 | jboolean attached_thread = JNI_FALSE; | |
11fdf7f2 | 323 | JNIEnv* env = getJniEnv(&attached_thread); |
7c673cae FG |
324 | assert(env != nullptr); |
325 | ||
326 | if(m_jSliceA != nullptr) { | |
327 | env->DeleteGlobalRef(m_jSliceA); | |
328 | } | |
329 | ||
330 | if(m_jSliceB != nullptr) { | |
331 | env->DeleteGlobalRef(m_jSliceB); | |
332 | } | |
333 | ||
334 | if(m_jSliceLimit != nullptr) { | |
335 | env->DeleteGlobalRef(m_jSliceLimit); | |
336 | } | |
337 | ||
11fdf7f2 | 338 | releaseJniEnv(attached_thread); |
7c673cae FG |
339 | } |
340 | } // namespace rocksdb |