[glib-networking/mcatanzaro/tls-thread] progress



commit 8f14cdc769079bd2cea26a311a420a634466a5ac
Author: Michael Catanzaro <mcatanzaro igalia com>
Date:   Mon Jul 8 10:12:18 2019 -0500

    progress

 tls/base/gtlsconnection-base.c             |  60 ++-
 tls/base/gtlsconnection-base.h             |  15 +-
 tls/base/gtlsoperationsthread-base.c       | 776 +++++++++++++++++++++++++++++
 tls/base/gtlsoperationsthread-base.h       |  58 +++
 tls/base/meson.build                       |   3 +-
 tls/gnutls/gtlsconnection-gnutls.c         |  25 +-
 tls/gnutls/gtlsoperationsthread-gnutls.c   |  53 ++
 tls/gnutls/gtlsoperationsthread-gnutls.h   |  37 ++
 tls/gnutls/meson.build                     |   1 +
 tls/openssl/gtlsconnection-openssl.c       |  22 +-
 tls/openssl/gtlsoperationsthread-openssl.c |  53 ++
 tls/openssl/gtlsoperationsthread-openssl.h |  37 ++
 tls/openssl/meson.build                    |   1 +
 13 files changed, 1108 insertions(+), 33 deletions(-)
---
diff --git a/tls/base/gtlsconnection-base.c b/tls/base/gtlsconnection-base.c
index c72c3ec..19da6f1 100644
--- a/tls/base/gtlsconnection-base.c
+++ b/tls/base/gtlsconnection-base.c
@@ -3,6 +3,7 @@
  * GIO - GLib Input, Output and Streaming Library
  *
  * Copyright 2009-2011 Red Hat, Inc
+ * Copyright 2019 Igalia S.L.
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -30,6 +31,7 @@
 #include "gtlsconnection-base.h"
 #include "gtlsinputstream.h"
 #include "gtlslog.h"
+#include "gtlsoperationsthread-base.h"
 #include "gtlsoutputstream.h"
 
 #include <glib/gi18n-lib.h>
@@ -158,6 +160,8 @@ typedef struct
 
   gchar        **advertised_protocols;
   gchar         *negotiated_protocol;
+
+  GTlsOperationsThreadBase *thread;
 } GTlsConnectionBasePrivate;
 
 static void g_tls_connection_base_dtls_connection_iface_init (GDtlsConnectionInterface *iface);
@@ -236,12 +240,25 @@ g_tls_connection_base_init (GTlsConnectionBase *tls)
   priv->waiting_for_op = g_cancellable_new ();
 }
 
+static void
+g_tls_connection_base_constructed (GObject *object)
+{
+  GTlsConnectionBase *tls = G_TLS_CONNECTION_BASE (object);
+  GTlsConnectionBasePrivate *priv = g_tls_connection_base_get_instance_private (tls);
+
+  G_OBJECT_CLASS (g_tls_connection_base_parent_class)->constructed (object);
+
+  priv->thread = G_TLS_CONNECTION_BASE_GET_CLASS (tls)->create_op_thread (tls);
+}
+
 static void
 g_tls_connection_base_finalize (GObject *object)
 {
   GTlsConnectionBase *tls = G_TLS_CONNECTION_BASE (object);
   GTlsConnectionBasePrivate *priv = g_tls_connection_base_get_instance_private (tls);
 
+  g_clear_object (&priv->thread);
+
   g_clear_object (&priv->base_io_stream);
   g_clear_object (&priv->base_socket);
 
@@ -933,7 +950,8 @@ typedef struct {
 
 /* Use a custom dummy callback instead of g_source_set_dummy_callback(), as that
  * uses a GClosure and is slow. (The GClosure is necessary to deal with any
- * function prototype.) */
+ * function prototype.)
+ */
 static gboolean
 dummy_callback (gpointer data)
 {
@@ -1075,7 +1093,6 @@ g_tls_connection_tls_source_dtls_closure_callback (GDatagramBased *datagram_base
   g_value_unset (&param[1]);
 
   return result;
-
 }
 
 static GSourceFuncs tls_source_funcs =
@@ -1098,6 +1115,7 @@ static GSourceFuncs dtls_source_funcs =
   (GSourceDummyMarshal)g_cclosure_marshal_generic
 };
 
+/* FIXME: all needs to be threadsafe... */
 GSource *
 g_tls_connection_base_create_source (GTlsConnectionBase  *tls,
                                      GIOCondition         condition,
@@ -1157,13 +1175,33 @@ g_tls_connection_base_dtls_create_source (GDatagramBased  *datagram_based,
 
 static GIOCondition
 g_tls_connection_base_condition_check (GDatagramBased  *datagram_based,
-                                         GIOCondition     condition)
+                                       GIOCondition     condition)
 {
   GTlsConnectionBase *tls = G_TLS_CONNECTION_BASE (datagram_based);
 
   return g_tls_connection_base_check (tls, condition) ? condition : 0;
 }
 
+/* Returns a GSource for the underlying GDatagramBased or base stream, not for
+ * the GTlsConnectionBase itself.
+ */
+GSource *
+g_tls_connection_base_create_base_source (GTlsConnectionBase *tls,
+                                          GIOCondition        condition,
+                                          GCancellable       *cancellable)
+{
+  GTlsConnectionBasePrivate *priv = g_tls_connection_base_get_instance_private (tls);
+
+  if (g_tls_connection_base_is_dtls (tls))
+    return g_datagram_based_create_source (priv->base_socket, condition, cancellable);
+  if (condition & G_IO_IN)
+    return g_pollable_input_stream_create_source (priv->base_istream, cancellable);
+  if (condition & G_IO_OUT)
+    return g_pollable_output_stream_create_source (priv->base_ostream, cancellable);
+
+  g_assert_not_reached ();
+}
+
 static gboolean
 g_tls_connection_base_condition_wait (GDatagramBased  *datagram_based,
                                       GIOCondition     condition,
@@ -1909,7 +1947,7 @@ do_implicit_handshake (GTlsConnectionBase  *tls,
 gssize
 g_tls_connection_base_read (GTlsConnectionBase  *tls,
                             void                *buffer,
-                            gsize                count,
+                            gsize                size,
                             gint64               timeout,
                             GCancellable        *cancellable,
                             GError             **error)
@@ -1928,7 +1966,7 @@ g_tls_connection_base_read (GTlsConnectionBase  *tls,
 
       if (priv->app_data_buf && !priv->handshaking)
         {
-          nread = MIN (count, priv->app_data_buf->len);
+          nread = MIN (size, priv->app_data_buf->len);
           memcpy (buffer, priv->app_data_buf->data, nread);
           if (nread == priv->app_data_buf->len)
             g_clear_pointer (&priv->app_data_buf, g_byte_array_unref);
@@ -1938,8 +1976,7 @@ g_tls_connection_base_read (GTlsConnectionBase  *tls,
         }
       else
         {
-          status = G_TLS_CONNECTION_BASE_GET_CLASS (tls)->
-            read_fn (tls, buffer, count, timeout, &nread, cancellable, error);
+          status = g_tls_operations_thread_base_read (priv->thread, buffer, size, timeout, &nread, 
cancellable, error);
         }
 
       yield_op (tls, G_TLS_CONNECTION_BASE_OP_READ, status);
@@ -2098,7 +2135,7 @@ g_tls_connection_base_receive_messages (GDatagramBased  *datagram_based,
 gssize
 g_tls_connection_base_write (GTlsConnectionBase  *tls,
                              const void          *buffer,
-                             gsize                count,
+                             gsize                size,
                              gint64               timeout,
                              GCancellable        *cancellable,
                              GError             **error)
@@ -2107,7 +2144,7 @@ g_tls_connection_base_write (GTlsConnectionBase  *tls,
   GTlsConnectionBaseStatus status;
   gssize nwrote;
 
-  g_tls_log_debug (tls, "starting to write %" G_GSIZE_FORMAT " bytes to TLS connection", count);
+  g_tls_log_debug (tls, "starting to write %" G_GSIZE_FORMAT " bytes to TLS connection", size);
 
   do
     {
@@ -2115,9 +2152,7 @@ g_tls_connection_base_write (GTlsConnectionBase  *tls,
                      timeout, cancellable, error))
         return -1;
 
-      status = G_TLS_CONNECTION_BASE_GET_CLASS (tls)->
-        write_fn (tls, buffer, count, timeout, &nwrote, cancellable, error);
-
+      status = g_tls_operations_thread_base_write (priv->thread, buffer, size, timeout, &nwrote, 
cancellable, error);
       yield_op (tls, G_TLS_CONNECTION_BASE_OP_WRITE, status);
     }
   while (status == G_TLS_CONNECTION_BASE_REHANDSHAKE);
@@ -2654,6 +2689,7 @@ g_tls_connection_base_class_init (GTlsConnectionBaseClass *klass)
 
   gobject_class->get_property = g_tls_connection_base_get_property;
   gobject_class->set_property = g_tls_connection_base_set_property;
+  gobject_class->constructed  = g_tls_connection_base_constructed;
   gobject_class->finalize     = g_tls_connection_base_finalize;
 
   connection_class->handshake        = g_tls_connection_base_handshake;
diff --git a/tls/base/gtlsconnection-base.h b/tls/base/gtlsconnection-base.h
index bacefab..791c06b 100644
--- a/tls/base/gtlsconnection-base.h
+++ b/tls/base/gtlsconnection-base.h
@@ -3,6 +3,7 @@
  * GIO - GLib Input, Output and Streaming Library
  *
  * Copyright 2009-2011 Red Hat, Inc
+ * Copyright 2019 Igalia S.L.
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -54,10 +55,14 @@ typedef enum {
 
 #define G_TLS_DIRECTION_BOTH (G_TLS_DIRECTION_READ | G_TLS_DIRECTION_WRITE)
 
+typedef struct _GTlsOperationsThreadBase GTlsOperationsThreadBase;
+
 struct _GTlsConnectionBaseClass
 {
   GTlsConnectionClass parent_class;
 
+  GTlsOperationsThreadBase   *(*create_op_thread)           (GTlsConnectionBase   *tls);
+
   void                        (*prepare_handshake)          (GTlsConnectionBase   *tls,
                                                              gchar               **advertised_protocols);
   GTlsSafeRenegotiationStatus (*handshake_thread_safe_renegotiation_status)
@@ -90,10 +95,10 @@ struct _GTlsConnectionBaseClass
                                                              gboolean              success,
                                                              GError              **error);
 
+  /* FIXME: must remove timeout parameters from all vfuncs, including handshake vfuncs */
   GTlsConnectionBaseStatus    (*read_fn)                    (GTlsConnectionBase   *tls,
                                                              void                 *buffer,
-                                                             gsize                 count,
-                                                             gint64                timeout,
+                                                             gsize                 size,
                                                              gssize               *nread,
                                                              GCancellable         *cancellable,
                                                              GError              **error);
@@ -107,8 +112,7 @@ struct _GTlsConnectionBaseClass
 
   GTlsConnectionBaseStatus    (*write_fn)                   (GTlsConnectionBase   *tls,
                                                              const void           *buffer,
-                                                             gsize                 count,
-                                                             gint64                timeout,
+                                                             gsize                 size,
                                                              gssize               *nwrote,
                                                              GCancellable         *cancellable,
                                                              GError              **error);
@@ -158,6 +162,9 @@ gboolean                  g_tls_connection_base_base_check              (GTlsCon
 GSource                  *g_tls_connection_base_create_source           (GTlsConnectionBase  *tls,
                                                                          GIOCondition         condition,
                                                                          GCancellable        *cancellable);
+GSource                  *g_tls_connection_base_create_base_source      (GTlsConnectionBase  *tls,
+                                                                         GIOCondition         condition,
+                                                                         GCancellable        *cancellable);
 
 gboolean                  g_tls_connection_base_close_internal          (GIOStream      *stream,
                                                                          GTlsDirection   direction,
diff --git a/tls/base/gtlsoperationsthread-base.c b/tls/base/gtlsoperationsthread-base.c
new file mode 100644
index 0000000..1f77d3e
--- /dev/null
+++ b/tls/base/gtlsoperationsthread-base.c
@@ -0,0 +1,776 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright 2019 Igalia S.L.
+ * Copyright 2019 Metrological Group B.V.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * In addition, when the library is used with OpenSSL, a special
+ * exception applies. Refer to the LICENSE_EXCEPTION file for details.
+ */
+
+#include "config.h"
+
+#include "gtlsoperationsthread-base.h"
+
+#include <glib/gi18n-lib.h>
+#include <glib/gstdio.h>
+
+/* The purpose of this class is to ensure the underlying TLS library is only
+ * ever used on a single thread. There are multiple benefits of this:
+ *
+ * - OpenSSL objects like the SSL* are not threadsafe and must only be accessed
+ *   from a single thread.
+ *
+ * - With GnuTLS, this dramatically simplifies implementation of post-handshake
+ *   authentication and alerts, which are hard to handle when the
+ *   gnutls_session_t may be used on multiple threads at once. Moving
+ *   gnutls_session_t use to a single thread should also make it easier to
+ *   implement support for downloading missing certificates using the
+ *   Authority Information Access extension.
+ *
+ * - GTlsConnectionBase and its subclasses are very complicated, and it has
+ *   become difficult to ensure the correctness of the code considering that the
+ *   threadsafety semantics of its parent class, GIOStream, allow it to be used
+ *   from separate reader and writer threads simultaneously.
+ *
+ * While the TLS thread class is intended to simplify our code, it has one major
+ * disadvantage: the TLS thread *must never block* because GIOStream users are
+ * allowed to do a sync read and a sync write simultaneously in separate
+ * threads. Consider a hypothetical scenario:
+ *
+ * (1) Application starts a read on thread A
+ * (2) Application starts a write on thread B
+ * (3) Application's peer waits for the write to complete before sending data.
+ *
+ * In this scenario, the read on thread A is stalled until the write on thread B
+ * is completed. The application is allowed to do this and expect it to work,
+ * because GIOStream says it will work. If our TLS thread were to block on the
+ * read, then the write would never start, and the read could never complete.
+ *
+ * This means that underlying TLS operations must use entirely nonblocking I/O.
+ * We specify a timeout of 0 for every operation to ensure it returns
+ * immediately with an error if I/O cannot be performed immediately. If so, we
+ * create a GSource that will trigger later on, when possibly ready to perform
+ * I/O. In this way, we can simultaneously handle separate synchronous read and
+ * write operations on one thread without either one blocking the other.
+ */
+typedef struct {
+  GTlsConnectionBase *connection; /* unowned */
+
+  GThread *op_thread;
+  GMainContext *op_thread_context;
+
+  /* When pushing ops onto the queue, call g_main_context_wakeup() on
+   * op_thread_context to ensure the op is noticed.
+   */
+  GAsyncQueue *queue;
+} GTlsOperationsThreadBasePrivate;
+
+typedef enum {
+  G_TLS_THREAD_OP_READ,
+  G_TLS_THREAD_OP_WRITE,
+  G_TLS_THREAD_OP_SHUTDOWN
+} GTlsThreadOperationType;
+
+typedef struct {
+  GTlsThreadOperationType type;
+  GIOCondition io_condition;
+
+  GTlsConnectionBase *connection; /* FIXME: threadsafety nightmare, not OK */
+
+  /* Input */
+  void *data; /* unowned */
+  gsize size;
+  gint64 timeout;
+  gint64 start_time;
+  GCancellable *cancellable;
+
+  /* Used to indicate op completion. */
+  GMutex finished_mutex;
+  GCond finished_condition;
+  gboolean finished;
+
+  /* Result */
+  GTlsConnectionBaseStatus result;
+  gssize count; /* Bytes read or written */
+  GError *error;
+} GTlsThreadOperation;
+
+static gboolean process_op (GAsyncQueue         *queue,
+                            GTlsThreadOperation *delayed_op,
+                            GMainLoop           *main_loop);
+
+enum
+{
+  PROP_0,
+  PROP_TLS_CONNECTION,
+  LAST_PROP
+};
+
+static GParamSpec *obj_properties[LAST_PROP];
+
+G_DEFINE_TYPE_WITH_PRIVATE (GTlsOperationsThreadBase, g_tls_operations_thread_base, G_TYPE_OBJECT)
+
+static GTlsThreadOperation *
+g_tls_thread_operation_new (GTlsThreadOperationType  type,
+                            GTlsConnectionBase      *connection,
+                            void                    *data,
+                            gsize                    size,
+                            gint64                   timeout,
+                            GCancellable            *cancellable)
+{
+  GTlsThreadOperation *op;
+
+  op = g_new0 (GTlsThreadOperation, 1);
+  op->type = type;
+  op->connection = g_object_ref (connection);
+  op->data = data;
+  op->size = size;
+  op->timeout = timeout;
+  op->cancellable = cancellable ? g_object_ref (cancellable) : NULL;
+
+  g_mutex_init (&op->finished_mutex);
+  g_cond_init (&op->finished_condition);
+
+  switch (type)
+    {
+    case G_TLS_THREAD_OP_READ:
+      op->io_condition = G_IO_IN;
+      break;
+    case G_TLS_THREAD_OP_WRITE:
+      op->io_condition = G_IO_OUT;
+      break;
+    /* FIXME: more pls */
+    case G_TLS_THREAD_OP_SHUTDOWN:
+      break;
+    }
+
+  return op;
+}
+
+static GTlsThreadOperation *
+g_tls_thread_shutdown_operation_new (void)
+{
+  GTlsThreadOperation *op;
+
+  op = g_new0 (GTlsThreadOperation, 1);
+  op->type = G_TLS_THREAD_OP_SHUTDOWN;
+
+  return op;
+}
+
+static void
+g_tls_thread_operation_free (GTlsThreadOperation *op)
+{
+  g_clear_object (&op->connection);
+  g_clear_object (&op->cancellable);
+
+  if (op->type != G_TLS_THREAD_OP_SHUTDOWN)
+    {
+      g_mutex_clear (&op->finished_mutex);
+      g_cond_clear (&op->finished_condition);
+    }
+
+  g_free (op);
+}
+
+static void
+wait_for_op_completion (GTlsThreadOperation *op)
+{
+  g_mutex_lock (&op->finished_mutex);
+  while (!op->finished)
+    g_cond_wait (&op->finished_condition, &op->finished_mutex);
+  g_mutex_unlock (&op->finished_mutex);
+}
+
+GTlsConnectionBaseStatus
+g_tls_operations_thread_base_read (GTlsOperationsThreadBase  *self,
+                                   void                      *buffer,
+                                   gsize                      size,
+                                   gint64                     timeout,
+                                   gssize                    *nread,
+                                   GCancellable              *cancellable,
+                                   GError                   **error)
+{
+  GTlsOperationsThreadBasePrivate *priv = g_tls_operations_thread_base_get_instance_private (self);
+  GTlsThreadOperation *op;
+  GTlsConnectionBaseStatus result;
+
+  op = g_tls_thread_operation_new (G_TLS_THREAD_OP_READ,
+                                   priv->connection,
+                                   buffer, size, timeout,
+                                   cancellable);
+  g_async_queue_push (priv->queue, op);
+  g_main_context_wakeup (priv->op_thread_context);
+
+  wait_for_op_completion (op);
+
+  *nread = op->count;
+
+  if (op->error)
+    {
+      g_propagate_error (error, op->error);
+      op->error = NULL;
+    }
+
+  result = op->result;
+
+  g_tls_thread_operation_free (op);
+
+  return result;
+}
+
+GTlsConnectionBaseStatus
+g_tls_operations_thread_base_write (GTlsOperationsThreadBase  *self,
+                                    const void                *buffer,
+                                    gsize                      size,
+                                    gint64                     timeout,
+                                    gssize                    *nwrote,
+                                    GCancellable              *cancellable,
+                                    GError                   **error)
+{
+  GTlsOperationsThreadBasePrivate *priv = g_tls_operations_thread_base_get_instance_private (self);
+  GTlsThreadOperation *op;
+  GTlsConnectionBaseStatus result;
+
+  op = g_tls_thread_operation_new (G_TLS_THREAD_OP_WRITE,
+                                   priv->connection,
+                                   (void *)buffer, size, timeout,
+                                   cancellable);
+  g_async_queue_push (priv->queue, op);
+  g_main_context_wakeup (priv->op_thread_context);
+
+  wait_for_op_completion (op);
+
+  *nwrote = op->count;
+
+  if (op->error)
+    {
+      g_propagate_error (error, op->error);
+      op->error = NULL;
+    }
+
+  result = op->result;
+
+  g_tls_thread_operation_free (op);
+
+  return result;
+}
+
+typedef struct {
+  GSource source;
+
+  GAsyncQueue *queue;
+} GTlsOpQueueSource;
+
+typedef gboolean (*GTlsOpQueueSourceFunc) (GAsyncQueue         *queue,
+                                           GTlsThreadOperation *op,
+                                           GMainLoop           *main_loop);
+
+static gboolean
+queue_has_pending_op (GAsyncQueue *queue)
+{
+  GTlsThreadOperation *op;
+  gboolean ready = FALSE;
+
+  g_async_queue_lock (queue);
+
+  op = g_async_queue_try_pop_unlocked (queue);
+  if (op)
+    {
+      g_async_queue_push_front_unlocked (queue, op);
+      ready = TRUE;
+    }
+
+  g_async_queue_unlock (queue);
+
+  return ready;
+}
+
+static gboolean
+tls_op_queue_source_prepare (GSource *source,
+                             gint    *timeout)
+{
+  GTlsOpQueueSource *op_source = (GTlsOpQueueSource *)source;
+  gboolean ready;
+
+  ready = queue_has_pending_op (op_source->queue);
+
+  /* If we are ready to dispatch, timeout should be 0 to ensure poll() returns
+   * immediately. Otherwise, we are in no hurry and can wait "forever." If
+   * a new op is pushed onto the queue, the code performing the push is
+   * responsible for calling g_main_context_wakeup() to end the wait.
+   */
+  *timeout = ready ? 0 : -1;
+
+  return ready;
+}
+
+static gboolean
+tls_op_queue_source_check (GSource *source)
+{
+  GTlsOpQueueSource *op_source = (GTlsOpQueueSource *)source;
+
+  return queue_has_pending_op (op_source->queue);
+}
+
+static gboolean
+tls_op_queue_source_dispatch (GSource     *source,
+                              GSourceFunc  callback,
+                              gpointer     user_data)
+{
+  GTlsOpQueueSource *op_source = (GTlsOpQueueSource *)source;
+
+  return ((GTlsOpQueueSourceFunc)callback) (op_source->queue,
+                                            NULL, /* no delayed source */
+                                            user_data);
+}
+
+static void
+tls_op_queue_source_finalize (GSource *source)
+{
+  GTlsOpQueueSource *op_source = (GTlsOpQueueSource *)source;
+
+  g_async_queue_unref (op_source->queue);
+}
+
+static gboolean
+tls_op_queue_source_closure_callback (GAsyncQueue *queue,
+                                      GMainLoop   *main_loop,
+                                      gpointer     data)
+{
+  GClosure *closure = data;
+
+  GValue param[3] = { G_VALUE_INIT, G_VALUE_INIT, G_VALUE_INIT };
+  GValue result_value = G_VALUE_INIT;
+  gboolean result;
+
+  g_value_init (&result_value, G_TYPE_BOOLEAN);
+
+  g_value_init (&param[0], G_TYPE_POINTER);
+  g_value_set_pointer (&param[0], queue);
+  g_value_init (&param[1], G_TYPE_POINTER);
+  g_value_set_pointer (&param[1], NULL);
+  g_value_init (&param[2], G_TYPE_MAIN_LOOP);
+  g_value_set_pointer (&param[2], main_loop);
+
+  g_closure_invoke (closure, &result_value, 3, param, NULL);
+
+  result = g_value_get_boolean (&result_value);
+  g_value_unset (&result_value);
+  g_value_unset (&param[0]);
+  g_value_unset (&param[1]);
+  g_value_unset (&param[2]);
+
+  return result;
+}
+
+static GSourceFuncs tls_op_queue_source_funcs =
+{
+  tls_op_queue_source_prepare,
+  tls_op_queue_source_check,
+  tls_op_queue_source_dispatch,
+  tls_op_queue_source_finalize,
+  (GSourceFunc)tls_op_queue_source_closure_callback,
+  (GSourceDummyMarshal)g_cclosure_marshal_generic
+};
+
+/* TODO: Move this into GLib so we don't need a custom source. glib#94 */
+static GSource *
+tls_op_queue_source_new (GAsyncQueue *queue)
+{
+  GTlsOpQueueSource *source;
+
+  source = (GTlsOpQueueSource *)g_source_new (&tls_op_queue_source_funcs, sizeof (GTlsOpQueueSource));
+  source->queue = g_async_queue_ref (queue);
+
+  return (GSource *)source;
+}
+
+typedef struct
+{
+  GAsyncQueue *queue;
+  GTlsThreadOperation *op;
+  GMainLoop *main_loop;
+} DelayedOpAsyncData;
+
+static DelayedOpAsyncData *
+delayed_op_async_data_new (GAsyncQueue         *queue,
+                           GTlsThreadOperation *op,
+                           GMainLoop           *main_loop)
+{
+  DelayedOpAsyncData *data;
+
+  data = g_new (DelayedOpAsyncData, 1);
+
+  /* No refs because these are guaranteed to outlive data. */
+  data->queue = queue;
+  data->op = op;
+  data->main_loop = main_loop;
+
+  return data;
+}
+
+static void
+delayed_op_async_data_free (DelayedOpAsyncData *data)
+{
+  g_free (data);
+}
+
+static gboolean
+resume_tls_op (GObject  *pollable_stream,
+               gpointer  user_data)
+{
+  DelayedOpAsyncData *data = (DelayedOpAsyncData *)user_data;
+  gboolean ret;
+
+  ret = process_op (data->queue, data->op, data->main_loop);
+  g_assert (ret == G_SOURCE_CONTINUE);
+
+  delayed_op_async_data_free (data);
+
+  return G_SOURCE_REMOVE;
+}
+
+static gboolean
+resume_dtls_op (GDatagramBased *datagram_based,
+                GIOCondition    condition,
+                gpointer        user_data)
+{
+  DelayedOpAsyncData *data = (DelayedOpAsyncData *)user_data;
+  gboolean ret;
+
+  ret = process_op (data->queue, data->op, data->main_loop);
+  g_assert (ret == G_SOURCE_CONTINUE);
+
+  delayed_op_async_data_free (data);
+
+  return G_SOURCE_REMOVE;
+}
+
+/* Use a custom dummy callback instead of g_source_set_dummy_callback(), as that
+ * uses a GClosure and is slow. (The GClosure is necessary to deal with any
+ * function prototype.)
+ */
+static gboolean
+dummy_callback (gpointer data)
+{
+  return G_SOURCE_CONTINUE;
+}
+
+static void
+adjust_op_timeout (GTlsThreadOperation *op)
+{
+  GSocket *socket = NULL;
+
+  /* Nonblocking? */
+  if (op->timeout == 0)
+    return;
+
+  if (g_tls_connection_base_is_dtls (op->connection))
+    {
+      GDatagramBased *base_socket = g_tls_connection_base_get_base_socket (op->connection);
+
+      if (G_IS_SOCKET (base_socket))
+        socket = (GSocket *)base_socket;
+    }
+  else
+    {
+      GIOStream *base_stream = g_tls_connection_base_get_base_iostream (op->connection);
+
+      if (G_IS_SOCKET_CONNECTION (base_stream))
+        socket = g_socket_connection_get_socket ((GSocketConnection *)base_stream);
+    }
+
+  /* We have to "massage" the timeout here because we are using only nonblocking
+   * I/O, so the underlying socket will never time out even if a timeout has
+   * been set. But if we are emulating a blocking operation, we need to make
+   * sure we don't block for longer than the underyling timeout.
+   */
+  if (socket)
+    {
+      gint64 socket_timeout = g_socket_get_timeout (socket);
+
+      if (socket_timeout > 0)
+        {
+          if (op->timeout == -1)
+            op->timeout = socket_timeout;
+
+          g_assert (op->timeout > 0);
+          op->timeout = MIN (op->timeout, socket_timeout);
+        }
+    }
+}
+
+static gboolean
+process_op (GAsyncQueue         *queue,
+            GTlsThreadOperation *delayed_op,
+            GMainLoop           *main_loop)
+{
+  GTlsThreadOperation *op;
+
+  if (delayed_op)
+    {
+      op = delayed_op;
+      g_clear_error (&op->error);
+
+      if (op->timeout != -1)
+        {
+          op->timeout -= g_get_monotonic_time () - op->start_time;
+          op->timeout = MAX (op->timeout, 0);
+        }
+
+      if (!g_tls_connection_base_base_check (op->connection, op->io_condition))
+        {
+          /* Not ready for I/O. Either we timed out, or were cancelled, or we
+           * could have a spurious wakeup caused by GTlsConnectionBase yield_op.
+           */
+          /* FIXME: very fragile, assumes op->cancellable is the GTlsConnectionBase's cancellable */
+          if (g_cancellable_is_cancelled (op->cancellable))
+            {
+              op->count = 0;
+              g_set_error (&op->error, G_IO_ERROR, G_IO_ERROR_CANCELLED,
+                           _("Operation cancelled"));
+              goto finished;
+            }
+
+          if (op->timeout == 0)
+            {
+              op->count = 0;
+              g_set_error (&op->error, G_IO_ERROR, G_IO_ERROR_TIMED_OUT,
+                           _("Socket I/O timed out"));
+              goto finished;
+            }
+
+          /* Spurious wakeup. Try again later. */
+          op->result = G_TLS_CONNECTION_BASE_WOULD_BLOCK;
+          goto wait;
+        }
+    }
+  else
+    {
+      op = g_async_queue_try_pop (queue);
+      g_assert (op);
+
+      if (op->type == G_TLS_THREAD_OP_SHUTDOWN)
+        {
+          g_main_loop_quit (main_loop);
+          return G_SOURCE_REMOVE;
+        }
+
+      adjust_op_timeout (op);
+    }
+
+  switch (op->type)
+    {
+    /* FIXME: handle all op types, including handshake and directional closes */
+    case G_TLS_THREAD_OP_READ:
+      op->result = G_TLS_CONNECTION_BASE_GET_CLASS (op->connection)->read_fn (op->connection,
+                                                                              op->data, op->size,
+                                                                              &op->count,
+                                                                              op->cancellable,
+                                                                              &op->error);
+      break;
+    case G_TLS_THREAD_OP_WRITE:
+      op->result = G_TLS_CONNECTION_BASE_GET_CLASS (op->connection)->write_fn (op->connection,
+                                                                               op->data, op->size,
+                                                                               &op->count,
+                                                                               op->cancellable,
+                                                                               &op->error);
+      break;
+    case G_TLS_THREAD_OP_SHUTDOWN:
+      g_assert_not_reached ();
+    }
+
+wait:
+  if (op->result == G_TLS_CONNECTION_BASE_WOULD_BLOCK &&
+      op->timeout != 0)
+    {
+      GSource *tls_source;
+      GSource *timeout_source;
+      GMainContext *main_context;
+      DelayedOpAsyncData *data;
+
+      tls_source = g_tls_connection_base_create_base_source (op->connection,
+                                                             op->io_condition,
+                                                             op->cancellable);
+      if (op->timeout > 0)
+        {
+          op->start_time = g_get_monotonic_time ();
+
+          /* tls_source should fire if (a) we're ready to ready/write without
+           * blocking, or (b) the timeout has elasped.
+           */
+          timeout_source = g_timeout_source_new (op->timeout);
+          g_source_set_callback (timeout_source, dummy_callback, NULL, NULL);
+          g_source_add_child_source (tls_source, timeout_source);
+          g_source_unref (timeout_source);
+        }
+
+      data = delayed_op_async_data_new (queue, op, main_loop);
+      if (g_tls_connection_base_is_dtls (op->connection))
+        g_source_set_callback (tls_source, G_SOURCE_FUNC (resume_dtls_op), data, NULL);
+      else
+        g_source_set_callback (tls_source, G_SOURCE_FUNC (resume_tls_op), data, NULL);
+
+      main_context = g_main_loop_get_context (main_loop);
+      g_source_attach (tls_source, main_context);
+      g_source_unref (tls_source);
+
+      return G_SOURCE_CONTINUE;
+    }
+
+finished:
+  g_mutex_lock (&op->finished_mutex);
+  op->finished = TRUE;
+  g_cond_signal (&op->finished_condition);
+  g_mutex_unlock (&op->finished_mutex);
+
+  return G_SOURCE_CONTINUE;
+}
+
+static gpointer
+tls_op_thread (gpointer data)
+{
+  GTlsOperationsThreadBase *self = G_TLS_OPERATIONS_THREAD_BASE (data);
+  GTlsOperationsThreadBasePrivate *priv = g_tls_operations_thread_base_get_instance_private (self);
+  GMainLoop *main_loop;
+  GSource *source;
+
+  main_loop = g_main_loop_new (priv->op_thread_context, FALSE);
+
+  g_main_context_push_thread_default (priv->op_thread_context);
+
+  source = tls_op_queue_source_new (priv->queue);
+  g_source_set_callback (source, G_SOURCE_FUNC (process_op), main_loop, NULL);
+  g_source_attach (source, priv->op_thread_context);
+  g_source_unref (source);
+
+  g_main_loop_run (main_loop);
+
+  /* FIXME FIXME: what happens if there are still ops in progress?
+   * They should be cancelled somehow. Figure out how.
+   */
+
+  g_main_context_pop_thread_default (priv->op_thread_context);
+
+  g_main_loop_unref (main_loop);
+
+  return NULL;
+}
+
+static void
+g_tls_operations_thread_base_get_property (GObject    *object,
+                                           guint       prop_id,
+                                           GValue     *value,
+                                           GParamSpec *pspec)
+{
+  GTlsOperationsThreadBase *self = G_TLS_OPERATIONS_THREAD_BASE (object);
+  GTlsOperationsThreadBasePrivate *priv = g_tls_operations_thread_base_get_instance_private (self);
+
+  switch (prop_id)
+    {
+    case PROP_TLS_CONNECTION:
+      g_assert (priv->connection);
+      g_value_set_object (value, priv->connection);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+g_tls_operations_thread_base_set_property (GObject      *object,
+                                           guint         prop_id,
+                                           const GValue *value,
+                                           GParamSpec   *pspec)
+{
+  GTlsOperationsThreadBase *self = G_TLS_OPERATIONS_THREAD_BASE (object);
+  GTlsOperationsThreadBasePrivate *priv = g_tls_operations_thread_base_get_instance_private (self);
+
+  switch (prop_id)
+    {
+    case PROP_TLS_CONNECTION:
+      priv->connection = g_value_get_object (value);
+
+      /* This weak pointer is not required for correctness, because the
+       * thread should never outlive its GTlsConnection. It's only here
+       * as a sanity-check and debugging aid, to ensure self->connection
+       * isn't ever dangling.
+       */
+      g_object_add_weak_pointer (G_OBJECT (priv->connection),
+                                 (gpointer *)&priv->connection);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+g_tls_operations_thread_base_init (GTlsOperationsThreadBase *self)
+{
+  GTlsOperationsThreadBasePrivate *priv = g_tls_operations_thread_base_get_instance_private (self);
+
+  priv->queue = g_async_queue_new ();
+  priv->op_thread_context = g_main_context_new ();
+  priv->op_thread = g_thread_new ("[glib-networking] GTlsOperationsThreadBase TLS operations thread",
+                                  tls_op_thread,
+                                  self);
+}
+
+static void
+g_tls_operations_thread_base_finalize (GObject *object)
+{
+  GTlsOperationsThreadBase *self = G_TLS_OPERATIONS_THREAD_BASE (object);
+  GTlsOperationsThreadBasePrivate *priv = g_tls_operations_thread_base_get_instance_private (self);
+  GTlsThreadOperation *op;
+
+  op = g_tls_thread_shutdown_operation_new ();
+  g_async_queue_push (priv->queue, op);
+  g_main_context_wakeup (priv->op_thread_context);
+
+  g_clear_pointer (&priv->op_thread, g_thread_join);
+  g_clear_pointer (&priv->op_thread_context, g_main_context_unref);
+  g_clear_pointer (&priv->queue, g_async_queue_unref);
+  g_tls_thread_operation_free (op);
+
+  g_clear_weak_pointer (&priv->connection);
+
+  G_OBJECT_CLASS (g_tls_operations_thread_base_parent_class)->finalize (object);
+}
+
+static void
+g_tls_operations_thread_base_class_init (GTlsOperationsThreadBaseClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+  gobject_class->finalize     = g_tls_operations_thread_base_finalize;
+  gobject_class->get_property = g_tls_operations_thread_base_get_property;
+  gobject_class->set_property = g_tls_operations_thread_base_set_property;
+
+  obj_properties[PROP_TLS_CONNECTION] =
+    g_param_spec_object ("tls-connection",
+                         "TLS Connection",
+                         "The thread's GTlsConnection",
+                         G_TYPE_TLS_CONNECTION_BASE,
+                         G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | 
G_PARAM_STATIC_STRINGS);
+
+  g_object_class_install_properties (gobject_class, LAST_PROP, obj_properties);
+}
diff --git a/tls/base/gtlsoperationsthread-base.h b/tls/base/gtlsoperationsthread-base.h
new file mode 100644
index 0000000..f567b14
--- /dev/null
+++ b/tls/base/gtlsoperationsthread-base.h
@@ -0,0 +1,58 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright 2019 Igalia S.L.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * In addition, when the library is used with OpenSSL, a special
+ * exception applies. Refer to the LICENSE_EXCEPTION file for details.
+ */
+
+#pragma once
+
+#include <gio/gio.h>
+
+#include "gtlsconnection-base.h"
+
+G_BEGIN_DECLS
+
+#define G_TYPE_TLS_OPERATIONS_THREAD_BASE (g_tls_operations_thread_base_get_type ())
+
+G_DECLARE_DERIVABLE_TYPE (GTlsOperationsThreadBase, g_tls_operations_thread_base, G, 
TLS_OPERATIONS_THREAD_BASE, GObject)
+
+struct _GTlsOperationsThreadBaseClass
+{
+  GObjectClass parent_class;
+};
+
+GTlsConnectionBaseStatus  g_tls_operations_thread_base_read  (GTlsOperationsThreadBase  *self,
+                                                              void                      *buffer,
+                                                              gsize                      size,
+                                                              gint64                     timeout,
+                                                              gssize                    *nread,
+                                                              GCancellable              *cancellable,
+                                                              GError                   **error);
+
+GTlsConnectionBaseStatus  g_tls_operations_thread_base_write (GTlsOperationsThreadBase  *self,
+                                                              const void                *buffer,
+                                                              gsize                      size,
+                                                              gint64                     timeout,
+                                                              gssize                    *nwrote,
+                                                              GCancellable              *cancellable,
+                                                              GError                   **error);
+
+G_END_DECLS
diff --git a/tls/base/meson.build b/tls/base/meson.build
index ca7d5a3..268f060 100644
--- a/tls/base/meson.build
+++ b/tls/base/meson.build
@@ -2,7 +2,8 @@ tlsbase_sources = files(
   'gtlsconnection-base.c',
   'gtlsinputstream.c',
   'gtlslog.c',
-  'gtlsoutputstream.c',
+  'gtlsoperationsthread-base.c',
+  'gtlsoutputstream.c'
 )
 
 tlsbase = static_library('tlsbase',
diff --git a/tls/gnutls/gtlsconnection-gnutls.c b/tls/gnutls/gtlsconnection-gnutls.c
index 8dd73d0..0efb4b7 100644
--- a/tls/gnutls/gtlsconnection-gnutls.c
+++ b/tls/gnutls/gtlsconnection-gnutls.c
@@ -4,6 +4,7 @@
  *
  * Copyright 2009 Red Hat, Inc
  * Copyright 2015, 2016 Collabora, Ltd.
+ * Copyright 2019 Igalia S.L.
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -36,6 +37,7 @@
 #include "gtlsbackend-gnutls.h"
 #include "gtlscertificate-gnutls.h"
 #include "gtlsclientconnection-gnutls.h"
+#include "gtlsoperationsthread-gnutls.h"
 
 #ifdef G_OS_WIN32
 #include <winsock2.h>
@@ -506,6 +508,8 @@ set_gnutls_error (GTlsConnectionGnutls *gnutls,
     gnutls_transport_set_errno (priv->session, EIO);
 }
 
+/* FIXME: remove timeouts, make these always nonblocking */
+
 static ssize_t
 g_tls_connection_gnutls_pull_func (gnutls_transport_ptr_t  transport_data,
                                    void                   *buf,
@@ -750,6 +754,12 @@ g_tls_connection_gnutls_pull_timeout_func (gnutls_transport_ptr_t transport_data
   return 0;
 }
 
+static GTlsOperationsThreadBase *
+g_tls_connection_gnutls_create_op_thread (GTlsConnectionBase *tls)
+{
+  return g_tls_operations_thread_gnutls_new (G_TLS_CONNECTION_GNUTLS (tls));
+}
+
 static GTlsSafeRenegotiationStatus
 g_tls_connection_gnutls_handshake_thread_safe_renegotiation_status (GTlsConnectionBase *tls)
 {
@@ -916,8 +926,7 @@ g_tls_connection_gnutls_is_session_resumed (GTlsConnectionBase *tls)
 static GTlsConnectionBaseStatus
 g_tls_connection_gnutls_read (GTlsConnectionBase  *tls,
                               void                *buffer,
-                              gsize                count,
-                              gint64               timeout,
+                              gsize                size,
                               gssize              *nread,
                               GCancellable        *cancellable,
                               GError             **error)
@@ -927,8 +936,8 @@ g_tls_connection_gnutls_read (GTlsConnectionBase  *tls,
   GTlsConnectionBaseStatus status;
   gssize ret;
 
-  BEGIN_GNUTLS_IO (gnutls, G_IO_IN, timeout, cancellable);
-  ret = gnutls_record_recv (priv->session, buffer, count);
+  BEGIN_GNUTLS_IO (gnutls, G_IO_IN, 0, cancellable);
+  ret = gnutls_record_recv (priv->session, buffer, size);
   END_GNUTLS_IO (gnutls, G_IO_IN, ret, status, _("Error reading data from TLS socket"), error);
 
   *nread = MAX (ret, 0);
@@ -998,8 +1007,7 @@ g_tls_connection_gnutls_read_message (GTlsConnectionBase  *tls,
 static GTlsConnectionBaseStatus
 g_tls_connection_gnutls_write (GTlsConnectionBase  *tls,
                                const void          *buffer,
-                               gsize                count,
-                               gint64               timeout,
+                               gsize                size,
                                gssize              *nwrote,
                                GCancellable        *cancellable,
                                GError             **error)
@@ -1009,8 +1017,8 @@ g_tls_connection_gnutls_write (GTlsConnectionBase  *tls,
   GTlsConnectionBaseStatus status;
   gssize ret;
 
-  BEGIN_GNUTLS_IO (gnutls, G_IO_OUT, timeout, cancellable);
-  ret = gnutls_record_send (priv->session, buffer, count);
+  BEGIN_GNUTLS_IO (gnutls, G_IO_OUT, 0, cancellable);
+  ret = gnutls_record_send (priv->session, buffer, size);
   END_GNUTLS_IO (gnutls, G_IO_OUT, ret, status, _("Error writing data to TLS socket"), error);
 
   *nwrote = MAX (ret, 0);
@@ -1130,6 +1138,7 @@ g_tls_connection_gnutls_class_init (GTlsConnectionGnutlsClass *klass)
 
   gobject_class->finalize                                = g_tls_connection_gnutls_finalize;
 
+  base_class->create_op_thread                           = g_tls_connection_gnutls_create_op_thread;
   base_class->prepare_handshake                          = g_tls_connection_gnutls_prepare_handshake;
   base_class->handshake_thread_safe_renegotiation_status = 
g_tls_connection_gnutls_handshake_thread_safe_renegotiation_status;
   base_class->handshake_thread_request_rehandshake       = 
g_tls_connection_gnutls_handshake_thread_request_rehandshake;
diff --git a/tls/gnutls/gtlsoperationsthread-gnutls.c b/tls/gnutls/gtlsoperationsthread-gnutls.c
new file mode 100644
index 0000000..d48a356
--- /dev/null
+++ b/tls/gnutls/gtlsoperationsthread-gnutls.c
@@ -0,0 +1,53 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright 2019 Igalia S.L.
+ * Copyright 2019 Metrological Group B.V.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * In addition, when the library is used with OpenSSL, a special
+ * exception applies. Refer to the LICENSE_EXCEPTION file for details.
+ */
+
+#include "config.h"
+
+#include "gtlsconnection-gnutls.h"
+#include "gtlsoperationsthread-gnutls.h"
+
+struct _GTlsOperationsThreadGnutls {
+  GTlsOperationsThreadBase parent_instance;
+};
+
+G_DEFINE_TYPE (GTlsOperationsThreadGnutls, g_tls_operations_thread_gnutls, G_TYPE_TLS_OPERATIONS_THREAD_BASE)
+
+static void
+g_tls_operations_thread_gnutls_init (GTlsOperationsThreadGnutls *self)
+{
+}
+
+static void
+g_tls_operations_thread_gnutls_class_init (GTlsOperationsThreadGnutlsClass *klass)
+{
+}
+
+GTlsOperationsThreadBase *
+g_tls_operations_thread_gnutls_new (GTlsConnectionGnutls *tls)
+{
+  return G_TLS_OPERATIONS_THREAD_BASE (g_object_new (G_TYPE_TLS_OPERATIONS_THREAD_GNUTLS,
+                                                     "tls-connection", tls,
+                                                     NULL));
+}
diff --git a/tls/gnutls/gtlsoperationsthread-gnutls.h b/tls/gnutls/gtlsoperationsthread-gnutls.h
new file mode 100644
index 0000000..4bffd58
--- /dev/null
+++ b/tls/gnutls/gtlsoperationsthread-gnutls.h
@@ -0,0 +1,37 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright 2019 Igalia S.L.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * In addition, when the library is used with OpenSSL, a special
+ * exception applies. Refer to the LICENSE_EXCEPTION file for details.
+ */
+
+#pragma once
+
+#include "gtlsoperationsthread-base.h"
+
+G_BEGIN_DECLS
+
+#define G_TYPE_TLS_OPERATIONS_THREAD_GNUTLS (g_tls_operations_thread_gnutls_get_type ())
+
+G_DECLARE_FINAL_TYPE (GTlsOperationsThreadGnutls, g_tls_operations_thread_gnutls, G, 
TLS_OPERATIONS_THREAD_GNUTLS, GTlsOperationsThreadBase)
+
+GTlsOperationsThreadBase *g_tls_operations_thread_gnutls_new (GTlsConnectionGnutls *tls);
+
+G_END_DECLS
diff --git a/tls/gnutls/meson.build b/tls/gnutls/meson.build
index ac46981..e993301 100644
--- a/tls/gnutls/meson.build
+++ b/tls/gnutls/meson.build
@@ -6,6 +6,7 @@ sources = files(
   'gtlsconnection-gnutls.c',
   'gtlsdatabase-gnutls.c',
   'gtlsfiledatabase-gnutls.c',
+  'gtlsoperationsthread-gnutls.c',
   'gtlsserverconnection-gnutls.c'
 )
 
diff --git a/tls/openssl/gtlsconnection-openssl.c b/tls/openssl/gtlsconnection-openssl.c
index 2e728f9..fc4603c 100644
--- a/tls/openssl/gtlsconnection-openssl.c
+++ b/tls/openssl/gtlsconnection-openssl.c
@@ -34,6 +34,7 @@
 #include "gtlsbackend-openssl.h"
 #include "gtlscertificate-openssl.h"
 #include "gtlsdatabase-openssl.h"
+#include "gtlsoperationsthread-openssl.h"
 #include "gtlsbio.h"
 
 #include <glib/gi18n-lib.h>
@@ -66,6 +67,12 @@ g_tls_connection_openssl_finalize (GObject *object)
   G_OBJECT_CLASS (g_tls_connection_openssl_parent_class)->finalize (object);
 }
 
+static GTlsOperationsThreadBase *
+g_tls_connection_openssl_create_op_thread (GTlsConnectionBase *tls)
+{
+  return g_tls_operations_thread_openssl_new (G_TLS_CONNECTION_OPENSSL (tls));
+}
+
 static GTlsSafeRenegotiationStatus
 g_tls_connection_openssl_handshake_thread_safe_renegotiation_status (GTlsConnectionBase *tls)
 {
@@ -375,8 +382,7 @@ g_tls_connection_openssl_pop_io (GTlsConnectionBase  *tls,
 static GTlsConnectionBaseStatus
 g_tls_connection_openssl_read (GTlsConnectionBase    *tls,
                                void                  *buffer,
-                               gsize                  count,
-                               gint64                 timeout,
+                               gsize                  size,
                                gssize                *nread,
                                GCancellable          *cancellable,
                                GError               **error)
@@ -402,10 +408,10 @@ g_tls_connection_openssl_read (GTlsConnectionBase    *tls,
       g_tls_connection_base_push_io (G_TLS_CONNECTION_BASE (openssl),
                                      G_IO_IN, 0, cancellable);
 
-      ret = SSL_read (ssl, buffer, count);
+      ret = SSL_read (ssl, buffer, size);
 
       ERR_error_string_n (SSL_get_error (ssl, ret), error_str, sizeof (error_str));
-      status = end_openssl_io (openssl, G_IO_IN, ret, timeout == -1, error,
+      status = end_openssl_io (openssl, G_IO_IN, ret, FALSE, error,
                                _("Error reading data from TLS socket"), error_str);
 
       if (status != G_TLS_CONNECTION_BASE_TRY_AGAIN)
@@ -422,8 +428,7 @@ g_tls_connection_openssl_read (GTlsConnectionBase    *tls,
 static GTlsConnectionBaseStatus
 g_tls_connection_openssl_write (GTlsConnectionBase    *tls,
                                 const void            *buffer,
-                                gsize                  count,
-                                gint64                 timeout,
+                                gsize                  size,
                                 gssize                *nwrote,
                                 GCancellable          *cancellable,
                                 GError               **error)
@@ -446,10 +451,10 @@ g_tls_connection_openssl_write (GTlsConnectionBase    *tls,
       g_tls_connection_base_push_io (G_TLS_CONNECTION_BASE (openssl),
                                      G_IO_OUT, 0, cancellable);
 
-      ret = SSL_write (ssl, buffer, count);
+      ret = SSL_write (ssl, buffer, size);
 
       ERR_error_string_n (SSL_get_error (ssl, ret), error_str, sizeof (error_str));
-      status = end_openssl_io (openssl, G_IO_OUT, ret, timeout == -1, error,
+      status = end_openssl_io (openssl, G_IO_OUT, ret, FALSE, error,
                                _("Error writing data to TLS socket"), error_str);
 
       if (status != G_TLS_CONNECTION_BASE_TRY_AGAIN)
@@ -500,6 +505,7 @@ g_tls_connection_openssl_class_init (GTlsConnectionOpensslClass *klass)
 
   object_class->finalize                                 = g_tls_connection_openssl_finalize;
 
+  base_class->create_op_thread                           = g_tls_connection_openssl_create_op_thread;
   base_class->handshake_thread_safe_renegotiation_status = 
g_tls_connection_openssl_handshake_thread_safe_renegotiation_status;
   base_class->handshake_thread_request_rehandshake       = 
g_tls_connection_openssl_handshake_thread_request_rehandshake;
   base_class->handshake_thread_handshake                 = 
g_tls_connection_openssl_handshake_thread_handshake;
diff --git a/tls/openssl/gtlsoperationsthread-openssl.c b/tls/openssl/gtlsoperationsthread-openssl.c
new file mode 100644
index 0000000..22ae78c
--- /dev/null
+++ b/tls/openssl/gtlsoperationsthread-openssl.c
@@ -0,0 +1,53 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright 2019 Igalia S.L.
+ * Copyright 2019 Metrological Group B.V.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * In addition, when the library is used with OpenSSL, a special
+ * exception applies. Refer to the LICENSE_EXCEPTION file for details.
+ */
+
+#include "config.h"
+
+#include "gtlsconnection-openssl.h"
+#include "gtlsoperationsthread-openssl.h"
+
+struct _GTlsOperationsThreadOpenssl {
+  GTlsOperationsThreadBase parent_instance;
+};
+
+G_DEFINE_TYPE (GTlsOperationsThreadOpenssl, g_tls_operations_thread_openssl, 
G_TYPE_TLS_OPERATIONS_THREAD_BASE)
+
+static void
+g_tls_operations_thread_openssl_init (GTlsOperationsThreadOpenssl *self)
+{
+}
+
+static void
+g_tls_operations_thread_openssl_class_init (GTlsOperationsThreadOpensslClass *klass)
+{
+}
+
+GTlsOperationsThreadBase *
+g_tls_operations_thread_openssl_new (GTlsConnectionOpenssl *tls)
+{
+  return G_TLS_OPERATIONS_THREAD_BASE (g_object_new (G_TYPE_TLS_OPERATIONS_THREAD_OPENSSL,
+                                       "tls-connection", tls,
+                                       NULL));
+}
diff --git a/tls/openssl/gtlsoperationsthread-openssl.h b/tls/openssl/gtlsoperationsthread-openssl.h
new file mode 100644
index 0000000..318c8a2
--- /dev/null
+++ b/tls/openssl/gtlsoperationsthread-openssl.h
@@ -0,0 +1,37 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright 2019 Igalia S.L.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * In addition, when the library is used with OpenSSL, a special
+ * exception applies. Refer to the LICENSE_EXCEPTION file for details.
+ */
+
+#pragma once
+
+#include "gtlsoperationsthread-base.h"
+
+G_BEGIN_DECLS
+
+#define G_TYPE_TLS_OPERATIONS_THREAD_OPENSSL (g_tls_operations_thread_openssl_get_type ())
+
+G_DECLARE_FINAL_TYPE (GTlsOperationsThreadOpenssl, g_tls_operations_thread_openssl, G, 
TLS_OPERATIONS_THREAD_OPENSSL, GTlsOperationsThreadBase)
+
+GTlsOperationsThreadBase *g_tls_operations_thread_openssl_new (GTlsConnectionOpenssl *tls);
+
+G_END_DECLS
diff --git a/tls/openssl/meson.build b/tls/openssl/meson.build
index 0ac25c8..9b799ec 100644
--- a/tls/openssl/meson.build
+++ b/tls/openssl/meson.build
@@ -7,6 +7,7 @@ sources = files(
   'gtlsclientconnection-openssl.c',
   'gtlsdatabase-openssl.c',
   'gtlsfiledatabase-openssl.c',
+  'gtlsoperationsthread-openssl.c',
   'gtlsbio.c',
   'openssl-util.c',
 )


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]