[glib-networking/mcatanzaro/tls-thread: 3/24] progress - last known good point?



commit 82b27b0c5cffe519cb4aeafef219d67f48435b15
Author: Michael Catanzaro <mcatanzaro gnome org>
Date:   Fri Nov 29 18:14:21 2019 -0600

    progress - last known good point?

 meson.build                                |    2 +-
 tls/base/gtlsconnection-base.c             |  417 +++++++----
 tls/base/gtlsconnection-base.h             |   50 +-
 tls/base/gtlsoperationsthread-base.c       | 1061 ++++++++++++++++++++++++++++
 tls/base/gtlsoperationsthread-base.h       |  121 ++++
 tls/base/meson.build                       |    3 +-
 tls/gnutls/gtlsconnection-gnutls.c         |  214 +-----
 tls/gnutls/gtlsconnection-gnutls.h         |    1 +
 tls/gnutls/gtlsoperationsthread-gnutls.c   |  509 +++++++++++++
 tls/gnutls/gtlsoperationsthread-gnutls.h   |   40 ++
 tls/gnutls/meson.build                     |    1 +
 tls/openssl/gtlsbio.c                      |   52 --
 tls/openssl/gtlsconnection-openssl.c       |  166 +----
 tls/openssl/gtlsconnection-openssl.h       |    2 +-
 tls/openssl/gtlsoperationsthread-openssl.c |  276 ++++++++
 tls/openssl/gtlsoperationsthread-openssl.h |   40 ++
 tls/openssl/meson.build                    |    1 +
 17 files changed, 2375 insertions(+), 581 deletions(-)
---
diff --git a/meson.build b/meson.build
index f04b60f..82333a3 100644
--- a/meson.build
+++ b/meson.build
@@ -76,7 +76,7 @@ gsettings_desktop_schemas_dep = dependency('gsettings-desktop-schemas', required
 backends = []
 
 # *** Checks for GnuTLS     ***
-gnutls_dep = dependency('gnutls', version: '>= 3.6.5', required: get_option('gnutls'))
+gnutls_dep = dependency('gnutls', version: '>= 3.6.7', required: get_option('gnutls'))
 
 if gnutls_dep.found()
   backends += ['gnutls']
diff --git a/tls/base/gtlsconnection-base.c b/tls/base/gtlsconnection-base.c
index 1971300..a0b2b16 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>
@@ -52,7 +54,7 @@
  *    communications.
  *  • Implements GDtlsConnection and GDatagramBased, for DTLS and datagram
  *    communications.
- *  • Implements GInitable for failable initialisation.
+ *  • Implements GInitable for failable initialization.
  */
 
 typedef struct
@@ -93,6 +95,7 @@ typedef struct
   GTlsCertificate       *peer_certificate;
   GTlsCertificateFlags   peer_certificate_errors;
 
+  /* FIXME: remove */
   GMutex                 verify_certificate_mutex;
   GCond                  verify_certificate_condition;
   gboolean               peer_certificate_accepted;
@@ -122,13 +125,14 @@ typedef struct
    * future operations). ever_handshaked indicates that TLS has been
    * successfully negotiated at some point.
    */
+  /* FIXME: remove a few of these */
   gboolean       need_handshake;
   gboolean       need_finish_handshake;
   gboolean       sync_handshake_in_progress;
   gboolean       started_handshake;
   gboolean       handshaking;
   gboolean       ever_handshaked;
-  GMainContext  *handshake_context;
+  GMainContext  *handshake_context; /* FIXME remove */
   GTask         *implicit_handshake;
   GError        *handshake_error;
   GByteArray    *app_data_buf;
@@ -158,12 +162,16 @@ typedef struct
 
   gchar        **advertised_protocols;
   gchar         *negotiated_protocol;
+
+  GTlsOperationsThreadBase *thread;
 } GTlsConnectionBasePrivate;
 
 static void g_tls_connection_base_dtls_connection_iface_init (GDtlsConnectionInterface *iface);
 
 static void g_tls_connection_base_datagram_based_iface_init  (GDatagramBasedInterface  *iface);
 
+static void g_tls_connection_base_initable_iface_init (GInitableIface *iface);
+
 static gboolean do_implicit_handshake (GTlsConnectionBase  *tls,
                                        gint64               timeout,
                                        GCancellable        *cancellable,
@@ -189,6 +197,8 @@ G_DEFINE_ABSTRACT_TYPE_WITH_CODE (GTlsConnectionBase, g_tls_connection_base, G_T
                                                          g_tls_connection_base_datagram_based_iface_init);
                                   G_IMPLEMENT_INTERFACE (G_TYPE_DTLS_CONNECTION,
                                                          g_tls_connection_base_dtls_connection_iface_init);
+                                  G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,
+                                                         g_tls_connection_base_initable_iface_init);
                                   );
 
 
@@ -236,12 +246,27 @@ g_tls_connection_base_init (GTlsConnectionBase *tls)
   priv->waiting_for_op = g_cancellable_new ();
 }
 
+static gboolean
+g_tls_connection_base_initable_init (GInitable    *initable,
+                                     GCancellable *cancellable,
+                                     GError       **error)
+{
+  GTlsConnectionBase *tls = G_TLS_CONNECTION_BASE (initable);
+  GTlsConnectionBasePrivate *priv = g_tls_connection_base_get_instance_private (tls);
+
+  priv->thread = G_TLS_CONNECTION_BASE_GET_CLASS (tls)->create_op_thread (tls);
+
+  return TRUE;
+}
+
 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);
 
@@ -934,7 +959,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)
 {
@@ -1076,7 +1102,6 @@ g_tls_connection_tls_source_dtls_closure_callback (GDatagramBased *datagram_base
   g_value_unset (&param[1]);
 
   return result;
-
 }
 
 static GSourceFuncs tls_source_funcs =
@@ -1099,6 +1124,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,
@@ -1158,13 +1184,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,
@@ -1399,6 +1445,85 @@ g_tls_connection_base_handshake_thread_verify_certificate (GTlsConnectionBase *t
   return accepted;
 }
 
+static gboolean /* FIXME rename */
+op_thread_handshake (GTlsConnectionBase  *tls,
+                     gint64               timeout,
+                     GCancellable        *cancellable,
+                     GError             **error)
+{
+  GTlsConnectionBasePrivate *priv = g_tls_connection_base_get_instance_private (tls);
+  GTlsConnectionBaseClass *tls_class = G_TLS_CONNECTION_BASE_GET_CLASS (tls);
+  gint64 start_time;
+
+  g_tls_log_debug (tls, "TLS handshake starts");
+
+  start_time = g_get_monotonic_time ();
+
+  priv->started_handshake = FALSE;
+  priv->missing_requested_client_certificate = FALSE;
+
+  if (!claim_op (tls, G_TLS_CONNECTION_BASE_OP_HANDSHAKE,
+                 timeout, cancellable, error))
+    {
+      g_tls_log_debug (tls, "TLS handshake failed: claiming op failed");
+      return FALSE;
+    }
+
+  g_clear_error (&priv->handshake_error);
+
+  if (priv->ever_handshaked && !priv->need_handshake)
+    {
+      GTlsConnectionBaseStatus status;
+
+      /* FIXME: no longer handshake thread */
+      if (tls_class->handshake_thread_safe_renegotiation_status (tls) != 
G_TLS_SAFE_RENEGOTIATION_SUPPORTED_BY_PEER)
+        {
+          g_set_error_literal (error, G_TLS_ERROR, G_TLS_ERROR_MISC,
+                               _("Peer does not support safe renegotiation"));
+          g_tls_log_debug (tls, "TLS handshake failed: peer does not support safe renegotiation");
+          return FALSE;
+        }
+
+      /* Adjust the timeout for the next operation in the sequence. */
+      if (timeout > 0)
+        {
+          timeout -= (g_get_monotonic_time () - start_time);
+          if (timeout <= 0)
+            timeout = 1;
+        }
+
+      /* FIXME: no longer handshake thread */
+      status = tls_class->handshake_thread_request_rehandshake (tls, timeout, cancellable, error);
+      if (status != G_TLS_CONNECTION_BASE_OK)
+        {
+          g_tls_log_debug (tls, "TLS handshake failed: %s", *error ? (*error)->message : "no error");
+          return FALSE;
+        }
+    }
+
+  /* Adjust the timeout for the next operation in the sequence. */
+  if (timeout > 0)
+    {
+      timeout -= (g_get_monotonic_time () - start_time);
+      if (timeout <= 0)
+        timeout = 1;
+    }
+
+  priv->started_handshake = TRUE;
+  g_tls_operations_thread_base_handshake (priv->thread, timeout, cancellable, error);
+  priv->need_handshake = FALSE;
+
+  if (error && *error)
+    {
+      g_tls_log_debug (tls, "TLS handshake failed: %s", (*error)->message);
+      return FALSE;
+    }
+
+  priv->ever_handshaked = TRUE;
+  g_tls_log_debug (tls, "TLS handshake succeeded");
+  return TRUE;
+}
+
 static void
 handshake_thread (GTask        *task,
                   gpointer      object,
@@ -1486,61 +1611,74 @@ handshake_thread (GTask        *task,
     }
 }
 
-static void
-sync_handshake_thread_completed (GObject      *object,
-                                 GAsyncResult *result,
-                                 gpointer      user_data)
+static gboolean /* FIXME remove */
+finish_handshake (GTlsConnectionBase  *tls,
+                  GTask               *task,
+                  GError             **error)
 {
-  GTlsConnectionBase *tls = G_TLS_CONNECTION_BASE (object);
   GTlsConnectionBasePrivate *priv = g_tls_connection_base_get_instance_private (tls);
-  gpointer source_tag;
-
-  g_tls_log_debug (tls, "synchronous TLS handshake thread completed");
-
-  source_tag = g_task_get_source_tag (G_TASK (result));
-  g_assert (source_tag == do_implicit_handshake || source_tag == g_tls_connection_base_handshake);
-  g_assert (g_task_is_valid (result, object));
+  GTlsConnectionBaseClass *tls_class = G_TLS_CONNECTION_BASE_GET_CLASS (tls);
+  gchar *original_negotiated_protocol;
+  GError *my_error = NULL;
 
-  g_assert (g_main_context_is_owner (priv->handshake_context));
+  g_tls_log_debug (tls, "finishing TLS handshake");
 
-  g_mutex_lock (&priv->op_mutex);
-  priv->sync_handshake_in_progress = FALSE;
-  g_mutex_unlock (&priv->op_mutex);
+  original_negotiated_protocol = g_steal_pointer (&priv->negotiated_protocol);
 
-  g_main_context_wakeup (priv->handshake_context);
-}
+  if (g_task_propagate_boolean (task, &my_error))
+    {
+      if (tls_class->is_session_resumed && tls_class->is_session_resumed (tls))
+        {
+          /* Because this session was resumed, we skipped certificate
+           * verification on this handshake, so we missed our earlier
+           * chance to set peer_certificate and peer_certificate_errors.
+           * Do so here instead.
+           *
+           * The certificate has already been accepted, so we don't do
+           * anything with the result here.
+           */
+          g_mutex_lock (&priv->verify_certificate_mutex);
+          update_peer_certificate_and_compute_errors (tls);
+          priv->peer_certificate_examined = TRUE;
+          priv->peer_certificate_accepted = TRUE;
+          g_mutex_unlock (&priv->verify_certificate_mutex);
+        }
 
-static void
-crank_sync_handshake_context (GTlsConnectionBase *tls,
-                              GCancellable       *cancellable)
-{
-  GTlsConnectionBasePrivate *priv = g_tls_connection_base_get_instance_private (tls);
+      /* FIXME: Return an error from the handshake thread instead. */
+      if (priv->peer_certificate && !priv->peer_certificate_accepted)
+        {
+          g_set_error_literal (&my_error, G_TLS_ERROR, G_TLS_ERROR_BAD_CERTIFICATE,
+                               _("Unacceptable TLS certificate"));
+        }
+    }
 
-  /* need_finish_handshake will be set inside sync_handshake_thread_completed(),
-   * which should only ever be invoked while iterating the handshake context
-   * here. So need_finish_handshake should only change on this thread.
-   *
-   * FIXME: This function is not cancellable. We should figure out how to
-   * support cancellation. We must not return from this function before it is
-   * safe to destroy handshake_context, but it's not safe to destroy
-   * handshake_context until after the handshake has completed. And the
-   * handshake operation is not cancellable, so we have a problem.
-   */
-  g_mutex_lock (&priv->op_mutex);
-  priv->sync_handshake_in_progress = TRUE;
-  while (priv->sync_handshake_in_progress)
+  if (tls_class->complete_handshake)
     {
-      g_mutex_unlock (&priv->op_mutex);
-      g_main_context_iteration (priv->handshake_context, TRUE);
-      g_mutex_lock (&priv->op_mutex);
+      /* If we already have an error, ignore further errors. */
+      tls_class->complete_handshake (tls, &priv->negotiated_protocol, my_error ? NULL : &my_error);
+
+      if (g_strcmp0 (original_negotiated_protocol, priv->negotiated_protocol) != 0)
+        g_object_notify (G_OBJECT (tls), "negotiated-protocol");
     }
-  g_mutex_unlock (&priv->op_mutex);
+  g_free (original_negotiated_protocol);
+
+  if (my_error && priv->started_handshake)
+    priv->handshake_error = g_error_copy (my_error);
+
+  if (!my_error) {
+    g_tls_log_debug (tls, "TLS handshake has finished successfully");
+    return TRUE;
+  }
+
+  g_tls_log_debug (tls, "TLS handshake has finished with error: %s", my_error->message);
+  g_propagate_error (error, my_error);
+  return FALSE;
 }
 
-static gboolean
-finish_handshake (GTlsConnectionBase  *tls,
-                  GTask               *task,
-                  GError             **error)
+static gboolean /* FIXME rename */
+finish_op_thread_handshake (GTlsConnectionBase  *tls,
+                            gboolean             success,
+                            GError             **error)
 {
   GTlsConnectionBasePrivate *priv = g_tls_connection_base_get_instance_private (tls);
   GTlsConnectionBaseClass *tls_class = G_TLS_CONNECTION_BASE_GET_CLASS (tls);
@@ -1551,7 +1689,7 @@ finish_handshake (GTlsConnectionBase  *tls,
 
   original_negotiated_protocol = g_steal_pointer (&priv->negotiated_protocol);
 
-  if (g_task_propagate_boolean (task, &my_error))
+  if (success)
     {
       if (tls_class->is_session_resumed && tls_class->is_session_resumed (tls))
         {
@@ -1570,7 +1708,7 @@ finish_handshake (GTlsConnectionBase  *tls,
           g_mutex_unlock (&priv->verify_certificate_mutex);
         }
 
-      /* FIXME: Return an error from the handshake thread instead. */
+      /* FIXME: Return an error from the handshake thread instead? */
       if (priv->peer_certificate && !priv->peer_certificate_accepted)
         {
           g_set_error_literal (&my_error, G_TLS_ERROR, G_TLS_ERROR_BAD_CERTIFICATE,
@@ -1609,9 +1747,7 @@ g_tls_connection_base_handshake (GTlsConnection   *conn,
   GTlsConnectionBase *tls = G_TLS_CONNECTION_BASE (conn);
   GTlsConnectionBasePrivate *priv = g_tls_connection_base_get_instance_private (tls);
   GTlsConnectionBaseClass *tls_class = G_TLS_CONNECTION_BASE_GET_CLASS (tls);
-  GTask *task;
   gboolean success;
-  gint64 *timeout = NULL;
   GError *my_error = NULL;
 
   g_tls_log_debug (tls, "Starting synchronous TLS handshake");
@@ -1624,20 +1760,16 @@ g_tls_connection_base_handshake (GTlsConnection   *conn,
   if (tls_class->prepare_handshake)
     tls_class->prepare_handshake (tls, priv->advertised_protocols);
 
-  task = g_task_new (conn, cancellable, sync_handshake_thread_completed, NULL);
-  g_task_set_source_tag (task, g_tls_connection_base_handshake);
-  g_task_set_name (task, "[glib-networking] g_tls_connection_base_handshake");
-  g_task_set_return_on_cancel (task, TRUE);
+  success = op_thread_handshake (tls, -1 /* blocking */, cancellable, error);
 
-  timeout = g_new0 (gint64, 1);
-  *timeout = -1; /* blocking */
-  g_task_set_task_data (task, timeout, g_free);
+  g_mutex_lock (&priv->op_mutex);
+  priv->sync_handshake_in_progress = FALSE;
+  g_mutex_unlock (&priv->op_mutex);
 
-  g_task_run_in_thread (task, handshake_thread);
-  crank_sync_handshake_context (tls, cancellable);
+  g_main_context_wakeup (priv->handshake_context);
 
-  success = finish_handshake (tls, task, &my_error);
-  g_object_unref (task);
+  /* If we already have an error, ignore further errors. */
+  success = finish_op_thread_handshake (tls, success, my_error ? NULL : &my_error);
 
   g_main_context_pop_thread_default (priv->handshake_context);
   g_clear_pointer (&priv->handshake_context, g_main_context_unref);
@@ -1817,35 +1949,25 @@ g_tls_connection_base_dtls_handshake_finish (GDtlsConnection  *conn,
                                                  result, error);
 }
 
-static gboolean
-do_implicit_handshake (GTlsConnectionBase  *tls,
-                       gint64               timeout,
-                       GCancellable        *cancellable,
-                       GError             **error)
+static gboolean /* FIXME remove? */
+do_async_implicit_handshake (GTlsConnectionBase  *tls,
+                             GCancellable        *cancellable,
+                             GError             **error)
 {
   GTlsConnectionBasePrivate *priv = g_tls_connection_base_get_instance_private (tls);
   GTlsConnectionBaseClass *tls_class = G_TLS_CONNECTION_BASE_GET_CLASS (tls);
   gint64 *thread_timeout = NULL;
 
-  g_tls_log_debug (tls, "Implcit TLS handshaking starts");
+  g_tls_log_debug (tls, "Implicit TLS handshaking starts");
 
   /* We have op_mutex */
 
   g_assert (!priv->handshake_context);
-  if (timeout != 0)
-    {
-      priv->handshake_context = g_main_context_new ();
-      g_main_context_push_thread_default (priv->handshake_context);
-    }
-  else
-    {
-      priv->handshake_context = g_main_context_ref_thread_default ();
-    }
+  priv->handshake_context = g_main_context_ref_thread_default ();
 
   g_assert (!priv->implicit_handshake);
   priv->implicit_handshake = g_task_new (tls, cancellable,
-                                        timeout ? sync_handshake_thread_completed : NULL,
-                                        NULL);
+                                        NULL, NULL);
   g_task_set_source_tag (priv->implicit_handshake, do_implicit_handshake);
   g_task_set_name (priv->implicit_handshake, "[glib-networking] do_implicit_handshake");
 
@@ -1856,64 +1978,79 @@ do_implicit_handshake (GTlsConnectionBase  *tls,
   if (tls_class->prepare_handshake)
     tls_class->prepare_handshake (tls, priv->advertised_protocols);
 
-  if (timeout != 0)
-    {
-      GError *my_error = NULL;
-      gboolean success;
+  /* In the non-blocking case, start the asynchronous handshake operation
+   * and return EWOULDBLOCK to the caller, who will handle polling for
+   * completion of the handshake and whatever operation they actually cared
+   * about. Run the actual operation as blocking in its thread. */
+  *thread_timeout = -1; /* blocking */
 
-      /* In the blocking case, run the handshake operation synchronously in
-       * another thread, and delegate handling the timeout to that thread; it
-       * should return G_IO_ERROR_TIMED_OUT iff (timeout > 0) and the operation
-       * times out. If (timeout < 0) it should block indefinitely until the
-       * operation is complete or errors. */
-      *thread_timeout = timeout;
+  g_task_run_in_thread (priv->implicit_handshake,
+                        async_handshake_thread);
 
-      g_mutex_unlock (&priv->op_mutex);
+  /* Intentionally not translated because this is not a fatal error to be
+   * presented to the user, and to avoid this showing up in profiling. */
+  g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK, "Operation would block");
+  return FALSE;
+}
 
-      g_task_set_return_on_cancel (priv->implicit_handshake, TRUE);
-      g_task_run_in_thread (priv->implicit_handshake, handshake_thread);
+static gboolean
+do_implicit_handshake (GTlsConnectionBase  *tls,
+                       gint64               timeout,
+                       GCancellable        *cancellable,
+                       GError             **error)
+{
+  GTlsConnectionBasePrivate *priv = g_tls_connection_base_get_instance_private (tls);
+  GTlsConnectionBaseClass *tls_class = G_TLS_CONNECTION_BASE_GET_CLASS (tls);
+  GError *my_error = NULL;
+  gboolean success;
 
-      crank_sync_handshake_context (tls, cancellable);
+  if (timeout == 0) /* FIXME: code duplication */
+    return do_async_implicit_handshake (tls, cancellable, error);
 
-      success = finish_handshake (tls,
-                                  priv->implicit_handshake,
-                                  &my_error);
+  g_tls_log_debug (tls, "Implicit TLS handshaking starts");
 
-      g_main_context_pop_thread_default (priv->handshake_context);
-      g_clear_pointer (&priv->handshake_context, g_main_context_unref);
-      g_clear_object (&priv->implicit_handshake);
+  /* We have op_mutex */
 
-      yield_op (tls, G_TLS_CONNECTION_BASE_OP_HANDSHAKE,
-                G_TLS_CONNECTION_BASE_OK);
+  g_assert (!priv->handshake_context);
+  priv->handshake_context = g_main_context_new ();
+  g_main_context_push_thread_default (priv->handshake_context);
 
-      g_mutex_lock (&priv->op_mutex);
+  if (tls_class->prepare_handshake)
+    tls_class->prepare_handshake (tls, priv->advertised_protocols);
 
-      if (my_error)
-        g_propagate_error (error, my_error);
-      return success;
-    }
-  else
-    {
-      /* In the non-blocking case, start the asynchronous handshake operation
-       * and return EWOULDBLOCK to the caller, who will handle polling for
-       * completion of the handshake and whatever operation they actually cared
-       * about. Run the actual operation as blocking in its thread. */
-      *thread_timeout = -1; /* blocking */
-
-      g_task_run_in_thread (priv->implicit_handshake,
-                            async_handshake_thread);
-
-      /* Intentionally not translated because this is not a fatal error to be
-       * presented to the user, and to avoid this showing up in profiling. */
-      g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK, "Operation would block");
-      return FALSE;
-    }
+  g_mutex_unlock (&priv->op_mutex);
+
+  success = op_thread_handshake (tls, timeout, cancellable, &my_error);
+
+  g_mutex_lock (&priv->op_mutex);
+  priv->sync_handshake_in_progress = FALSE;
+  g_mutex_unlock (&priv->op_mutex);
+
+  g_main_context_wakeup (priv->handshake_context);
+
+  /* If we already have an error, ignore further errors. */
+  success = finish_op_thread_handshake (tls,
+                                        success,
+                                        my_error ? NULL : &my_error);
+
+  g_main_context_pop_thread_default (priv->handshake_context);
+  g_clear_pointer (&priv->handshake_context, g_main_context_unref);
+  g_clear_object (&priv->implicit_handshake);
+
+  yield_op (tls, G_TLS_CONNECTION_BASE_OP_HANDSHAKE,
+            G_TLS_CONNECTION_BASE_OK);
+
+  g_mutex_lock (&priv->op_mutex);
+
+  if (my_error)
+    g_propagate_error (error, my_error);
+  return success;
 }
 
 gssize
 g_tls_connection_base_read (GTlsConnectionBase  *tls,
                             void                *buffer,
-                            gsize                count,
+                            gsize                size,
                             gint64               timeout,
                             GCancellable        *cancellable,
                             GError             **error)
@@ -1932,7 +2069,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);
@@ -1942,8 +2079,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);
@@ -2003,9 +2139,7 @@ g_tls_connection_base_read_message (GTlsConnectionBase  *tls,
       }
     else
       {
-        g_assert (G_TLS_CONNECTION_BASE_GET_CLASS (tls)->read_message_fn);
-        status = G_TLS_CONNECTION_BASE_GET_CLASS (tls)->
-          read_message_fn (tls, vectors, num_vectors, timeout, &nread, cancellable, error);
+        status = g_tls_operations_thread_base_read_message (priv->thread, vectors, num_vectors, timeout, 
&nread, cancellable, error);
       }
 
     yield_op (tls, G_TLS_CONNECTION_BASE_OP_READ, status);
@@ -2102,7 +2236,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)
@@ -2111,7 +2245,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
     {
@@ -2119,9 +2253,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);
@@ -2156,9 +2288,7 @@ g_tls_connection_base_write_message (GTlsConnectionBase  *tls,
                    timeout, cancellable, error))
       return -1;
 
-    g_assert (G_TLS_CONNECTION_BASE_GET_CLASS (tls)->read_message_fn);
-    status = G_TLS_CONNECTION_BASE_GET_CLASS (tls)->
-      write_message_fn (tls, vectors, num_vectors, timeout, &nwrote, cancellable, error);
+    status = g_tls_operations_thread_base_write_message (priv->thread, vectors, num_vectors, timeout, 
&nwrote, cancellable, error);
 
     yield_op (tls, G_TLS_CONNECTION_BASE_OP_WRITE, status);
   } while (status == G_TLS_CONNECTION_BASE_REHANDSHAKE);
@@ -2294,8 +2424,7 @@ g_tls_connection_base_close_internal (GIOStream      *stream,
   if (priv->ever_handshaked && !priv->write_closed &&
       direction & G_TLS_DIRECTION_WRITE)
     {
-      status = G_TLS_CONNECTION_BASE_GET_CLASS (tls)->
-        close_fn (tls, timeout, cancellable, &close_error);
+      status = g_tls_operations_thread_base_close (priv->thread, cancellable, &close_error);
 
       priv->write_closed = TRUE;
     }
@@ -2710,3 +2839,9 @@ g_tls_connection_base_datagram_based_iface_init (GDatagramBasedInterface *iface)
   iface->condition_check = g_tls_connection_base_condition_check;
   iface->condition_wait = g_tls_connection_base_condition_wait;
 }
+
+static void
+g_tls_connection_base_initable_iface_init (GInitableIface *iface)
+{
+  iface->init = g_tls_connection_base_initable_init;
+}
diff --git a/tls/base/gtlsconnection-base.h b/tls/base/gtlsconnection-base.h
index bacefab..fa155f3 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
@@ -39,7 +40,7 @@ typedef enum {
   G_TLS_CONNECTION_BASE_REHANDSHAKE,
   G_TLS_CONNECTION_BASE_TRY_AGAIN,
   G_TLS_CONNECTION_BASE_ERROR,
-} GTlsConnectionBaseStatus;
+} GTlsConnectionBaseStatus; /* FIXME: move? rename? GTlsOperationsThreadBaseStatus */
 
 typedef enum {
   G_TLS_DIRECTION_NONE = 0,
@@ -54,10 +55,15 @@ 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);
+
+  /* FIXME: deal with all the handshaking stuff */
   void                        (*prepare_handshake)          (GTlsConnectionBase   *tls,
                                                              gchar               **advertised_protocols);
   GTlsSafeRenegotiationStatus (*handshake_thread_safe_renegotiation_status)
@@ -83,47 +89,12 @@ struct _GTlsConnectionBaseClass
 
   void                        (*push_io)                    (GTlsConnectionBase   *tls,
                                                              GIOCondition          direction,
-                                                             gint64                timeout,
+                                                             gint64                timeout, /* FIXME: remove 
timeout */
                                                              GCancellable         *cancellable);
   GTlsConnectionBaseStatus    (*pop_io)                     (GTlsConnectionBase   *tls,
                                                              GIOCondition          direction,
                                                              gboolean              success,
                                                              GError              **error);
-
-  GTlsConnectionBaseStatus    (*read_fn)                    (GTlsConnectionBase   *tls,
-                                                             void                 *buffer,
-                                                             gsize                 count,
-                                                             gint64                timeout,
-                                                             gssize               *nread,
-                                                             GCancellable         *cancellable,
-                                                             GError              **error);
-  GTlsConnectionBaseStatus    (*read_message_fn)            (GTlsConnectionBase   *tls,
-                                                             GInputVector         *vectors,
-                                                             guint                 num_vectors,
-                                                             gint64                timeout,
-                                                             gssize               *nread,
-                                                             GCancellable         *cancellable,
-                                                             GError              **error);
-
-  GTlsConnectionBaseStatus    (*write_fn)                   (GTlsConnectionBase   *tls,
-                                                             const void           *buffer,
-                                                             gsize                 count,
-                                                             gint64                timeout,
-                                                             gssize               *nwrote,
-                                                             GCancellable         *cancellable,
-                                                             GError              **error);
-  GTlsConnectionBaseStatus    (*write_message_fn)           (GTlsConnectionBase   *tls,
-                                                             GOutputVector        *vectors,
-                                                             guint                 num_vectors,
-                                                             gint64                timeout,
-                                                             gssize               *nwrote,
-                                                             GCancellable         *cancellable,
-                                                             GError              **error);
-
-  GTlsConnectionBaseStatus    (*close_fn)                   (GTlsConnectionBase   *tls,
-                                                             gint64                timeout,
-                                                             GCancellable         *cancellable,
-                                                             GError              **error);
 };
 
 gboolean                  g_tls_connection_base_handshake_thread_verify_certificate
@@ -131,7 +102,7 @@ gboolean                  g_tls_connection_base_handshake_thread_verify_certific
 
 void                      g_tls_connection_base_push_io                 (GTlsConnectionBase *tls,
                                                                          GIOCondition        direction,
-                                                                         gint64              timeout,
+                                                                         gint64              timeout, /* 
FIXME: remove timeout */
                                                                          GCancellable       *cancellable);
 GTlsConnectionBaseStatus  g_tls_connection_base_pop_io                  (GTlsConnectionBase  *tls,
                                                                          GIOCondition         direction,
@@ -158,6 +129,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..825e6c2
--- /dev/null
+++ b/tls/base/gtlsoperationsthread-base.c
@@ -0,0 +1,1061 @@
+/* -*- 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>
+
+/* 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 {
+  /* FIXME: remove to prevent misuse? */
+  GTlsConnectionBase *connection; /* unowned */
+
+  GThread *op_thread;
+  GMainContext *op_thread_context;
+
+  GAsyncQueue *queue;
+} GTlsOperationsThreadBasePrivate;
+
+typedef enum {
+  G_TLS_THREAD_OP_HANDSHAKE,
+  G_TLS_THREAD_OP_READ,
+  G_TLS_THREAD_OP_READ_MESSAGE,
+  G_TLS_THREAD_OP_WRITE,
+  G_TLS_THREAD_OP_WRITE_MESSAGE,
+  G_TLS_THREAD_OP_CLOSE,
+  G_TLS_THREAD_OP_SHUTDOWN_THREAD
+} GTlsThreadOperationType;
+
+typedef struct {
+  GTlsThreadOperationType type;
+  GIOCondition io_condition;
+
+  GTlsOperationsThreadBase *thread;
+  GTlsConnectionBase *connection; /* FIXME: threadsafety nightmare, not OK */
+
+  union {
+    void *data;
+    GInputVector *input_vectors;
+    GOutputVector *output_vectors;
+  } /* unowned */;
+
+  union {
+    gsize size; /* for non-vectored data buffer */
+    guint num_vectors;
+  };
+
+  gint64 timeout;
+  gint64 start_time;
+
+  GCancellable *cancellable;
+
+  /* Async ops */
+  GTask *task;
+
+  /* Sync ops */
+  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)
+
+GTlsConnectionBase *
+g_tls_operations_thread_base_get_connection (GTlsOperationsThreadBase *self)
+{
+  GTlsOperationsThreadBasePrivate *priv = g_tls_operations_thread_base_get_instance_private (self);
+
+  return priv->connection;
+}
+
+static GTlsThreadOperation *
+g_tls_thread_operation_new (GTlsThreadOperationType   type,
+                            GTlsOperationsThreadBase *thread,
+                            GTlsConnectionBase       *connection,
+                            void                     *data,
+                            gsize                     size,
+                            gint64                    timeout,
+                            GCancellable             *cancellable)
+{
+  GTlsThreadOperation *op;
+
+  op = g_new0 (GTlsThreadOperation, 1);
+  op->type = type;
+  op->thread = thread; /* FIXME: use a weak ref? */
+  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;
+    case G_TLS_THREAD_OP_HANDSHAKE:
+      /* fallthrough */
+    case G_TLS_THREAD_OP_CLOSE:
+      op->io_condition = G_IO_IN | G_IO_OUT;
+      break;
+    default:
+      g_assert_not_reached ();
+    }
+
+  return op;
+}
+
+#if 0
+static GTlsThreadOperation *
+g_tls_thread_operation_new_async (GTlsThreadOperationType   type,
+                                  GTlsOperationsThreadBase *thread,
+                                  GTlsConnectionBase       *connection,
+                                  GCancellable             *cancellable,
+                                  GAsyncReadyCallback       callback,
+                                  gpointer                  user_data)
+{
+  GTlsThreadOperation *op;
+
+  op = g_new0 (GTlsThreadOperation, 1);
+  op->type = type;
+  op->thread = thread; /* FIXME: use a weak ref? */
+  op->connection = g_object_ref (connection);
+  op->timeout = -1 /* blocking on the thread */;
+  op->cancellable = cancellable ? g_object_ref (cancellable) : NULL;
+
+  g_assert (type == G_TLS_THREAD_OP_CLOSE /* FIXME: || type == G_TLS_THREAD_OP_HANDSHAKE*/);
+  op->io_condition = G_IO_IN | G_IO_OUT;
+
+  op->task = g_task_new (thread, cancellable, callback, user_data);
+
+  return op;
+}
+#endif
+
+static GTlsThreadOperation *
+g_tls_thread_operation_new_with_input_vectors (GTlsOperationsThreadBase *thread,
+                                               GTlsConnectionBase       *connection,
+                                               GInputVector             *vectors,
+                                               guint                     num_vectors,
+                                               gint64                    timeout,
+                                               GCancellable             *cancellable)
+{
+  GTlsThreadOperation *op;
+
+  op = g_new0 (GTlsThreadOperation, 1);
+  op->type = G_TLS_THREAD_OP_READ_MESSAGE;
+  op->io_condition = G_IO_IN;
+  op->thread = thread; /* FIXME: use a weak ref? */
+  op->connection = g_object_ref (connection);
+  op->input_vectors = vectors;
+  op->num_vectors = num_vectors;
+  op->timeout = timeout;
+  op->cancellable = cancellable ? g_object_ref (cancellable) : NULL;
+
+  g_mutex_init (&op->finished_mutex);
+  g_cond_init (&op->finished_condition);
+
+  return op;
+}
+
+static GTlsThreadOperation *
+g_tls_thread_operation_new_with_output_vectors (GTlsOperationsThreadBase *thread,
+                                                GTlsConnectionBase       *connection,
+                                                GOutputVector            *vectors,
+                                                guint                     num_vectors,
+                                                gint64                    timeout,
+                                                GCancellable             *cancellable)
+{
+  GTlsThreadOperation *op;
+
+  op = g_new0 (GTlsThreadOperation, 1);
+  op->type = G_TLS_THREAD_OP_WRITE_MESSAGE;
+  op->io_condition = G_IO_OUT;
+  op->thread = thread; /* FIXME: use a weak ref? */
+  op->connection = g_object_ref (connection);
+  op->output_vectors = vectors;
+  op->num_vectors = num_vectors;
+  op->timeout = timeout;
+  op->cancellable = cancellable ? g_object_ref (cancellable) : NULL;
+
+  g_mutex_init (&op->finished_mutex);
+  g_cond_init (&op->finished_condition);
+
+  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_THREAD;
+
+  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_THREAD)
+    {
+      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);
+}
+
+static GTlsConnectionBaseStatus
+execute_sync_op (GTlsOperationsThreadBase *self,
+                 GTlsThreadOperation      *op /* owned */,
+                 gssize                   *count,
+                 GError                  **error)
+{
+  GTlsOperationsThreadBasePrivate *priv = g_tls_operations_thread_base_get_instance_private (self);
+  GTlsConnectionBaseStatus result;
+
+  g_assert (!op->task);
+
+  g_async_queue_push (priv->queue, op);
+  g_main_context_wakeup (priv->op_thread_context);
+
+  wait_for_op_completion (op);
+
+  if (count)
+    *count = op->count;
+
+  result = op->result;
+
+  if (op->error)
+    {
+      g_propagate_error (error, op->error);
+      op->error = NULL;
+    }
+
+  g_tls_thread_operation_free (op);
+
+  return result;
+}
+
+#if 0
+static void
+execute_async_op (GTlsOperationsThreadBase *self,
+                  GTlsThreadOperation      *op)
+{
+  GTlsOperationsThreadBasePrivate *priv = g_tls_operations_thread_base_get_instance_private (self);
+
+  g_assert (op->task);
+
+  /* FIXME: Design flaw? Here the queue owns the ops only for async tasks.
+   * But it doesn't free them when destroyed (though there should not be any
+   * when destroyed anyway?). It's confusing to have both owned and unowned ops
+   * stored in the same queue. Do we need ops to be refcounted?
+   */
+  g_async_queue_push (priv->queue, g_steal_pointer (&op));
+  g_main_context_wakeup (priv->op_thread_context);
+}
+#endif
+
+GTlsConnectionBaseStatus
+g_tls_operations_thread_base_handshake (GTlsOperationsThreadBase  *self,
+                                        gint64                     timeout,
+                                        GCancellable              *cancellable,
+                                        GError                   **error)
+{
+  GTlsOperationsThreadBasePrivate *priv = g_tls_operations_thread_base_get_instance_private (self);
+  GTlsThreadOperation *op;
+
+  op = g_tls_thread_operation_new (G_TLS_THREAD_OP_HANDSHAKE,
+                                   self,
+                                   priv->connection,
+                                   NULL, 0,
+                                   timeout,
+                                   cancellable);
+
+  return execute_sync_op (self, g_steal_pointer (&op), NULL, error);
+}
+
+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;
+
+  op = g_tls_thread_operation_new (G_TLS_THREAD_OP_READ,
+                                   self,
+                                   priv->connection,
+                                   buffer, size,
+                                   timeout,
+                                   cancellable);
+
+  return execute_sync_op (self, g_steal_pointer (&op), nread, error);
+}
+
+GTlsConnectionBaseStatus
+g_tls_operations_thread_base_read_message (GTlsOperationsThreadBase  *self,
+                                           GInputVector              *vectors,
+                                           guint                      num_vectors,
+                                           gint64                     timeout,
+                                           gssize                    *nread,
+                                           GCancellable              *cancellable,
+                                           GError                   **error)
+{
+  GTlsOperationsThreadBasePrivate *priv = g_tls_operations_thread_base_get_instance_private (self);
+  GTlsThreadOperation *op;
+
+  op = g_tls_thread_operation_new_with_input_vectors (self,
+                                                      priv->connection,
+                                                      vectors, num_vectors,
+                                                      timeout,
+                                                      cancellable);
+
+  return execute_sync_op (self, g_steal_pointer (&op), nread, error);
+}
+
+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;
+
+  op = g_tls_thread_operation_new (G_TLS_THREAD_OP_WRITE,
+                                   self,
+                                   priv->connection,
+                                   (void *)buffer, size,
+                                   timeout,
+                                   cancellable);
+
+  return execute_sync_op (self, g_steal_pointer (&op), nwrote, error);
+}
+
+GTlsConnectionBaseStatus
+g_tls_operations_thread_base_write_message (GTlsOperationsThreadBase  *self,
+                                            GOutputVector             *vectors,
+                                            guint                      num_vectors,
+                                            gint64                     timeout,
+                                            gssize                    *nwrote,
+                                            GCancellable              *cancellable,
+                                            GError                   **error)
+{
+  GTlsOperationsThreadBasePrivate *priv = g_tls_operations_thread_base_get_instance_private (self);
+  GTlsThreadOperation *op;
+
+  op = g_tls_thread_operation_new_with_output_vectors (self,
+                                                       priv->connection,
+                                                       vectors, num_vectors,
+                                                       timeout,
+                                                       cancellable);
+
+  return execute_sync_op (self, g_steal_pointer (&op), nwrote, error);
+}
+
+GTlsConnectionBaseStatus
+g_tls_operations_thread_base_close (GTlsOperationsThreadBase  *self,
+                                    GCancellable              *cancellable,
+                                    GError                   **error)
+{
+  GTlsOperationsThreadBasePrivate *priv = g_tls_operations_thread_base_get_instance_private (self);
+  GTlsThreadOperation *op;
+
+  op = g_tls_thread_operation_new (G_TLS_THREAD_OP_CLOSE,
+                                   self,
+                                   priv->connection,
+                                   NULL, 0,
+                                   -1 /* blocking */,
+                                   cancellable);
+
+  return execute_sync_op (self, g_steal_pointer (&op), NULL, error);
+}
+
+#if 0
+FIXME: needs removed, but good template for handshake?
+
+void
+g_tls_operations_thread_base_close_async (GTlsOperationsThreadBase  *self,
+                                          GCancellable              *cancellable,
+                                          GAsyncReadyCallback        callback,
+                                          gpointer                   user_data)
+{
+  GTlsOperationsThreadBasePrivate *priv = g_tls_operations_thread_base_get_instance_private (self);
+  GTlsThreadOperation *op;
+
+  op = g_tls_thread_operation_new_async (G_TLS_THREAD_OP_CLOSE,
+                                         self,
+                                         priv->connection,
+                                         cancellable,
+                                         callback,
+                                         user_data);
+
+  return execute_async_op (self, g_steal_pointer (&op));
+}
+
+GTlsConnectionBaseStatus
+g_tls_operations_thread_base_close_finish (GTlsOperationsThreadBase  *self,
+                                           GAsyncResult              *result,
+                                           GError                   **error)
+{
+  g_assert (g_task_is_valid (result, self));
+
+  return g_task_propagate_int (G_TASK (result), error);
+}
+#endif
+
+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;
+  GTlsOperationsThreadBaseClass *base_class;
+
+  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_THREAD)
+        {
+          g_main_loop_quit (main_loop);
+          return G_SOURCE_REMOVE;
+        }
+
+      adjust_op_timeout (op);
+    }
+
+  if (op->type != G_TLS_THREAD_OP_SHUTDOWN_THREAD)
+    {
+      g_assert (op->thread);
+      base_class = G_TLS_OPERATIONS_THREAD_BASE_GET_CLASS (op->thread);
+    }
+
+  switch (op->type)
+    {
+    case G_TLS_THREAD_OP_HANDSHAKE:
+      op->result = base_class->handshake_fn (op->thread,
+                                             op->timeout,
+                                             op->cancellable,
+                                             &op->error);
+      break;
+    case G_TLS_THREAD_OP_READ:
+      op->result = base_class->read_fn (op->thread,
+                                        op->data, op->size,
+                                        &op->count,
+                                        op->cancellable,
+                                        &op->error);
+      break;
+    case G_TLS_THREAD_OP_READ_MESSAGE:
+      g_assert (base_class->read_message_fn);
+      op->result = base_class->read_message_fn (op->thread,
+                                                op->input_vectors, op->num_vectors,
+                                                &op->count,
+                                                op->cancellable,
+                                                &op->error);
+      break;
+    case G_TLS_THREAD_OP_WRITE:
+      op->result = base_class->write_fn (op->thread,
+                                         op->data, op->size,
+                                         &op->count,
+                                         op->cancellable,
+                                         &op->error);
+      break;
+    case G_TLS_THREAD_OP_WRITE_MESSAGE:
+      g_assert (base_class->write_message_fn);
+      op->result = base_class->write_message_fn (op->thread,
+                                                 op->output_vectors, op->num_vectors,
+                                                 &op->count,
+                                                 op->cancellable,
+                                                 &op->error);
+      break;
+    case G_TLS_THREAD_OP_CLOSE:
+      op->result = base_class->close_fn (op->thread,
+                                         op->cancellable,
+                                         &op->error);
+      break;
+    case G_TLS_THREAD_OP_SHUTDOWN_THREAD:
+      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:
+  if (op->task) /* async op */
+    {
+      if (op->error)
+        g_task_return_error (op->task, op->error);
+      else
+        g_task_return_int (op->task, op->result);
+
+      /* The op is owned only for async ops, not for sync ops. */
+      g_tls_thread_operation_free (op);
+    }
+  else /* sync op */
+    {
+      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..63f7a34
--- /dev/null
+++ b/tls/base/gtlsoperationsthread-base.h
@@ -0,0 +1,121 @@
+/* -*- 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 "gtlsconnection-base.h"
+
+#include <gio/gio.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    (*handshake_fn)               (GTlsOperationsThreadBase  *self,
+                                                             gint64                     timeout,
+                                                             GCancellable              *cancellable,
+                                                             GError                   **error);
+
+  GTlsConnectionBaseStatus    (*read_fn)                    (GTlsOperationsThreadBase  *self,
+                                                             void                      *buffer,
+                                                             gsize                      size,
+                                                             gssize                    *nread,
+                                                             GCancellable              *cancellable,
+                                                             GError                   **error);
+  GTlsConnectionBaseStatus    (*read_message_fn)            (GTlsOperationsThreadBase  *self,
+                                                             GInputVector              *vectors,
+                                                             guint                      num_vectors,
+                                                             gssize                    *nread,
+                                                             GCancellable              *cancellable,
+                                                             GError                   **error);
+
+  GTlsConnectionBaseStatus    (*write_fn)                   (GTlsOperationsThreadBase  *self,
+                                                             const void                *buffer,
+                                                             gsize                      size,
+                                                             gssize                    *nwrote,
+                                                             GCancellable              *cancellable,
+                                                             GError                   **error);
+  GTlsConnectionBaseStatus    (*write_message_fn)           (GTlsOperationsThreadBase  *self,
+                                                             GOutputVector             *vectors,
+                                                             guint                      num_vectors,
+                                                             gssize                    *nwrote,
+                                                             GCancellable              *cancellable,
+                                                             GError                   **error);
+
+  GTlsConnectionBaseStatus    (*close_fn)                   (GTlsOperationsThreadBase  *self,
+                                                             GCancellable              *cancellable,
+                                                             GError                   **error);
+};
+
+/* FIXME: remove? */
+GTlsConnectionBase       *g_tls_operations_thread_base_get_connection (GTlsOperationsThreadBase  *self);
+
+GTlsConnectionBaseStatus  g_tls_operations_thread_base_handshake      (GTlsOperationsThreadBase  *self,
+                                                                       gint64                     timeout,
+                                                                       GCancellable              
*cancellable,
+                                                                       GError                   **error);
+
+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_read_message   (GTlsOperationsThreadBase  *self,
+                                                                       GInputVector              *vectors,
+                                                                       guint                      
num_vectors,
+                                                                       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);
+
+GTlsConnectionBaseStatus  g_tls_operations_thread_base_write_message  (GTlsOperationsThreadBase  *self,
+                                                                       GOutputVector             *vectors,
+                                                                       guint                      
num_vectors,
+                                                                       gint64                     timeout,
+                                                                       gssize                    *nwrote,
+                                                                       GCancellable              
*cancellable,
+                                                                       GError                   **error);
+
+GTlsConnectionBaseStatus  g_tls_operations_thread_base_close          (GTlsOperationsThreadBase  *self,
+                                                                       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 94c5620..57658db 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>
@@ -63,6 +65,8 @@ static ssize_t g_tls_connection_gnutls_pull_func (gnutls_transport_ptr_t  transp
 static int     g_tls_connection_gnutls_pull_timeout_func (gnutls_transport_ptr_t transport_data,
                                                           unsigned int           ms);
 
+static GInitableIface *g_tls_connection_gnutls_parent_initable_iface;
+
 static void g_tls_connection_gnutls_initable_iface_init (GInitableIface *iface);
 
 static int verify_certificate_cb (gnutls_session_t session);
@@ -72,7 +76,7 @@ static gnutls_priority_t priority;
 typedef struct
 {
   gnutls_certificate_credentials_t creds;
-  gnutls_session_t session;
+  gnutls_session_t session; /* FIXME: should be used only by GTlsOperationsThreadGnutls */
   gchar *interaction_id;
   GCancellable *cancellable;
 } GTlsConnectionGnutlsPrivate;
@@ -164,6 +168,7 @@ g_tls_connection_gnutls_initable_init (GInitable     *initable,
                                       g_tls_connection_gnutls_push_func);
   gnutls_transport_set_pull_function (priv->session,
                                       g_tls_connection_gnutls_pull_func);
+  /* FIXME: remove timeout func and switch to GNUTLS_NONBLOCK */
   gnutls_transport_set_pull_timeout_function (priv->session,
                                               g_tls_connection_gnutls_pull_timeout_func);
   gnutls_transport_set_ptr (priv->session, gnutls);
@@ -179,7 +184,7 @@ g_tls_connection_gnutls_initable_init (GInitable     *initable,
   if (flags & GNUTLS_DATAGRAM)
     gnutls_dtls_set_mtu (priv->session, 1400);
 
-  return TRUE;
+  return g_tls_connection_gnutls_parent_initable_iface->init (initable, cancellable, error);
 }
 
 static void
@@ -316,6 +321,7 @@ g_tls_connection_gnutls_handshake_thread_get_certificate (GTlsConnectionGnutls
     }
 }
 
+// FIXME: remove
 static GTlsConnectionBaseStatus
 end_gnutls_io (GTlsConnectionGnutls  *gnutls,
                GIOCondition           direction,
@@ -469,6 +475,8 @@ end_gnutls_io (GTlsConnectionGnutls  *gnutls,
   return G_TLS_CONNECTION_BASE_ERROR;
 }
 
+// FIXME: remove
+
 #define BEGIN_GNUTLS_IO(gnutls, direction, timeout, cancellable)        \
   g_tls_connection_base_push_io (G_TLS_CONNECTION_BASE (gnutls),        \
                                  direction, timeout, cancellable);      \
@@ -511,6 +519,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,
@@ -755,6 +765,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)
 {
@@ -918,192 +934,6 @@ g_tls_connection_gnutls_is_session_resumed (GTlsConnectionBase *tls)
   return gnutls_session_is_resumed (priv->session);
 }
 
-static GTlsConnectionBaseStatus
-g_tls_connection_gnutls_read (GTlsConnectionBase  *tls,
-                              void                *buffer,
-                              gsize                count,
-                              gint64               timeout,
-                              gssize              *nread,
-                              GCancellable        *cancellable,
-                              GError             **error)
-{
-  GTlsConnectionGnutls *gnutls = G_TLS_CONNECTION_GNUTLS (tls);
-  GTlsConnectionGnutlsPrivate *priv = g_tls_connection_gnutls_get_instance_private (gnutls);
-  GTlsConnectionBaseStatus status;
-  gssize ret;
-
-  BEGIN_GNUTLS_IO (gnutls, G_IO_IN, timeout, cancellable);
-  ret = gnutls_record_recv (priv->session, buffer, count);
-  END_GNUTLS_IO (gnutls, G_IO_IN, ret, status, _("Error reading data from TLS socket"), error);
-
-  *nread = MAX (ret, 0);
-  return status;
-}
-
-static gsize
-input_vectors_from_gnutls_datum_t (GInputVector         *vectors,
-                                   guint                 num_vectors,
-                                   const gnutls_datum_t *datum)
-{
-  guint i;
-  gsize total = 0;
-
-  /* Copy into the receive vectors. */
-  for (i = 0; i < num_vectors && total < datum->size; i++)
-    {
-      gsize count;
-      GInputVector *vec = &vectors[i];
-
-      count = MIN (vec->size, datum->size - total);
-
-      memcpy (vec->buffer, datum->data + total, count);
-      total += count;
-    }
-
-  g_assert (total <= datum->size);
-
-  return total;
-}
-
-static GTlsConnectionBaseStatus
-g_tls_connection_gnutls_read_message (GTlsConnectionBase  *tls,
-                                      GInputVector        *vectors,
-                                      guint                num_vectors,
-                                      gint64               timeout,
-                                      gssize              *nread,
-                                      GCancellable        *cancellable,
-                                      GError             **error)
-{
-  GTlsConnectionGnutls *gnutls = G_TLS_CONNECTION_GNUTLS (tls);
-  GTlsConnectionGnutlsPrivate *priv = g_tls_connection_gnutls_get_instance_private (gnutls);
-  GTlsConnectionBaseStatus status;
-  gssize ret;
-  gnutls_packet_t packet = { 0, };
-
-  BEGIN_GNUTLS_IO (gnutls, G_IO_IN, timeout, cancellable);
-
-  /* Receive the entire datagram (zero-copy). */
-  ret = gnutls_record_recv_packet (priv->session, &packet);
-
-  if (ret > 0)
-    {
-      gnutls_datum_t data = { 0, };
-
-      gnutls_packet_get (packet, &data, NULL);
-      ret = input_vectors_from_gnutls_datum_t (vectors, num_vectors, &data);
-      gnutls_packet_deinit (packet);
-    }
-
-  END_GNUTLS_IO (gnutls, G_IO_IN, ret, status, _("Error reading data from TLS socket"), error);
-
-  *nread = MAX (ret, 0);
-  return status;
-}
-
-static GTlsConnectionBaseStatus
-g_tls_connection_gnutls_write (GTlsConnectionBase  *tls,
-                               const void          *buffer,
-                               gsize                count,
-                               gint64               timeout,
-                               gssize              *nwrote,
-                               GCancellable        *cancellable,
-                               GError             **error)
-{
-  GTlsConnectionGnutls *gnutls = G_TLS_CONNECTION_GNUTLS (tls);
-  GTlsConnectionGnutlsPrivate *priv = g_tls_connection_gnutls_get_instance_private (gnutls);
-  GTlsConnectionBaseStatus status;
-  gssize ret;
-
-  BEGIN_GNUTLS_IO (gnutls, G_IO_OUT, timeout, cancellable);
-  ret = gnutls_record_send (priv->session, buffer, count);
-  END_GNUTLS_IO (gnutls, G_IO_OUT, ret, status, _("Error writing data to TLS socket"), error);
-
-  *nwrote = MAX (ret, 0);
-  return status;
-}
-
-static GTlsConnectionBaseStatus
-g_tls_connection_gnutls_write_message (GTlsConnectionBase  *tls,
-                                       GOutputVector       *vectors,
-                                       guint                num_vectors,
-                                       gint64               timeout,
-                                       gssize              *nwrote,
-                                       GCancellable        *cancellable,
-                                       GError             **error)
-{
-  GTlsConnectionGnutls *gnutls = G_TLS_CONNECTION_GNUTLS (tls);
-  GTlsConnectionGnutlsPrivate *priv = g_tls_connection_gnutls_get_instance_private (gnutls);
-  GTlsConnectionBaseStatus status;
-  gssize ret;
-  guint i;
-  gsize total_message_size;
-
-  /* Calculate the total message size and check it’s not too big. */
-  for (i = 0, total_message_size = 0; i < num_vectors; i++)
-    total_message_size += vectors[i].size;
-
-  if (g_tls_connection_base_is_dtls (tls) &&
-      gnutls_dtls_get_data_mtu (priv->session) < total_message_size)
-    {
-      char *message;
-      guint mtu = gnutls_dtls_get_data_mtu (priv->session);
-
-      ret = GNUTLS_E_LARGE_PACKET;
-      message = g_strdup_printf("%s %s",
-                                ngettext ("Message of size %lu byte is too large for DTLS connection",
-                                          "Message of size %lu bytes is too large for DTLS connection", 
total_message_size),
-                                ngettext ("(maximum is %u byte)", "(maximum is %u bytes)", mtu));
-      g_set_error (error, G_IO_ERROR, G_IO_ERROR_MESSAGE_TOO_LARGE,
-                   message,
-                   total_message_size,
-                   mtu);
-      g_free (message);
-
-      return G_TLS_CONNECTION_BASE_ERROR;
-    }
-
-  /* Queue up the data from all the vectors. */
-  gnutls_record_cork (priv->session);
-
-  for (i = 0; i < num_vectors; i++)
-    {
-      ret = gnutls_record_send (priv->session,
-                                vectors[i].buffer, vectors[i].size);
-
-      if (ret < 0 || ret < vectors[i].size)
-        {
-          /* Uncork to restore state, then bail. The peer will receive a
-           * truncated datagram. */
-          break;
-        }
-    }
-
-  BEGIN_GNUTLS_IO (gnutls, G_IO_OUT, timeout, cancellable);
-  ret = gnutls_record_uncork (priv->session, 0  /* flags */);
-  END_GNUTLS_IO (gnutls, G_IO_OUT, ret, status, _("Error writing data to TLS socket"), error);
-
-  *nwrote = MAX (ret, 0);
-  return status;
-}
-
-static GTlsConnectionBaseStatus
-g_tls_connection_gnutls_close (GTlsConnectionBase  *tls,
-                               gint64               timeout,
-                               GCancellable        *cancellable,
-                               GError             **error)
-{
-  GTlsConnectionGnutls *gnutls = G_TLS_CONNECTION_GNUTLS (tls);
-  GTlsConnectionGnutlsPrivate *priv = g_tls_connection_gnutls_get_instance_private (gnutls);
-  GTlsConnectionBaseStatus status;
-  int ret;
-
-  BEGIN_GNUTLS_IO (gnutls, G_IO_IN | G_IO_OUT, timeout, cancellable);
-  ret = gnutls_bye (priv->session, GNUTLS_SHUT_WR);
-  END_GNUTLS_IO (gnutls, G_IO_IN | G_IO_OUT, ret, status, _("Error performing TLS close: %s"), error);
-
-  return status;
-}
-
 static void
 initialize_gnutls_priority (void)
 {
@@ -1135,6 +965,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;
@@ -1142,11 +973,6 @@ g_tls_connection_gnutls_class_init (GTlsConnectionGnutlsClass *klass)
   base_class->retrieve_peer_certificate                  = g_tls_connection_gnutls_retrieve_peer_certificate;
   base_class->complete_handshake                         = g_tls_connection_gnutls_complete_handshake;
   base_class->is_session_resumed                         = g_tls_connection_gnutls_is_session_resumed;
-  base_class->read_fn                                    = g_tls_connection_gnutls_read;
-  base_class->read_message_fn                            = g_tls_connection_gnutls_read_message;
-  base_class->write_fn                                   = g_tls_connection_gnutls_write;
-  base_class->write_message_fn                           = g_tls_connection_gnutls_write_message;
-  base_class->close_fn                                   = g_tls_connection_gnutls_close;
 
   initialize_gnutls_priority ();
 }
@@ -1154,5 +980,7 @@ g_tls_connection_gnutls_class_init (GTlsConnectionGnutlsClass *klass)
 static void
 g_tls_connection_gnutls_initable_iface_init (GInitableIface *iface)
 {
+  g_tls_connection_gnutls_parent_initable_iface = g_type_interface_peek_parent (iface);
+
   iface->init = g_tls_connection_gnutls_initable_init;
 }
diff --git a/tls/gnutls/gtlsconnection-gnutls.h b/tls/gnutls/gtlsconnection-gnutls.h
index 55ae5ee..3e9fa9b 100644
--- a/tls/gnutls/gtlsconnection-gnutls.h
+++ b/tls/gnutls/gtlsconnection-gnutls.h
@@ -43,6 +43,7 @@ struct _GTlsConnectionGnutlsClass
 
 gnutls_certificate_credentials_t g_tls_connection_gnutls_get_credentials (GTlsConnectionGnutls *connection);
 
+/* FIXME: remove to ensure threadsafety */
 gnutls_session_t                 g_tls_connection_gnutls_get_session     (GTlsConnectionGnutls *connection);
 
 void     g_tls_connection_gnutls_handshake_thread_get_certificate     (GTlsConnectionGnutls  *gnutls,
diff --git a/tls/gnutls/gtlsoperationsthread-gnutls.c b/tls/gnutls/gtlsoperationsthread-gnutls.c
new file mode 100644
index 0000000..11b1bc9
--- /dev/null
+++ b/tls/gnutls/gtlsoperationsthread-gnutls.c
@@ -0,0 +1,509 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright 2009 Red Hat, Inc
+ * Copyright 2015, 2016 Collabora, Ltd.
+ * 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-gnutls.h"
+
+#include "gtlsconnection-gnutls.h"
+
+#include <glib/gi18n-lib.h>
+#include <gnutls/dtls.h>
+
+struct _GTlsOperationsThreadGnutls {
+  GTlsOperationsThreadBase parent_instance;
+
+  gnutls_session_t         session;
+};
+
+static gnutls_priority_t priority;
+
+G_DEFINE_TYPE (GTlsOperationsThreadGnutls, g_tls_operations_thread_gnutls, G_TYPE_TLS_OPERATIONS_THREAD_BASE)
+
+static GTlsConnectionBaseStatus
+end_gnutls_io (GTlsOperationsThreadGnutls  *self,
+               GIOCondition                 direction,
+               int                          ret,
+               GError                     **error,
+               const char                  *err_prefix)
+{
+  GTlsConnectionBase *tls;
+  GTlsConnectionBaseStatus status;
+  gboolean handshaking;
+  gboolean ever_handshaked;
+  GError *my_error = NULL;
+
+  /* We intentionally do not check for GNUTLS_E_INTERRUPTED here
+   * Instead, the caller may poll for the source to become ready again.
+   * (Note that GTlsOutputStreamGnutls and GTlsInputStreamGnutls inherit
+   * from GPollableOutputStream and GPollableInputStream, respectively.)
+   * See also the comment in set_gnutls_error().
+   */
+  if (ret == GNUTLS_E_AGAIN ||
+      ret == GNUTLS_E_WARNING_ALERT_RECEIVED)
+    return G_TLS_CONNECTION_BASE_TRY_AGAIN;
+
+  tls = g_tls_operations_thread_base_get_connection (G_TLS_OPERATIONS_THREAD_BASE (self));
+
+  status = g_tls_connection_base_pop_io (tls, direction, ret >= 0, &my_error);
+  if (status == G_TLS_CONNECTION_BASE_OK ||
+      status == G_TLS_CONNECTION_BASE_WOULD_BLOCK ||
+      status == G_TLS_CONNECTION_BASE_TIMED_OUT)
+    {
+      if (my_error)
+        g_propagate_error (error, my_error);
+      return status;
+    }
+
+  g_assert (status == G_TLS_CONNECTION_BASE_ERROR);
+
+  handshaking = g_tls_connection_base_is_handshaking (tls);
+  ever_handshaked = g_tls_connection_base_ever_handshaked (tls);
+
+  if (handshaking && !ever_handshaked)
+    {
+      if (g_error_matches (my_error, G_IO_ERROR, G_IO_ERROR_FAILED) ||
+          g_error_matches (my_error, G_IO_ERROR, G_IO_ERROR_BROKEN_PIPE))
+        {
+          g_set_error (error, G_TLS_ERROR, G_TLS_ERROR_NOT_TLS,
+                       _("Peer failed to perform TLS handshake: %s"), my_error->message);
+          g_clear_error (&my_error);
+          return G_TLS_CONNECTION_BASE_ERROR;
+        }
+
+      if (status == GNUTLS_E_UNEXPECTED_PACKET_LENGTH ||
+          status == GNUTLS_E_DECRYPTION_FAILED ||
+          status == GNUTLS_E_UNSUPPORTED_VERSION_PACKET)
+        {
+          g_clear_error (&my_error);
+          g_set_error (error, G_TLS_ERROR, G_TLS_ERROR_NOT_TLS,
+                       _("Peer failed to perform TLS handshake: %s"), gnutls_strerror (ret));
+          return G_TLS_CONNECTION_BASE_ERROR;
+        }
+    }
+
+  if (ret == GNUTLS_E_REHANDSHAKE)
+    return G_TLS_CONNECTION_BASE_REHANDSHAKE;
+
+  if (ret == GNUTLS_E_PREMATURE_TERMINATION)
+    {
+      if (handshaking && !ever_handshaked)
+        {
+          g_clear_error (&my_error);
+          g_set_error (error, G_TLS_ERROR, G_TLS_ERROR_NOT_TLS,
+                       _("Peer failed to perform TLS handshake: %s"), gnutls_strerror (ret));
+          return G_TLS_CONNECTION_BASE_ERROR;
+        }
+
+      if (g_tls_connection_get_require_close_notify (G_TLS_CONNECTION (tls)))
+        {
+          g_clear_error (&my_error);
+          g_set_error_literal (error, G_TLS_ERROR, G_TLS_ERROR_EOF,
+                               _("TLS connection closed unexpectedly"));
+          return G_TLS_CONNECTION_BASE_ERROR;
+        }
+
+      return G_TLS_CONNECTION_BASE_OK;
+    }
+
+  if (ret == GNUTLS_E_NO_CERTIFICATE_FOUND || ret == GNUTLS_E_CERTIFICATE_REQUIRED)
+    {
+      g_clear_error (&my_error);
+      g_set_error_literal (error, G_TLS_ERROR, G_TLS_ERROR_CERTIFICATE_REQUIRED,
+                           _("TLS connection peer did not send a certificate"));
+      return G_TLS_CONNECTION_BASE_ERROR;
+    }
+
+  if (ret == GNUTLS_E_CERTIFICATE_ERROR)
+    {
+      g_clear_error (&my_error);
+      g_set_error_literal (error, G_TLS_ERROR, G_TLS_ERROR_BAD_CERTIFICATE,
+                           _("Unacceptable TLS certificate"));
+      return G_TLS_CONNECTION_BASE_ERROR;
+    }
+
+  if (ret == GNUTLS_E_FATAL_ALERT_RECEIVED)
+    {
+      g_clear_error (&my_error);
+      g_set_error (error, G_TLS_ERROR, G_TLS_ERROR_MISC,
+                   _("Peer sent fatal TLS alert: %s"),
+                   gnutls_alert_get_name (gnutls_alert_get (self->session)));
+      return G_TLS_CONNECTION_BASE_ERROR;
+    }
+
+  if (ret == GNUTLS_E_INAPPROPRIATE_FALLBACK)
+    {
+      g_clear_error (&my_error);
+      g_set_error_literal (error, G_TLS_ERROR,
+                           G_TLS_ERROR_INAPPROPRIATE_FALLBACK,
+                           _("Protocol version downgrade attack detected"));
+      return G_TLS_CONNECTION_BASE_ERROR;
+    }
+
+  if (ret == GNUTLS_E_LARGE_PACKET)
+    {
+      guint mtu = gnutls_dtls_get_data_mtu (self->session);
+      g_clear_error (&my_error);
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_MESSAGE_TOO_LARGE,
+                   ngettext ("Message is too large for DTLS connection; maximum is %u byte",
+                             "Message is too large for DTLS connection; maximum is %u bytes", mtu), mtu);
+      return G_TLS_CONNECTION_BASE_ERROR;
+    }
+
+  if (ret == GNUTLS_E_TIMEDOUT)
+    {
+      g_clear_error (&my_error);
+      g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_TIMED_OUT,
+                           _("The operation timed out"));
+      return G_TLS_CONNECTION_BASE_ERROR;
+    }
+
+  if (error && my_error)
+    g_propagate_error (error, my_error);
+
+  if (error && !*error)
+    {
+      *error = g_error_new (G_TLS_ERROR, G_TLS_ERROR_MISC, "%s: %s",
+                            err_prefix, gnutls_strerror (ret));
+    }
+
+  return G_TLS_CONNECTION_BASE_ERROR;
+}
+
+#define BEGIN_GNUTLS_IO(self, direction, cancellable)          \
+  g_tls_connection_base_push_io (g_tls_operations_thread_base_get_connection (G_TLS_OPERATIONS_THREAD_BASE 
(self)),        \
+                                 direction, 0, cancellable);    \
+  do {
+
+#define END_GNUTLS_IO(self, direction, ret, status, errmsg, err)      \
+    status = end_gnutls_io (self, direction, ret, err, errmsg);       \
+  } while (status == G_TLS_CONNECTION_BASE_TRY_AGAIN);
+
+static void
+initialize_gnutls_priority (void)
+{
+  const gchar *priority_override;
+  const gchar *error_pos = NULL;
+  int ret;
+
+  g_assert (!priority);
+
+  priority_override = g_getenv ("G_TLS_GNUTLS_PRIORITY");
+  if (priority_override)
+    {
+      ret = gnutls_priority_init2 (&priority, priority_override, &error_pos, 0);
+      if (ret != GNUTLS_E_SUCCESS)
+        g_warning ("Failed to set GnuTLS session priority with beginning at %s: %s", error_pos, 
gnutls_strerror (ret));
+      return;
+    }
+
+  ret = gnutls_priority_init2 (&priority, "%COMPAT:-VERS-TLS1.1:-VERS-TLS1.0", &error_pos, 
GNUTLS_PRIORITY_INIT_DEF_APPEND);
+  if (ret != GNUTLS_E_SUCCESS)
+    g_warning ("Failed to set GnuTLS session priority with error beginning at %s: %s", error_pos, 
gnutls_strerror (ret));
+}
+
+static void
+set_handshake_priority (GTlsOperationsThreadGnutls *self)
+{
+  int ret;
+
+  g_assert (priority);
+
+  ret = gnutls_priority_set (self->session, priority);
+  if (ret != GNUTLS_E_SUCCESS)
+    g_warning ("Failed to set GnuTLS session priority: %s", gnutls_strerror (ret));
+}
+
+static GTlsConnectionBaseStatus
+g_tls_operations_thread_gnutls_handshake (GTlsOperationsThreadBase *base,
+                                          gint64                    timeout,
+                                          GCancellable             *cancellable,
+                                          GError                  **error)
+{
+  GTlsOperationsThreadGnutls *self = G_TLS_OPERATIONS_THREAD_GNUTLS (base);
+  GTlsConnectionBase *tls;
+  GTlsConnectionBaseStatus status;
+  int ret;
+
+  tls = g_tls_operations_thread_base_get_connection (base);
+
+  if (!g_tls_connection_base_ever_handshaked (tls))
+    set_handshake_priority (self);
+
+  if (timeout > 0)
+    {
+      unsigned int timeout_ms;
+
+      /* Convert from microseconds to milliseconds, but ensure the timeout
+       * remains positive. */
+      timeout_ms = (timeout + 999) / 1000;
+
+      gnutls_handshake_set_timeout (self->session, timeout_ms);
+      gnutls_dtls_set_timeouts (self->session, 1000 /* default */, timeout_ms);
+    }
+
+  BEGIN_GNUTLS_IO (self, G_IO_IN | G_IO_OUT, cancellable);
+  ret = gnutls_handshake (self->session);
+  if (ret == GNUTLS_E_GOT_APPLICATION_DATA)
+    {
+      guint8 buf[1024];
+
+      /* Got app data while waiting for rehandshake; buffer it and try again */
+      ret = gnutls_record_recv (self->session, buf, sizeof (buf));
+      if (ret > -1)
+        {
+          /* FIXME: no longer belongs in GTlsConnectionBase? */
+          g_tls_connection_base_handshake_thread_buffer_application_data (tls, buf, ret);
+          ret = GNUTLS_E_AGAIN;
+        }
+    }
+  END_GNUTLS_IO (self, G_IO_IN | G_IO_OUT, ret, status,
+                 _("Error performing TLS handshake"), error);
+
+  return status;
+}
+
+static GTlsConnectionBaseStatus
+g_tls_operations_thread_gnutls_read (GTlsOperationsThreadBase  *base,
+                                     void                      *buffer,
+                                     gsize                      size,
+                                     gssize                    *nread,
+                                     GCancellable              *cancellable,
+                                     GError                   **error)
+{
+  GTlsOperationsThreadGnutls *self = G_TLS_OPERATIONS_THREAD_GNUTLS (base);
+  GTlsConnectionBaseStatus status;
+  gssize ret;
+
+  BEGIN_GNUTLS_IO (self, G_IO_IN, cancellable);
+  ret = gnutls_record_recv (self->session, buffer, size);
+  END_GNUTLS_IO (self, G_IO_IN, ret, status, _("Error reading data from TLS socket"), error);
+
+  *nread = MAX (ret, 0);
+  return status;
+}
+
+static gsize
+input_vectors_from_gnutls_datum_t (GInputVector         *vectors,
+                                   guint                 num_vectors,
+                                   const gnutls_datum_t *datum)
+{
+  guint i;
+  gsize total = 0;
+
+  /* Copy into the receive vectors. */
+  for (i = 0; i < num_vectors && total < datum->size; i++)
+    {
+      gsize count;
+      GInputVector *vec = &vectors[i];
+
+      count = MIN (vec->size, datum->size - total);
+
+      memcpy (vec->buffer, datum->data + total, count);
+      total += count;
+    }
+
+  g_assert (total <= datum->size);
+
+  return total;
+}
+
+static GTlsConnectionBaseStatus
+g_tls_operations_thread_gnutls_read_message (GTlsOperationsThreadBase  *base,
+                                             GInputVector              *vectors,
+                                             guint                      num_vectors,
+                                             gssize                    *nread,
+                                             GCancellable              *cancellable,
+                                             GError                   **error)
+{
+  GTlsOperationsThreadGnutls *self = G_TLS_OPERATIONS_THREAD_GNUTLS (base);
+  GTlsConnectionBaseStatus status;
+  gssize ret;
+  gnutls_packet_t packet = { 0, };
+
+  BEGIN_GNUTLS_IO (self, G_IO_IN, cancellable);
+
+  /* Receive the entire datagram (zero-copy). */
+  ret = gnutls_record_recv_packet (self->session, &packet);
+
+  if (ret > 0)
+    {
+      gnutls_datum_t data = { 0, };
+
+      gnutls_packet_get (packet, &data, NULL);
+      ret = input_vectors_from_gnutls_datum_t (vectors, num_vectors, &data);
+      gnutls_packet_deinit (packet);
+    }
+
+  END_GNUTLS_IO (self, G_IO_IN, ret, status, _("Error reading data from TLS socket"), error);
+
+  *nread = MAX (ret, 0);
+  return status;
+}
+
+static GTlsConnectionBaseStatus
+g_tls_operations_thread_gnutls_write (GTlsOperationsThreadBase  *base,
+                                      const void                *buffer,
+                                      gsize                      size,
+                                      gssize                    *nwrote,
+                                      GCancellable              *cancellable,
+                                      GError                   **error)
+{
+  GTlsOperationsThreadGnutls *self = G_TLS_OPERATIONS_THREAD_GNUTLS (base);
+  GTlsConnectionBaseStatus status;
+  gssize ret;
+
+  BEGIN_GNUTLS_IO (self, G_IO_OUT, cancellable);
+  ret = gnutls_record_send (self->session, buffer, size);
+  END_GNUTLS_IO (self, G_IO_OUT, ret, status, _("Error writing data to TLS socket"), error);
+
+  *nwrote = MAX (ret, 0);
+  return status;
+}
+
+static GTlsConnectionBaseStatus
+g_tls_operations_thread_gnutls_write_message (GTlsOperationsThreadBase  *base,
+                                              GOutputVector             *vectors,
+                                              guint                      num_vectors,
+                                              gssize                    *nwrote,
+                                              GCancellable              *cancellable,
+                                              GError                   **error)
+{
+  GTlsOperationsThreadGnutls *self = G_TLS_OPERATIONS_THREAD_GNUTLS (base);
+  GTlsConnectionBase *connection;
+  GTlsConnectionBaseStatus status;
+  gssize ret;
+  guint i;
+  gsize total_message_size;
+
+  connection = g_tls_operations_thread_base_get_connection (base);
+
+  /* Calculate the total message size and check it’s not too big. */
+  for (i = 0, total_message_size = 0; i < num_vectors; i++)
+    total_message_size += vectors[i].size;
+
+  if (g_tls_connection_base_is_dtls (connection) &&
+      gnutls_dtls_get_data_mtu (self->session) < total_message_size)
+    {
+      char *message;
+      guint mtu = gnutls_dtls_get_data_mtu (self->session);
+
+      ret = GNUTLS_E_LARGE_PACKET;
+      message = g_strdup_printf("%s %s",
+                                ngettext ("Message of size %lu byte is too large for DTLS connection",
+                                          "Message of size %lu bytes is too large for DTLS connection", 
total_message_size),
+                                ngettext ("(maximum is %u byte)", "(maximum is %u bytes)", mtu));
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_MESSAGE_TOO_LARGE,
+                   message,
+                   total_message_size,
+                   mtu);
+      g_free (message);
+
+      return G_TLS_CONNECTION_BASE_ERROR;
+    }
+
+  /* Queue up the data from all the vectors. */
+  gnutls_record_cork (self->session);
+
+  for (i = 0; i < num_vectors; i++)
+    {
+      ret = gnutls_record_send (self->session,
+                                vectors[i].buffer, vectors[i].size);
+
+      if (ret < 0 || ret < vectors[i].size)
+        {
+          /* Uncork to restore state, then bail. The peer will receive a
+           * truncated datagram. */
+          break;
+        }
+    }
+
+  BEGIN_GNUTLS_IO (self, G_IO_OUT, cancellable);
+  ret = gnutls_record_uncork (self->session, 0  /* flags */);
+  END_GNUTLS_IO (self, G_IO_OUT, ret, status, _("Error writing data to TLS socket"), error);
+
+  *nwrote = MAX (ret, 0);
+  return status;
+}
+
+static GTlsConnectionBaseStatus
+g_tls_operations_thread_gnutls_close (GTlsOperationsThreadBase  *base,
+                                      GCancellable              *cancellable,
+                                      GError                   **error)
+{
+  GTlsOperationsThreadGnutls *self = G_TLS_OPERATIONS_THREAD_GNUTLS (base);
+  GTlsConnectionBaseStatus status;
+  int ret;
+
+  BEGIN_GNUTLS_IO (self, G_IO_IN | G_IO_OUT, cancellable);
+  ret = gnutls_bye (self->session, GNUTLS_SHUT_WR);
+  END_GNUTLS_IO (self, G_IO_IN | G_IO_OUT, ret, status, _("Error performing TLS close: %s"), error);
+
+  return status;
+}
+
+static void
+g_tls_operations_thread_gnutls_constructed (GObject *object)
+{
+  GTlsOperationsThreadGnutls *self = G_TLS_OPERATIONS_THREAD_GNUTLS (object);
+  GTlsConnectionBase *tls;
+
+  G_OBJECT_CLASS (g_tls_operations_thread_gnutls_parent_class)->constructed (object);
+
+  tls = g_tls_operations_thread_base_get_connection (G_TLS_OPERATIONS_THREAD_BASE (self));
+  self->session = g_tls_connection_gnutls_get_session (G_TLS_CONNECTION_GNUTLS (tls));
+}
+
+static void
+g_tls_operations_thread_gnutls_init (GTlsOperationsThreadGnutls *self)
+{
+}
+
+static void
+g_tls_operations_thread_gnutls_class_init (GTlsOperationsThreadGnutlsClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+  GTlsOperationsThreadBaseClass *base_class = G_TLS_OPERATIONS_THREAD_BASE_CLASS (klass);
+
+  gobject_class->constructed   = g_tls_operations_thread_gnutls_constructed;
+
+  base_class->handshake_fn     = g_tls_operations_thread_gnutls_handshake;
+  base_class->read_fn          = g_tls_operations_thread_gnutls_read;
+  base_class->read_message_fn  = g_tls_operations_thread_gnutls_read_message;
+  base_class->write_fn         = g_tls_operations_thread_gnutls_write;
+  base_class->write_message_fn = g_tls_operations_thread_gnutls_write_message;
+  base_class->close_fn         = g_tls_operations_thread_gnutls_close;
+
+  initialize_gnutls_priority ();
+}
+
+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..b3fce2d
--- /dev/null
+++ b/tls/gnutls/gtlsoperationsthread-gnutls.h
@@ -0,0 +1,40 @@
+/* -*- 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 "gtlsconnection-gnutls.h"
+#include "gtlsoperationsthread-base.h"
+
+#include <gio/gio.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/gtlsbio.c b/tls/openssl/gtlsbio.c
index b138432..e19edd2 100644
--- a/tls/openssl/gtlsbio.c
+++ b/tls/openssl/gtlsbio.c
@@ -35,8 +35,6 @@ typedef struct {
   gboolean write_blocking;
   GError **read_error;
   GError **write_error;
-  GMainContext *context;
-  GMainLoop *loop;
 } GTlsBio;
 
 static void
@@ -45,8 +43,6 @@ free_gbio (gpointer user_data)
   GTlsBio *bio = (GTlsBio *)user_data;
 
   g_object_unref (bio->io_stream);
-  g_main_context_unref (bio->context);
-  g_main_loop_unref (bio->loop);
   g_free (bio);
 }
 
@@ -294,8 +290,6 @@ g_tls_bio_new (GIOStream *io_stream)
 
   gbio = g_new0 (GTlsBio, 1);
   gbio->io_stream = g_object_ref (io_stream);
-  gbio->context = g_main_context_new ();
-  gbio->loop = g_main_loop_new (gbio->context, FALSE);
 
 #if OPENSSL_VERSION_NUMBER < 0x10100000L || defined (LIBRESSL_VERSION_NUMBER)
   ret->ptr = gbio;
@@ -403,49 +397,3 @@ g_tls_bio_set_write_error (BIO     *bio,
 #endif
   gbio->write_error = error;
 }
-
-static gboolean
-on_source_ready (GObject *pollable_stream,
-                 gpointer user_data)
-{
-  GMainLoop *loop = user_data;
-
-  g_main_loop_quit (loop);
-
-  return G_SOURCE_REMOVE;
-}
-
-void
-g_tls_bio_wait_available (BIO          *bio,
-                          GIOCondition  condition,
-                          GCancellable *cancellable)
-{
-  GTlsBio *gbio;
-  GSource *source;
-
-  g_return_if_fail (bio);
-
-#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined (LIBRESSL_VERSION_NUMBER)
-  gbio = (GTlsBio *)bio->ptr;
-#else
-  gbio = BIO_get_data (bio);
-#endif
-
-  g_main_context_push_thread_default (gbio->context);
-
-  if (condition & G_IO_IN)
-    source = g_pollable_input_stream_create_source (G_POLLABLE_INPUT_STREAM (g_io_stream_get_input_stream 
(gbio->io_stream)),
-                                                    cancellable);
-  else
-    source = g_pollable_output_stream_create_source (G_POLLABLE_OUTPUT_STREAM (g_io_stream_get_output_stream 
(gbio->io_stream)),
-                                                     cancellable);
-
-  g_source_set_callback (source, (GSourceFunc)on_source_ready, gbio->loop, NULL);
-  g_source_attach (source, gbio->context);
-
-  g_main_loop_run (gbio->loop);
-  g_main_context_pop_thread_default (gbio->context);
-
-  g_source_destroy (source);
-  g_source_unref (source);
-}
diff --git a/tls/openssl/gtlsconnection-openssl.c b/tls/openssl/gtlsconnection-openssl.c
index 2e728f9..b06465e 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>
@@ -41,11 +42,10 @@
 typedef struct _GTlsConnectionOpensslPrivate
 {
   BIO *bio;
-  GMutex ssl_mutex;
-
-  gboolean shutting_down;
 } GTlsConnectionOpensslPrivate;
 
+static GInitableIface *g_tls_connection_openssl_parent_initable_iface;
+
 static void g_tls_connection_openssl_initable_iface_init (GInitableIface *iface);
 
 G_DEFINE_ABSTRACT_TYPE_WITH_CODE (GTlsConnectionOpenssl, g_tls_connection_openssl, 
G_TYPE_TLS_CONNECTION_BASE,
@@ -53,17 +53,10 @@ G_DEFINE_ABSTRACT_TYPE_WITH_CODE (GTlsConnectionOpenssl, g_tls_connection_openss
                                   G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,
                                                          g_tls_connection_openssl_initable_iface_init))
 
-static void
-g_tls_connection_openssl_finalize (GObject *object)
+static GTlsOperationsThreadBase *
+g_tls_connection_openssl_create_op_thread (GTlsConnectionBase *tls)
 {
-  GTlsConnectionOpenssl *openssl = G_TLS_CONNECTION_OPENSSL (object);
-  GTlsConnectionOpensslPrivate *priv;
-
-  priv = g_tls_connection_openssl_get_instance_private (openssl);
-
-  g_mutex_clear (&priv->ssl_mutex);
-
-  G_OBJECT_CLASS (g_tls_connection_openssl_parent_class)->finalize (object);
+  return g_tls_operations_thread_openssl_new (G_TLS_CONNECTION_OPENSSL (tls));
 }
 
 static GTlsSafeRenegotiationStatus
@@ -88,14 +81,11 @@ end_openssl_io (GTlsConnectionOpenssl  *openssl,
                 const char             *err_str)
 {
   GTlsConnectionBase *tls = G_TLS_CONNECTION_BASE (openssl);
-  GTlsConnectionOpensslPrivate *priv;
   int err_code, err, err_lib, reason;
   GError *my_error = NULL;
   GTlsConnectionBaseStatus status;
   SSL *ssl;
 
-  priv = g_tls_connection_openssl_get_instance_private (openssl);
-
   ssl = g_tls_connection_openssl_get_ssl (openssl);
 
   err_code = SSL_get_error (ssl, ret);
@@ -124,9 +114,9 @@ end_openssl_io (GTlsConnectionOpenssl  *openssl,
     }
 
   /* This case is documented that it may happen and that is perfectly fine */
+  /* FIXME: broke this, but entire func should be deleted so OK */
   if (err_code == SSL_ERROR_SYSCALL &&
-      ((priv->shutting_down && !my_error) ||
-       g_error_matches (my_error, G_IO_ERROR, G_IO_ERROR_BROKEN_PIPE)))
+      g_error_matches (my_error, G_IO_ERROR, G_IO_ERROR_BROKEN_PIPE))
     {
       g_clear_error (&my_error);
       return G_TLS_CONNECTION_BASE_OK;
@@ -345,8 +335,6 @@ g_tls_connection_openssl_push_io (GTlsConnectionBase *tls,
       g_clear_error (error);
       g_tls_bio_set_write_error (priv->bio, error);
     }
-
-  g_mutex_lock (&priv->ssl_mutex);
 }
 
 static GTlsConnectionBaseStatus
@@ -360,8 +348,6 @@ g_tls_connection_openssl_pop_io (GTlsConnectionBase  *tls,
 
   priv = g_tls_connection_openssl_get_instance_private (openssl);
 
-  g_mutex_unlock (&priv->ssl_mutex);
-
   if (direction & G_IO_IN)
     g_tls_bio_set_read_cancellable (priv->bio, NULL);
 
@@ -372,143 +358,18 @@ g_tls_connection_openssl_pop_io (GTlsConnectionBase  *tls,
                                                                                       success, error);
 }
 
-static GTlsConnectionBaseStatus
-g_tls_connection_openssl_read (GTlsConnectionBase    *tls,
-                               void                  *buffer,
-                               gsize                  count,
-                               gint64                 timeout,
-                               gssize                *nread,
-                               GCancellable          *cancellable,
-                               GError               **error)
-{
-  GTlsConnectionOpenssl *openssl = G_TLS_CONNECTION_OPENSSL (tls);
-  GTlsConnectionOpensslPrivate *priv;
-  GTlsConnectionBaseStatus status;
-  SSL *ssl;
-  gssize ret;
-
-  priv = g_tls_connection_openssl_get_instance_private (openssl);
-
-  ssl = g_tls_connection_openssl_get_ssl (openssl);
-
-  /* FIXME: revert back to use BEGIN/END_OPENSSL_IO once we move all the ssl
-   * operations into a worker thread
-   */
-  while (TRUE)
-    {
-      char error_str[256];
-
-      /* We want to always be non blocking here to avoid deadlocks */
-      g_tls_connection_base_push_io (G_TLS_CONNECTION_BASE (openssl),
-                                     G_IO_IN, 0, cancellable);
-
-      ret = SSL_read (ssl, buffer, count);
-
-      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,
-                               _("Error reading data from TLS socket"), error_str);
-
-      if (status != G_TLS_CONNECTION_BASE_TRY_AGAIN)
-        break;
-
-      /* Wait for the socket to be available again to avoid an infinite loop */
-      g_tls_bio_wait_available (priv->bio, G_IO_IN, cancellable);
-    }
-
-  *nread = MAX (ret, 0);
-  return status;
-}
-
-static GTlsConnectionBaseStatus
-g_tls_connection_openssl_write (GTlsConnectionBase    *tls,
-                                const void            *buffer,
-                                gsize                  count,
-                                gint64                 timeout,
-                                gssize                *nwrote,
-                                GCancellable          *cancellable,
-                                GError               **error)
-{
-  GTlsConnectionOpenssl *openssl = G_TLS_CONNECTION_OPENSSL (tls);
-  GTlsConnectionOpensslPrivate *priv;
-  GTlsConnectionBaseStatus status;
-  SSL *ssl;
-  gssize ret;
-
-  priv = g_tls_connection_openssl_get_instance_private (openssl);
-
-  ssl = g_tls_connection_openssl_get_ssl (openssl);
-
-  while (TRUE)
-    {
-      char error_str[256];
-
-      /* We want to always be non blocking here to avoid deadlocks */
-      g_tls_connection_base_push_io (G_TLS_CONNECTION_BASE (openssl),
-                                     G_IO_OUT, 0, cancellable);
-
-      ret = SSL_write (ssl, buffer, count);
-
-      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,
-                               _("Error writing data to TLS socket"), error_str);
-
-      if (status != G_TLS_CONNECTION_BASE_TRY_AGAIN)
-        break;
-
-      /* Wait for the socket to be available again to avoid an infinite loop */
-      g_tls_bio_wait_available (priv->bio, G_IO_OUT, cancellable);
-    }
-
-  *nwrote = MAX (ret, 0);
-  return status;
-}
-
-static GTlsConnectionBaseStatus
-g_tls_connection_openssl_close (GTlsConnectionBase  *tls,
-                                gint64               timeout,
-                                GCancellable        *cancellable,
-                                GError             **error)
-{
-  GTlsConnectionOpenssl *openssl = G_TLS_CONNECTION_OPENSSL (tls);
-  GTlsConnectionOpensslPrivate *priv;
-  GTlsConnectionBaseStatus status;
-  SSL *ssl;
-  int ret;
-
-  ssl = g_tls_connection_openssl_get_ssl (openssl);
-  priv = g_tls_connection_openssl_get_instance_private (openssl);
-
-  priv->shutting_down = TRUE;
-
-  BEGIN_OPENSSL_IO (openssl, G_IO_IN | G_IO_OUT, timeout, cancellable);
-  ret = SSL_shutdown (ssl);
-  /* Note it is documented that getting 0 is correct when shutting down since
-   * it means it will close the write direction
-   */
-  ret = ret == 0 ? 1 : ret;
-  END_OPENSSL_IO (openssl, G_IO_IN | G_IO_OUT, ret, timeout, status,
-                  _("Error performing TLS close"), error);
-
-  return status;
-}
-
 static void
 g_tls_connection_openssl_class_init (GTlsConnectionOpensslClass *klass)
 {
-  GObjectClass *object_class = G_OBJECT_CLASS (klass);
   GTlsConnectionBaseClass *base_class = G_TLS_CONNECTION_BASE_CLASS (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;
   base_class->retrieve_peer_certificate                  = 
g_tls_connection_openssl_retrieve_peer_certificate;
   base_class->push_io                                    = g_tls_connection_openssl_push_io;
   base_class->pop_io                                     = g_tls_connection_openssl_pop_io;
-  base_class->read_fn                                    = g_tls_connection_openssl_read;
-  base_class->write_fn                                   = g_tls_connection_openssl_write;
-  base_class->close_fn                                   = g_tls_connection_openssl_close;
 }
 
 static int data_index = -1;
@@ -543,23 +404,20 @@ g_tls_connection_openssl_initable_init (GInitable     *initable,
 
   SSL_set_bio (ssl, priv->bio, priv->bio);
 
-  return TRUE;
+  return g_tls_connection_openssl_parent_initable_iface->init (initable, cancellable, error);
 }
 
 static void
 g_tls_connection_openssl_initable_iface_init (GInitableIface *iface)
 {
+  g_tls_connection_openssl_parent_initable_iface = g_type_interface_peek_parent (iface);
+
   iface->init = g_tls_connection_openssl_initable_init;
 }
 
 static void
 g_tls_connection_openssl_init (GTlsConnectionOpenssl *openssl)
 {
-  GTlsConnectionOpensslPrivate *priv;
-
-  priv = g_tls_connection_openssl_get_instance_private (openssl);
-
-  g_mutex_init (&priv->ssl_mutex);
 }
 
 SSL *
diff --git a/tls/openssl/gtlsconnection-openssl.h b/tls/openssl/gtlsconnection-openssl.h
index 7b85fdc..10f93c3 100644
--- a/tls/openssl/gtlsconnection-openssl.h
+++ b/tls/openssl/gtlsconnection-openssl.h
@@ -40,7 +40,7 @@ struct _GTlsConnectionOpensslClass
 {
   GTlsConnectionBaseClass parent_class;
 
-  SSL *(*get_ssl) (GTlsConnectionOpenssl *connection);
+  SSL      *(*get_ssl)          (GTlsConnectionOpenssl *connection);
 };
 
 SSL *g_tls_connection_openssl_get_ssl (GTlsConnectionOpenssl *connection);
diff --git a/tls/openssl/gtlsoperationsthread-openssl.c b/tls/openssl/gtlsoperationsthread-openssl.c
new file mode 100644
index 0000000..d7ecc0b
--- /dev/null
+++ b/tls/openssl/gtlsoperationsthread-openssl.c
@@ -0,0 +1,276 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright 2015 NICE s.r.l.
+ * 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-openssl.h"
+
+#include "gtlsconnection-openssl.h"
+
+#include <glib/gi18n-lib.h>
+
+struct _GTlsOperationsThreadOpenssl {
+  GTlsOperationsThreadBase parent_instance;
+
+  SSL *ssl;
+
+  gboolean shutting_down;
+};
+
+G_DEFINE_TYPE (GTlsOperationsThreadOpenssl, g_tls_operations_thread_openssl, 
G_TYPE_TLS_OPERATIONS_THREAD_BASE)
+
+static GTlsConnectionBaseStatus
+end_openssl_io (GTlsOperationsThreadOpenssl  *self,
+                GIOCondition                  direction,
+                int                           ret,
+                GError                      **error,
+                const char                   *err_prefix,
+                const char                   *err_str)
+{
+  GTlsConnectionBase *tls;
+  int err_code, err, err_lib, reason;
+  GError *my_error = NULL;
+  GTlsConnectionBaseStatus status;
+
+  tls = g_tls_operations_thread_base_get_connection (G_TLS_OPERATIONS_THREAD_BASE (self));
+
+  err_code = SSL_get_error (self->ssl, ret);
+
+  status = g_tls_connection_base_pop_io (tls, direction, ret > 0, &my_error);
+
+  if (err_code == SSL_ERROR_ZERO_RETURN)
+    return G_TLS_CONNECTION_BASE_OK;
+
+  if (status == G_TLS_CONNECTION_BASE_OK ||
+      status == G_TLS_CONNECTION_BASE_WOULD_BLOCK ||
+      status == G_TLS_CONNECTION_BASE_TIMED_OUT)
+    {
+      if (my_error)
+        g_propagate_error (error, my_error);
+      return status;
+    }
+
+  /* This case is documented that it may happen and that is perfectly fine */
+  if (err_code == SSL_ERROR_SYSCALL &&
+      ((self->shutting_down && !my_error) || g_error_matches (my_error, G_IO_ERROR, G_IO_ERROR_BROKEN_PIPE)))
+    {
+      g_clear_error (&my_error);
+      return G_TLS_CONNECTION_BASE_OK;
+    }
+
+  err = ERR_get_error ();
+  err_lib = ERR_GET_LIB (err);
+  reason = ERR_GET_REASON (err);
+
+  if (g_tls_connection_base_is_handshaking (tls) && !g_tls_connection_base_ever_handshaked (tls))
+    {
+      if (reason == SSL_R_BAD_PACKET_LENGTH ||
+          reason == SSL_R_UNKNOWN_ALERT_TYPE ||
+          reason == SSL_R_DECRYPTION_FAILED ||
+          reason == SSL_R_DECRYPTION_FAILED_OR_BAD_RECORD_MAC ||
+          reason == SSL_R_BAD_PROTOCOL_VERSION_NUMBER ||
+          reason == SSL_R_SSLV3_ALERT_HANDSHAKE_FAILURE ||
+          reason == SSL_R_UNKNOWN_PROTOCOL)
+        {
+          g_clear_error (&my_error);
+          g_set_error (error, G_TLS_ERROR, G_TLS_ERROR_NOT_TLS,
+                       _("Peer failed to perform TLS handshake: %s"), ERR_reason_error_string (err));
+          return G_TLS_CONNECTION_BASE_ERROR;
+        }
+    }
+
+#ifdef SSL_R_SHUTDOWN_WHILE_IN_INIT
+  /* XXX: this error happens on ubuntu when shutting down the connection, it
+   * seems to be a bug in a specific version of openssl, so let's handle it
+   * gracefully
+   */
+  if (reason == SSL_R_SHUTDOWN_WHILE_IN_INIT)
+    {
+      g_clear_error (&my_error);
+      return G_TLS_CONNECTION_BASE_OK;
+    }
+#endif
+
+  if (reason == SSL_R_PEER_DID_NOT_RETURN_A_CERTIFICATE
+#ifdef SSL_R_TLSV13_ALERT_CERTIFICATE_REQUIRED
+      || reason == SSL_R_TLSV13_ALERT_CERTIFICATE_REQUIRED
+#endif
+     )
+    {
+      g_clear_error (&my_error);
+      g_set_error_literal (error, G_TLS_ERROR, G_TLS_ERROR_CERTIFICATE_REQUIRED,
+                           _("TLS connection peer did not send a certificate"));
+      return status;
+    }
+
+  if (reason == SSL_R_CERTIFICATE_VERIFY_FAILED)
+    {
+      g_clear_error (&my_error);
+      g_set_error (error, G_TLS_ERROR, G_TLS_ERROR_BAD_CERTIFICATE,
+                   _("Unacceptable TLS certificate"));
+      return G_TLS_CONNECTION_BASE_ERROR;
+    }
+
+  if (reason == SSL_R_TLSV1_ALERT_UNKNOWN_CA)
+    {
+      g_clear_error (&my_error);
+      g_set_error (error, G_TLS_ERROR, G_TLS_ERROR_BAD_CERTIFICATE,
+                   _("Unacceptable TLS certificate authority"));
+      return G_TLS_CONNECTION_BASE_ERROR;
+    }
+
+  if (err_lib == ERR_LIB_RSA && reason == RSA_R_DIGEST_TOO_BIG_FOR_RSA_KEY)
+    {
+      g_clear_error (&my_error);
+      g_set_error_literal (error, G_TLS_ERROR, G_TLS_ERROR_BAD_CERTIFICATE,
+                           _("Digest too big for RSA key"));
+      return G_TLS_CONNECTION_BASE_ERROR;
+    }
+
+  if (my_error)
+    g_propagate_error (error, my_error);
+  else
+    /* FIXME: this is just for debug */
+    g_message ("end_openssl_io %s: %d, %d, %d", G_IS_TLS_CLIENT_CONNECTION (tls) ? "client" : "server", 
err_code, err_lib, reason);
+
+  if (error && !*error)
+    *error = g_error_new (G_TLS_ERROR, G_TLS_ERROR_MISC, "%s: %s", err_prefix, err_str);
+
+  return G_TLS_CONNECTION_BASE_ERROR;
+}
+
+#define BEGIN_OPENSSL_IO(self, direction, cancellable)          \
+  do {                                                          \
+    char error_str[256];                                        \
+    g_tls_connection_base_push_io (g_tls_operations_thread_base_get_connection (G_TLS_OPERATIONS_THREAD_BASE 
(self)), \
+                                   direction, 0, cancellable);
+
+#define END_OPENSSL_IO(self, direction, ret, status, errmsg, err) \
+    ERR_error_string_n (SSL_get_error (self->ssl, ret), error_str, sizeof (error_str)); \
+    status = end_openssl_io (self, direction, ret, err, errmsg, error_str); \
+  } while (status == G_TLS_CONNECTION_BASE_TRY_AGAIN);
+
+static GTlsConnectionBaseStatus
+g_tls_operations_thread_openssl_read (GTlsOperationsThreadBase   *base,
+                                      void                       *buffer,
+                                      gsize                       size,
+                                      gssize                     *nread,
+                                      GCancellable               *cancellable,
+                                      GError                    **error)
+{
+  GTlsOperationsThreadOpenssl *self = G_TLS_OPERATIONS_THREAD_OPENSSL (base);
+  GTlsConnectionBaseStatus status;
+  gssize ret;
+
+  BEGIN_OPENSSL_IO (self, G_IO_OUT, cancellable);
+  ret = SSL_read (self->ssl, buffer, size);
+  END_OPENSSL_IO (self, G_IO_OUT, ret, status,
+                  _("Error reading data from TLS socket"), error);
+
+
+  *nread = MAX (ret, 0);
+  return status;
+}
+
+static GTlsConnectionBaseStatus
+g_tls_operations_thread_openssl_write (GTlsOperationsThreadBase  *base,
+                                       const void                *buffer,
+                                       gsize                      size,
+                                       gssize                    *nwrote,
+                                       GCancellable              *cancellable,
+                                       GError                   **error)
+{
+  GTlsOperationsThreadOpenssl *self = G_TLS_OPERATIONS_THREAD_OPENSSL (base);
+  GTlsConnectionBaseStatus status;
+  gssize ret;
+
+  BEGIN_OPENSSL_IO (self, G_IO_OUT, cancellable);
+  ret = SSL_write (self->ssl, buffer, size);
+  END_OPENSSL_IO (self, G_IO_OUT, ret, status,
+                  _("Error writing data to TLS socket"), error);
+  *nwrote = MAX (ret, 0);
+  return status;
+}
+
+static GTlsConnectionBaseStatus
+g_tls_operations_thread_openssl_close (GTlsOperationsThreadBase  *base,
+                                       GCancellable              *cancellable,
+                                       GError                   **error)
+{
+  GTlsOperationsThreadOpenssl *self = G_TLS_OPERATIONS_THREAD_OPENSSL (base);
+  GTlsConnectionBaseStatus status;
+  int ret;
+
+  self->shutting_down = TRUE;
+
+  BEGIN_OPENSSL_IO (self, G_IO_IN | G_IO_OUT, cancellable);
+  ret = SSL_shutdown (self->ssl);
+  /* Note it is documented that getting 0 is correct when shutting down since
+   * it means it will close the write direction
+   */
+  ret = ret == 0 ? 1 : ret;
+  END_OPENSSL_IO (self, G_IO_IN | G_IO_OUT, ret, status,
+                  _("Error performing TLS close"), error);
+
+  return status;
+}
+
+static void
+g_tls_operations_thread_openssl_constructed (GObject *object)
+{
+  GTlsOperationsThreadOpenssl *self = G_TLS_OPERATIONS_THREAD_OPENSSL (object);
+  GTlsConnectionBase *openssl;
+
+  G_OBJECT_CLASS (g_tls_operations_thread_openssl_parent_class)->constructed (object);
+
+  openssl = g_tls_operations_thread_base_get_connection (G_TLS_OPERATIONS_THREAD_BASE (self));
+  self->ssl = g_tls_connection_openssl_get_ssl (G_TLS_CONNECTION_OPENSSL (openssl));
+}
+
+static void
+g_tls_operations_thread_openssl_init (GTlsOperationsThreadOpenssl *self)
+{
+}
+
+static void
+g_tls_operations_thread_openssl_class_init (GTlsOperationsThreadOpensslClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+  GTlsOperationsThreadBaseClass *base_class = G_TLS_OPERATIONS_THREAD_BASE_CLASS (klass);
+
+  gobject_class->constructed = g_tls_operations_thread_openssl_constructed;
+
+  base_class->read_fn        = g_tls_operations_thread_openssl_read;
+  base_class->write_fn       = g_tls_operations_thread_openssl_write;
+  base_class->close_fn       = g_tls_operations_thread_openssl_close;
+}
+
+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..da6daa4
--- /dev/null
+++ b/tls/openssl/gtlsoperationsthread-openssl.h
@@ -0,0 +1,40 @@
+/* -*- 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 "gtlsconnection-openssl.h"
+#include "gtlsoperationsthread-base.h"
+
+#include <gio/gio.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]