On Fri, Sep 25, 2009 at 12:25:50PM +0100, Daniel P. Berrange wrote: > On Fri, Sep 25, 2009 at 12:09:34PM +0200, Daniel Veillard wrote: > > On Mon, Aug 24, 2009 at 09:51:04PM +0100, Daniel P. Berrange wrote: > > > @@ -1448,6 +1466,81 @@ void virEventRegisterImpl(virEventAddHandleFunc addHandle, > > > virEventAddTimeoutFunc addTimeout, > > > virEventUpdateTimeoutFunc updateTimeout, > > > virEventRemoveTimeoutFunc removeTimeout); > > > + > > > +enum { > > > + VIR_STREAM_NONBLOCK = (1 << 0), > > > +}; > > > + > > > +virStreamPtr virStreamNew(virConnectPtr conn, > > > + unsigned int flags); > > > > Would flags be sufficient if we were to encode some priorities to > > streams? If we end up limiting the number of active streams, giving > > higher priority or some reserved slots for quicker operations may > > be important, flags should be sufficient for this if the need arise > > so I don't see a problem but I raise the point. > > I guess it would be sufficient if you wanted todo LOW/MEDIUM/HIGH > static priorties. yeah, I just wanted to make sure it didn't contradict something you may have in mind :-) > If we wanted to get more advanced, we could always introduce an > extra API, since there is always a point between virStreamNew() > and then using it, eg in virConnectUploadFile(), when you can > do more setup calls. We already allow event callbacks to be > registered this way, so we could in future add a > > virStreamSetPriority(virStreamPtr st, ....options ...); right, > if we needed more than just plain flags. > > > On the subject of flags though, I've never been entirely sure > about whether it would be worth mandating the use of a flag(s) > for indicating I/O direction at time of stream creation. > Currently this is implicit base on the API that the stream is > later used with, > > eg, currently > > virStreamPtr st = virStreamNew(conn, 0); > > virConnectUploadFile(conn, st, filename); > > implicitly configures the stream for writing, but I constantly > wonder whether we ought to make it explicit via a flag like > > virStreamPtr st = virStreamNew(conn, VIR_STREAM_WRITE); > > virConnectUploadFile(conn, st, filename); > > and require that the call of virStreamNew() provide either > VIR_STREAM_WRITE, or VIR_STREAM_READ, or both. ANd then also > have the methods using streams like virConnectUploadFile > check that the flags match. Hum, this would then raise the signal that stream can be used both ways, do we really want to suggest this at the API level, I can see how we're gonna use this internally, but aren't we opening the door to much complexity ? > If we wanted to mandate use of READ/WRITE flags for stream > creation, we'd obviously need todo it from teh start, since > we couldn't add that as a mandatory flag once the API is > released & in use by apps. yes that's a good point, a design issue too. If you really expect API usage for both read and write, then I would say we should make those flags mandatory. The only point is that our existing flags use in APIs are just fine with 0 as being the 'default', and we would break this but it's not a big deal IMHO, that will be caught immediately. > > > > > +int virStreamRef(virStreamPtr st); > > > + > > > +int virStreamSend(virStreamPtr st, > > > + const char *data, > > > + size_t nbytes); > > > + > > > +int virStreamRecv(virStreamPtr st, > > > + char *data, > > > + size_t nbytes); > > > + > > > + > > > +typedef int (*virStreamSourceFunc)(virStreamPtr st, > > > + char *data, > > > + size_t nbytes, > > > + void *opaque); > > > > I think the signature is fine but we need to document all the > > arguments and the return values. > > > > > +int virStreamSendAll(virStreamPtr st, > > > + virStreamSourceFunc handler, > > > + void *opaque); > > > > Hum, I had to look at the comment to really understand. I'm not > > 100% sure, maybe we should allow for handler() to be called multiple > > time, not just once. > > For example I would allow virStreamSendAll() code to call handler > > multiple time, until the handler like a read() returns 0 (or -1 on > > error), in any case the signature should be documented fully. > > This method is essentially just a convenient way to call the > virStreamSend() in a loop, for apps that are happy todo blocking > data I/O. As such the handler() will definitely be called multiple > times to fetch the data to be sent. You can see how it was used > later on in this patch, where virStreamSendAll is implemented. > > I expect most apps would use virStreamSend() though, to allow > them todo interruptible & non-blocking I/O Okay, but let's describe all args and return values for the callbacks. > > > +typedef int (*virStreamSinkFunc)(virStreamPtr st, > > > + const char *data, > > > + size_t nbytes, > > > + void *opaque); > > > > Same thing do we allow a sink function to be called repeatedly ? > > If we want to allow this in some ways we will need an extra argument > > to indicate the end of the stream. Even if we don't plan this yet, I > > would suggest to add a flags to allow for this possibility in the > > future. With a chunk size of 256K at the protocol level it may not > > be a good idea to keep the full data in memory, so I would allow > > for this interface to call the sink multiple times. And IMHO it's best > > to pass the indication of end of transfer directly at the sink level > > rather than wait for the virStreamFree() coming from the user. > > > > > +int virStreamRecvAll(virStreamPtr st, > > > + virStreamSinkFunc handler, > > > + void *opaque); > > > > Okay > > Same as for SendAll, this API will invoke the handler multilpe > times to write out data that is being received. In both cases > the implementation is invoking the handler with 64kb buffers > to avoid pulling lots of data into memory. Okay, but I think being able to indicate there that a packet is the last one may be important, for example if the application design prefer to initiate the closure of the transfer (close/sync/...) as soon as possible. Also how flexible are we in the design with callbacks taling a long time to complete, for example reads crossing the network, or slow output devices ? Maybe this should be hinted in the callback descriptions. [...] > > > +int virStreamFinish(virStreamPtr st); > > > +int virStreamAbort(virStreamPtr st); > > > > For those 2 maybe add a flag, to allow for example background disconnection. > > Even if the stream wasn't created ASYNC, we may want sometime to > > abruptly end without waiting. > > The virStreamAbort() operation is always asynchronous, regardless of > whether the stream is non-blocking. The virStreamFinish() operation > has to be synchronous, because for it to be useful you need to do > a round-trip to flush any error being sent back from the server. So > I don't think we need any flags here. If you want to close it abruptly > then virStreamAbort() is suitable, if you want to close it cleanly > then virStreamFinish() is suitable. hum, okay ... [...] > > > + * virConnectUploadFile(conn, st); > > > + * virStreamSendAll(st, mysource, &fd); > > > + * virStreamFree(st); > > > + * close(fd); > > > > > > Hum, the clasic example of blocking I/Os is if people use multithreaded > > apps. > > What is a second thread calls calls virStreamAbort() while > > virStreamSendAll() is in progress, e.g. the user clicked on an abort > > button from the UI ? > > Same question for virStreamRecvAll() ? > > If you want to abort a stream part way through, then you should not > be using virStreamSendAll/RecvAll. These APIs are optional convenience > APIs for apps which are happy todo a blocking operation. Apps which > need ability to abort part way through can use the virStreamRecv() > and virStreamSend() APIs instead. Callback programming is hard, people get very easilly confused by them and handling of errors can turn really nasty if you use them a lot. So I think it's important to have foolproof synchronous transfers, even if you think they may be a bit abused, it's really easier on the programmer. > That all said, it is safe for another thread to call virStreamAbort(), > it will cause this Send/RecvAll method to return an error on the next > virStreamSend/Recv call it makes good :-) > > > > > + * Returns 0 if all the data was succesfully sent. The stream > > > + * will be marked as finished on success, so the caller need > > > + * only call virStreamFree(). > > > + * > > > + * Returns -1 upon any error, with the stream being marked as > > > + * aborted, so the caller need only call virStreamFree() > > > + */ > > > +int virStreamSendAll(virStreamPtr stream, > > > + virStreamSourceFunc handler, > > > + void *opaque) > > > > > > > + * virConnectUploadFile(conn, st); > > > + * virStreamRecvAll(st, mysink, &fd); > > > + * virStreamFree(st); > > > + * close(fd); > > > > Would the current API allow for the sink callback to close the fd() > > at the end of the transfer ? Right now, I don't think so because we > > don't know what is the last callback (assuming multiple ones). > > The sink/source callbacks do not need to close the FD since this is easily > done with RecvAll/SendAll returns control to the application. THis is > in fact important, because it is not until RecvAll/SendAll returns that > you can call virStreamFinish to check for success. If it did not suceed > then you may want do other cleanup before closing the FD, such as > unlinking the file Hum, the two example for RecvAll and SendAll don't suggest virStreamFinish() to be called to get the status, I would expect error reporting to show up as the result code from RecvAll and SendAll. I still think the callbacks behaviour should be clarified especially behaviour and reporting in case of error. thanks, Daniel -- Daniel Veillard | libxml Gnome XML XSLT toolkit http://xmlsoft.org/ daniel@xxxxxxxxxxxx | Rpmfind RPM search engine http://rpmfind.net/ http://veillard.com/ | virtualization library http://libvirt.org/ -- Libvir-list mailing list Libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list