[libvirt] [PATCH] Java bindings for domain events
David Lively
dlively at virtualiron.com
Fri Nov 14 17:00:10 UTC 2008
On Wed, 2008-11-12 at 10:22 +0100, Daniel Veillard wrote:
> On Fri, Nov 07, 2008 at 03:46:37PM -0500, David Lively wrote:
> > It shouldn't be hard to make this thread-safe using Java synchronized
> > methods and statements, but I haven't done that yet. Should I??
>
> Well if we can, we probably should, yes. I found a bit of explanations
> at
> http://research.operationaldynamics.com/blogs/andrew/software/java-gnome/thread-safety-for-java.html
Right. Mostly it consists of declaring certain methods as
"synchronized", and wrapping other code in "synchronized" blocks.
The more I think about it, the more I'm convinced this should be done.
> if we can do that entierely in the java part of the bindings, then yes
> that looks a really good idea. We just need to make sure locking is at
> the connection granularity, not at the library level to not force
> applications monitoring a bunch of nodes to serialize all their access
> on a single lock.
I'll go ahead and do this with per-connection locks. I suppose this
means I need to make the various Domain/Network/StoragePool/StorageVol
methods synchronize on the connection as well .... so this will involve
a fair amount of churn in the Java code. I'll try to implement it over
the weekend and submit something by Monday.
> > +++ b/EventTest.java
> > @@ -0,0 +1,35 @@
> > +import org.libvirt.*;
> > +
> > +class TestDomainEventListener implements DomainEventListener {
> Can we move this under src/ ... along test.java ?
Sure.
>
> > +++ b/src/jni/org_libvirt_Connect.c
> > @@ -1,3 +1,4 @@
> > +#include <jni.h>
> [...]
> > +static JavaVM *jvm;
> > +
> > +jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) {
> > + jvm = vm;
> > + return JNI_VERSION_1_4;
> > +}
>
> Hum, what is this ?
This is a little hook that gets called when the libvirt JNI bindings are
loaded. I use it to grab the JavaVM pointer, which we'll need later in
domainEventCallback. domainEventCallback is called asynchronously from
the libvirt eventImpl. It's not a JNI call that gets a JNIEnv passed to
it, but it needs the JNIEnv in order to make the Java callbacks. We
need the JavaVM pointer so that we can get the JNIEnv via
(*jvm)->GetEnv() (see below).
> > +static int domainEventCallback(virConnectPtr conn, virDomainPtr dom,
> > + int event, void *opaque)
> > +{
> > + jobject conn_obj = (jobject)opaque;
> > + JNIEnv * env;
> > + jobject dom_obj;
> > + jclass dom_cls, conn_cls;
> > + jmethodID ctorId, methId;
> > +
> > + // Invoke conn.fireDomainEventCallbacks(dom, event)
> > +
> > + if ((*jvm)->GetEnv(jvm, (void **)&env, JNI_VERSION_1_4) != JNI_OK || env == NULL) {
> > + printf("error getting JNIEnv\n");
> > + return -1;
> > + }
> > +
> > + dom_cls = (*env)->FindClass(env, "org/libvirt/Domain");
> > + if (dom_cls == NULL) {
> > + printf("error finding class Domain\n");
> > + return -1;
> > + }
> > +
> > + ctorId = (*env)->GetMethodID(env, dom_cls, "<init>", "(Lorg/libvirt/Connect;J)V");
> > + if (ctorId == NULL) {
> > + printf("error finding Domain constructor\n");
> > + return -1;
> > + }
> > +
> > + dom_obj = (*env)->NewObject(env, dom_cls, ctorId, conn_obj, dom);
> > + if (dom_obj == NULL) {
> > + printf("error constructing Domain\n");
> > + return -1;
> > + }
> > +
> > + conn_cls = (*env)->FindClass(env, "org/libvirt/Connect");
> > + if (conn_cls == NULL) {
> > + printf("error finding class Connect\n");
> > + return -1;
> > + }
> > +
> > + methId = (*env)->GetMethodID(env, conn_cls, "fireDomainEventCallbacks", "(Lorg/libvirt/Domain;I)V");
> > + if (methId == NULL) {
> > + printf("error finding Connect.fireDomainEventCallbacks\n");
> > + return -1;
> > + }
> > +
> > + (*env)->CallVoidMethod(env, conn_obj, methId, dom_obj, event);
> > +
> > + return 0;
> > +}
> > +JNIEXPORT void JNICALL Java_org_libvirt_Connect_registerForDomainEvents
> > +(JNIEnv *env, jobject obj, jlong VCP){
> > + // TODO: Need to DeleteGlobalRef(obj) when deregistering for callbacks.
> > + // But then need to track global obj per Connect object.
>
> Hum, that's a bit nasty. Can we make sure we can plug the leaks
> without having to change the APIs, that would be a bummer...
Yeah. It's really not acceptable as is. The easiest solution (as you
hint) is changing the API so virConnectDomainEventDeregister returns the
void * registered with that callback. That would (of course) be my
preference. What do you think? That API hasn't been released quite
yet ...
> > diff --git a/src/org/libvirt/Connect.java b/src/org/libvirt/Connect.java
> > index 4271937..7ccaecd 100644
> > --- a/src/org/libvirt/Connect.java
> > +++ b/src/org/libvirt/Connect.java
> [...]
> > public class Connect {
> >
> > + static public EventImpl eventImpl;
> > +
> > // Load the native part
> > static {
> > System.loadLibrary("virt_jni");
> > _virInitialize();
> > + eventImpl = new EventImpl();
> > }
> >
>
> okay, that's the hook.
> Another possibility might be to use a new creation method for
> Connect.new() taking an extra EventImpl class implementation.
But remember EventImpl is global, not per-Connect.
> > diff --git a/src/org/libvirt/EventImpl.java b/src/org/libvirt/EventImpl.java
> > new file mode 100644
> > index 0000000..1868fe3
> > --- /dev/null
> > +++ b/src/org/libvirt/EventImpl.java
> > @@ -0,0 +1,31 @@
> > +package org.libvirt;
> > +
> > +import java.util.HashMap;
> > +
> > +// While EventImpl does implement Runnable, don't declare that until
> > +// the we add concurrency control for the libvirt Java bindings and
> > +// the EventImpl callbacks.
> > +
> > +final public class EventImpl {
>
> Hum, why final ?
Oops. No reason -- left over from some other experiment.
More information about the libvir-list
mailing list