From 5429d57cb18e7a759000d142d93bae4cb3f189fa Mon Sep 17 00:00:00 2001
From: Julian Bouzas <julian.bouzas@collabora.com>
Date: Fri, 28 Jun 2019 12:33:00 -0400
Subject: [PATCH] endpoint-link: make contruction async

---
 lib/wp/endpoint.c                             | 308 +++++++++++++-----
 lib/wp/endpoint.h                             |   9 +-
 modules/module-pipewire.c                     |   8 +-
 .../module-pipewire/simple-endpoint-link.c    | 135 +++++---
 modules/module-pw-alsa-udev.c                 |   2 +-
 modules/module-pw-audio-softdsp-endpoint.c    |   2 +-
 modules/module-simple-policy.c                |  32 +-
 7 files changed, 364 insertions(+), 132 deletions(-)

diff --git a/lib/wp/endpoint.c b/lib/wp/endpoint.c
index 2ce211a4..a2f86350 100644
--- a/lib/wp/endpoint.c
+++ b/lib/wp/endpoint.c
@@ -737,39 +737,199 @@ wp_endpoint_get_links (WpEndpoint * self)
 typedef struct _WpEndpointLinkPrivate WpEndpointLinkPrivate;
 struct _WpEndpointLinkPrivate
 {
-  WpEndpoint *src;
+  /* The task to signal the endpoint link is initialized */
+  GTask *init_task;
+
+  GWeakRef src;
   guint32 src_stream;
-  WpEndpoint *sink;
+  GWeakRef sink;
   guint32 sink_stream;
 };
 
-G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (WpEndpointLink, wp_endpoint_link, G_TYPE_OBJECT)
+enum {
+  LINKPROP_0,
+  LINKPROP_SRC,
+  LINKPROP_SRC_STREAM,
+  LINKPROP_SINK,
+  LINKPROP_SINK_STREAM,
+};
+
+static void wp_endpoint_link_async_initable_init (gpointer iface,
+    gpointer iface_data);
+
+G_DEFINE_ABSTRACT_TYPE_WITH_CODE (WpEndpointLink, wp_endpoint_link, G_TYPE_OBJECT,
+    G_ADD_PRIVATE (WpEndpointLink)
+    G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_INITABLE,
+                           wp_endpoint_link_async_initable_init))
 
 static void
-wp_endpoint_link_init (WpEndpointLink * self)
+endpoint_link_finalize (GObject * object)
 {
+  WpEndpointLinkPrivate *priv =
+      wp_endpoint_link_get_instance_private (WP_ENDPOINT_LINK (object));
+
+  /* Destroy the init task */
+  g_clear_object(&priv->init_task);
+
+  /* Clear the endpoint weak reaferences */
+  g_weak_ref_clear(&priv->src);
+  g_weak_ref_clear(&priv->sink);
 }
 
 static void
-wp_endpoint_link_class_init (WpEndpointLinkClass * klass)
+endpoint_link_set_property (GObject * object, guint property_id,
+    const GValue * value, GParamSpec * pspec)
 {
+  WpEndpointLinkPrivate *priv =
+      wp_endpoint_link_get_instance_private (WP_ENDPOINT_LINK (object));
+
+  switch (property_id) {
+  case LINKPROP_SRC:
+    g_weak_ref_set (&priv->src, g_value_get_object (value));
+    break;
+  case LINKPROP_SRC_STREAM:
+    priv->src_stream = g_value_get_uint(value);
+    break;
+  case LINKPROP_SINK:
+    g_weak_ref_set (&priv->sink, g_value_get_object (value));
+    break;
+  case LINKPROP_SINK_STREAM:
+    priv->sink_stream = g_value_get_uint(value);
+    break;
+  default:
+    G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+    break;
+  }
 }
 
-void
-wp_endpoint_link_set_endpoints (WpEndpointLink * self, WpEndpoint * src,
-    guint32 src_stream, WpEndpoint * sink, guint32 sink_stream)
+static void
+endpoint_link_get_property (GObject * object, guint property_id,
+    GValue * value, GParamSpec * pspec)
 {
-  WpEndpointLinkPrivate *priv;
+  WpEndpointLinkPrivate *priv =
+      wp_endpoint_link_get_instance_private (WP_ENDPOINT_LINK (object));
 
-  g_return_if_fail (WP_IS_ENDPOINT_LINK (self));
-  g_return_if_fail (WP_IS_ENDPOINT (src));
-  g_return_if_fail (WP_IS_ENDPOINT (sink));
+  switch (property_id) {
+  case LINKPROP_SRC:
+    g_value_take_object (value, g_weak_ref_get (&priv->src));
+    break;
+  case LINKPROP_SRC_STREAM:
+    g_value_set_uint (value, priv->src_stream);
+    break;
+  case LINKPROP_SINK:
+    g_value_take_object (value, g_weak_ref_get (&priv->sink));
+    break;
+  case LINKPROP_SINK_STREAM:
+    g_value_set_uint (value, priv->sink_stream);
+    break;
+  default:
+    G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+    break;
+  }
+}
 
-  priv = wp_endpoint_link_get_instance_private (self);
-  priv->src = src;
-  priv->src_stream = src_stream;
-  priv->sink = sink;
-  priv->sink_stream = sink_stream;
+static void
+wp_endpoint_link_init_async (GAsyncInitable *initable, int io_priority,
+    GCancellable *cancellable, GAsyncReadyCallback callback, gpointer data)
+{
+  WpEndpointLink *link = WP_ENDPOINT_LINK(initable);
+  WpEndpointLinkPrivate *priv =
+      wp_endpoint_link_get_instance_private (WP_ENDPOINT_LINK (initable));
+  g_autoptr (WpEndpoint) src = g_weak_ref_get (&priv->src);
+  g_autoptr (WpEndpoint) sink = g_weak_ref_get (&priv->sink);
+  g_autoptr (GError) error = NULL;
+  g_autoptr (GVariant) src_props = NULL;
+  g_autoptr (GVariant) sink_props = NULL;
+  WpEndpointPrivate *endpoint_priv;
+
+  /* Create the async task */
+  priv->init_task = g_task_new (initable, cancellable, callback, data);
+
+  /* Prepare the endpoints */
+  if (!WP_ENDPOINT_GET_CLASS (src)->prepare_link (src, priv->src_stream, link,
+      &src_props, &error)) {
+    g_task_return_error (priv->init_task, error);
+    g_clear_object(&priv->init_task);
+    return;
+  }
+  if (!WP_ENDPOINT_GET_CLASS (sink)->prepare_link (sink, priv->sink_stream,
+      link, &sink_props, &error)) {
+    g_task_return_error (priv->init_task, error);
+    g_clear_object(&priv->init_task);
+    return;
+  }
+
+  /* Create the link */
+  g_return_if_fail (WP_ENDPOINT_LINK_GET_CLASS (link)->create);
+  if (!WP_ENDPOINT_LINK_GET_CLASS (link)->create (link, src_props,
+      sink_props, &error)) {
+    g_task_return_error (priv->init_task, error);
+    g_clear_object(&priv->init_task);
+    return;
+  }
+
+  /* Register the link on the endpoints */
+  endpoint_priv = wp_endpoint_get_instance_private (src);
+  g_ptr_array_add (endpoint_priv->links, g_object_ref (link));
+  endpoint_priv = wp_endpoint_get_instance_private (sink);
+  g_ptr_array_add (endpoint_priv->links, g_object_ref (link));
+
+  /* Finish the creation of the endpoint */
+  g_task_return_boolean (priv->init_task, TRUE);
+  g_clear_object(&priv->init_task);
+}
+
+static gboolean
+wp_endpoint_link_init_finish (GAsyncInitable *initable, GAsyncResult *result,
+    GError **error)
+{
+  g_return_val_if_fail (g_task_is_valid (result, initable), FALSE);
+
+  return g_task_propagate_boolean (G_TASK (result), error);
+}
+
+static void
+wp_endpoint_link_async_initable_init (gpointer iface, gpointer iface_data)
+{
+  GAsyncInitableIface *ai_iface = iface;
+
+  ai_iface->init_async = wp_endpoint_link_init_async;
+  ai_iface->init_finish = wp_endpoint_link_init_finish;
+}
+
+static void
+wp_endpoint_link_init (WpEndpointLink * self)
+{
+  WpEndpointLinkPrivate *priv = wp_endpoint_link_get_instance_private (self);
+
+  /* Init the endpoint weak references */
+  g_weak_ref_init (&priv->src, NULL);
+  g_weak_ref_init (&priv->sink, NULL);
+}
+
+static void
+wp_endpoint_link_class_init (WpEndpointLinkClass * klass)
+{
+  GObjectClass *object_class = (GObjectClass *) klass;
+
+  object_class->finalize = endpoint_link_finalize;
+  object_class->set_property = endpoint_link_set_property;
+  object_class->get_property = endpoint_link_get_property;
+
+  g_object_class_install_property (object_class, LINKPROP_SRC,
+      g_param_spec_object ("src", "src", "The src endpoint", WP_TYPE_ENDPOINT,
+          G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
+  g_object_class_install_property (object_class, LINKPROP_SRC_STREAM,
+      g_param_spec_uint ("src-stream", "src-stream", "The src stream",
+          0, G_MAXUINT, 0,
+          G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
+  g_object_class_install_property (object_class, LINKPROP_SINK,
+      g_param_spec_object ("sink", "sink", "The sink endpoint", WP_TYPE_ENDPOINT,
+          G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
+  g_object_class_install_property (object_class, LINKPROP_SINK_STREAM,
+      g_param_spec_uint ("sink-stream", "sink-stream", "The sink stream",
+          0, G_MAXUINT, 0,
+          G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
 }
 
 WpEndpoint *
@@ -780,7 +940,7 @@ wp_endpoint_link_get_source_endpoint (WpEndpointLink * self)
   g_return_val_if_fail (WP_IS_ENDPOINT_LINK (self), NULL);
 
   priv = wp_endpoint_link_get_instance_private (self);
-  return priv->src;
+  return g_weak_ref_get (&priv->src);
 }
 
 guint32
@@ -802,7 +962,7 @@ wp_endpoint_link_get_sink_endpoint (WpEndpointLink * self)
   g_return_val_if_fail (WP_IS_ENDPOINT_LINK (self), NULL);
 
   priv = wp_endpoint_link_get_instance_private (self);
-  return priv->sink;
+  return g_weak_ref_get (&priv->sink);
 }
 
 guint32
@@ -816,19 +976,19 @@ wp_endpoint_link_get_sink_stream (WpEndpointLink * self)
   return priv->sink_stream;
 }
 
-WpEndpointLink * wp_endpoint_link_new (WpCore * core, WpEndpoint * src,
-    guint32 src_stream, WpEndpoint * sink, guint32 sink_stream, GError ** error)
+void
+wp_endpoint_link_new (WpCore * core, WpEndpoint * src, guint32 src_stream,
+    WpEndpoint * sink, guint32 sink_stream, GAsyncReadyCallback ready,
+    gpointer data)
 {
-  g_autoptr (WpEndpointLink) link = NULL;
-  g_autoptr (GVariant) src_props = NULL;
-  g_autoptr (GVariant) sink_props = NULL;
   const gchar *src_factory = NULL, *sink_factory = NULL;
-  WpEndpointPrivate *endpoint_priv;
+  GVariantBuilder b;
+  g_autoptr (GVariant) link_props = NULL;
 
-  g_return_val_if_fail (WP_IS_ENDPOINT (src), NULL);
-  g_return_val_if_fail (WP_IS_ENDPOINT (sink), NULL);
-  g_return_val_if_fail (WP_ENDPOINT_GET_CLASS (src)->prepare_link, NULL);
-  g_return_val_if_fail (WP_ENDPOINT_GET_CLASS (sink)->prepare_link, NULL);
+  g_return_if_fail (WP_IS_ENDPOINT (src));
+  g_return_if_fail (WP_IS_ENDPOINT (sink));
+  g_return_if_fail (WP_ENDPOINT_GET_CLASS (src)->prepare_link);
+  g_return_if_fail (WP_ENDPOINT_GET_CLASS (sink)->prepare_link);
 
   /* find the factory */
 
@@ -839,52 +999,38 @@ WpEndpointLink * wp_endpoint_link_new (WpCore * core, WpEndpoint * src,
 
   if (src_factory || sink_factory) {
     if (src_factory && sink_factory && strcmp (src_factory, sink_factory) != 0) {
-      g_set_error (error, WP_DOMAIN_LIBRARY, WP_LIBRARY_ERROR_INVALID_ARGUMENT,
-          "It is not possible to link endpoints that both specify different "
-          "custom link factories");
-      return NULL;
+      g_critical ("It is not possible to link endpoints that both specify"
+          "different custom link factories");
+      return;
     } else if (sink_factory)
       src_factory = sink_factory;
   } else {
     src_factory = "pipewire-simple-endpoint-link";
   }
 
-  /* create link object */
-
-  link = wp_factory_make (core, src_factory, WP_TYPE_ENDPOINT_LINK, NULL);
-  if (!link) {
-    g_set_error (error, WP_DOMAIN_LIBRARY, WP_LIBRARY_ERROR_OPERATION_FAILED,
-        "Failed to create link object from factory %s", src_factory);
-    return NULL;
-  }
-  g_return_val_if_fail (WP_ENDPOINT_LINK_GET_CLASS (link)->create, NULL);
-
-  /* prepare the link */
-
-  wp_endpoint_link_set_endpoints (link, src, src_stream, sink, sink_stream);
-
-  if (!WP_ENDPOINT_GET_CLASS (src)->prepare_link (src, src_stream, link,
-          &src_props, error))
-    return NULL;
-  if (!WP_ENDPOINT_GET_CLASS (sink)->prepare_link (sink, sink_stream, link,
-          &sink_props, error))
-    return NULL;
-
-  /* create the link */
-
-  if (!WP_ENDPOINT_LINK_GET_CLASS (link)->create (link, src_props, sink_props,
-          error))
-    return NULL;
-
-  /* register the link on the endpoints */
-
-  endpoint_priv = wp_endpoint_get_instance_private (src);
-  g_ptr_array_add (endpoint_priv->links, g_object_ref (link));
-
-  endpoint_priv = wp_endpoint_get_instance_private (sink);
-  g_ptr_array_add (endpoint_priv->links, g_object_ref (link));
+  /* Build the properties */
+  g_variant_builder_init (&b, G_VARIANT_TYPE_VARDICT);
+  g_variant_builder_add (&b, "{sv}", "src",
+      g_variant_new_uint64 ((guint64)src));
+  g_variant_builder_add (&b, "{sv}", "src-stream",
+      g_variant_new_uint32 (src_stream));
+  g_variant_builder_add (&b, "{sv}", "sink",
+      g_variant_new_uint64 ((guint64)sink));
+  g_variant_builder_add (&b, "{sv}", "sink-stream",
+      g_variant_new_uint32 (sink_stream));
+  link_props = g_variant_builder_end (&b);
+
+  /* Create the link object async */
+  wp_factory_make (core, src_factory, WP_TYPE_ENDPOINT_LINK, link_props, ready,
+      data);
+}
 
-  return link;
+WpEndpointLink *
+wp_endpoint_link_new_finish (GObject *initable, GAsyncResult *res,
+    GError **error)
+{
+  GAsyncInitable *ai = G_ASYNC_INITABLE(initable);
+  return WP_ENDPOINT_LINK(g_async_initable_new_finish(ai, res, error));
 }
 
 void
@@ -892,21 +1038,29 @@ wp_endpoint_link_destroy (WpEndpointLink * self)
 {
   WpEndpointLinkPrivate *priv;
   WpEndpointPrivate *endpoint_priv;
+  g_autoptr (WpEndpoint) src = NULL;
+  g_autoptr (WpEndpoint) sink = NULL;
 
   g_return_if_fail (WP_IS_ENDPOINT_LINK (self));
   g_return_if_fail (WP_ENDPOINT_LINK_GET_CLASS (self)->destroy);
 
   priv = wp_endpoint_link_get_instance_private (self);
+  src = g_weak_ref_get (&priv->src);
+  sink = g_weak_ref_get (&priv->sink);
 
-  WP_ENDPOINT_LINK_GET_CLASS (self)->destroy (self);
-  if (WP_ENDPOINT_GET_CLASS (priv->src)->release_link)
-    WP_ENDPOINT_GET_CLASS (priv->src)->release_link (priv->src, self);
-  if (WP_ENDPOINT_GET_CLASS (priv->sink)->release_link)
-    WP_ENDPOINT_GET_CLASS (priv->sink)->release_link (priv->sink, self);
+  if (src && WP_ENDPOINT_GET_CLASS (src)->release_link)
+    WP_ENDPOINT_GET_CLASS (src)->release_link (src, self);
+  if (sink && WP_ENDPOINT_GET_CLASS (sink)->release_link)
+    WP_ENDPOINT_GET_CLASS (sink)->release_link (sink, self);
 
-  endpoint_priv = wp_endpoint_get_instance_private (priv->src);
-  g_ptr_array_remove_fast (endpoint_priv->links, self);
+  if (src) {
+    endpoint_priv = wp_endpoint_get_instance_private (src);
+    g_ptr_array_remove_fast (endpoint_priv->links, self);
+  }
+  if (sink) {
+    endpoint_priv = wp_endpoint_get_instance_private (sink);
+    g_ptr_array_remove_fast (endpoint_priv->links, self);
+  }
 
-  endpoint_priv = wp_endpoint_get_instance_private (priv->sink);
-  g_ptr_array_remove_fast (endpoint_priv->links, self);
+  WP_ENDPOINT_LINK_GET_CLASS (self)->destroy (self);
 }
diff --git a/lib/wp/endpoint.h b/lib/wp/endpoint.h
index 4c5c4b5a..a6cdfa81 100644
--- a/lib/wp/endpoint.h
+++ b/lib/wp/endpoint.h
@@ -78,17 +78,16 @@ struct _WpEndpointLinkClass
   void (*destroy) (WpEndpointLink * self);
 };
 
-void wp_endpoint_link_set_endpoints (WpEndpointLink * self, WpEndpoint * src,
-    guint32 src_stream, WpEndpoint * sink, guint32 sink_stream);
-
 WpEndpoint * wp_endpoint_link_get_source_endpoint (WpEndpointLink * self);
 guint32 wp_endpoint_link_get_source_stream (WpEndpointLink * self);
 WpEndpoint * wp_endpoint_link_get_sink_endpoint (WpEndpointLink * self);
 guint32 wp_endpoint_link_get_sink_stream (WpEndpointLink * self);
 
-WpEndpointLink * wp_endpoint_link_new (WpCore * core, WpEndpoint * src,
+void wp_endpoint_link_new (WpCore * core, WpEndpoint * src,
     guint32 src_stream, WpEndpoint * sink, guint32 sink_stream,
-    GError ** error);
+    GAsyncReadyCallback ready, gpointer data);
+WpEndpointLink * wp_endpoint_link_new_finish (GObject *initable,
+    GAsyncResult *res, GError **error);
 void wp_endpoint_link_destroy (WpEndpointLink * self);
 
 G_END_DECLS
diff --git a/modules/module-pipewire.c b/modules/module-pipewire.c
index 9394b1e6..5df31239 100644
--- a/modules/module-pipewire.c
+++ b/modules/module-pipewire.c
@@ -19,8 +19,8 @@ void remote_endpoint_init (WpCore * core, struct pw_core * pw_core,
     struct pw_remote * remote);
 void simple_endpoint_factory (WpFactory * factory, GType type,
     GVariant * properties, GAsyncReadyCallback ready, gpointer user_data);
-gpointer simple_endpoint_link_factory (WpFactory * factory, GType type,
-    GVariant * properties);
+void simple_endpoint_link_factory (WpFactory * factory, GType type,
+    GVariant * properties, GAsyncReadyCallback ready, gpointer user_data);
 
 struct module_data
 {
@@ -89,7 +89,7 @@ on_node_added (WpRemotePipewire *rp, guint id, guint parent_id, gconstpointer p,
   endpoint_props = g_variant_builder_end (&b);
 
   /* Create the endpoint async */
-  wp_factory_make_async (core, "pipewire-simple-endpoint", WP_TYPE_ENDPOINT,
+  wp_factory_make (core, "pipewire-simple-endpoint", WP_TYPE_ENDPOINT,
       endpoint_props, on_endpoint_created, data);
 }
 
@@ -162,7 +162,7 @@ wireplumber__module_init (WpModule * module, WpCore * core, GVariant * args)
   remote_endpoint_init (core, pw_core, pw_remote);
 
   /* Register simple-endpoint and simple-endpoint-link */
-  wp_factory_new_async (core, "pipewire-simple-endpoint",
+  wp_factory_new (core, "pipewire-simple-endpoint",
       simple_endpoint_factory);
   wp_factory_new (core, "pipewire-simple-endpoint-link",
       simple_endpoint_link_factory);
diff --git a/modules/module-pipewire/simple-endpoint-link.c b/modules/module-pipewire/simple-endpoint-link.c
index a7e8edc7..a0dc2b8d 100644
--- a/modules/module-pipewire/simple-endpoint-link.c
+++ b/modules/module-pipewire/simple-endpoint-link.c
@@ -27,8 +27,13 @@ struct _WpPipewireSimpleEndpointLink
 {
   WpEndpointLink parent;
 
-  /* The core proxy */
-  struct pw_core_proxy *core_proxy;
+  /* The wireplumber core */
+  GWeakRef core;
+};
+
+enum {
+  PROP_0,
+  PROP_CORE,
 };
 
 G_DECLARE_FINAL_TYPE (WpPipewireSimpleEndpointLink,
@@ -40,6 +45,51 @@ G_DEFINE_TYPE (WpPipewireSimpleEndpointLink,
 static void
 simple_endpoint_link_init (WpPipewireSimpleEndpointLink * self)
 {
+  /* Init the core weak reference */
+  g_weak_ref_init (&self->core, NULL);
+}
+
+static void
+simple_endpoint_link_finalize (GObject * object)
+{
+  WpPipewireSimpleEndpointLink *self = WP_PIPEWIRE_SIMPLE_ENDPOINT_LINK(object);
+
+  /* Clear the core weak reference */
+  g_weak_ref_clear (&self->core);
+}
+
+static void
+simple_endpoint_link_set_property (GObject * object, guint property_id,
+    const GValue * value, GParamSpec * pspec)
+{
+  WpPipewireSimpleEndpointLink *self =
+      WP_PIPEWIRE_SIMPLE_ENDPOINT_LINK (object);
+
+  switch (property_id) {
+  case PROP_CORE:
+    g_weak_ref_set (&self->core, g_value_get_object (value));
+    break;
+  default:
+    G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+    break;
+  }
+}
+
+static void
+simple_endpoint_link_get_property (GObject * object, guint property_id,
+    GValue * value, GParamSpec * pspec)
+{
+  WpPipewireSimpleEndpointLink *self =
+      WP_PIPEWIRE_SIMPLE_ENDPOINT_LINK (object);
+
+  switch (property_id) {
+  case PROP_CORE:
+    g_value_take_object (value, g_weak_ref_get (&self->core));
+    break;
+  default:
+    G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+    break;
+  }
 }
 
 static gboolean
@@ -47,12 +97,18 @@ simple_endpoint_link_create (WpEndpointLink * epl, GVariant * src_data,
     GVariant * sink_data, GError ** error)
 {
   WpPipewireSimpleEndpointLink *self = WP_PIPEWIRE_SIMPLE_ENDPOINT_LINK(epl);
+  g_autoptr (WpCore) core = g_weak_ref_get (&self->core);
+  WpRemotePipewire *remote_pipewire;
   struct pw_properties *props;
   guint32 output_node_id, input_node_id;
   GVariant *src_ports, *sink_ports;
   GVariantIter *out_iter, *in_iter;
   guint64 out_ptr, in_ptr;
 
+  /* Get the remote pipewire */
+  remote_pipewire = wp_core_get_global (core, WP_GLOBAL_REMOTE_PIPEWIRE);
+  g_return_val_if_fail (remote_pipewire, FALSE);
+
   /* Get the node ids and port ids */
   if (!g_variant_lookup (src_data, "node-id", "u", &output_node_id))
       return FALSE;
@@ -90,8 +146,8 @@ simple_endpoint_link_create (WpEndpointLink * epl, GVariant * src_data,
       pw_properties_setf(props, PW_LINK_INPUT_PORT_ID, "%d", in_id);
 
       /* Create the link */
-      pw_core_proxy_create_object(self->core_proxy, "link-factory",
-          PW_TYPE_INTERFACE_Link, PW_VERSION_LINK, &props->dict, 0);
+      wp_remote_pipewire_create_object(remote_pipewire, "link-factory",
+          PW_TYPE_INTERFACE_Link, &props->dict);
 
       /* Clean up */
       pw_properties_free(props);
@@ -112,49 +168,54 @@ simple_endpoint_link_destroy (WpEndpointLink * self)
 static void
 simple_endpoint_link_class_init (WpPipewireSimpleEndpointLinkClass * klass)
 {
+  GObjectClass *object_class = (GObjectClass *) klass;
   WpEndpointLinkClass *link_class = (WpEndpointLinkClass *) klass;
 
+  object_class->finalize = simple_endpoint_link_finalize;
+  object_class->set_property = simple_endpoint_link_set_property;
+  object_class->get_property = simple_endpoint_link_get_property;
+
   link_class->create = simple_endpoint_link_create;
   link_class->destroy = simple_endpoint_link_destroy;
+
+  g_object_class_install_property (object_class, PROP_CORE,
+      g_param_spec_object ("core", "core",
+          "The wireplumber core object this links belongs to", WP_TYPE_CORE,
+          G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
 }
 
-gpointer
+void
 simple_endpoint_link_factory (WpFactory * factory, GType type,
-    GVariant * properties)
+    GVariant * properties, GAsyncReadyCallback ready, gpointer data)
 {
-  WpCore *wp_core = NULL;
-  WpRemote *remote;
-  struct pw_remote *pw_remote;
+  g_autoptr(WpCore) core = NULL;
+  guint64 src, sink;
+  guint src_stream, sink_stream;
 
   /* Make sure the type is an endpoint link */
-  if (type != WP_TYPE_ENDPOINT_LINK)
-    return NULL;
-
-  /* Get the WirePlumber core */
-  wp_core = wp_factory_get_core(factory);
-  if (!wp_core) {
-    g_warning("failed to get wireplumbe core. Skipping...");
-    return NULL;
-  }
-
-  /* Get the remote */
-  remote = wp_core_get_global(wp_core, WP_GLOBAL_REMOTE_PIPEWIRE);
-  if (!remote) {
-    g_warning("failed to get core remote. Skipping...");
-    return NULL;
-  }
+  g_return_if_fail (type == WP_TYPE_ENDPOINT_LINK);
+
+  /* Get the Core */
+  core = wp_factory_get_core (factory);
+  g_return_if_fail (core);
+
+  /* Get the properties */
+  if (!g_variant_lookup (properties, "src", "t", &src))
+      return;
+  if (!g_variant_lookup (properties, "src-stream", "u", &src_stream))
+      return;
+  if (!g_variant_lookup (properties, "sink", "t", &sink))
+      return;
+  if (!g_variant_lookup (properties, "sink-stream", "u", &sink_stream))
+      return;
 
   /* Create the endpoint link */
-  WpPipewireSimpleEndpointLink *epl = g_object_new (
-      simple_endpoint_link_get_type (), NULL);
-
-  /* Set the core proxy */
-  g_object_get (remote, "pw-remote", &pw_remote, NULL);
-  epl->core_proxy = pw_remote_get_core_proxy(pw_remote);
-  if (!epl->core_proxy) {
-    g_warning("failed to get core proxy. Skipping...");
-    return NULL;
-  }
-
-  return epl;
+  g_async_initable_new_async (
+      simple_endpoint_link_get_type (), G_PRIORITY_DEFAULT, NULL, ready, data,
+      "src", (gpointer)src,
+      "src-stream", src_stream,
+      "sink", (gpointer)sink,
+      "sink-stream", sink_stream,
+      "core", core,
+      NULL);
 }
diff --git a/modules/module-pw-alsa-udev.c b/modules/module-pw-alsa-udev.c
index 74c34979..a353cad4 100644
--- a/modules/module-pw-alsa-udev.c
+++ b/modules/module-pw-alsa-udev.c
@@ -76,7 +76,7 @@ on_node_added(WpRemotePipewire *rp, guint id, guint parent_id, gconstpointer p,
   endpoint_props = g_variant_builder_end (&b);
 
   /* Create the endpoint async */
-  wp_factory_make_async (core, "pw-audio-softdsp-endpoint", WP_TYPE_ENDPOINT,
+  wp_factory_make (core, "pw-audio-softdsp-endpoint", WP_TYPE_ENDPOINT,
       endpoint_props, on_endpoint_created, impl);
 }
 
diff --git a/modules/module-pw-audio-softdsp-endpoint.c b/modules/module-pw-audio-softdsp-endpoint.c
index dd2d5bc7..49a5c6c0 100644
--- a/modules/module-pw-audio-softdsp-endpoint.c
+++ b/modules/module-pw-audio-softdsp-endpoint.c
@@ -727,5 +727,5 @@ void
 wireplumber__module_init (WpModule * module, WpCore * core, GVariant * args)
 {
   /* Register the softdsp endpoint */
-  wp_factory_new_async (core, "pw-audio-softdsp-endpoint", endpoint_factory);
+  wp_factory_new (core, "pw-audio-softdsp-endpoint", endpoint_factory);
 }
diff --git a/modules/module-simple-policy.c b/modules/module-simple-policy.c
index d47818ea..161586f6 100644
--- a/modules/module-simple-policy.c
+++ b/modules/module-simple-policy.c
@@ -225,6 +225,29 @@ simple_policy_endpoint_removed (WpPolicy *policy, WpEndpoint *ep)
       g_object_ref (self), g_object_unref);
 }
 
+static void
+on_endpoint_link_created(GObject *initable, GAsyncResult *res, gpointer d)
+{
+  g_autoptr (WpEndpointLink) link = NULL;
+  g_autoptr (GError) error = NULL;
+  g_autoptr (WpEndpoint) src_ep = NULL;
+  g_autoptr (WpEndpoint) sink_ep = NULL;
+
+  /* Get the link */
+  link = wp_endpoint_link_new_finish(initable, res, &error);
+  g_return_if_fail (link);
+
+  /* Log linking info */
+  if (error) {
+    g_warning ("Could not link endpoints: %s\n", error->message);
+  } else {
+    src_ep = wp_endpoint_link_get_source_endpoint (link);
+    sink_ep = wp_endpoint_link_get_sink_endpoint (link);
+    g_info ("Sucessfully linked '%s' to '%s'\n", wp_endpoint_get_name (src_ep),
+        wp_endpoint_get_name (sink_ep));
+  }
+}
+
 static gboolean
 simple_policy_handle_endpoint (WpPolicy *policy, WpEndpoint *ep)
 {
@@ -232,7 +255,6 @@ simple_policy_handle_endpoint (WpPolicy *policy, WpEndpoint *ep)
   GVariantDict d;
   g_autoptr (WpCore) core = NULL;
   g_autoptr (WpEndpoint) target = NULL;
-  g_autoptr (GError) error = NULL;
   guint32 stream_id;
 
   /* TODO: For now we only accept audio output clients */
@@ -255,12 +277,8 @@ simple_policy_handle_endpoint (WpPolicy *policy, WpEndpoint *ep)
   }
 
   /* Link the client with the target */
-  if (!wp_endpoint_link_new (core, ep, 0, target, stream_id, &error)) {
-    g_warning ("Could not link endpoints: %s\n", error->message);
-  } else {
-    g_info ("Sucessfully linked '%s' to '%s'\n", wp_endpoint_get_name (ep),
-        wp_endpoint_get_name (target));
-  }
+  wp_endpoint_link_new (core, ep, 0, target, stream_id,
+      on_endpoint_link_created, NULL);
 
   return TRUE;
 }
-- 
GitLab