2016-02-10 00:12:00 +01:00
|
|
|
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
2017-07-16 01:03:42 +02:00
|
|
|
// This source code is licensed under both the GPLv2 (found in the
|
|
|
|
// COPYING file in the root directory) and Apache 2.0 License
|
|
|
|
// (found in the LICENSE.Apache file in the root directory).
|
2014-08-03 22:11:44 +02:00
|
|
|
//
|
|
|
|
// This file implements the callback "bridge" between Java and C++ for
|
|
|
|
// rocksdb::Comparator.
|
|
|
|
|
|
|
|
#include "rocksjni/comparatorjnicallback.h"
|
2014-08-21 22:55:51 +02:00
|
|
|
#include "rocksjni/portal.h"
|
2014-08-03 22:11:44 +02:00
|
|
|
|
|
|
|
namespace rocksdb {
|
|
|
|
BaseComparatorJniCallback::BaseComparatorJniCallback(
|
2014-08-21 22:55:51 +02:00
|
|
|
JNIEnv* env, jobject jComparator,
|
2014-10-06 19:35:53 +02:00
|
|
|
const ComparatorJniCallbackOptions* copt)
|
2017-10-12 20:06:51 +02:00
|
|
|
: JniCallback(env, jComparator),
|
|
|
|
mtx_compare(new port::Mutex(copt->use_adaptive_mutex)),
|
2014-10-06 19:35:53 +02:00
|
|
|
mtx_findShortestSeparator(new port::Mutex(copt->use_adaptive_mutex)) {
|
2014-08-03 22:11:44 +02:00
|
|
|
|
|
|
|
// Note: The name of a Comparator will not change during it's lifetime,
|
|
|
|
// so we cache it in a global var
|
|
|
|
jmethodID jNameMethodId = AbstractComparatorJni::getNameMethodId(env);
|
2017-02-28 01:26:12 +01:00
|
|
|
if(jNameMethodId == nullptr) {
|
|
|
|
// exception thrown: NoSuchMethodException or OutOfMemoryError
|
|
|
|
return;
|
|
|
|
}
|
2017-10-12 20:06:51 +02:00
|
|
|
jstring jsName = (jstring)env->CallObjectMethod(m_jcallback_obj, jNameMethodId);
|
2017-02-28 01:26:12 +01:00
|
|
|
if(env->ExceptionCheck()) {
|
|
|
|
// exception thrown
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
jboolean has_exception = JNI_FALSE;
|
|
|
|
m_name = JniUtil::copyString(env, jsName,
|
|
|
|
&has_exception); // also releases jsName
|
|
|
|
if (has_exception == JNI_TRUE) {
|
|
|
|
// exception thrown
|
|
|
|
return;
|
|
|
|
}
|
2014-08-03 22:11:44 +02:00
|
|
|
|
|
|
|
m_jCompareMethodId = AbstractComparatorJni::getCompareMethodId(env);
|
2017-02-28 01:26:12 +01:00
|
|
|
if(m_jCompareMethodId == nullptr) {
|
|
|
|
// exception thrown: NoSuchMethodException or OutOfMemoryError
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-08-21 22:55:51 +02:00
|
|
|
m_jFindShortestSeparatorMethodId =
|
|
|
|
AbstractComparatorJni::getFindShortestSeparatorMethodId(env);
|
2017-02-28 01:26:12 +01:00
|
|
|
if(m_jFindShortestSeparatorMethodId == nullptr) {
|
|
|
|
// exception thrown: NoSuchMethodException or OutOfMemoryError
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-08-21 22:55:51 +02:00
|
|
|
m_jFindShortSuccessorMethodId =
|
|
|
|
AbstractComparatorJni::getFindShortSuccessorMethodId(env);
|
2017-02-28 01:26:12 +01:00
|
|
|
if(m_jFindShortSuccessorMethodId == nullptr) {
|
|
|
|
// exception thrown: NoSuchMethodException or OutOfMemoryError
|
|
|
|
return;
|
|
|
|
}
|
2014-08-21 22:55:51 +02:00
|
|
|
}
|
2014-08-03 22:11:44 +02:00
|
|
|
|
|
|
|
const char* BaseComparatorJniCallback::Name() const {
|
2017-10-12 20:06:51 +02:00
|
|
|
return m_name.get();
|
2014-08-03 22:11:44 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
int BaseComparatorJniCallback::Compare(const Slice& a, const Slice& b) const {
|
2017-02-28 01:26:12 +01:00
|
|
|
jboolean attached_thread = JNI_FALSE;
|
2017-10-12 20:06:51 +02:00
|
|
|
JNIEnv* env = getJniEnv(&attached_thread);
|
2017-02-28 01:26:12 +01:00
|
|
|
assert(env != nullptr);
|
2014-08-03 22:11:44 +02:00
|
|
|
|
2014-10-06 19:35:53 +02:00
|
|
|
// TODO(adamretter): slice objects can potentially be cached using thread
|
|
|
|
// local variables to avoid locking. Could make this configurable depending on
|
|
|
|
// performance.
|
2017-10-12 20:06:51 +02:00
|
|
|
mtx_compare.get()->Lock();
|
2014-08-15 14:34:10 +02:00
|
|
|
|
2017-02-28 01:26:12 +01:00
|
|
|
bool pending_exception =
|
|
|
|
AbstractSliceJni::setHandle(env, m_jSliceA, &a, JNI_FALSE);
|
|
|
|
if(pending_exception) {
|
|
|
|
if(env->ExceptionCheck()) {
|
|
|
|
// exception thrown from setHandle or descendant
|
|
|
|
env->ExceptionDescribe(); // print out exception to stderr
|
|
|
|
}
|
2017-10-12 20:06:51 +02:00
|
|
|
releaseJniEnv(attached_thread);
|
2017-02-28 01:26:12 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
pending_exception =
|
|
|
|
AbstractSliceJni::setHandle(env, m_jSliceB, &b, JNI_FALSE);
|
|
|
|
if(pending_exception) {
|
|
|
|
if(env->ExceptionCheck()) {
|
|
|
|
// exception thrown from setHandle or descendant
|
|
|
|
env->ExceptionDescribe(); // print out exception to stderr
|
|
|
|
}
|
2017-10-12 20:06:51 +02:00
|
|
|
releaseJniEnv(attached_thread);
|
2017-02-28 01:26:12 +01:00
|
|
|
return 0;
|
|
|
|
}
|
2017-10-12 20:06:51 +02:00
|
|
|
|
2014-08-21 22:55:51 +02:00
|
|
|
jint result =
|
2017-10-12 20:06:51 +02:00
|
|
|
env->CallIntMethod(m_jcallback_obj, m_jCompareMethodId, m_jSliceA,
|
2014-08-21 22:55:51 +02:00
|
|
|
m_jSliceB);
|
2014-08-03 22:11:44 +02:00
|
|
|
|
2017-10-12 20:06:51 +02:00
|
|
|
mtx_compare.get()->Unlock();
|
2014-08-15 14:34:10 +02:00
|
|
|
|
2017-02-28 01:26:12 +01:00
|
|
|
if(env->ExceptionCheck()) {
|
|
|
|
// exception thrown from CallIntMethod
|
|
|
|
env->ExceptionDescribe(); // print out exception to stderr
|
|
|
|
result = 0; // we could not get a result from java callback so use 0
|
|
|
|
}
|
|
|
|
|
2017-10-12 20:06:51 +02:00
|
|
|
releaseJniEnv(attached_thread);
|
2014-08-03 22:11:44 +02:00
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2014-08-21 22:55:51 +02:00
|
|
|
void BaseComparatorJniCallback::FindShortestSeparator(
|
2017-10-12 20:06:51 +02:00
|
|
|
std::string* start, const Slice& limit) const {
|
2014-08-03 22:11:44 +02:00
|
|
|
if (start == nullptr) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-02-28 01:26:12 +01:00
|
|
|
jboolean attached_thread = JNI_FALSE;
|
2017-10-12 20:06:51 +02:00
|
|
|
JNIEnv* env = getJniEnv(&attached_thread);
|
2017-02-28 01:26:12 +01:00
|
|
|
assert(env != nullptr);
|
2014-08-03 22:11:44 +02:00
|
|
|
|
|
|
|
const char* startUtf = start->c_str();
|
2017-02-28 01:26:12 +01:00
|
|
|
jstring jsStart = env->NewStringUTF(startUtf);
|
|
|
|
if(jsStart == nullptr) {
|
|
|
|
// unable to construct string
|
|
|
|
if(env->ExceptionCheck()) {
|
|
|
|
env->ExceptionDescribe(); // print out exception to stderr
|
|
|
|
}
|
2017-10-12 20:06:51 +02:00
|
|
|
releaseJniEnv(attached_thread);
|
2017-02-28 01:26:12 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
if(env->ExceptionCheck()) {
|
|
|
|
// exception thrown: OutOfMemoryError
|
|
|
|
env->ExceptionDescribe(); // print out exception to stderr
|
|
|
|
env->DeleteLocalRef(jsStart);
|
2017-10-12 20:06:51 +02:00
|
|
|
releaseJniEnv(attached_thread);
|
2017-02-28 01:26:12 +01:00
|
|
|
return;
|
|
|
|
}
|
2014-08-03 22:11:44 +02:00
|
|
|
|
2014-10-06 19:35:53 +02:00
|
|
|
// TODO(adamretter): slice object can potentially be cached using thread local
|
|
|
|
// variable to avoid locking. Could make this configurable depending on
|
|
|
|
// performance.
|
2017-10-12 20:06:51 +02:00
|
|
|
mtx_findShortestSeparator.get()->Lock();
|
2014-08-03 22:11:44 +02:00
|
|
|
|
2017-02-28 01:26:12 +01:00
|
|
|
bool pending_exception =
|
|
|
|
AbstractSliceJni::setHandle(env, m_jSliceLimit, &limit, JNI_FALSE);
|
|
|
|
if(pending_exception) {
|
|
|
|
if(env->ExceptionCheck()) {
|
|
|
|
// exception thrown from setHandle or descendant
|
|
|
|
env->ExceptionDescribe(); // print out exception to stderr
|
|
|
|
}
|
|
|
|
if(jsStart != nullptr) {
|
|
|
|
env->DeleteLocalRef(jsStart);
|
|
|
|
}
|
2017-10-12 20:06:51 +02:00
|
|
|
releaseJniEnv(attached_thread);
|
2017-02-28 01:26:12 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-08-21 22:55:51 +02:00
|
|
|
jstring jsResultStart =
|
2017-10-12 20:06:51 +02:00
|
|
|
(jstring)env->CallObjectMethod(m_jcallback_obj,
|
2014-08-21 22:55:51 +02:00
|
|
|
m_jFindShortestSeparatorMethodId, jsStart, m_jSliceLimit);
|
2014-08-03 22:11:44 +02:00
|
|
|
|
2017-10-12 20:06:51 +02:00
|
|
|
mtx_findShortestSeparator.get()->Unlock();
|
2014-08-15 14:34:10 +02:00
|
|
|
|
2017-02-28 01:26:12 +01:00
|
|
|
if(env->ExceptionCheck()) {
|
|
|
|
// exception thrown from CallObjectMethod
|
|
|
|
env->ExceptionDescribe(); // print out exception to stderr
|
|
|
|
env->DeleteLocalRef(jsStart);
|
2017-10-12 20:06:51 +02:00
|
|
|
releaseJniEnv(attached_thread);
|
2017-02-28 01:26:12 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
env->DeleteLocalRef(jsStart);
|
2014-08-03 22:11:44 +02:00
|
|
|
|
2014-08-21 22:55:51 +02:00
|
|
|
if (jsResultStart != nullptr) {
|
|
|
|
// update start with result
|
2017-02-28 01:26:12 +01:00
|
|
|
jboolean has_exception = JNI_FALSE;
|
2017-10-12 20:06:51 +02:00
|
|
|
std::unique_ptr<const char[]> result_start = JniUtil::copyString(env, jsResultStart,
|
2017-02-28 01:26:12 +01:00
|
|
|
&has_exception); // also releases jsResultStart
|
|
|
|
if (has_exception == JNI_TRUE) {
|
|
|
|
if (env->ExceptionCheck()) {
|
|
|
|
env->ExceptionDescribe(); // print out exception to stderr
|
|
|
|
}
|
2017-10-12 20:06:51 +02:00
|
|
|
releaseJniEnv(attached_thread);
|
2017-02-28 01:26:12 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-10-12 20:06:51 +02:00
|
|
|
start->assign(result_start.get());
|
2014-08-03 22:11:44 +02:00
|
|
|
}
|
2017-10-12 20:06:51 +02:00
|
|
|
releaseJniEnv(attached_thread);
|
2014-08-03 22:11:44 +02:00
|
|
|
}
|
|
|
|
|
2017-10-12 20:06:51 +02:00
|
|
|
void BaseComparatorJniCallback::FindShortSuccessor(
|
|
|
|
std::string* key) const {
|
2014-08-03 22:11:44 +02:00
|
|
|
if (key == nullptr) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-02-28 01:26:12 +01:00
|
|
|
jboolean attached_thread = JNI_FALSE;
|
2017-10-12 20:06:51 +02:00
|
|
|
JNIEnv* env = getJniEnv(&attached_thread);
|
2017-02-28 01:26:12 +01:00
|
|
|
assert(env != nullptr);
|
2014-08-03 22:11:44 +02:00
|
|
|
|
|
|
|
const char* keyUtf = key->c_str();
|
2017-02-28 01:26:12 +01:00
|
|
|
jstring jsKey = env->NewStringUTF(keyUtf);
|
|
|
|
if(jsKey == nullptr) {
|
|
|
|
// unable to construct string
|
|
|
|
if(env->ExceptionCheck()) {
|
|
|
|
env->ExceptionDescribe(); // print out exception to stderr
|
|
|
|
}
|
2017-10-12 20:06:51 +02:00
|
|
|
releaseJniEnv(attached_thread);
|
2017-02-28 01:26:12 +01:00
|
|
|
return;
|
|
|
|
} else if(env->ExceptionCheck()) {
|
|
|
|
// exception thrown: OutOfMemoryError
|
|
|
|
env->ExceptionDescribe(); // print out exception to stderr
|
|
|
|
env->DeleteLocalRef(jsKey);
|
2017-10-12 20:06:51 +02:00
|
|
|
releaseJniEnv(attached_thread);
|
2017-02-28 01:26:12 +01:00
|
|
|
return;
|
|
|
|
}
|
2014-08-03 22:11:44 +02:00
|
|
|
|
2014-08-21 22:55:51 +02:00
|
|
|
jstring jsResultKey =
|
2017-10-12 20:06:51 +02:00
|
|
|
(jstring)env->CallObjectMethod(m_jcallback_obj,
|
2014-08-21 22:55:51 +02:00
|
|
|
m_jFindShortSuccessorMethodId, jsKey);
|
2014-08-03 22:11:44 +02:00
|
|
|
|
2017-02-28 01:26:12 +01:00
|
|
|
if(env->ExceptionCheck()) {
|
|
|
|
// exception thrown from CallObjectMethod
|
|
|
|
env->ExceptionDescribe(); // print out exception to stderr
|
|
|
|
env->DeleteLocalRef(jsKey);
|
2017-10-12 20:06:51 +02:00
|
|
|
releaseJniEnv(attached_thread);
|
2017-02-28 01:26:12 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
env->DeleteLocalRef(jsKey);
|
2014-08-03 22:11:44 +02:00
|
|
|
|
2014-08-21 22:55:51 +02:00
|
|
|
if (jsResultKey != nullptr) {
|
2014-10-06 19:35:53 +02:00
|
|
|
// updates key with result, also releases jsResultKey.
|
2017-02-28 01:26:12 +01:00
|
|
|
jboolean has_exception = JNI_FALSE;
|
2017-10-12 20:06:51 +02:00
|
|
|
std::unique_ptr<const char[]> result_key = JniUtil::copyString(env, jsResultKey,
|
|
|
|
&has_exception); // also releases jsResultKey
|
2017-02-28 01:26:12 +01:00
|
|
|
if (has_exception == JNI_TRUE) {
|
|
|
|
if (env->ExceptionCheck()) {
|
|
|
|
env->ExceptionDescribe(); // print out exception to stderr
|
|
|
|
}
|
2017-10-12 20:06:51 +02:00
|
|
|
releaseJniEnv(attached_thread);
|
2017-02-28 01:26:12 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-10-12 20:06:51 +02:00
|
|
|
key->assign(result_key.get());
|
2017-02-28 01:26:12 +01:00
|
|
|
}
|
2014-08-03 22:11:44 +02:00
|
|
|
|
2017-10-12 20:06:51 +02:00
|
|
|
releaseJniEnv(attached_thread);
|
2014-08-03 22:11:44 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
ComparatorJniCallback::ComparatorJniCallback(
|
2014-08-21 22:55:51 +02:00
|
|
|
JNIEnv* env, jobject jComparator,
|
|
|
|
const ComparatorJniCallbackOptions* copt) :
|
|
|
|
BaseComparatorJniCallback(env, jComparator, copt) {
|
2014-08-03 22:11:44 +02:00
|
|
|
m_jSliceA = env->NewGlobalRef(SliceJni::construct0(env));
|
2017-02-28 01:26:12 +01:00
|
|
|
if(m_jSliceA == nullptr) {
|
|
|
|
// exception thrown: OutOfMemoryError
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-08-03 22:11:44 +02:00
|
|
|
m_jSliceB = env->NewGlobalRef(SliceJni::construct0(env));
|
2017-02-28 01:26:12 +01:00
|
|
|
if(m_jSliceB == nullptr) {
|
|
|
|
// exception thrown: OutOfMemoryError
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-08-03 22:11:44 +02:00
|
|
|
m_jSliceLimit = env->NewGlobalRef(SliceJni::construct0(env));
|
2017-02-28 01:26:12 +01:00
|
|
|
if(m_jSliceLimit == nullptr) {
|
|
|
|
// exception thrown: OutOfMemoryError
|
|
|
|
return;
|
|
|
|
}
|
2014-08-03 22:11:44 +02:00
|
|
|
}
|
|
|
|
|
2014-10-06 19:35:53 +02:00
|
|
|
ComparatorJniCallback::~ComparatorJniCallback() {
|
2017-02-28 01:26:12 +01:00
|
|
|
jboolean attached_thread = JNI_FALSE;
|
2017-10-12 20:06:51 +02:00
|
|
|
JNIEnv* env = getJniEnv(&attached_thread);
|
2017-02-28 01:26:12 +01:00
|
|
|
assert(env != nullptr);
|
|
|
|
|
|
|
|
if(m_jSliceA != nullptr) {
|
|
|
|
env->DeleteGlobalRef(m_jSliceA);
|
|
|
|
}
|
|
|
|
|
|
|
|
if(m_jSliceB != nullptr) {
|
|
|
|
env->DeleteGlobalRef(m_jSliceB);
|
|
|
|
}
|
|
|
|
|
|
|
|
if(m_jSliceLimit != nullptr) {
|
|
|
|
env->DeleteGlobalRef(m_jSliceLimit);
|
|
|
|
}
|
|
|
|
|
2017-10-12 20:06:51 +02:00
|
|
|
releaseJniEnv(attached_thread);
|
2014-10-06 19:35:53 +02:00
|
|
|
}
|
|
|
|
|
2014-08-03 22:11:44 +02:00
|
|
|
DirectComparatorJniCallback::DirectComparatorJniCallback(
|
2014-08-21 22:55:51 +02:00
|
|
|
JNIEnv* env, jobject jComparator,
|
|
|
|
const ComparatorJniCallbackOptions* copt) :
|
|
|
|
BaseComparatorJniCallback(env, jComparator, copt) {
|
2014-08-03 22:11:44 +02:00
|
|
|
m_jSliceA = env->NewGlobalRef(DirectSliceJni::construct0(env));
|
2017-02-28 01:26:12 +01:00
|
|
|
if(m_jSliceA == nullptr) {
|
|
|
|
// exception thrown: OutOfMemoryError
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-08-03 22:11:44 +02:00
|
|
|
m_jSliceB = env->NewGlobalRef(DirectSliceJni::construct0(env));
|
2017-02-28 01:26:12 +01:00
|
|
|
if(m_jSliceB == nullptr) {
|
|
|
|
// exception thrown: OutOfMemoryError
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-08-03 22:11:44 +02:00
|
|
|
m_jSliceLimit = env->NewGlobalRef(DirectSliceJni::construct0(env));
|
2017-02-28 01:26:12 +01:00
|
|
|
if(m_jSliceLimit == nullptr) {
|
|
|
|
// exception thrown: OutOfMemoryError
|
|
|
|
return;
|
|
|
|
}
|
2014-08-03 22:11:44 +02:00
|
|
|
}
|
2014-10-06 19:35:53 +02:00
|
|
|
|
|
|
|
DirectComparatorJniCallback::~DirectComparatorJniCallback() {
|
2017-02-28 01:26:12 +01:00
|
|
|
jboolean attached_thread = JNI_FALSE;
|
2017-10-12 20:06:51 +02:00
|
|
|
JNIEnv* env = getJniEnv(&attached_thread);
|
2017-02-28 01:26:12 +01:00
|
|
|
assert(env != nullptr);
|
|
|
|
|
|
|
|
if(m_jSliceA != nullptr) {
|
|
|
|
env->DeleteGlobalRef(m_jSliceA);
|
|
|
|
}
|
|
|
|
|
|
|
|
if(m_jSliceB != nullptr) {
|
|
|
|
env->DeleteGlobalRef(m_jSliceB);
|
|
|
|
}
|
|
|
|
|
|
|
|
if(m_jSliceLimit != nullptr) {
|
|
|
|
env->DeleteGlobalRef(m_jSliceLimit);
|
|
|
|
}
|
|
|
|
|
2017-10-12 20:06:51 +02:00
|
|
|
releaseJniEnv(attached_thread);
|
2014-10-06 19:35:53 +02:00
|
|
|
}
|
2014-08-03 22:11:44 +02:00
|
|
|
} // namespace rocksdb
|