diff --git a/libpurple/connection.h b/libpurple/connection.h
--- a/libpurple/connection.h
+++ b/libpurple/connection.h
@@ -390,6 +390,19 @@
 void purple_connection_set_password(PurpleConnection *connection, const char *password);
 
 /**
+ * purple_connection_get_cancellable:
+ * @connection: The instance.
+ *
+ * Gets the cancellable that should be used with @connection.
+ *
+ * Returns: (transfer none): The cancellable.
+ *
+ * Since: 3.0.0
+ */
+PURPLE_AVAILABLE_IN_3_0
+GCancellable *purple_connection_get_cancellable(PurpleConnection *connection);
+
+/**
  * purple_connection_get_active_chats:
  * @gc: The connection.
  *
# HG changeset patch
# Node ID 3ffdd1edb7db
# Parent  fd8cdc9d991d
diff --git a/libpurple/connection.c b/libpurple/connection.c
--- a/libpurple/connection.c
+++ b/libpurple/connection.c
@@ -46,6 +46,8 @@
 
 	gchar *id;
 
+	GCancellable *cancellable;
+
 	PurpleProtocol *protocol;     /* The protocol.                     */
 	PurpleConnectionFlags flags;  /* Connection flags.                 */
 
@@ -81,6 +83,7 @@
 enum {
 	PROP_0,
 	PROP_ID,
+	PROP_CANCELLABLE,
 	PROP_PROTOCOL,
 	PROP_FLAGS,
 	PROP_STATE,
@@ -715,6 +718,12 @@
 
 	priv = purple_connection_get_instance_private(connection);
 
+	/* Tell everyone we're shutting down. */
+	if(G_IS_CANCELLABLE(priv->cancellable)) {
+		g_cancellable_cancel(priv->cancellable);
+		g_clear_object(&priv->cancellable);
+	}
+
 	purple_protocol_close(priv->protocol, connection);
 
 	return TRUE;
@@ -773,6 +782,10 @@
 		case PROP_ID:
 			g_value_set_string(value, purple_connection_get_id(connection));
 			break;
+		case PROP_CANCELLABLE:
+			g_value_set_object(value,
+			                   purple_connection_get_cancellable(connection));
+			break;
 		case PROP_PROTOCOL:
 			g_value_set_object(value,
 			                   purple_connection_get_protocol(connection));
@@ -803,6 +816,12 @@
 
 static void
 purple_connection_init(PurpleConnection *connection) {
+	PurpleConnectionPrivate *priv = NULL;
+
+	priv = purple_connection_get_instance_private(connection);
+
+	priv->cancellable = g_cancellable_new();
+
 	purple_connection_set_state(connection, PURPLE_CONNECTION_STATE_CONNECTING);
 	connections = g_list_append(connections, connection);
 }
@@ -855,6 +874,8 @@
 	g_free(priv->display_name);
 	g_free(priv->id);
 
+	g_clear_object(&priv->cancellable);
+
 	G_OBJECT_CLASS(purple_connection_parent_class)->finalize(object);
 }
 
@@ -877,6 +898,22 @@
 		NULL,
 		G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
 
+	/**
+	 * PurpleConnection:cancellable:
+	 *
+	 * A [class@Gio.Cancellable] to be used with the connection.
+	 *
+	 * This can be passed function that require a cancellable for the
+	 * connection.
+	 *
+	 * Since: 3.0.0
+	 */
+	properties[PROP_CANCELLABLE] = g_param_spec_object(
+		"cancellable", "cancellable",
+		"A cancellable for the connection.",
+		G_TYPE_CANCELLABLE,
+		G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+
 	properties[PROP_PROTOCOL] = g_param_spec_object(
 		"protocol", "Protocol",
 		"The protocol that the connection is using.",
@@ -1035,6 +1072,17 @@
 	return ret;
 }
 
+GCancellable *
+purple_connection_get_cancellable(PurpleConnection *connection) {
+	PurpleConnectionPrivate *priv = NULL;
+
+	g_return_val_if_fail(PURPLE_IS_CONNECTION(connection), NULL);
+
+	priv = purple_connection_get_instance_private(connection);
+
+	return priv->cancellable;
+}
+
 /**************************************************************************
  * Connections API
  **************************************************************************/
diff --git a/libpurple/protocols/ircv3/purpleircv3connection.h b/libpurple/protocols/ircv3/purpleircv3connection.h
--- a/libpurple/protocols/ircv3/purpleircv3connection.h
+++ b/libpurple/protocols/ircv3/purpleircv3connection.h
@@ -63,18 +63,6 @@
 G_GNUC_INTERNAL void purple_ircv3_connection_register(GPluginNativePlugin *plugin);
 
 /**
- * purple_ircv3_connection_get_cancellable: (skip)
- * @connection: The instance.
- *
- * This is a private method that is not exposed externally.
- *
- * Gets the cancellable for @connection.
- *
- * Since: 3.0.0
- */
-G_GNUC_INTERNAL GCancellable *purple_ircv3_connection_get_cancellable(PurpleIRCv3Connection *connection);
-
-/**
  * purple_ircv3_connection_emit_ctcp_request:
  * @connection: The instance.
  * @command: The CTCP command.
diff --git a/libpurple/protocols/ircv3/purpleircv3connection.c b/libpurple/protocols/ircv3/purpleircv3connection.c
--- a/libpurple/protocols/ircv3/purpleircv3connection.c
+++ b/libpurple/protocols/ircv3/purpleircv3connection.c
@@ -27,7 +27,6 @@
 
 enum {
 	PROP_0,
-	PROP_CANCELLABLE,
 	PROP_CAPABILITIES,
 	PROP_REGISTERED,
 	N_PROPERTIES,
@@ -44,7 +43,6 @@
 
 typedef struct {
 	GSocketConnection *connection;
-	GCancellable *cancellable;
 
 	gchar *server_name;
 	gboolean registered;
@@ -155,6 +153,7 @@
 {
 	PurpleIRCv3Connection *connection = data;
 	PurpleIRCv3ConnectionPrivate *priv = NULL;
+	GCancellable *cancellable = NULL;
 	GDataInputStream *istream = G_DATA_INPUT_STREAM(source);
 	GError *error = NULL;
 	gchar *line = NULL;
@@ -195,9 +194,10 @@
 	g_free(line);
 
 	/* Call read_line_async again to continue reading lines. */
+	cancellable = purple_connection_get_cancellable(PURPLE_CONNECTION(connection));
 	g_data_input_stream_read_line_async(priv->input,
 	                                    G_PRIORITY_DEFAULT,
-	                                    priv->cancellable,
+	                                    cancellable,
 	                                    purple_ircv3_connection_read_cb,
 	                                    connection);
 }
@@ -231,6 +231,7 @@
 {
 	PurpleIRCv3Connection *connection = data;
 	PurpleIRCv3ConnectionPrivate *priv = NULL;
+	GCancellable *cancellable = NULL;
 	GError *error = NULL;
 	GInputStream *istream = NULL;
 	GOutputStream *ostream = NULL;
@@ -266,10 +267,12 @@
 	g_data_input_stream_set_newline_type(G_DATA_INPUT_STREAM(priv->input),
 	                                     G_DATA_STREAM_NEWLINE_TYPE_CR_LF);
 
+	cancellable = purple_connection_get_cancellable(PURPLE_CONNECTION(connection));
+
 	/* Add our read callback. */
 	g_data_input_stream_read_line_async(priv->input,
 	                                    G_PRIORITY_DEFAULT,
-	                                    priv->cancellable,
+	                                    cancellable,
 	                                    purple_ircv3_connection_read_cb,
 	                                    connection);
 
@@ -309,6 +312,7 @@
 	PurpleIRCv3Connection *connection = NULL;
 	PurpleIRCv3ConnectionPrivate *priv = NULL;
 	PurpleAccount *account = NULL;
+	GCancellable *cancellable = NULL;
 	GSocketClient *client = NULL;
 	gint default_port = PURPLE_IRCV3_DEFAULT_TLS_PORT;
 	gint port = 0;
@@ -339,9 +343,11 @@
 	}
 	port = purple_account_get_int(account, "port", default_port);
 
+	cancellable = purple_connection_get_cancellable(purple_connection);
+
 	/* Finally start the async connection. */
 	g_socket_client_connect_to_host_async(client, priv->server_name,
-	                                      port, priv->cancellable,
+	                                      port, cancellable,
 	                                      purple_ircv3_connection_connected_cb,
 	                                      connection);
 
@@ -352,25 +358,23 @@
 
 static gboolean
 purple_ircv3_connection_disconnect(PurpleConnection *purple_connection,
-                                   G_GNUC_UNUSED GError **error)
+                                   GError **error)
 {
 	PurpleIRCv3Connection *connection = NULL;
 	PurpleIRCv3ConnectionPrivate *priv = NULL;
+	PurpleConnectionClass *parent_class = NULL;
 
 	g_return_val_if_fail(PURPLE_IRCV3_IS_CONNECTION(purple_connection), FALSE);
 
+	/* Chain up to our parent's disconnect to do initial clean up. */
+	parent_class = PURPLE_CONNECTION_CLASS(purple_ircv3_connection_parent_class);
+	parent_class->disconnect(purple_connection, error);
+
 	connection = PURPLE_IRCV3_CONNECTION(purple_connection);
 	priv = purple_ircv3_connection_get_instance_private(connection);
 
 	/* TODO: send QUIT command. */
 
-	/* Cancel the cancellable to tell everyone we're shutting down. */
-	if(G_IS_CANCELLABLE(priv->cancellable)) {
-		g_cancellable_cancel(priv->cancellable);
-
-		g_clear_object(&priv->cancellable);
-	}
-
 	if(G_IS_SOCKET_CONNECTION(priv->connection)) {
 		GInputStream *istream = G_INPUT_STREAM(priv->input);
 		GOutputStream *ostream = G_OUTPUT_STREAM(priv->output);
@@ -406,10 +410,6 @@
 	PurpleIRCv3Connection *connection = PURPLE_IRCV3_CONNECTION(obj);
 
 	switch(param_id) {
-		case PROP_CANCELLABLE:
-			g_value_set_object(value,
-			                   purple_ircv3_connection_get_cancellable(connection));
-			break;
 		case PROP_CAPABILITIES:
 			g_value_set_object(value,
 			                   purple_ircv3_connection_get_capabilities(connection));
@@ -431,8 +431,6 @@
 
 	priv = purple_ircv3_connection_get_instance_private(connection);
 
-	g_clear_object(&priv->cancellable);
-
 	g_clear_object(&priv->input);
 	g_clear_object(&priv->output);
 	g_clear_object(&priv->connection);
@@ -512,8 +510,6 @@
 	g_clear_pointer(&title, g_free);
 
 	/* Finally create our objects. */
-	priv->cancellable = g_cancellable_new();
-
 	priv->capabilities = purple_ircv3_capabilities_new(connection);
 	g_signal_connect_object(priv->capabilities, "done",
 	                        G_CALLBACK(purple_ircv3_connection_caps_done_cb),
@@ -542,19 +538,6 @@
 	connection_class->disconnect = purple_ircv3_connection_disconnect;
 
 	/**
-	 * PurpleIRCv3Connection:cancellable:
-	 *
-	 * The [class@Gio.Cancellable] for this connection.
-	 *
-	 * Since: 3.0.0
-	 */
-	properties[PROP_CANCELLABLE] = g_param_spec_object(
-		"cancellable", "cancellable",
-		"The cancellable for this connection",
-		G_TYPE_CANCELLABLE,
-		G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
-
-	/**
 	 * PurpleIRCv3Connection:capabilities:
 	 *
 	 * The capabilities that the server supports.
@@ -676,17 +659,6 @@
 	g_type_class_unref(hack);
 }
 
-GCancellable *
-purple_ircv3_connection_get_cancellable(PurpleIRCv3Connection *connection) {
-	PurpleIRCv3ConnectionPrivate *priv = NULL;
-
-	g_return_val_if_fail(PURPLE_IRCV3_IS_CONNECTION(connection), NULL);
-
-	priv = purple_ircv3_connection_get_instance_private(connection);
-
-	return priv->cancellable;
-}
-
 void
 purple_ircv3_connection_emit_ctcp_request(PurpleIRCv3Connection *connection,
                                           const char *command,
@@ -720,6 +692,7 @@
 {
 	PurpleIRCv3ConnectionPrivate *priv = NULL;
 	GBytes *bytes = NULL;
+	GCancellable *cancellable = NULL;
 	GString *msg = NULL;
 	va_list vargs;
 
@@ -740,9 +713,11 @@
 
 	/* Finally turn the string into bytes and send it! */
 	bytes = g_string_free_to_bytes(msg);
+
+	cancellable = purple_connection_get_cancellable(PURPLE_CONNECTION(connection));
 	purple_queued_output_stream_push_bytes_async(priv->output, bytes,
 	                                             G_PRIORITY_DEFAULT,
-	                                             priv->cancellable,
+	                                             cancellable,
 	                                             purple_ircv3_connection_write_cb,
 	                                             connection);
 
