[libvirt] [PATCH 2/2] Java bindings for domain events

2008-11-18 Thread David Lively
This patch allows the remote driver to work with an asynchronous
EventImpl (it's the only one using an externally-supplied one), assuming
libvirt is compiled with pthread support.  (Without pthreads, this code
is harmless in a single-threaded environment.)

Basically it uses a mutex to protect reads from the RPC socket in such a
way that message reads (in their entirety) are done atomically
(otherwise the remoteDomainEventFired() can race the call() code that
reads replies  events).

In addition, I update the EventImpl handle to prevent
remoteDomainEventFired() from being called everytime a reply is sent.
(This helps us dispatch events in a timely manner, though it's not
strictly necessary.  Without it, any events coming in during a call()
won't be dispatched until the call drops the socket lock (because
remoteDomainEventFired() will be stuck awaiting the lock).

Dave

diff --git a/src/remote_internal.c b/src/remote_internal.c
index 2ca7930..59128f6 100644
--- a/src/remote_internal.c
+++ b/src/remote_internal.c
@@ -116,6 +116,7 @@ struct private_data {
 virDomainEventQueuePtr domainEvents;
 /* Timer for flushing domainEvents queue */
 int eventFlushTimer;
+PTHREAD_MUTEX_T(lock);  /* Serializes socket reads w/async EventImpl */
 };
 
 #define GET_PRIVATE(conn,retcode)   \
@@ -700,6 +701,9 @@ doRemoteOpen (virConnectPtr conn,
 } /* switch (transport) */
 
 
+/* This must precede the first call() */
+priv-eventFlushTimer = -1;
+
 /* Try and authenticate with server */
 if (remoteAuthenticate(conn, priv, 1, auth, authtype) == -1)
 goto failed;
@@ -744,6 +748,8 @@ doRemoteOpen (virConnectPtr conn,
 }
 }
 
+pthread_mutex_init(priv-lock, NULL);
+
 if(VIR_ALLOC(priv-callbackList)0) {
 error(conn, VIR_ERR_INVALID_ARG, _(Error allocating callbacks list));
 goto failed;
@@ -1250,6 +1256,8 @@ doRemoteClose (virConnectPtr conn, struct private_data *priv)
 /* Free queued events */
 virDomainEventQueueFree(priv-domainEvents);
 
+pthread_mutex_destroy(priv-lock);
+
 return 0;
 }
 
@@ -4536,11 +4544,11 @@ static int really_read (virConnectPtr conn, struct private_data *priv,
  * else Bad Things will happen in the XDR code.
  */
 static int
-call (virConnectPtr conn, struct private_data *priv,
-  int flags /* if we are in virConnectOpen */,
-  int proc_nr,
-  xdrproc_t args_filter, char *args,
-  xdrproc_t ret_filter, char *ret)
+really_call (virConnectPtr conn, struct private_data *priv,
+ int flags /* if we are in virConnectOpen */,
+ int proc_nr,
+ xdrproc_t args_filter, char *args,
+ xdrproc_t ret_filter, char *ret)
 {
 char buffer[REMOTE_MESSAGE_MAX];
 char buffer2[4];
@@ -4596,16 +4604,18 @@ call (virConnectPtr conn, struct private_data *priv,
 really_write (conn, priv, flags  REMOTE_CALL_IN_OPEN, buffer, len-4) == -1)
 return -1;
 
+pthread_mutex_lock(priv-lock);
+
 retry_read:
 /* Read and deserialise length word. */
 if (really_read (conn, priv, flags  REMOTE_CALL_IN_OPEN, buffer2, sizeof buffer2) == -1)
-return -1;
+goto unlock_return_err;
 
 xdrmem_create (xdr, buffer2, sizeof buffer2, XDR_DECODE);
 if (!xdr_int (xdr, len)) {
 error (flags  REMOTE_CALL_IN_OPEN ? NULL : conn,
VIR_ERR_RPC, _(xdr_int (length word, reply)));
-return -1;
+goto unlock_return_err;
 }
 xdr_destroy (xdr);
 
@@ -4615,12 +4625,14 @@ retry_read:
 if (len  0 || len  REMOTE_MESSAGE_MAX) {
 error (flags  REMOTE_CALL_IN_OPEN ? NULL : conn,
VIR_ERR_RPC, _(packet received from server too large));
-return -1;
+goto unlock_return_err;
 }
 
 /* Read reply header and what follows (either a ret or an error). */
 if (really_read (conn, priv, flags  REMOTE_CALL_IN_OPEN, buffer, len) == -1)
-return -1;
+goto unlock_return_err;
+
+pthread_mutex_unlock(priv-lock);
 
 /* Deserialise reply header. */
 xdrmem_create (xdr, buffer, len, XDR_DECODE);
@@ -4729,8 +4741,33 @@ retry_read:
 xdr_destroy (xdr);
 return -1;
 }
+
+ unlock_return_err:
+pthread_mutex_unlock(priv-lock);
+return -1;
+}
+
+static int call (virConnectPtr conn, struct private_data *priv,
+ int flags /* if we are in virConnectOpen */,
+ int proc_nr,
+ xdrproc_t args_filter, char *args,
+ xdrproc_t ret_filter, char *ret)
+{
+int rv;
+if (priv-eventFlushTimer = 0)
+virEventUpdateHandle(priv-sock, 0);
+rv = really_call(conn, priv, flags, proc_nr,
+ args_filter, args,
+ ret_filter, ret);
+if (priv-eventFlushTimer = 0)
+virEventUpdateHandle(priv-sock,
+ VIR_EVENT_HANDLE_READABLE |
+ 

Re: [libvirt] [PATCH 2/2] Java bindings for domain events

2008-11-18 Thread Daniel P. Berrange
On Tue, Nov 18, 2008 at 11:18:38AM -0500, David Lively wrote:
 This patch allows the remote driver to work with an asynchronous
 EventImpl (it's the only one using an externally-supplied one), assuming
 libvirt is compiled with pthread support.  (Without pthreads, this code
 is harmless in a single-threaded environment.)

I don't like this patch, since it is only making one tiny part of the
API thread-safe, for one tiny use case. eg, if someone uses events from
the Xen driver in Java with threads, they'll crash  burn, since only
the remote driver is being protected.

IMHO, if we want to allow multi-threaded access to a single virConnectPtr
object then we need to do the full job and make the whole thing safe,
including adding thread-local error objects, to replace the inherantly
non-thread safe  global  per-virConnect error objects.

The other alternative that we've discussed in the past is to add the
ability to 'clone' an existing virConnectPtr object. The idea being
that a 2nd thread could be given a clone, and safely use that. Internally
we'd share  synchronize on relevant state information, so we didn't
actually need to setup a separate network connection  re-auth for each.

As it stands, this patch adding semi-thread safe access will make it
harder for us to pursue this 2nd option of cloning virConnect, without
breaking the Java bindings in future. 

 Basically it uses a mutex to protect reads from the RPC socket in such a
 way that message reads (in their entirety) are done atomically
 (otherwise the remoteDomainEventFired() can race the call() code that
 reads replies  events).

Why doesn't the Java code simply synchronize on the virConnect object
before invoking the FD event callbacks. That will ensure another
thread has finished whatever API call it was doing

 In addition, I update the EventImpl handle to prevent
 remoteDomainEventFired() from being called everytime a reply is sent.
 (This helps us dispatch events in a timely manner, though it's not
 strictly necessary.  Without it, any events coming in during a call()
 won't be dispatched until the call drops the socket lock (because
 remoteDomainEventFired() will be stuck awaiting the lock).

I don't really like the idea of applying any of this patch, since I think
it'll cause us unexpected complications when doing proper thread support
in the next release of libvirt, and risk us breaking the java bindings.

Daniel
-- 
|: Red Hat, Engineering, London   -o-   http://people.redhat.com/berrange/ :|
|: http://libvirt.org  -o-  http://virt-manager.org  -o-  http://ovirt.org :|
|: http://autobuild.org   -o- http://search.cpan.org/~danberr/ :|
|: GnuPG: 7D3B9505  -o-  F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

--
Libvir-list mailing list
Libvir-list@redhat.com
https://www.redhat.com/mailman/listinfo/libvir-list


Re: [libvirt] [PATCH 2/2] Java bindings for domain events

2008-11-18 Thread David Lively
On Tue, 2008-11-18 at 17:05 +, Daniel P. Berrange wrote:
 On Tue, Nov 18, 2008 at 11:18:38AM -0500, David Lively wrote:
  This patch allows the remote driver to work with an asynchronous
  EventImpl (it's the only one using an externally-supplied one), assuming
  libvirt is compiled with pthread support.  (Without pthreads, this code
  is harmless in a single-threaded environment.)
 
 I don't like this patch, since it is only making one tiny part of the
 API thread-safe, for one tiny use case. eg, if someone uses events from
 the Xen driver in Java with threads, they'll crash  burn, since only
 the remote driver is being protected.

Currently ONLY the remote driver (and yes, soon - the xen driver, which
would also need thread-safety changes) use an EventImpl supplied
externally.  All others use the libvirtd-provided synchronous impl.


 Why doesn't the Java code simply synchronize on the virConnect object
 before invoking the FD event callbacks. That will ensure another
 thread has finished whatever API call it was doing

Note that the Java code uses (Java's builtin) Connect-level lock to
avoid concurrent calls using the same virConnect.  This even applies to
domain event delivery - note that Connect.fireDomainEventCallbacks is
(in the Java patch) a synchronized method.  A FD event callback isn't
associated with a particular virConnect object, and EventImpls aren't
virConnect-specific, so I can't lock the virConnect (see below).

When we exposed virEventRegisterImpl, we said that externally-supplied
event impls must not make callbacks when a virConnect is being used.  If
the Java EventImpl were following this rule, we wouldn't need this
patch.  BUT because the EventImpl can't know which virConnect (if any)
is involved in a particular callback, the only way to satisfy this rule
is to lock ALL Connect objects before making an EventImpl callback. 

This is (IMO) both way too restrictive, and (I'm a little less sure of
this) not restrictive enough.  (I suspect we'd find that making two
callbacks concurrently can break things, even if no virConnects are in
use at the time.)

I think we have to allow externally supplied EventImpls to make
callbacks without regard to which virConnect, etc. objects are in use,
since EventImpls don't know anything about virConnect, etc.  This
doesn't require all of libvirt to be made thread-safe.

So I view this fix (and the one necessary for the xen driver once it
starts using an externally-supplied EventImpl) as providing just enough
concurrency control to allow an asynchronous EventImpl, while still
making the libvirt user (the Java bindings, in my case) responsible for
avoiding concurrent virConnect usage.




--
Libvir-list mailing list
Libvir-list@redhat.com
https://www.redhat.com/mailman/listinfo/libvir-list