gio: use GPollable* to implement fallback read_async/write_async

If a GInputStream does not provide a read_async() implementation, but
does implement GPollableInputStream, then instead of doing
read-synchronously-in-a-thread, just use
g_pollable_input_stream_read_nonblocking() and
g_pollable_input_stream_create_source() to implement an async read in
the same thread. Similarly for GOutputStream.

Remove a bunch of existing read_async()/write_async() implementations
that are basically equivalent to the new fallback method.

https://bugzilla.gnome.org/show_bug.cgi?id=673997
This commit is contained in:
Dan Winship
2012-02-04 16:46:29 -05:00
parent 82ec4dcaed
commit 00ee06e6a3
10 changed files with 153 additions and 892 deletions

View File

@@ -100,16 +100,6 @@ static gssize g_buffered_input_stream_read (GInputStream *s
gsize count,
GCancellable *cancellable,
GError **error);
static void g_buffered_input_stream_read_async (GInputStream *stream,
void *buffer,
gsize count,
int io_priority,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
static gssize g_buffered_input_stream_read_finish (GInputStream *stream,
GAsyncResult *result,
GError **error);
static gssize g_buffered_input_stream_real_fill (GBufferedInputStream *stream,
gssize count,
GCancellable *cancellable,
@@ -150,8 +140,6 @@ g_buffered_input_stream_class_init (GBufferedInputStreamClass *klass)
istream_class->skip_async = g_buffered_input_stream_skip_async;
istream_class->skip_finish = g_buffered_input_stream_skip_finish;
istream_class->read_fn = g_buffered_input_stream_read;
istream_class->read_async = g_buffered_input_stream_read_async;
istream_class->read_finish = g_buffered_input_stream_read_finish;
bstream_class = G_BUFFERED_INPUT_STREAM_CLASS (klass);
bstream_class->fill = g_buffered_input_stream_real_fill;
@@ -1017,189 +1005,6 @@ g_buffered_input_stream_real_fill_finish (GBufferedInputStream *stream,
return nread;
}
typedef struct
{
gssize bytes_read;
gssize count;
void *buffer;
} ReadAsyncData;
static void
free_read_async_data (gpointer _data)
{
ReadAsyncData *data = _data;
g_slice_free (ReadAsyncData, data);
}
static void
large_read_callback (GObject *source_object,
GAsyncResult *result,
gpointer user_data)
{
GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (user_data);
ReadAsyncData *data;
GError *error;
gssize nread;
data = g_simple_async_result_get_op_res_gpointer (simple);
error = NULL;
nread = g_input_stream_read_finish (G_INPUT_STREAM (source_object),
result, &error);
/* Only report the error if we've not already read some data */
if (nread < 0 && data->bytes_read == 0)
g_simple_async_result_take_error (simple, error);
else if (error)
g_error_free (error);
if (nread > 0)
data->bytes_read += nread;
/* Complete immediately, not in idle, since we're already
* in a mainloop callout
*/
g_simple_async_result_complete (simple);
g_object_unref (simple);
}
static void
read_fill_buffer_callback (GObject *source_object,
GAsyncResult *result,
gpointer user_data)
{
GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (user_data);
GBufferedInputStream *bstream;
GBufferedInputStreamPrivate *priv;
ReadAsyncData *data;
GError *error;
gssize nread;
gsize available;
bstream = G_BUFFERED_INPUT_STREAM (source_object);
priv = bstream->priv;
data = g_simple_async_result_get_op_res_gpointer (simple);
error = NULL;
nread = g_buffered_input_stream_fill_finish (bstream,
result, &error);
if (nread < 0 && data->bytes_read == 0)
g_simple_async_result_take_error (simple, error);
else if (error)
g_error_free (error);
if (nread > 0)
{
available = priv->end - priv->pos;
data->count = MIN (data->count, available);
memcpy ((char *)data->buffer + data->bytes_read, (char *)priv->buffer + priv->pos, data->count);
data->bytes_read += data->count;
priv->pos += data->count;
}
/* Complete immediately, not in idle, since we're already
* in a mainloop callout
*/
g_simple_async_result_complete (simple);
g_object_unref (simple);
}
static void
g_buffered_input_stream_read_async (GInputStream *stream,
void *buffer,
gsize count,
int io_priority,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
GBufferedInputStream *bstream;
GBufferedInputStreamPrivate *priv;
GBufferedInputStreamClass *class;
GInputStream *base_stream;
gsize available;
GSimpleAsyncResult *simple;
ReadAsyncData *data;
bstream = G_BUFFERED_INPUT_STREAM (stream);
priv = bstream->priv;
data = g_slice_new (ReadAsyncData);
data->buffer = buffer;
data->bytes_read = 0;
simple = g_simple_async_result_new (G_OBJECT (stream),
callback, user_data,
g_buffered_input_stream_read_async);
g_simple_async_result_set_op_res_gpointer (simple, data, free_read_async_data);
available = priv->end - priv->pos;
if (count <= available)
{
memcpy (buffer, priv->buffer + priv->pos, count);
priv->pos += count;
data->bytes_read = count;
g_simple_async_result_complete_in_idle (simple);
g_object_unref (simple);
return;
}
/* Full request not available, read all currently available
* and request refill for more
*/
memcpy (buffer, priv->buffer + priv->pos, available);
priv->pos = 0;
priv->end = 0;
count -= available;
data->bytes_read = available;
data->count = count;
if (count > priv->len)
{
/* Large request, shortcut buffer */
base_stream = G_FILTER_INPUT_STREAM (stream)->base_stream;
g_input_stream_read_async (base_stream,
(char *)buffer + data->bytes_read,
count,
io_priority, cancellable,
large_read_callback,
simple);
}
else
{
class = G_BUFFERED_INPUT_STREAM_GET_CLASS (stream);
class->fill_async (bstream, priv->len, io_priority, cancellable,
read_fill_buffer_callback, simple);
}
}
static gssize
g_buffered_input_stream_read_finish (GInputStream *stream,
GAsyncResult *result,
GError **error)
{
GSimpleAsyncResult *simple;
ReadAsyncData *data;
simple = G_SIMPLE_ASYNC_RESULT (result);
g_warn_if_fail (g_simple_async_result_get_source_tag (simple) == g_buffered_input_stream_read_async);
data = g_simple_async_result_get_op_res_gpointer (simple);
return data->bytes_read;
}
typedef struct
{
gssize bytes_skipped;