Aw: Re: Calling async functions of NMClient from another thread

2021-10-12 Thread Luis Felipe via networkmanager-list
> Gesendet: Sonntag, den 10.10.2021 um 10:36 Uhr
> Von: "Thomas Haller" 
> An: "Luis Felipe" , networkmanager-list@gnome.org
> Betreff: Re: Calling async functions of NMClient from another thread
> 
> On Sat, 2021-10-09 at 14:05 +0200, Luis Felipe via networkmanager-list
> wrote:
> > Hi all,
> > 
> > it's my first post, so please apologize for any mistake.
> > 
> > I have a trouble with NMClient (libnm) and threads in my C-application.
> > Here the scenario:
> > 
> > - in the main thread, gmain-loop and NMClient are created; here
> > 'g_main_loop_run()' is called too
> > 
> > - in a separated thread, a XML-RPC server is started. Each time a new
> > RPC arrives, this server creates a new thread with the proper function
> > 
> > - the RPC-function calls an async NMClient-function
> > (nm_client_activate_connection_async), and the following errors appear:
> > 
> > 
> > (process:668): GLib-CRITICAL **: 17:53:29.664:
> > g_main_context_push_thread_default: assertion 'acquired_context
> > ' failed
> > 
> > (process:668): GLib-CRITICAL **: 17:53:29.664:
> > g_main_context_pop_thread_default: assertion 'g_queue_peek_head
> >  (stack) == context' failed
> > 
> > 
> > I expected that the async-functions can be called from any context, but
> > doesn't look like, or am I doing something wrong?
> > Please can somebody give me a hint for solving this issue? Thanks in
> > advance.
> > 
> > BR,
> 
> 
> hi,
> 
> 
> First of all, NMClient is a cache of the NetworkManager data you see on
> the D-Bus API. Accessing that cache is not thread safe. You you need to
> coordinate access. This does NOT work via some mutex. Instead you need
> "acquire" exclusive access to the GMainContext (which is associated
> with the NMClient instance: nm_client_get_main_context()). 
> 
> That is, because as nm_client_get_main_context() gets iterated, D-Bus
> events and timeout events may be processed, and the NMClient instance
> may change at any time. So to ensure that the NMClient instance is
> unchanging, you need to ensure that nobody else is currently iterating
> the context. Technically, when accessing NMClient you don't need to
> acquire the GMainContext on your current thread, it would be enough
> that no other thread has it acquired. If another thread has it
> acquired, there are races because the data might change.
> 
> In particular, running the context on the main thread with
> g_main_loop_run() means you must not access NMClient (aside
> g_object_ref/g_object_unref and accessing a subset of trivial functions
> like nm_client_get_dbus_connection() and nm_client_get_main_context() -
> - but not for example nm_client_get_dbus_name_owner()).
> 
> 
> Indeed, nm_client_activate_connection_async() uses GTask and does
> little more than calling g_dbus_connection_call()... except, before
> making the D-Bus call it does 
> 
>   dbus_context = 
> nm_g_main_context_push_thread_default_if_necessary(priv->dbus_context);
> 
> (see [1]). The reason is that the D-Bus reply must first be processed
> on nm_client_get_main_context() so that the cache can be updated with
> the new data, and that the response is in order with the other D-Bus
> signals we process, then GTask will make that the response gets
> dispatched on the callers main-context. So the problem is really that
> nm_client_get_main_context() is already acquired on the main thread and
> g_main_context_push_thread_default() fails.
> 
> [1] 
> https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/blob/06d448245b26709d6eb5c24279c352cb17df7cdf/src/libnm-client-impl/nm-client.c#L535
> 
> Instead, you need to coordinate access to the NMClient instance by
> passing access to the GMainContext around. You might iterate the
> g_main_context_default() on the main thread, and give NMClient a
> *different* GMainContext instance. The problem then is that you still
> need to take care that NMClient's context gets iterated at the right
> times (but also that it gets iterated *all* the time). That
> unfortunately seems non trivial, also because glib does not provide a
> function to integrate a GMainContext in another GMainContext. In libnm,
> we do that this way: [2].
> 
> *
> TL;DR: THE RIGHT SOLUTION: So the best solution might be to schedule
> any access via g_idle_source_new() on NMClient's GMainContext.
> 
> Another theoretical solution is that you just make the D-Bus call
> ActivateConnection yourself, with g_dbus_connection_call(). Of course,
> then the response is again not in sync/

Re: Calling async functions of NMClient from another thread

2021-10-10 Thread Thomas Haller via networkmanager-list
On Sat, 2021-10-09 at 14:05 +0200, Luis Felipe via networkmanager-list
wrote:
> Hi all,
> 
> it's my first post, so please apologize for any mistake.
> 
> I have a trouble with NMClient (libnm) and threads in my C-application.
> Here the scenario:
> 
> - in the main thread, gmain-loop and NMClient are created; here
> 'g_main_loop_run()' is called too
> 
> - in a separated thread, a XML-RPC server is started. Each time a new
> RPC arrives, this server creates a new thread with the proper function
> 
> - the RPC-function calls an async NMClient-function
> (nm_client_activate_connection_async), and the following errors appear:
> 
> 
> (process:668): GLib-CRITICAL **: 17:53:29.664:
> g_main_context_push_thread_default: assertion 'acquired_context
> ' failed
> 
> (process:668): GLib-CRITICAL **: 17:53:29.664:
> g_main_context_pop_thread_default: assertion 'g_queue_peek_head
>  (stack) == context' failed
> 
> 
> I expected that the async-functions can be called from any context, but
> doesn't look like, or am I doing something wrong?
> Please can somebody give me a hint for solving this issue? Thanks in
> advance.
> 
> BR,


hi,


First of all, NMClient is a cache of the NetworkManager data you see on
the D-Bus API. Accessing that cache is not thread safe. You you need to
coordinate access. This does NOT work via some mutex. Instead you need
"acquire" exclusive access to the GMainContext (which is associated
with the NMClient instance: nm_client_get_main_context()). 

That is, because as nm_client_get_main_context() gets iterated, D-Bus
events and timeout events may be processed, and the NMClient instance
may change at any time. So to ensure that the NMClient instance is
unchanging, you need to ensure that nobody else is currently iterating
the context. Technically, when accessing NMClient you don't need to
acquire the GMainContext on your current thread, it would be enough
that no other thread has it acquired. If another thread has it
acquired, there are races because the data might change.

In particular, running the context on the main thread with
g_main_loop_run() means you must not access NMClient (aside
g_object_ref/g_object_unref and accessing a subset of trivial functions
like nm_client_get_dbus_connection() and nm_client_get_main_context() -
- but not for example nm_client_get_dbus_name_owner()).


Indeed, nm_client_activate_connection_async() uses GTask and does
little more than calling g_dbus_connection_call()... except, before
making the D-Bus call it does 

  dbus_context = 
nm_g_main_context_push_thread_default_if_necessary(priv->dbus_context);

(see [1]). The reason is that the D-Bus reply must first be processed
on nm_client_get_main_context() so that the cache can be updated with
the new data, and that the response is in order with the other D-Bus
signals we process, then GTask will make that the response gets
dispatched on the callers main-context. So the problem is really that
nm_client_get_main_context() is already acquired on the main thread and
g_main_context_push_thread_default() fails.

[1] 
https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/blob/06d448245b26709d6eb5c24279c352cb17df7cdf/src/libnm-client-impl/nm-client.c#L535

Instead, you need to coordinate access to the NMClient instance by
passing access to the GMainContext around. You might iterate the
g_main_context_default() on the main thread, and give NMClient a
*different* GMainContext instance. The problem then is that you still
need to take care that NMClient's context gets iterated at the right
times (but also that it gets iterated *all* the time). That
unfortunately seems non trivial, also because glib does not provide a
function to integrate a GMainContext in another GMainContext. In libnm,
we do that this way: [2].

*
TL;DR: THE RIGHT SOLUTION: So the best solution might be to schedule
any access via g_idle_source_new() on NMClient's GMainContext.

Another theoretical solution is that you just make the D-Bus call
ActivateConnection yourself, with g_dbus_connection_call(). Of course,
then the response is again not in sync/ordered with respect to the
events/state of NMClient, which brings other problems. And you still
couldn't access NMClient directly from the other thread.


Btw, before version 1.22, you could *only* use NMClient with
g_main_context_default(). Try not to use such an old libnm version.

[2] 
https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/blob/06d448245b26709d6eb5c24279c352cb17df7cdf/src/libnm-glib-aux/nm-shared-utils.c#L5488

best,
Thomas

___
networkmanager-list mailing list
networkmanager-list@gnome.org
https://mail.gnome.org/mailman/listinfo/networkmanager-list


Calling async functions of NMClient from another thread

2021-10-09 Thread Luis Felipe via networkmanager-list
Hi all,

it's my first post, so please apologize for any mistake.

I have a trouble with NMClient (libnm) and threads in my C-application. Here 
the scenario:

- in the main thread, gmain-loop and NMClient are created; here 
'g_main_loop_run()' is called too

- in a separated thread, a XML-RPC server is started. Each time a new RPC 
arrives, this server creates a new thread with the proper function

- the RPC-function calls an async NMClient-function 
(nm_client_activate_connection_async), and the following errors appear:


(process:668): GLib-CRITICAL **: 17:53:29.664: 
g_main_context_push_thread_default: assertion 'acquired_context
' failed

(process:668): GLib-CRITICAL **: 17:53:29.664: 
g_main_context_pop_thread_default: assertion 'g_queue_peek_head
 (stack) == context' failed


I expected that the async-functions can be called from any context, but doesn't 
look like, or am I doing something wrong?
Please can somebody give me a hint for solving this issue? Thanks in advance.

BR,

Entelequio
___
networkmanager-list mailing list
networkmanager-list@gnome.org
https://mail.gnome.org/mailman/listinfo/networkmanager-list