summary refs log tree commit diff
path: root/examples/python/gi/gmaincontext.py
diff options
context:
space:
mode:
Diffstat (limited to 'examples/python/gi/gmaincontext.py')
-rwxr-xr-xexamples/python/gi/gmaincontext.py130
1 files changed, 79 insertions, 51 deletions
diff --git a/examples/python/gi/gmaincontext.py b/examples/python/gi/gmaincontext.py
index 90a9fa25..64a5620b 100755
--- a/examples/python/gi/gmaincontext.py
+++ b/examples/python/gi/gmaincontext.py
@@ -36,7 +36,6 @@
 
 ###############################################################################
 
-import os
 import sys
 import time
 import traceback
@@ -260,7 +259,6 @@ def create_nmc(dbus_connection):
     # This actually should not happen. There is no other reason why
     # initialization can fail.
     assert False, "NMClient initialization is not supposed to fail"
-    return nmc
 
 
 ###############################################################################
@@ -297,12 +295,11 @@ def make_call(nmc):
                 else:
                     log(f"[make_call]: dbus_call() completed with error: {e}")
 
-                if False:
-                    # I don't understand why, but if you hit this exception (e.g. by setting a low
-                    # timeout) and pass the exception to the out context, then an additional reference
-                    # to nmc is leaked, and destroy_nmc() will fail. Workaround
-                    r.error = e
-
+                # I don't understand why, but if you hit this exception (e.g. by setting a low
+                # timeout) and pass the exception to the out context, then an additional reference
+                # to nmc is leaked, and destroy_nmc() will fail. Workaround
+                #
+                # r.error = e
                 r.error = str(e)
             else:
                 log(
@@ -329,7 +326,7 @@ def make_call(nmc):
 ###############################################################################
 
 
-def destroy_nmc(nmc_holder):
+def destroy_nmc(nmc_holder, destroy_mode):
     # The way to shutdown an NMClient is just by unrefing it.
     #
     # At any moment, can an NMClient instance have pending async operations.
@@ -362,53 +359,77 @@ def destroy_nmc(nmc_holder):
     (nmc,) = nmc_holder
     nmc_holder.clear()
 
-    if not nmc:
-        log(f"[destroy_nmc]: nothing to destroy")
-        return
-
     log(
-        f"[destroy_nmc]: destroying NMClient {nmc}: pyref={sys.getrefcount(nmc)}, ref_count={nmc.ref_count}"
+        f"[destroy_nmc]: destroying NMClient {nmc}: pyref={sys.getrefcount(nmc)}, ref_count={nmc.ref_count}, destroy_mode={destroy_mode}"
     )
 
-    ctx = nmc.get_main_context()
+    if destroy_mode == 0:
+        ctx = nmc.get_main_context()
 
-    finished = []
+        finished = []
 
-    def _weak_ref_cb():
-        log(f"[destroy_nmc]: context busy watcher is gone")
-        finished.clear()
-        finished.append(True)
+        def _weak_ref_cb():
+            log(f"[destroy_nmc]: context busy watcher is gone")
+            finished.clear()
+            finished.append(True)
 
-    # We take a weak ref on the context-busy-watcher object and give up
-    # our reference on nmc. This must be the last reference, which initiates
-    # the shutdown of the NMClient.
-    weak_ref = nmc.get_context_busy_watcher().weak_ref(_weak_ref_cb)
-    del nmc
+        # We take a weak ref on the context-busy-watcher object and give up
+        # our reference on nmc. This must be the last reference, which initiates
+        # the shutdown of the NMClient.
+        weak_ref = nmc.get_context_busy_watcher().weak_ref(_weak_ref_cb)
+        del nmc
 
-    def _timeout_cb(unused):
-        if not finished:
-            # Somebody else holds a reference to the NMClient and keeps
-            # it alive. We cannot properly clean up.
-            log(
-                f"[destroy_nmc]: ERROR: timeout waiting for context busy watcher to be gone"
-            )
-            finished.append(False)
-        return False
+        def _timeout_cb(unused):
+            if not finished:
+                # Somebody else holds a reference to the NMClient and keeps
+                # it alive. We cannot properly clean up.
+                log(
+                    f"[destroy_nmc]: ERROR: timeout waiting for context busy watcher to be gone"
+                )
+                finished.append(False)
+            return False
 
-    timeout_source = GLib.timeout_source_new(1000)
-    timeout_source.set_callback(_timeout_cb)
-    timeout_source.attach(ctx)
+        timeout_source = GLib.timeout_source_new(1000)
+        timeout_source.set_callback(_timeout_cb)
+        timeout_source.attach(ctx)
 
-    while not finished:
-        log(f"[destroy_nmc]: iterating main context")
-        ctx.iteration(True)
+        while not finished:
+            log(f"[destroy_nmc]: iterating main context")
+            ctx.iteration(True)
 
-    timeout_source.destroy()
+        timeout_source.destroy()
 
-    log(f"[destroy_nmc]: done: {finished[0]}")
-    if not finished[0]:
-        weak_ref.unref()
-        raise Exception("Failure to destroy NMClient: something keeps it alive")
+        log(f"[destroy_nmc]: done: {finished[0]}")
+        if not finished[0]:
+            weak_ref.unref()
+            raise Exception("Failure to destroy NMClient: something keeps it alive")
+
+    else:
+
+        if destroy_mode == 1:
+            ctx = GLib.MainContext.default()
+        else:
+            # Run the maincontext of the NMClient.
+            ctx = nmc.get_main_context()
+        with MainLoopRun("destroy_nmc", ctx, 2) as r:
+
+            def _wait_shutdown_cb(source_unused, result, r):
+                try:
+                    NM.Client.wait_shutdown_finish(result)
+                except Exception as e:
+                    if error_is_cancelled(e):
+                        log(
+                            f"[destroy_nmc]: wait_shutdown() completed with cancellation after timeout"
+                        )
+                    else:
+                        log(f"[destroy_nmc]: wait_shutdown() completed with error: {e}")
+                else:
+                    log(f"[destroy_nmc]: wait_shutdown() completed with success")
+
+                r.quit()
+
+            nmc.wait_shutdown(True, r.cancellable, _wait_shutdown_cb, r)
+            del nmc
 
 
 ###############################################################################
@@ -425,18 +446,25 @@ def run1():
         make_call(nmc)
         log()
 
-        # To cleanup the NMClient, we need to give up the reference. Move
-        # it to a list, and destroy_nmc() will take care of it.
-        nmc_holder = [nmc]
-        del nmc
-        destroy_nmc(nmc_holder)
+        if not nmc:
+            log(f"[destroy_nmc]: nothing to destroy")
+        else:
+            # To cleanup the NMClient, we need to give up the reference. Move
+            # it to a list, and destroy_nmc() will take care of it.
+            nmc_holder = [nmc]
+            del nmc
+
+            # In the example, there are three modes how the destroy is
+            # implemented.
+            destroy_nmc(nmc_holder, destroy_mode=1)
+
         log()
         log("done")
     except Exception as e:
         log()
         log("EXCEPTION:")
         log(f"{e}")
-        for tb in traceback.format_exception(e):
+        for tb in traceback.format_exception(None, e, e.__traceback__):
             for l in tb.split("\n"):
                 log(f">>> {l}")
         return False