After an evaluation, GNOME has moved from Bugzilla to GitLab. Learn more about GitLab.
No new issues can be reported in GNOME Bugzilla anymore.
To report an issue in a GNOME project, go to GNOME GitLab.
Do not go to GNOME Gitlab for: Bluefish, Doxygen, GnuCash, GStreamer, java-gnome, LDTP, NetworkManager, Tomboy.
Bug 606913 - GUnix*Stream should work on regular files
GUnix*Stream should work on regular files
Status: RESOLVED FIXED
Product: glib
Classification: Platform
Component: gio
unspecified
Other Linux
: Normal normal
: ---
Assigned To: gtkdev
gtkdev
Depends on:
Blocks: 621187
 
 
Reported: 2010-01-14 01:20 UTC by Behdad Esfahbod
Modified: 2011-11-13 21:38 UTC
See Also:
GNOME target: ---
GNOME version: ---


Attachments
GUnixInputStream, GUnixOutputStream: clarify the problems with regular files (2.73 KB, patch)
2010-04-23 14:43 UTC, Dan Winship
none Details | Review
GUnixInputStream, GUnixOutputStream: support ordinary files better (6.77 KB, patch)
2010-06-22 19:43 UTC, Dan Winship
committed Details | Review

Description Behdad Esfahbod 2010-01-14 01:20:55 UTC
The docs say:

"GUnixInputStream implements GInputStream for reading from a UNIX file descriptor, including asynchronous operations. The file descriptor must be selectable, so it doesn't work with opened files."

I have a very legit usecase to want to use that API with regular opened files.  I want to use g_output_stream_splice() to write vte buffer contents to a GOutputStream.  The vte buffer is stored in a fd.  I need to create a GInputStream from an fd and I see no way to do that other than GUnixInputStream.  Indeed, unlike the docs suggest, it works.  If there are systems that this doesn't work, I think gio should handle those transparently.
Comment 1 Dan Winship 2010-04-23 14:43:21 UTC
Created attachment 159435 [details] [review]
GUnixInputStream, GUnixOutputStream: clarify the problems with regular files

funny, we were just talking about this yesterday on IRC.

Perhaps we should have some way of creating a GFileInput/OutputStream
from an fd? Or else GUnix*Stream could be clever and do thread tricks
with file-based fds? (Can you tell if a file descriptor refers to a file?)

Attaching the patch so someone else can verify that I got the details
correct.
Comment 2 Behdad Esfahbod 2010-04-23 15:08:18 UTC
Well, poll() only tells you whether data is available for reading.  Whether you get blocked if you read is a different story :).  Can aio_* help?

As for detecting file fd, sure, fstat() provides:

           S_ISREG(m)  is it a regular file?

           S_ISDIR(m)  directory?

           S_ISCHR(m)  character device?

           S_ISBLK(m)  block device?

           S_ISFIFO(m) FIFO (named pipe)?

           S_ISLNK(m)  symbolic link? (Not in POSIX.1-1996.)

           S_ISSOCK(m) socket? (Not in POSIX.1-1996.)
Comment 3 Dan Winship 2010-04-23 16:58:54 UTC
(In reply to comment #2)
> Can aio_* help?

I thought aio was (a) useless, (b) not terribly portable, (c) not designed with libraries in mind (ie, it requires you to arbitrarily claim a particular signal number for yourself and there's no way to coordinate that with anyone else)?

The "do async reads by reading in a thread" code is in ginputstream.c (and likewise with writing and goutputstream.c), so all we'd have to do is just fall back to the default implementation if the fd is a file.  What would the test be? S_ISFIFO or S_ISSOCK supports poll "correctly", everything else doesn't?
Comment 4 Dan Winship 2010-04-23 17:05:37 UTC
actually, it's not quite that simple: gunixinputstream needs to poll the cancellable fd and the input fd at the same time in order to be cancellable properly. (glocalfileinputstream doesn't worry about that, presumably under the assumption that read() won't block *too* long.)
Comment 5 Behdad Esfahbod 2010-04-23 17:18:31 UTC
(In reply to comment #3)

> I thought aio was (a) useless, (b) not terribly portable, (c) not designed with
> libraries in mind (ie, it requires you to arbitrarily claim a particular signal
> number for yourself and there's no way to coordinate that with anyone else)?

I see.


> The "do async reads by reading in a thread" code is in ginputstream.c (and
> likewise with writing and goutputstream.c), so all we'd have to do is just fall
> back to the default implementation if the fd is a file.  What would the test
> be? S_ISFIFO or S_ISSOCK supports poll "correctly", everything else doesn't?

S_ISCHR also looks safe to me.  I'd say invert it around, assume IS_REG and IS_DIR may block, anything else is fine.

Should resolve IS_LINK?


(In reply to comment #4)
> (glocalfileinputstream doesn't worry about that, presumably under the
> assumption that read() won't block *too* long.)

Fix that then?
Comment 6 Behdad Esfahbod 2010-04-23 17:20:57 UTC
Err, nevermind the part about symlinks.  We don't need to follow them or anything.  fstat() can't return those.
Comment 7 Dan Winship 2010-06-22 19:43:35 UTC
Created attachment 164340 [details] [review]
GUnixInputStream, GUnixOutputStream: support ordinary files better

If the fd is not a pipe or socket, fall back to using threads to do
async I/O rather than poll, since poll doesn't work the way you want
for ordinary files.

---

I had this patch lying around on my hard drive, but I'm not sure now why
I hadn't attached it. The gio tests still pass with it...

The lseek hack is something we'd come up with on #nautilus a while back
when discussing GFileDescriptorBased stuff
Comment 8 Matthias Clasen 2010-07-29 04:50:00 UTC
Review of attachment 164340 [details] [review]:

::: gio/gunixinputstream.c
@@ +55,1 @@
  *

Referring to GLocalFileInputStream is not useful, since that class is not part of the public api and doesn't show up in the docs.

@@ +507,3 @@
+      return;
+    }
+

Not sure if this is really good enough; if you look at ginputstream.c, there are places where it does different things depending on class->read_async, e.g:

 if (class->read_async == g_input_stream_real_read_async)
    {
      /* Read is thread-using async fallback.
       * Make skip use threads too, so that we can use a possible sync skip
       * implementation. */
Comment 9 Dan Winship 2010-07-31 07:42:57 UTC
(In reply to comment #8)
> Not sure if this is really good enough; if you look at ginputstream.c, there
> are places where it does different things depending on class->read_async, e.g:

Ah. OK, in GInputStream, that's the only such check, and in GOutputStream, the only checks involve flush/flush_async, which we don't override, so we're ok.

For the skip/read thing, the current state of the patch will result in skip_async() always falling back to read_async() (and skip() always falling back to read()) while ideally we would use lseek() if !priv->is_pipe_or_socket.
Comment 10 Dan Winship 2011-11-13 21:38:01 UTC
OK, pushed this with the existing skip/skip_async implementations;
there was already a note about possibly optimizing them in the future
anyway.

Attachment 164340 [details] pushed as 9b4cc6e - GUnixInputStream, GUnixOutputStream: support ordinary files better