-
Notifications
You must be signed in to change notification settings - Fork 495
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
OpenDAL Java Bindings #1572
Comments
Could I contribute to this issue? |
@kidylee I'd suggest you briefly outline your ideas first. |
Do we need to build the C-binding before starting on the Java binding? |
Thanks for your advice. As @tisonkun said, we don't need a C-binding for Java, a A few candidates I'm looking into:
|
FYI - if we have a C binding, using JNA can avoid JNI glue code. But it still uses the JNI technology underneath. |
Thanks @tisonkun, it is a solid approach. Here are snippets of my approach: public void testFileSystem(){
FileSystemService service = new FileSystemBuilder()
.root("/tmp")
.build();
var op = new OperatorBuilder(service)
.build();
op.write("hello.txt","hello world");
var rs = op.read("hello.txt");
assert "hello world".equals(rs);
} For the rust part, I'm taking assumption where #[no_mangle]
pub extern "C" fn java_get_operater() -> *const i32 {
let op = Box::new(Operator{});
let op_ptr = Box::into_raw(op);
op_ptr as *const i32
}
#[no_mangle]
pub extern "C" fn java_free_operater(ptr: *mut Operator) {
unsafe {
let _ = Box::from_raw(ptr);
}
} Comments are welcomed. |
@kidylee I'm glad to review a MVP, go ahead! |
I found a possible solution: we can store JavaVM as thread local variable, and when we need to execute Java code, attach the rust-managed thread to obtain thread_local! {
static JAVA_VM: RefCell<Option<Arc<JavaVM>>> = RefCell::new(None);
}
#[no_mangle]
pub extern "system" fn Java_org_apache_opendal_Operator_getOperator0(
env: JNIEnv,
_class: JClass,
input: JString,
params: JObject,
) -> jlong {
// build operator
let operator = 1;
let java_vm = Arc::new(env.get_java_vm().unwrap());
let runtime = Builder::new_multi_thread()
.worker_threads(4)
.on_thread_start(move || {
JAVA_VM.with(|cell| {
*cell.borrow_mut() = Some(java_vm.clone());
});
})
.build()
.unwrap();
Box::into_raw(Box::new((operator, runtime))) as jlong
} And then we can use async/await without suspending the caller thread. #[no_mangle]
pub unsafe extern "system" fn Java_org_apache_opendal_Operator_async_write(
mut env: JNIEnv,
_class: JClass,
ptr: *mut (Operator, Runtime),
file: JString,
content: JString,
future: JObject,
) {
let (op, runtime) = &mut *ptr;
let file: String = env.get_string(&file).unwrap().into();
let content: String = env.get_string(&content).unwrap().into();
let future = env.new_global_ref(future).unwrap();
let x = async move {
op.write(&file, content).await.unwrap();
JAVA_VM.with(|cell| {
let java_vm = cell.borrow();
let java_vm = java_vm.as_ref().unwrap();
let mut env = java_vm.attach_current_thread().unwrap();
env.call_method(
future,
"complete",
"(Ljava/lang/Object;)Z",
&[JValue::Bool(1)],
)
.unwrap();
});
};
runtime.spawn(x);
} If the community approves this solution, I'd love to add asynchronous API to Java binding. |
Thanks @ShadowySpirits , looking forward for this feature. Regard to the approach, I think we need other design to manage thread lifecycle in Rust side, instead of creating it each time when calling native method. One approach is we can use the
We can create
Same as JVM guaranteed dynamic lib will only be loaded once:
|
Thanks @kidylee ! Great idea, I will look into this approach. |
After a quick review on the patch, as long as we use
|
This limitation is enough for most scenarios. But we do need to eliminate this risk. One solution could be to provide users with the option to either throw an exception or initiate a blocking operation. |
If each call obtains one global ref, I'm afraid that the iops is limited by 65535. |
IMO, this limitation means we can not hold over 65535 global refs at the same time. However, this limit is not a significant concern since the lifetime of each global reference ends with the completion of its respective operation. Therefore, our theoretical IOPS can exceed 65535. And most blob service limit IOPS, 65535 is already a big number: https://www.alibabacloud.com/help/en/object-storage-service/latest/usage-notes |
We don't need to address all possible problems right now. We can document this limitation and fix it in the future. |
I agree with @ShadowySpirits, the scenarios of OpenDAL are about file access, bandwidth and processor open file limitation are less than this global references threshold, and as long as we managed it well(no leaks and release immediately), it should be OK. |
No description provided.
The text was updated successfully, but these errors were encountered: