Re: Some comments about GVFS

2007-05-10 Thread Andy Wingo
Hi Alex,

On Thu, 2007-05-10 at 09:41 +0200, Alexander Larsson wrote:
> So, if you were to implement gstreamer sinks and sources for gvfs, an
> api like:
> gssize   g_input_stream_read (GInputStream  *stream,
> void  *buffer,
> gsize  count,
> GCancellable *cancellable,
> GError   **error);
> 
> Would be ok with you? Its a blocking call, but if you pass in a
> GCancellable you can use that to cancel the call from another thread.

I pinged this off a couple of other folks and we all think it's OK. Make
sure the read never happens if the cancellable was cancelled before the
read began, of course.

> And you wouldn't be interesting in async (mainloop-using) i/o operations
> at all?

I do not see GStreamer taking advantage of this, no.

Cheers,

Andy.
-- 
http://wingolog.org/
___
gtk-devel-list mailing list
gtk-devel-list@gnome.org
http://mail.gnome.org/mailman/listinfo/gtk-devel-list


Re: Some comments about GVFS

2007-05-10 Thread Alexander Larsson
On Wed, 2007-05-09 at 23:58 +0200, Andy Wingo wrote:
> Hello,
> 
> On Tue, 2007-05-08 at 16:59 +0200, Alexander Larsson wrote:
> > I'm still not sure why gstreamer needs to run its own mainloop in its
> > thread though. If its async, then it should be able to use the default
> > mainloop. Is it because there is no guarantee of the default mainloop
> > running always?
> 
> I am not certain how the mainloop got involved here. GStreamer uses
> threads so that it can use blocking read() and write() calls.
> 
> Underneath, it's true they go through select() first on the fd + a
> control fd from a socketpair, because you need to be able to cancel a
> wait from another thread, so that you can seek, for example. Perhaps
> that is the concern?
> 
> In any case, as long as one is able to cancel an in-progress sync
> operation from another thread, GStreamer is fine, AFAIK.

So, if you were to implement gstreamer sinks and sources for gvfs, an
api like:
gssize   g_input_stream_read (GInputStream  *stream,
  void  *buffer,
  gsize  count,
  GCancellable *cancellable,
  GError   **error);

Would be ok with you? Its a blocking call, but if you pass in a
GCancellable you can use that to cancel the call from another thread.

And you wouldn't be interesting in async (mainloop-using) i/o operations
at all?

=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
 Alexander LarssonRed Hat, Inc 
   [EMAIL PROTECTED][EMAIL PROTECTED] 
He's an impetuous small-town cat burglar on the wrong side of the law. She's a 
vivacious mutant fairy princess with a knack for trouble. They fight crime! 

___
gtk-devel-list mailing list
gtk-devel-list@gnome.org
http://mail.gnome.org/mailman/listinfo/gtk-devel-list


Re: Some comments about GVFS

2007-05-09 Thread Andy Wingo
Hello,

On Tue, 2007-05-08 at 16:59 +0200, Alexander Larsson wrote:
> I'm still not sure why gstreamer needs to run its own mainloop in its
> thread though. If its async, then it should be able to use the default
> mainloop. Is it because there is no guarantee of the default mainloop
> running always?

I am not certain how the mainloop got involved here. GStreamer uses
threads so that it can use blocking read() and write() calls.

Underneath, it's true they go through select() first on the fd + a
control fd from a socketpair, because you need to be able to cancel a
wait from another thread, so that you can seek, for example. Perhaps
that is the concern?

In any case, as long as one is able to cancel an in-progress sync
operation from another thread, GStreamer is fine, AFAIK.

Regards,

Andy.
-- 
http://wingolog.org/
___
gtk-devel-list mailing list
gtk-devel-list@gnome.org
http://mail.gnome.org/mailman/listinfo/gtk-devel-list


Re: Some comments about GVFS

2007-05-08 Thread Alexander Larsson
On Tue, 2007-05-08 at 11:19 +0200, Benjamin Otte wrote:
> (short reply here, so you can continue your interesting discussion,
> I'll answer in more detail when I'm back from UDS)
> 
> On 5/8/07, Alexander Larsson <[EMAIL PROTECTED]> wrote:
> > On Tue, 2007-05-08 at 03:33 -0500, Hans Petter Jansson wrote:
> >
> > I know Benjamin mentioned multiple contexts was needed for gstreamer
> > integration. It would be nice to hear some details about this
> >
> GStreamer runs the pipeline in seperate threads. While reads are
> typically scheduled from just one thread, any thread could potentially
> initiate actions on the stream. An example would be seeking: seek
> events are dispatched from the thread doing the seek - this could be
> the main thread for a user event or any other thread if a downstream
> element needs to seek. So the event would cancel the read operation
> and do the seek. This would wake up the read thread noticing it was
> interrupted and reschedule the read.

So, you would have one of gstreamers threads doing the read operation,
and this would be using async i/o and a custom glib mainloop on that
thread. Then say the main thread on user input decides to seek in the
stream, this seek call on the main thread will cancel the async read
operation running in the gstreamer thread and schedule a new seek
operation there instead. Is this a correct example?

I'm still not sure why gstreamer needs to run its own mainloop in its
thread though. If its async, then it should be able to use the default
mainloop. Is it because there is no guarantee of the default mainloop
running always?

=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
 Alexander LarssonRed Hat, Inc 
   [EMAIL PROTECTED][EMAIL PROTECTED] 
He's a short-sighted guitar-strumming assassin in drag. She's a foxy insomniac 
detective with a knack for trouble. They fight crime! 

___
gtk-devel-list mailing list
gtk-devel-list@gnome.org
http://mail.gnome.org/mailman/listinfo/gtk-devel-list


Re: Some comments about GVFS

2007-05-08 Thread Benjamin Otte
(short reply here, so you can continue your interesting discussion,
I'll answer in more detail when I'm back from UDS)

On 5/8/07, Alexander Larsson <[EMAIL PROTECTED]> wrote:
> On Tue, 2007-05-08 at 03:33 -0500, Hans Petter Jansson wrote:
>
> I know Benjamin mentioned multiple contexts was needed for gstreamer
> integration. It would be nice to hear some details about this
>
GStreamer runs the pipeline in seperate threads. While reads are
typically scheduled from just one thread, any thread could potentially
initiate actions on the stream. An example would be seeking: seek
events are dispatched from the thread doing the seek - this could be
the main thread for a user event or any other thread if a downstream
element needs to seek. So the event would cancel the read operation
and do the seek. This would wake up the read thread noticing it was
interrupted and reschedule the read.

Benjamin
___
gtk-devel-list mailing list
gtk-devel-list@gnome.org
http://mail.gnome.org/mailman/listinfo/gtk-devel-list


Re: Some comments about GVFS

2007-05-08 Thread Hans Petter Jansson
On Tue, 2007-05-08 at 10:57 +0200, Alexander Larsson wrote:
> On Tue, 2007-05-08 at 03:33 -0500, Hans Petter Jansson wrote:

> > I'm aware of the dangers - when I worked on Evolution, this caused
> > plenty of pain.
> > 
> > However, it's useful in very simple apps where you have e.g. a "Sync" or
> > "Connect" button which desensitizes most of the UI while the operation
> > is in progress, only updating a progress bar or allowing the user to hit
> > "Cancel". That's what I meant by a "program that lends itself to that
> > design" above :)
> > 
> > So I dunno. Maybe it's too seductive and should be eliminated as an
> > option.

> Avoiding this problem is one of the primary reasons of going from a
> stateless model (where you can get auth callbacks anytime) to a
> statefull model (auth only on mount). However, that problem in gnome-vfs
> happens for *all* apps, not only those that "lend itself to that
> design", so its a larger problem there.

Agree.

-- 
Hans Petter

___
gtk-devel-list mailing list
gtk-devel-list@gnome.org
http://mail.gnome.org/mailman/listinfo/gtk-devel-list


Re: Some comments about GVFS

2007-05-08 Thread Alexander Larsson
On Tue, 2007-05-08 at 03:33 -0500, Hans Petter Jansson wrote:

> I'm used to three main design patterns:
> 
> 1) Everything in one thread with sync I/O blocking the UI. Simple code
> but unacceptable performance.
> 
> 2) Everything in one thread with async I/O using callbacks. Fine for
> simple protocols or if you prefer an explicit state machine.
> 
> 3) Doing synchronous I/O operations in one or more subthreads that talk
> to the main thread using asynchronous messages queues. This is nice for
> complex protocol implementations, because synchronous code is easier to
> understand and maintain.

These are the main model i see too. But none of these need any main
context but the default mainloop one...

I know Benjamin mentioned multiple contexts was needed for gstreamer
integration. It would be nice to hear some details about this


> I'm aware of the dangers - when I worked on Evolution, this caused
> plenty of pain.
> 
> However, it's useful in very simple apps where you have e.g. a "Sync" or
> "Connect" button which desensitizes most of the UI while the operation
> is in progress, only updating a progress bar or allowing the user to hit
> "Cancel". That's what I meant by a "program that lends itself to that
> design" above :)
> 
> So I dunno. Maybe it's too seductive and should be eliminated as an
> option.

Avoiding this problem is one of the primary reasons of going from a
stateless model (where you can get auth callbacks anytime) to a
statefull model (auth only on mount). However, that problem in gnome-vfs
happens for *all* apps, not only those that "lend itself to that
design", so its a larger problem there.

=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
 Alexander LarssonRed Hat, Inc 
   [EMAIL PROTECTED][EMAIL PROTECTED] 
He's an ungodly native American paramedic on the hunt for the last specimen of 
a great and near-mythical creature. She's a wealthy mutant opera singer from 
the wrong side of the tracks. They fight crime! 

___
gtk-devel-list mailing list
gtk-devel-list@gnome.org
http://mail.gnome.org/mailman/listinfo/gtk-devel-list


Re: Some comments about GVFS

2007-05-08 Thread Hans Petter Jansson
On Tue, 2007-05-08 at 09:30 +0200, Alexander Larsson wrote:
> On Mon, 2007-05-07 at 14:08 -0500, Hans Petter Jansson wrote:

> > In Flow, I keep a GMainContext associated with each GThread. The API
> > user can set his own GMainContext for each thread, but for simplicity it
> > can only be set once, before any other Flow calls are made in that
> > thread.

> So, an async operation inherits the main context from the thread that
> started it. Its nice that you don't have to fill up the API with context
> arguments, but it also seems to make it a bit more complicated to handle
> multiple contexts. For instance, you can't have one thread starting
> async operations that run on multiple context. But perhaps this is not
> so important? What do users of multiple contexts need?

Being able to provide a different main context on each call is extremely
flexible, but in the end I doubt it is necessary in the majority of
apps, and as you pointed out, it clutters the code.

The main reason I have the main-context-per-thread is that a Flow
pipeline won't run without a main context - for instance, elements can
limit flow rate by installing a timeout and resuming when it fires, or
otherwise generate timed events. And applications will definitely want
to run pipelines in threads.

I'm used to three main design patterns:

1) Everything in one thread with sync I/O blocking the UI. Simple code
but unacceptable performance.

2) Everything in one thread with async I/O using callbacks. Fine for
simple protocols or if you prefer an explicit state machine.

3) Doing synchronous I/O operations in one or more subthreads that talk
to the main thread using asynchronous messages queues. This is nice for
complex protocol implementations, because synchronous code is easier to
understand and maintain.

All of the above can be done with I/O dispatched in a single
GMainContext per thread. 1) is a special case where you could have one
GMainContext for the UI and another for the I/O.

That said, it'd be interesting to know if anyone runs multiple main
contexts in a single thread and needs to dispatch I/O events in more
than one of those. It sounds rather contrived to me...

> > I find that this allows for a lot of flexibility without adding much
> > code complexity at all:
> > 
> > 1) The default GMainContext for the main thread is the default GLib
> > GMainContext. This allows sync operations to spin the main loop (so if
> > you have a program that lends itself to that design, you can process
> > redraws and UI operations while in a sync I/O call).

> Danger will robinson! This sounds like a recipe for disaster. Sync calls
> calling back into the mainloop can easily cause reentrancy, and we're
> back in bonobo hell again. I.E. call any flow function, and you could
> reenter (via a mainloop callback) into any random code in the app which
> could modify the state of the code that was calling flow. (And if that
> state is in a partially changed state things can go very bad.)
> 
> Its also very problematic wrt locking. We have the same problem with
> authentication callbacks in gnome-vfs. These are called back from any
> sync operation when authentication is needed, and often requires UI to
> finish, and thus a mainloop. However, since it is doing UI it needs to
> take the gdk lock, but that deadlocks if the gdk lock was taken when the
> sync call was called. In the case of flow, what if you get an idle
> callback inside a sync call, and the idle callback (correctly) takes the
> gdk lock...

I'm aware of the dangers - when I worked on Evolution, this caused
plenty of pain.

However, it's useful in very simple apps where you have e.g. a "Sync" or
"Connect" button which desensitizes most of the UI while the operation
is in progress, only updating a progress bar or allowing the user to hit
"Cancel". That's what I meant by a "program that lends itself to that
design" above :)

So I dunno. Maybe it's too seductive and should be eliminated as an
option.

-- 
Hans Petter

___
gtk-devel-list mailing list
gtk-devel-list@gnome.org
http://mail.gnome.org/mailman/listinfo/gtk-devel-list


Re: Some comments about GVFS

2007-05-08 Thread Alexander Larsson
On Mon, 2007-05-07 at 14:08 -0500, Hans Petter Jansson wrote:
> On Mon, 2007-05-07 at 09:24 +0200, Alexander Larsson wrote:
> > On Fri, 2007-05-04 at 17:52 +0200, Benjamin Otte wrote:
> 
> > > Yeah, I missed those implementations as I was only grepping for
> > > GMainContext which you don't use.
> > > I think it's a good idea to make the main context customizable so gvfs
> > > can be used from other threads.
> 
> > The api used to have this, but there was a lot of overhead code carrying
> > this around and adding it to the parameter list of all functions, and I
> > thought there would be very very few people using it, so i killed it.
> > Maybe we should discuss reverting that decision...
> 
> In Flow, I keep a GMainContext associated with each GThread. The API
> user can set his own GMainContext for each thread, but for simplicity it
> can only be set once, before any other Flow calls are made in that
> thread.

So, an async operation inherits the main context from the thread that
started it. Its nice that you don't have to fill up the API with context
arguments, but it also seems to make it a bit more complicated to handle
multiple contexts. For instance, you can't have one thread starting
async operations that run on multiple context. But perhaps this is not
so important? What do users of multiple contexts need?

> I find that this allows for a lot of flexibility without adding much
> code complexity at all:
> 
> 1) The default GMainContext for the main thread is the default GLib
> GMainContext. This allows sync operations to spin the main loop (so if
> you have a program that lends itself to that design, you can process
> redraws and UI operations while in a sync I/O call).

Danger will robinson! This sounds like a recipe for disaster. Sync calls
calling back into the mainloop can easily cause reentrancy, and we're
back in bonobo hell again. I.E. call any flow function, and you could
reenter (via a mainloop callback) into any random code in the app which
could modify the state of the code that was calling flow. (And if that
state is in a partially changed state things can go very bad.)

Its also very problematic wrt locking. We have the same problem with
authentication callbacks in gnome-vfs. These are called back from any
sync operation when authentication is needed, and often requires UI to
finish, and thus a mainloop. However, since it is doing UI it needs to
take the gdk lock, but that deadlocks if the gdk lock was taken when the
sync call was called. In the case of flow, what if you get an idle
callback inside a sync call, and the idle callback (correctly) takes the
gdk lock...

=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
 Alexander LarssonRed Hat, Inc 
   [EMAIL PROTECTED][EMAIL PROTECTED] 
He's a fiendish skateboarding cyborg She's a mistrustful motormouth angel 
living on borrowed time. They fight crime! 

___
gtk-devel-list mailing list
gtk-devel-list@gnome.org
http://mail.gnome.org/mailman/listinfo/gtk-devel-list


Re: Some comments about GVFS

2007-05-07 Thread Hans Petter Jansson
On Mon, 2007-05-07 at 09:24 +0200, Alexander Larsson wrote:
> On Fri, 2007-05-04 at 17:52 +0200, Benjamin Otte wrote:

> > Yeah, I missed those implementations as I was only grepping for
> > GMainContext which you don't use.
> > I think it's a good idea to make the main context customizable so gvfs
> > can be used from other threads.

> The api used to have this, but there was a lot of overhead code carrying
> this around and adding it to the parameter list of all functions, and I
> thought there would be very very few people using it, so i killed it.
> Maybe we should discuss reverting that decision...

In Flow, I keep a GMainContext associated with each GThread. The API
user can set his own GMainContext for each thread, but for simplicity it
can only be set once, before any other Flow calls are made in that
thread.

I find that this allows for a lot of flexibility without adding much
code complexity at all:

1) The default GMainContext for the main thread is the default GLib
GMainContext. This allows sync operations to spin the main loop (so if
you have a program that lends itself to that design, you can process
redraws and UI operations while in a sync I/O call).

2) If you want hard blocking sync I/O calls, just set a different
GMainContext for the thread, and don't use it for anything else.

3) You can get at the GMainContexts used in subthreads, so you can e.g.
add your own timeouts or whatever to those.

And the best thing is, you never have to pass the GMainContexts around.
The context-per-thread management API is tiny:

http://svn.gnome.org/viewcvs/flow/trunk/flow/flow-context-mgmt.h?revision=154&view=markup

The only thing that can cause trouble is that in Flow code proper, I
have to be careful to use flow_get_main_context_for_current_thread ()
instead of g_main_context_default (). Ditto for idle and timeout
convenience functions. But I consider that minimal cost.

-- 
Hans Petter

___
gtk-devel-list mailing list
gtk-devel-list@gnome.org
http://mail.gnome.org/mailman/listinfo/gtk-devel-list


Re: Some comments about GVFS

2007-05-07 Thread Alexander Larsson
On Fri, 2007-05-04 at 17:52 +0200, Benjamin Otte wrote:
> > > 1b) Cancelling operations from another thread doesn't look like good
> > > design to me. I've learnt (both in theory and in practice) that
> > > threads are supposed to be independant and not call into each other.
> >
> > Eh? How else would you cancel a blocking i/o call? From within the
> > blocked thread? This is just bogus, cancellation is all about
> > cross-thread operations.
> >
> I'd argue that if you do a blocking call, you're aware that it's
> blocking and don't want to cancel it. Otherwise you'd use async I/O
> with proper cancellation mechanism such as g_main_context_wakeup().
> You've told me that it's main use case are transactions like calling
> the equivalent of gnome_vfs_xfer_async () which you'd want to
> implement in a thread by calling lots of sync operations one after
> another.
> I can see the use case, but even though I still don't like it, I can't
> come up with a better model.

Take for instance the default async i/o implementation (the one used for e.g. 
local files). It uses threads and blocking calls. If we want the default async 
implementation to support cancellation, then the sync version must support 
cancellation too.

> Yeah, I missed those implementations as I was only grepping for
> GMainContext which you don't use.
> I think it's a good idea to make the main context customizable so gvfs
> can be used from other threads.

The api used to have this, but there was a lot of overhead code carrying
this around and adding it to the parameter list of all functions, and I
thought there would be very very few people using it, so i killed it.
Maybe we should discuss reverting that decision...

> I'd love if GVFS would advocate the async model over the sync model
> but provide both. So g_input_stream_read would be async and
> g_input_stream_read_async would exist, too.
> The reason for this is that I think in most cases you want the async
> behaviour and it helps to tell lazy programmers that this is the right
> way to go. It's purely psychological.

I dunno. Its contrary to what all other APIs do, and i'm not sure the
psychological advantage is all that great. Programmers generally need a
specific functions, and they'll look around until they find it. Who are
we to decide for them if sync or async is the better model for them.

> I think that solution is fine. However, there is one thing I am
> missing: The read_all_available_data_right_now() function call. Or at
> least a read_x_bytes_if_available() call. This is interesting because
> in some cases I want to avoid calling back into the main loop.
> It's an issue in Swfdec with gnome-vfs where I'm supposed to display
> how many percent of the file is loaded and that display gets updated
> via the main loop. So after every read () of DEFAULT_SIZE I get my
> display updated. So loading seems slow even though it isn't, just
> because every read goes via the main loop.

The openoffice stream class has a call that gets the number of bytes you
can currently read without blocking.  Maybe something like that can be
added? For many streams this is not really something you can generally
calculate in a sane way though. Take a stream to a file on smb or nfs
for example. The way a read call works there is that you send a request
for read on N bytes at offset O, and you get back a reply with the data.
So, this function would always return zero. 

And actually, this model is very similar to what the gvfs client code
does with the gvfs daemon, so it will be true for all vfs streams.
Automatic readahead can make it different, so can reading in larger
blocks, but the operation is still not quite what you expect.

Btw, how do you handle buffer management with a call like
read_all_availible_data_right_now? You have to pass in a buffer of some
size. When you do, how is this different from the normal read call? That
already returns immediately when it has read any data and further i/o
would block.

> > > 8) The API seems very forgiving to (IMO) obvious porogramming
> errors
> > > that should g_return_if_fail. g_input_stream_read for example provides
> > > a proper error message when there's already a pending operation or
> > > when the size parameter is too large.
> >
> > Is this such a bad thing? Should we convert these to asserts?
> >
> I would advocate this. For one it's not an error message a user should
> be presented with ("Hey, someone passed a too large value to read()").
> But it's even more interesting when you stick the error into the
> object as I'm advocating above.

Yeah, this is probably a good idea.

> Another thing that came to my mind is if it would be better to use
> signals instead of providing callbacks for some operations. It seems
> somewhat weird for read(), but open and in particular close might be
> interesting to be implemented using signals.

I don't think signals are easier for one-shot function call initiated
callbacks like that. It just means you have to type mo

Re: Some comments about GVFS

2007-05-04 Thread Carl Worth
On Fri, 04 May 2007 10:27:34 -0400, Havoc Pennington wrote:
> It's a run-time env variable. I think we don't advertise it at all, it's
> more of a defense against people who have strong views on the topic and
> prefer "undefined behavior probably a crash" to "just exit" ...

And one thing to note here is that cairo does provide an additional
option:

"No behavior, will not crash"

which I think can form a good compromise between the camps that prefer
each of the options you mention. (Though I'll agree that debugging
would be easier if cairo _also_ provided a run-time mechanism to get
the "just exit" along with an error message giving as much context as
possible for the time of the error.)

The point is, while it might not make sense to talk about error
recovery from any single low-level library operation, (see your
"failed to draw a rectangle" example), applications still will very
much want to have high-level behavior of "something has gone very
wrong, let's save the user's important state before exiting".

There are a few things a library could do that would defeat that
almost entirely:

1. Exit

2. Start doing undefined things

3. Require error-checks on every call into the library to prevent it
   from doing undefined things

You seem to be saying that it's impractical for a library to avoid 2
or 3, so there's no reason not to do 1. But I think with cairo we've
got a practical approach for getting close enough to avoiding all
three so that applications really can do high-level error recovery.

And that seems useful.

> When I first implemented this it found quite a few bugs, I then
> implemented something similar for part of libxml and also found a lot,
> so that's why my party line since has been that trying to handle OOM
> without testing the OOM codepaths is more hopeful fantasy than anything
> else.

I absolutely agree that testing is essential, and out-of-memory
handling is doomed without it.

And now that we've fixed so much in cairo, we really want to go fix
underlying libraries like fontconfig and freetype or else it's really
all in vain.

> (it's just too hard to get right without testing - in some cases
> in dbus-daemon I had to implement fairly complex "transactions" that
> could be rolled back, in other cases I had to change the library API to
> allow e.g. callbacks to signal OOM)

When we started testing cairo, we found several mistakes, (though
fewer than I had anticipated to tell the truth). But we've had nothing
come even close to needing any API changes. Part of that is certainly
due to cairo's problem domain where there aren't a lot of complex
interactions between separate operations, (can anyone imagine
transactions being useful when drawing?).

But another part comes from just accepting support for OOM-error-
handling as an upfront design constraint, rather than hoping to bolt
it on later.

-Carl


pgpDjQ9MQtXgn.pgp
Description: PGP signature
___
gtk-devel-list mailing list
gtk-devel-list@gnome.org
http://mail.gnome.org/mailman/listinfo/gtk-devel-list


Re: Some comments about GVFS

2007-05-04 Thread Benjamin Otte

I just had a chat with Alex on IRC. (I have attached the log if anyone
wants to read it.) Some of my ideas have changed after this
discussion, so I might contradict my previous mail now. Bear with me.
I've come to understand now that Alex' and my idea for what a stream
is and how it should work are very different because we have very
different use cases. Alex comes from the File manager world. For him
GVFS is primarily a way to interact with "files" all over the world,
rename them, tag them, and access it's contents.
For me it's all about getting a stream to/from somewhere and
write/read data to/from it.
I'm wondering is if there are use cases for an I/O library that are
different from those 2 use cases.

During the discussion the question of how to write an async version of
g_file_get_contents came up. (That's the paste link in the log.) I
have attached my quick'n'dirty showcase of how I'd imagine that would
work here, too. The git repo from today contains an implementation
from Alex. That one actually works.

So here's some followup comments. I'd like to note that what I care
most about, because that's important for a nice API is point 7 below.


> 1b) Cancelling operations from another thread doesn't look like good
> design to me. I've learnt (both in theory and in practice) that
> threads are supposed to be independant and not call into each other.

Eh? How else would you cancel a blocking i/o call? From within the
blocked thread? This is just bogus, cancellation is all about
cross-thread operations.


I'd argue that if you do a blocking call, you're aware that it's
blocking and don't want to cancel it. Otherwise you'd use async I/O
with proper cancellation mechanism such as g_main_context_wakeup().
You've told me that it's main use case are transactions like calling
the equivalent of gnome_vfs_xfer_async () which you'd want to
implement in a thread by calling lots of sync operations one after
another.
I can see the use case, but even though I still don't like it, I can't
come up with a better model.


> 2) GVFS seems to avoid the glib main loop for asynchronous operations
> in favour of relying on threads. I'm not sure this provides any
> benefits besides making apps way harder to debug. Is there a reason
> for that?

I wouldn't say it does. It gives you both sync (usefull if you do
threads) and async operations (useful it your typical gui app). Almost
all GUI apps are single threaded and async by desing, and integrating
glib-style async i/o into such apps is generally *much* easier than
manually mucking about with threads.

However, the default async operations are implemented using threads, so
in fact apps are often using threads already when using gvfs. Just in a
way that fits well with a mainloop based gui app. And when doing async
is *much* easier than using sync i/o we can avoid the thread cost by
having a custom async implementation. (For instance the gvfs daemon
protocol is very easy to do async without using threads.)


Yeah, I missed those implementations as I was only grepping for
GMainContext which you don't use.
I think it's a good idea to make the main context customizable so gvfs
can be used from other threads.


There is a default implementation of async operations using the sync
operations and threads. I don't think that means the model is only sync
i/o. I think it gives equal width to both sync and async models.


I'd love if GVFS would advocate the async model over the sync model
but provide both. So g_input_stream_read would be async and
g_input_stream_read_async would exist, too.
The reason for this is that I think in most cases you want the async
behaviour and it helps to tell lazy programmers that this is the right
way to go. It's purely psychological.


In fact, I agree with Linus (the syslet/threadlet thread on lkml is very
interesting wrt to this argument) that there are many operations that
just fit very badly with the asynchronous model, and things like
pathname lookup and complicated filename operations are examples of
that.


The relevant email can be found at http://lkml.org/lkml/2007/2/25/177
- the followup and previous mails might be interesting, too.
I need ot digest this before commenting on it though.


There are two basic models when it comes to async i/o. I call them
"push" and "pull".

The "push" model is when you initiate a transfer, and then you get a new
event each time there is more data availible. The "pull" model is that
you initiate each read and then get an even when data for that read is
availible.

The main difference for the two models are:
* flow control
  The push model doesn't give you much flow control. If you're copying
  from a fast source to a slow destination you will fill up memory with
  the data while waiting for the destination to accept your first block
  of data. You might also not really want to read the whole file (in
  for instance filetype sniffing), and this gets more complicated with
  push.
* pipelining
  The pull model means you 

Re: Some comments about GVFS

2007-05-04 Thread Havoc Pennington
Hi,

Carl Worth wrote:
> Anyway, I've learned a few new things, and found the discussion very
> useful. I hope you have as well.

Certainly.

> By the way, is that a compile-time or a run-tim environment variable?
> And what measures do you take to advertise it?

It's a run-time env variable. I think we don't advertise it at all, it's 
more of a defense against people who have strong views on the topic and 
prefer "undefined behavior probably a crash" to "just exit" ...

> How does your D-Bus unit testing arrange to test all the OOM code
> paths?

I have a dbus_malloc wrapper and it can be told to fail the Nth malloc. 
The test suite runs once and counts mallocs, then it runs over and over 
and over failing mallocs 1, 2, 3, 4, 5... ;-) Brute force!

When I first implemented this it found quite a few bugs, I then 
implemented something similar for part of libxml and also found a lot, 
so that's why my party line since has been that trying to handle OOM 
without testing the OOM codepaths is more hopeful fantasy than anything 
else. (it's just too hard to get right without testing - in some cases 
in dbus-daemon I had to implement fairly complex "transactions" that 
could be rolled back, in other cases I had to change the library API to 
allow e.g. callbacks to signal OOM)

> What we're doing in cairo, (only fairly recently, and thanks to Chris
> Wilson), is to run cairo's standard test suites but with a valgrind
> skin that Chris wrote to progressively inject malloc failures. The
> skin is quite general and is described here:
> 
> http://lists.freedesktop.org/archives/cairo/2007-April/010343.html

Very nice.

Havoc
___
gtk-devel-list mailing list
gtk-devel-list@gnome.org
http://mail.gnome.org/mailman/listinfo/gtk-devel-list


Re: Some comments about GVFS

2007-05-04 Thread Emmanuele Bassi
On Fri, 2007-05-04 at 15:27 +0200, Mathieu Lacage wrote:

> > > 5) You seem to use void * in as the data pointers. All applications I
> > > know use guchar * (some use gchar *) to handle data. From my stream
> > > handling experience this is to encourage people to think about what
> > > they pass to such a function. This seems to encourage calling
> > > functions like this: write (mywidget, sizeof (MyWidget)) - with is a
> > > bad idea for multiple reasons, including but not limited to struct
> > > padding. MS formats seem to have done that a lot.
> > 
> > I'm not partial to any format here. Do people thing that using a char
> > type rather than a void type is better? 
> 
> I tend to use uint8_t * because I rely on stdint.h: this makes it
> totally unambiguous that you are dealing with bytes, IMHO. I like to
> avoid char * whenever I can because I have gotten more than once into
> portability problems due to the fact that the signedness of char is
> different on different platforms. If I remember correctly one of the
> embedded platforms I used to work on (ARM or MIPS, I cannot recall at
> the moment) was unsigned char while x86 is usually signed char.

for unformatted blobs of data, the convention in GLib/GTK+ appears to be
using guchar*, to avoid the signedness issue like you reported; see the
GtkSelectionData and the drag and drop API in GTK+ and the base64 API in
GLib for precedents of that.

ciao,
 Emmanuele.

-- 
Emmanuele Bassi,  E: [EMAIL PROTECTED]
W: http://www.emmanuelebassi.net
B: http://log.emmanuelebassi.net

___
gtk-devel-list mailing list
gtk-devel-list@gnome.org
http://mail.gnome.org/mailman/listinfo/gtk-devel-list


Re: Some comments about GVFS

2007-05-04 Thread Mathieu Lacage
On Fri, 2007-05-04 at 11:40 +0200, Alexander Larsson wrote:

> > 5) You seem to use void * in as the data pointers. All applications I
> > know use guchar * (some use gchar *) to handle data. From my stream
> > handling experience this is to encourage people to think about what
> > they pass to such a function. This seems to encourage calling
> > functions like this: write (mywidget, sizeof (MyWidget)) - with is a
> > bad idea for multiple reasons, including but not limited to struct
> > padding. MS formats seem to have done that a lot.
> 
> I'm not partial to any format here. Do people thing that using a char
> type rather than a void type is better? 

I tend to use uint8_t * because I rely on stdint.h: this makes it
totally unambiguous that you are dealing with bytes, IMHO. I like to
avoid char * whenever I can because I have gotten more than once into
portability problems due to the fact that the signedness of char is
different on different platforms. If I remember correctly one of the
embedded platforms I used to work on (ARM or MIPS, I cannot recall at
the moment) was unsigned char while x86 is usually signed char.

regards,
Mathieu
-- 

___
gtk-devel-list mailing list
gtk-devel-list@gnome.org
http://mail.gnome.org/mailman/listinfo/gtk-devel-list


Re: Some comments about GVFS

2007-05-04 Thread Alexander Larsson
On Thu, 2007-05-03 at 11:11 +0200, Benjamin Otte wrote:
> Hi,
> 
> I had an initial look at gvfs in particular the Inputstream and
> Outputstream implementations, and some comments came to my mind, in
> particular about the API. So I thought I'd post them as early as
> possible.
> These comments are about the code in
> http://www.gnome.org/~alexl/git/gvfs.git from today - May 2nd.
> I should also note that these ideas are probably heavily influenced by
> where I'm coming from - namely GStreamer and Swfdec - which involve
> handling low level byte streams possibly caring about low latency. In
> this particular case I had been wondering about making the basic
> input/output objects in Swfdec GInput/OutputStream.
> 
> 1) I don't like GCancellable This is for several reasons:
> 
> 1a) GCancellable is far too visible. It's present in every function
> call, so it must be very important. Maybe exporting
> g_push/pop_current_cancellable () would be a better API because it
> decouples the cancelling from the actual operations?

There are two cases here. For async operations we clearly need to pass
the cancellable object to the operation, because we can't rely on a
thread-local storage setting over the lifetime of the async operations.

Now, for sync calls things are less obvious. At one time I had the
cancellable object as an implicit argument (via tls). However, I ran
into trouble with that and had to be explicit about it. I'm trying to
recall the details of that atm... I think the problem was that if your
implementation of an operation relies on other gio operations those
would always inherit the cancellable of the outer operation. This isn't
always what you want, as there are casese where you need to do a
non-cancellable i/o as part of a larger cancellable operation (an
example is that you don't want to send partial messages and mess up the
protocol state in the daemon backend, instead you want to send a cancel
request).

Maybe there were other reasons too, because it seems like this one would
be fixable if one allows to stack a NULL cancellable over a non-NULL
one.

> 1b) Cancelling operations from another thread doesn't look like good
> design to me. I've learnt (both in theory and in practice) that
> threads are supposed to be independant and not call into each other.

Eh? How else would you cancel a blocking i/o call? From within the
blocked thread? This is just bogus, cancellation is all about
cross-thread operations.

> 2) GVFS seems to avoid the glib main loop for asynchronous operations
> in favour of relying on threads. I'm not sure this provides any
> benefits besides making apps way harder to debug. Is there a reason
> for that?

I wouldn't say it does. It gives you both sync (usefull if you do
threads) and async operations (useful it your typical gui app). Almost
all GUI apps are single threaded and async by desing, and integrating
glib-style async i/o into such apps is generally *much* easier than
manually mucking about with threads.

However, the default async operations are implemented using threads, so
in fact apps are often using threads already when using gvfs. Just in a
way that fits well with a mainloop based gui app. And when doing async
is *much* easier than using sync i/o we can avoid the thread cost by
having a custom async implementation. (For instance the gvfs daemon
protocol is very easy to do async without using threads.)

Of course, threads have their place too. For instance, a complicated set
of file operations like a recursive directory copy is imho best
implemented using sync calls in a single thread. And gvfs is set up to
handle this case well too.

> 3) The whole API seems to be built around the model of synchronous
> operation. (I think so because of this code comment: /* Async ops:
> (optional in derived classes) */) I always thought everyone agrees
> that synchronous operation should be a thing of the past. and only be
> supported as an add-on for lazy coders or really simple apps.
> Almost everyone implements synchronous operations something like this:
>  void foo () { while (errno == EAGAIN) foo_async (); };
> The kernel sure does.

There is a default implementation of async operations using the sync
operations and threads. I don't think that means the model is only sync
i/o. I think it gives equal width to both sync and async models.

I also thing you are naive if you think sync i/o is a thing of the past
and only for "lazy" coders. There are many cases where asynchronous i/o
is way to complicated and doesn't give any advantages over sync i/o.
(Like the above recursive copy operation, i dare you to implement
gnome_vfs_xfer using only asynchronous primitives!). 

In fact, I agree with Linus (the syslet/threadlet thread on lkml is very
interesting wrt to this argument) that there are many operations that
just fit very badly with the asynchronous model, and things like
pathname lookup and complicated filename operations are examples of
that.

> 4) The asynchronous API seems t

Re: Some comments about GVFS

2007-05-03 Thread Carl Worth
On Thu, 03 May 2007 16:52:39 -0400, Havoc Pennington wrote:
> Carl Worth wrote:
> "cairo model" is probably confusing - write_to_png is a more typical and
> essentially GError-equivalent model, while "cairo_t model" is the thing
> that people mean is unique to Cairo.

Yeah, we can get in a lot of confusion here whether we're talking
about the merits of the approach in any particular library vs. the
idea of applying that approach to new things.

Anyway, I've learned a few new things, and found the discussion very
useful. I hope you have as well.

> Of course. The case where this would break is an object with its own
> locks, e.g. DBusConnection. I'm just guessing that gvfs has its own
> locks since I believe gnome-vfs did.

Yes, internal locks would definitely change things a lot. I just
generally don't believe in such locks, but that would be a whole
separate thread... ;-)

> There is an official GLib/GTK+ policy on this, which is that g_assert is
> used only for bugs in the library, and return_if_fail is used for bugs
> in the app. It is missing from the docs indeed.

OK, that makes sense.

> > I could imagine a consistent argument that all programmer errors
> > should lead to an abort, (to force errors to get noticed and dealt
> > with early).
>
> D-Bus in fact does this unless you disable it by env variable. It is
> pretty controversial, though. Or at least there's a vocal group who
> doesn't like it.
>
> Obviously I think said group is wrong ;-)

And that makes a _lot_ of sense. I was a little confused with your
rationale about programmer errors and then the discussion of
g_return_if_fail.

By the way, is that a compile-time or a run-tim environment variable?
And what measures do you take to advertise it?

> The big differences are 1) that it prints a message and 2) that all
> behavior is undefined after a return_if_fail - GTK doesn't contain
> handling code that frees resources or tries to recover or puts objects
> in a no-op state as a result of the return_if_fail.

Yup. I'd be glad to print in cairo if the programmer has asked for
undefined behavior, but in that case I might as well just abort.

> I don't think 2) really matters to library users, it only affects
> library maintainer effort.

Fair enough. My experience is that there's not much maintainer effort
involved here. Implementing the no-op behavior is no harder than the
moral equivalent of a single g_return_if_fail at the beginning of
every entry point. And once you've got the no-op it's really easy to
provide defined behavior without much extra code, (we just don't care
what the state of the object is, except that it's destroy function
should still work).

> Well, if and only if the error is a programming error. That's the
> problem, I don't want noisy in write_to_png, nor do I want noisy if the
> error is something I should be handling or is something I'm legitimately
> ignoring. I only want noisy about bugs.

Fair enough. I think I could be convinced to accept code to make cairo
fail with a message when the user asks for some undefined behavior.

> What D-Bus does in a couple of cases (and GTK may have one or two
> examples of this also, iirc) is that if you pass NULL to ignore the
> DBusError/GError, it is noisy, and if you don't pass NULL it is not
> noisy. However, this is only OK if passing NULL when an error occurs
> amounts to a programming bug, i.e. when you are only allowed to pass
> NULL if you know errors are impossible. If it would be legitimate and
> normal for the error to be silently ignored in some cases, you can't be
> verbose when one happens. I can't actually find an example in the code
> quickly, but I swear we do this in a couple places.

The stuff described in that paragraph seems ver confusing. :-)

> For example say you pass nonsense values; the warning can be
> "function_name: assertion failed: arg_foo != 0.0" so I look at my calls
> to function_name where arg_foo might be 0.0.

Like I said, I could see adding that, (and likely with an abort by
default). The hardest part I think would be doing a good job about
advertising this and advertising how to make it go away.

> With cairo_t drawing code, you have to add temporary code to check the
> status and print errors, then to figure out which function sets the
> error you have to keep moving this temporary code around, then you have
> to figure out what the status means once you know which function sets
> the error status.

Oh, yuck. You really should have complained to me about something like
that. Nobody should ever have to keep moving temporary error-checking
code around to find their mistake. That's awful.

> Setting a break on _cairo_error is certainly easier (thanks for that
> tip) but the reason I've never done that is that I did not know cairo
> had an internal symbol that was always used to set errors ;-) for the
> analogous situation in gtk, gdk_x_error, the warning when an X error is
> received has something in it like "try setting a break on gdk_x_error"
>

Re: Some comments about GVFS

2007-05-03 Thread Havoc Pennington
Hi,

Havoc Pennington wrote:
> In GLib/GTK it's library bugs vs. app bugs, D-Bus maintains the same 
> distinction for _dbus_return_if_fail vs. _dbus_assert *but* D-Bus makes 
> them both fatal so it doesn't matter much anyhow.
> 

And as Benjamin pointed out, D-Bus ships with assertions disabled in 
stable releases, so it can have loads and loads of them in devel snapshots.

Havoc

___
gtk-devel-list mailing list
gtk-devel-list@gnome.org
http://mail.gnome.org/mailman/listinfo/gtk-devel-list


Re: Some comments about GVFS

2007-05-03 Thread Havoc Pennington
Hi,

Carl Worth wrote:
> Sure. If you want the object to remain usable after an error, then it
> shouldn't shut down. And note that that with cairo, we do have
> functions that do return an error indication instead of shutting down,
> (cairo_surface_write_to_png, for example).
> 
> So I think a fair characterization of the "cairo model" is that it
> recognizes that you don't always do the shutdown-on-error thing, but
> it is darn handy when it's the right thing to do.

"cairo model" is probably confusing - write_to_png is a more typical and 
essentially GError-equivalent model, while "cairo_t model" is the thing 
that people mean is unique to Cairo.

> For cairo, at least, there's nothing broken here. If you want to use
> the same object from multiple threads, then you need to be doing your
> own locking.

Of course. The case where this would break is an object with its own 
locks, e.g. DBusConnection. I'm just guessing that gvfs has its own 
locks since I believe gnome-vfs did.

>   First and foremost: GError should only be used to report
>   recoverable runtime errors, never to report programming
>   errors. If the programmer has screwed up, then you should use
>   g_warning(), g_return_if_fail(), g_assert(), g_error(), or
>   some similar facility.
> 
> For that second sentence, if we're talking about a non-recoverable
> programmer error, then what guidance is there for choosing
> g_return_if_fail as opposed to g_assert? Are programmer errors
> actually divided into two classes?

There is an official GLib/GTK+ policy on this, which is that g_assert is 
used only for bugs in the library, and return_if_fail is used for bugs 
in the app. It is missing from the docs indeed.

> I could imagine a consistent argument that all programmer errors
> should lead to an abort, (to force errors to get noticed and dealt
> with early).

D-Bus in fact does this unless you disable it by env variable. It is 
pretty controversial, though. Or at least there's a vocal group who 
doesn't like it.

Obviously I think said group is wrong ;-)

> But how does g_return_if_fail fit into your model? Isn't it really
> doing basically the same thing as cairo's inert object functions?
> Differences I see are that:
> 
>   1. g_return_if_fail prints a message
> 
>   2. cairo's inert objects come with a guarantee that once an
>object shuts down, no future call to that object will have
>any effect.

The big differences are 1) that it prints a message and 2) that all 
behavior is undefined after a return_if_fail - GTK doesn't contain 
handling code that frees resources or tries to recover or puts objects 
in a no-op state as a result of the return_if_fail.

I don't think 2) really matters to library users, it only affects 
library maintainer effort.

> So all you really want is a mode to make cairo be noisy on the first
> detected error?

Well, if and only if the error is a programming error. That's the 
problem, I don't want noisy in write_to_png, nor do I want noisy if the 
error is something I should be handling or is something I'm legitimately 
ignoring. I only want noisy about bugs.

What D-Bus does in a couple of cases (and GTK may have one or two 
examples of this also, iirc) is that if you pass NULL to ignore the 
DBusError/GError, it is noisy, and if you don't pass NULL it is not 
noisy. However, this is only OK if passing NULL when an error occurs 
amounts to a programming bug, i.e. when you are only allowed to pass 
NULL if you know errors are impossible. If it would be legitimate and 
normal for the error to be silently ignored in some cases, you can't be 
verbose when one happens. I can't actually find an example in the code 
quickly, but I swear we do this in a couple places.

> But a little noise isn't really enough information to tell you where
> the error came from, so you're still going to have to track it
> down.

For example say you pass nonsense values; the warning can be 
"function_name: assertion failed: arg_foo != 0.0" so I look at my calls 
to function_name where arg_foo might be 0.0.

With cairo_t drawing code, you have to add temporary code to check the 
status and print errors, then to figure out which function sets the 
error you have to keep moving this temporary code around, then you have 
to figure out what the status means once you know which function sets 
the error status.

Setting a break on _cairo_error is certainly easier (thanks for that 
tip) but the reason I've never done that is that I did not know cairo 
had an internal symbol that was always used to set errors ;-) for the 
analogous situation in gtk, gdk_x_error, the warning when an X error is 
received has something in it like "try setting a break on gdk_x_error" 
even, or used to.

> What you'd really like is a nice application-level stack trace
> from when the error gets detected by cairo, (hopefully people using
> bindings are getting this already).

D-Bus does this on glibc systems - GNU libc 

Re: Some comments about GVFS

2007-05-03 Thread Benjamin Otte
On 5/3/07, Carl Worth <[EMAIL PROTECTED]> wrote:
> So that looks to me like if you modified _cairo_error to do the
> equivalent of g_assert, you'd basically get what you want for your
> recoverable and unavoidable cases. But that would leave you with
> always having g_assert and never g_return_if_fail for programming
> errors though. So please explain more to me about how that handling
> should be distinguished.
>
I've learned that the difference between a g_assert and a
g_return_if_fail is whose fault it is. g_assert would be used for
internal consistency checks inside cairo, where the cairo developers
ensure they don't write bad code. The best example here is a case
statement as in get/set_property functions, where it's common to add
an assert in the default case.
g_return_if_fail is for developers using your library. It's used to
tell them that they are doing something wrong. It's basically the same
thing, just catering to a different audience.
In short: applications should have no call to g_return_if_fail.
Everything should be g_assert.

There's some advantages to this differentiation:
1) It allows you as a developer to disable internal assertions for
various reasons (try running DBUS with all checks enabled and compare
the speed) while still getting informed about faults in using the
library.
2) It allows adding warnings later on. If people have been using your
library wrong because they didn't know about something, you can add a
g_return_if_fail to that function and the next time they'll run the
program, they'll notice the warning and hopefully fix the bug. GThread
has recently done something like this with printing a warning when
g_thread_init() wasn't the first function called.


There's some things that I don't like about the Glib/Gnome approach:
1) Assertions aren't disabled in releases. This leads to people often
not putting enough assertions where it matters. Noone wants inner
loops to be slow. DBUS does that differently and I think it has really
helped developing DBUS.
2) g_return_if_fail returning often isn't useful and will lead to a
crash. An example here is all those functions that have a GError
argument and return FALSE on failure. They return FALSE in the
return_if_fail case, too, but don't set the error. This of course
causes a program to assume an error was set to crash nonetheless. It
would probably be nice to just return TRUE.


In general, I think being as annoying as possible when a developer
does something wrong is a good thing, because people are lazy. So
unless you force them to care about something, they won't and just
hope it works anyway. So I could probably live fine with making every
g_return_if_fail an assertion. But maybe I'm a bit extreme that way.
I'm known to annoy people with -Werror. ;)


Benjamin
___
gtk-devel-list mailing list
gtk-devel-list@gnome.org
http://mail.gnome.org/mailman/listinfo/gtk-devel-list


Re: Some comments about GVFS

2007-05-03 Thread Carl Worth
On Thu, 03 May 2007 15:41:43 -0400, Havoc Pennington wrote:
> I'd be for that in the case that the API has a "required ending call,"
> the equivalent of close(). Then you can "force" error checking on that
> last call with a GError or warn_if_unused approach and in theory people
> could skip the intermediate error checks. That makes sense.

Yes, forcing the user to check the return value when finished with the
object is definitely a good idea.

Aside for the public error-handling strategy, we've been adopting the
same shutdown-on-error approach internally within cairo
bit-by-bit. And as we do that, we really need to propagate errors from
one object to another. And we've found that forcing a check when
disposing of an object is a good way to help us not forget that.

We've wondered whether we can change cairo_destroy from void to cair

> > I'm not sure there are "unavoidable-and-ignorable" errors.
>
> To me anything that could happen to cairo_t is probably in this
> category... what am I going to do if I draw a rectangle and it fails?

Well, for "draw a rectangle", cairo basically says that that can never
fail, (if you look at the X interfaces we get, there's no other
option).

So I think that's an error case that really doesn't exist, as opposed
to something that's unavoidable and ignorable. Just look at
cairo_status_t---there's nothing like "the requested drawing operation
didn't happen" there.

> really have no idea what kind of "recovery" code I'd write for that.
> Certainly nobody is writing such code that I've seen. I don't even know
> where the error check would go - in every widget's expose handler? In
> the toplevel event loop?

As you said before, at the very least at the "required ending call",
(which is cairo_destroy in this case), you should definitely be
checking this error.

> Is there anything that could happen to cairo_t in this category? I don't
> know. I'm mostly assuming there is because I have no idea what the
> cairo_t error state is for otherwise ;-) (other than as an internal
> "make everything a noop" flag)
>
> My guess is that out of memory (on client side or X server side) is in
> this category in cairo, it silently doesn't draw if OOM occurs, no?

There's certainly not a large class of errors that can happen while
drawing, (yes, out of memory is one). But the other way you can get an
error lodged into a cairo_t is via propagation from some other
object. So, if you forget to check a cairo_surface_t's status, (say
it's FILE_NOT_FOUND), and then you do "cairo_create(surface)" then the
FILE_NOT_FOUND status will propagate into the cairo_t.

It would have been kind of nice if GTK+ were creating the cairo_t for
the expose handler and then also checking the status, and printing
that message. But that's not how GTK+ integrates with cairo---oh,
well.

And, no, getting a FILE_NOT_FOUND status on a cairo_t is not going to
give you a meaningful error message. And no, I wouldn't think it would
be sane for anyone to right code to "handle" a FILE_NOT_FOUND status
from a cairo_t. But it might actually give enough of a hint to help
you know where to look, (or else just go and get a
_cairo_error-induced stack trace). And, yes, when you find the
cairo_image_surface_create_from_png call that's failing, (and was
missing any errro checking), you should add code there to print a
meaningful error message.

> As long as the individual calls failing don't affect control flow,
> there's some "close()" call that reminds you to grab the error, and the
> errors are interesting/recoverable in the first place, I agree this is nice.

That sounds quite reasonable to me.

I don't think everyone will agree on which errors are or are not
interesting. And I don't think it hurts at all to be able to fetch an
uninteresting status value from an object, (see my example above of
cairo_t reporting a FILE_NOT_FOUND status).

-Carl


pgpAjmYycGVjS.pgp
Description: PGP signature
___
gtk-devel-list mailing list
gtk-devel-list@gnome.org
http://mail.gnome.org/mailman/listinfo/gtk-devel-list


Re: Some comments about GVFS

2007-05-03 Thread Carl Worth
On Thu, 03 May 2007 14:11:02 -0400, Havoc Pennington wrote:
> Yeah, I think this keeps the Cairo model from being "fully general" - it
> only works for some types of operations or objects.

Sure. If you want the object to remain usable after an error, then it
shouldn't shut down. And note that that with cairo, we do have
functions that do return an error indication instead of shutting down,
(cairo_surface_write_to_png, for example).

So I think a fair characterization of the "cairo model" is that it
recognizes that you don't always do the shutdown-on-error thing, but
it is darn handy when it's the right thing to do.

> Another thing about
> the Cairo model is that it's either weird or possibly broken if an
> object is used by multiple threads, which could arise in the current
> gvfs presumably.

For cairo, at least, there's nothing broken here. If you want to use
the same object from multiple threads, then you need to be doing your
own locking. As for weird, the shutdown thing should only be happening
if there's really no other appropriate response. There's a difference
between causing an error _in_ an object, (in which case it will
shutdown, and it's appropriate for it to be shutdown for all threads),
as opposed to causing an error _with_ an object, (in which case it
should just return the error indication and not shutdown).

> Another thing that's needed for gvfs for sure but not for Cairo perhaps
> is the error message.

The model of lodging errors inside the objects doesn't prevent you
from putting as detailed a message in their as you'd like. With cairo
itself, we don't actually store anything more than an enum, but...

> 8859-1"), but nonetheless in a lot of cases I think a detailed error can
> be better than a strerror-style string. Sometimes the error will be
> user-helpful, e.g. "You aren't allowed to save to directory Foo"

... even with the very strerror-style approach that cairo encourages,
it's very easy for the calling application to add context like this
when generating its error message.

> Returning *helpful* errors, even if only to programmers and techies,
> also frequently depends on context - meaning, the error can be more
> helpful if it's checked right away after every call.

Sure. So everytime you want to emit a useful error message, check the
status and generate the message with all the context you want.

Or, like I said above, you could envision applying the cairo model and
also adding a string to it. (That is, if applying the cairo model
pushes the GError from a parameter to every function to instead be a
field within the object, that doesn't mean that GErrror needs to
become any less capable).

> Finally, Cairo of course mixes programmer errors (g_return_if_fail in
> GLib-world) with should-be-reported errors (GError in GLib-world) with
> unavoidable-and-should-be-ignored errors (just get ignored in
> GLib-world). See GError docs for how these are separated in GLib:
> http://developer.gnome.org/doc/API/2.2/glib/glib-Error-Reporting.html

I definitely agree with you that there are different classes of errors
and they require different kinds of handling. And I won't argue that
cairo gets all of this right yet. (In a very real sense I've always
felt that cairo's error-handling strategy was a big experiment, and it
would be interesting to see how it played out. So I'm quite glad to
see this discussion happening around it.)

In the meantime, it's not clear to me that either cairo or glib has
this all figured out yet. For example, the document above says:

First and foremost: GError should only be used to report
recoverable runtime errors, never to report programming
errors. If the programmer has screwed up, then you should use
g_warning(), g_return_if_fail(), g_assert(), g_error(), or
some similar facility.

For that second sentence, if we're talking about a non-recoverable
programmer error, then what guidance is there for choosing
g_return_if_fail as opposed to g_assert? Are programmer errors
actually divided into two classes?

I could imagine a consistent argument that all programmer errors
should lead to an abort, (to force errors to get noticed and dealt
with early). And I could accept that some people would want to
configure the abort away as well, (which G_DISABLE_ASSERT allows for
example). So I could imagine a consistent argument that g_assert
should be used for all detected programmer errors.

But how does g_return_if_fail fit into your model? Isn't it really
doing basically the same thing as cairo's inert object functions?
Differences I see are that:

1. g_return_if_fail prints a message

2. cairo's inert objects come with a guarantee that once an
   object shuts down, no future call to that object will have
   any effect.

> For me when using Cairo drawing I would say should-be-reported errors
> aren't really possible. When the problem is that I passed

Re: Some comments about GVFS

2007-05-03 Thread Havoc Pennington
Hi,

Benjamin Otte wrote:
> To bring up some examples: GdkPixbufLoaders, GKeyFile, GBookmarkFile, 
> GMarkup.

I'd be for that in the case that the API has a "required ending call," 
the equivalent of close(). Then you can "force" error checking on that 
last call with a GError or warn_if_unused approach and in theory people 
could skip the intermediate error checks. That makes sense.

> I'm not sure there are "unavoidable-and-ignorable" errors.

To me anything that could happen to cairo_t is probably in this 
category... what am I going to do if I draw a rectangle and it fails? I 
really have no idea what kind of "recovery" code I'd write for that. 
Certainly nobody is writing such code that I've seen. I don't even know 
where the error check would go - in every widget's expose handler? In 
the toplevel event loop?

Is there anything that could happen to cairo_t in this category? I don't 
know. I'm mostly assuming there is because I have no idea what the 
cairo_t error state is for otherwise ;-) (other than as an internal 
"make everything a noop" flag)

My guess is that out of memory (on client side or X server side) is in 
this category in cairo, it silently doesn't draw if OOM occurs, no?

You're right that if this class of error exists it's pretty rare. It 
would probably mean a bug or at least design bug in something, though 
possibly not your own app.

> At least
> I'm not very impressed by the error reporting of Gtk when X has
> problems. (That one's probably mostly Xlib's fault though).

Examples? Any recoverable error it should handle for you or report to 
you, the main unrecoverable error is BadAlloc which causes a g_error 
just like out of memory on the client side, and then some X errors are 
programming errors which GTK often tries to predict for you with 
return_if_fail but sometimes does not catch. Most X errors I would say 
are programming errors, the big exception is that if you are messing 
with another process's windows (as a WM does) there are lots of 
unavoidable but recoverable errors.

Patches adding return_if_fail to avoid all the "programming error" Xlib 
errors would probably be accepted, is my guess.

Xlib is definitely guilty of mixing up different kinds of error into one 
system that isn't appropriate or convenient for any kind of error...

> And to say it once again: It's nice that the object carries the object
> around with it. That way I don't have to carry the Error around myself
> all the time as an extra argument.

As long as the individual calls failing don't affect control flow, 
there's some "close()" call that reminds you to grab the error, and the 
errors are interesting/recoverable in the first place, I agree this is nice.

Havoc

___
gtk-devel-list mailing list
gtk-devel-list@gnome.org
http://mail.gnome.org/mailman/listinfo/gtk-devel-list


Re: Some comments about GVFS

2007-05-03 Thread Benjamin Otte
On 5/3/07, Havoc Pennington <[EMAIL PROTECTED]> wrote:
>
> Yeah, I think this keeps the Cairo model from being "fully general" - it
> only works for some types of operations or objects. Another thing about
> the Cairo model is that it's either weird or possibly broken if an
> object is used by multiple threads, which could arise in the current
> gvfs presumably.
>
[...]
> For those reasons I don't agree at all with Benjamin that GError is the
> "old" approach, I think it is still the right approach *for errors that
> should generally be handled or recovered from in some way*.
>
I think the cairo style is the best way for recoverable errors that
are fatal for the affected object. In that case you want to put the
object into an error state and ignore further functions called on the
object.
To bring up some examples: GdkPixbufLoaders, GKeyFile, GBookmarkFile, GMarkup.
All of those have a lot of functions (not all of them) that are fatal
to the object and both the API and the code using it would look a lot
nicer if there was one argument less.
It may be interesting for some objects to have a function to recover
from errors.

> It's very very important IMO to distinguish the three types of errors
> (programming, recoverable, and unavoidable-and-ignorable) when designing
> any API in this area.
>
I'm not sure there are "unavoidable-and-ignorable" errors. At least
I'm not very impressed by the error reporting of Gtk when X has
problems. (That one's probably mostly Xlib's fault though). I agree
with you that there are programming errors and recoverable errors, but
not ignorable errors. Even cairo drawing errors should be somehow
handled, and even if it's just printing to stdout. But almost all of
those errors indicate a problem in your code (like cairo_scale (cr,
0.0, x)).

The reason for allowing the programmer to check errors whenever he
wants is to make programs of lazy people easier. We all know that good
programs check errno after every call into libc, but most of us are
lazy. And lazy people don't remember later that they passed NULL to
g_key_file_get_double(). Nor do people that have to fix bugs.
And to say it once again: It's nice that the object carries the object
around with it. That way I don't have to carry the Error around myself
all the time as an extra argument.


Benjamin
___
gtk-devel-list mailing list
gtk-devel-list@gnome.org
http://mail.gnome.org/mailman/listinfo/gtk-devel-list


Re: Some comments about GVFS

2007-05-03 Thread Havoc Pennington
Hi,

Carl Worth wrote:
> On Thu, 3 May 2007 11:11:39 +0200, "Benjamin Otte" wrote:
>>I much prefer the cairo model, where basically the
>> object keeps its own GError and has a function like cairo_status [3]
>> that gives you the last error that occured.
> 
> It's worth pointing out an additional aspect of the cairo
> error-handling model: The object becomes entirely inert as soon as an
> error occurs within it. That is, all methods of the object first check
> the status and return immediately on error.
> 

Yeah, I think this keeps the Cairo model from being "fully general" - it 
only works for some types of operations or objects. Another thing about 
the Cairo model is that it's either weird or possibly broken if an 
object is used by multiple threads, which could arise in the current 
gvfs presumably.

Another thing that's needed for gvfs for sure but not for Cairo perhaps 
is the error message. A strerror()/errno type of thing is just not 
sufficient to give good errors from a system with pluggable backends. 
Even in Cairo, functions like cairo_surface_write_to_png return pretty 
unhelpful info on failure. Failure is uncommon, and the info would 
generally only be helpful to programmers not users anyway (gdk-pixbuf 
returns stuff like "Value for PNG text chunk cannot be converted to 
8859-1"), but nonetheless in a lot of cases I think a detailed error can 
be better than a strerror-style string. Sometimes the error will be 
user-helpful, e.g. "You aren't allowed to save to directory Foo"

Returning *helpful* errors, even if only to programmers and techies, 
also frequently depends on context - meaning, the error can be more 
helpful if it's checked right away after every call. errno obviously is 
an extreme example since it's garbage if not checked immediately. But 
say you do a series of gvfs operations then get a "no permissions" 
error, if the error message says which operation lacked permissions, 
that is much more helpful. strerror(EPERM) is not as good as "You aren't 
allowed to save to directory Foo"

Finally, Cairo of course mixes programmer errors (g_return_if_fail in 
GLib-world) with should-be-reported errors (GError in GLib-world) with 
unavoidable-and-should-be-ignored errors (just get ignored in 
GLib-world). See GError docs for how these are separated in GLib:
http://developer.gnome.org/doc/API/2.2/glib/glib-Error-Reporting.html

For me when using Cairo drawing I would say should-be-reported errors 
aren't really possible. When the problem is that I passed nonsense to a 
Cairo API, I would prefer some verbose warning to the silent failure 
plus an error code I have to figure out the origin of. When the problem 
is something unavoidable (say not enough memory or whatever) then I just 
don't care, I'm not going to check the error code for this or in any way 
recover. So in neither case here do I find the error codes useful.

I guess for some surfaces there might be a recoverable error I can 
imagine caring about, but for painting widgets in X, it just hasn't 
happened. The Cairo error reporting is something I simply don't use in 
connection with any drawing code, except in temporary code to figure out 
how I misused the API, where in GTK I'd instead have seen a warning 
right away.

When the programmer _should_ check the error, e.g. 
cairo_surface_write_to_png, I think GError-style reporting is ideal, and 
the return code as write_to_png has is OK and does the job and is pretty 
much equivalent to GError except there's no memory management (plus) and 
no error message (minus).

So to sum up:
  - cairo model is most usable when it can be correct to ignore errors,
which is particularly common for programming errors
(GLib would use return_if_fail/assert) or unavoidable runtime errors
that are best handled by simply ignoring them
  - cairo model should perhaps be extended to support an error message
for more general applications (since in the drawing case the idea
is to almost always ignore errors, there's no point, but for file
and network ops, there may well be)
  - cairo model raises issues when the error state is shared by
multiple threads accessing one object
  - when it's really probably wrong to not check errors (e.g. when
saving a file), a GError-type model with a function arg you must
consider, or a return value with warn_unused_result as Carl mentions,
is better than the cairo_t store-error-on-object model IMO
  - I personally believe programmer errors should get the return_if_fail
or assert type treatment and not be runtime-detectable, because
a nice warning is more helpful to the programmer or in bug reports,
and runtime detecting these is just wrong anyway - fix the bug, don't
recover from the bug by adding bug-handling code

For those reasons I don't agree at all with Benjamin that GError is the 
"old" approach, I think it is still the right approach *for errors that 
should generally be handled or recover

Re: Some comments about GVFS

2007-05-03 Thread Carl Worth
On Thu, 3 May 2007 11:11:39 +0200, "Benjamin Otte" wrote:
>I much prefer the cairo model, where basically the
> object keeps its own GError and has a function like cairo_status [3]
> that gives you the last error that occured.

It's worth pointing out an additional aspect of the cairo
error-handling model: The object becomes entirely inert as soon as an
error occurs within it. That is, all methods of the object first check
the status and return immediately on error.

That part of the model is essential for the application code to be
able to benefit by deferring error-checking to a convenient time.

-Carl

PS. After I wrote that much I also wrote the following rambling
treatise on the model. I didn't have time to make it shorter, so feel
free to delete it. (I almost deleted it myself, but I think someone
might find it useful).

I think the model is extremely successful in that it actually becomes
practical to write correct programs. We started with the assumption
that programmers rarely include all the necessary checks to make their
program correct, so we decided to make the program correct without all
of those checks.

And that means that the correct program can remain readable---there's
a lot less clutter without from error-checking paths. I love the fact
that code samples in the documentation of the library don't need those
often-seen disclaimers, "error-checking code removed for readability".

Another great benefit is that compiler features such as
attribute(warn_unused_result) can actually become feasible to use,
(that is, it doesn't result in a bunch of false-positive noise).

For example, in cairo's core API of about 200 functions, there are
less than 15 that can return an error status indication, (not counting
the calls where the user is explicitly asking for a status value). All
other functions are either void or are returning a value that is of
direct interest to the caller, (such as a constructor or a "get"
function). So, from the calling convention, it's obvious to the user
that the great majority of the time there's no further error-checking
required. And, in the few functions that _do_ return an error status,
it's actually very important for the user to check those, (and
something like the warn_unused_result attribute can be quite helpful
for this).

One thing that can be get trickier with this model is tracking down
what actually caused the error once it is detected. While it's
convenient to be able to defer the error checking, this also means
that detection of the error becomes separated in time and code from
when the error was committed. (With the "old" approach there is often
unintended deferral due to missing error-checks, but hopefully the
user gets lucky and a crash happens soon after the error. But the
"inert" stuff described above prevents this.)

So, to successfully adopt this model, the user really needs to be
provided with some means for getting early reports about detected
errors. Cairo is quite conservative about this, providing only a
function that can be set as a breakpoint for when an error is
detected---and that's probably not quite as much help as will be
desired in many cases. Within a glib world there should be no
compunction in spewing messages or allowing applications to register a
callback for when errors are detected by the library.

Also, another subtle issue is that application code can be incorrect
if it depends on side-effects of library calls that will not always
happen in the face of inert methods. For example, imagine some
fictitious code that looks like this:

while (collection_has_more_items (collection)) {
...
collection_remove_item (collection);
}

...

/* Finally check status of collection here. */

if (collection_has_error (collection))
handle_error();

If collection_remove_item could become inert, then the implicit
side-effect of reducing the return-value of collection_has_more_items
would be violated, and the application code would result in an
infinite loop.

So this would be a situation where the inert-object style would not
help at all. Writing a correct program in this case would be more
difficult, (the user would have to anticipate the problem and add an
extra call to check for errors within the loop), than if the
remove_item call directly returned an error indication, (letting the
user know it is important to check for that error).

For cairo, we largely get to ignore this issue, simply because the
side-effects of cairo operations, (drawing operations), rarely have
such a direct impact on a program's control flow.

So that's something else to keep in mind if considering this style.

Finally, cairo also avoids returning NULL from failed object-creation
functions. (The idea being to return a non-NULL object that can
indicate what type of failure occurred.) I don't know that that aspect
has been entirely successful. The application code ends up