The current implementation of global references in the form of GlobalRef
/ DetachedGlobalRef
mistakenly assumes that the reference should be attached to the thread. In fact, this is not the case, the same reference can be concurrently used in different threads at the same time.
In addition, the need to destroy an instance of GlobalRef
or DetachedGlobalRef
when attaching / detaching is not ergonomic, since it complicates the storage of the reference as part of the other structures.
On the other hand, the task to protect from a race condition by the type system of Rust goes beyond the scope of the functionality of GlobalRef
, and ultimately is not solved, due to the possibility of creating an additional global reference to the same data (and, it seems, not can be guaranteed to be solved).
Therefore, the functionality of the references and the attachments to threads should be divided into two independent structures that might roughly look like this:
#[derive(Clone)]
struct GlobalRef {
internal: Arc<GlobalRefGuard>,
}
struct GlobalRefGuard {
vm: JavaVM,
obj: JObject<'static>,
}
#[derive(Clone)]
struct JNIEnv {
env: Rc<JNIEnvGuard>,
}
GlobalRef
in such an implementation will allow to re-use the global reference, including between threads, and also automatically release it, as soon as the need for it gone. In addition, reusing an existing global reference, instead of creating a new one each time (by calling NewGlobalRef
), allows speeding up in some cases, but this speedup can be neglected if dozens of other calls to JNI or heavy computing operations occurs on each copy of the reference.
The structure JNIEnv
with the use of a similar pattern would be able to transparently hold the attached JVM thread, while we need it somewhere in the native thread, and detach as soon as the need for it gone (or, knowing that we will need it for a long time, we could keep it without detaching, just saving an additional copy of JNIEnv
). But this is beyond the scope of this issue and requires more detailed discussion and changes in JavaVM
.
A bit later I will do PR with a new implementation of GlobalRef
. But, since this is a breaking change, there may be open questions that should be discussed.
For example, should the current implementation of GlobalRef
+ DetachedGlobalRef
be deprecated and then discarded or GlobalRef
should remain as it is, but renamed to AttachedGlobalRef
, because someone heavily depends on it?