Compare commits

...

17 Commits

Author SHA1 Message Date
Marco Trevisan
82b0b2c933 Merge branch 'cancellable-child' into 'main'
gio: Add GCancellableChild, a GCancellable with a GCancellable parent

See merge request GNOME/glib!2746
2024-07-15 21:25:07 +00:00
Philip Withnall
d3cb9d638d Merge branch 'traverse-diagrams' into 'main'
docs(GNode): Traversal diagrams, color & dark-mode

See merge request GNOME/glib!4157
2024-07-15 15:58:45 +00:00
Philip Withnall
a931a75c4f docs(GNode): Traversal diagrams, color & dark-mode
* Create a dark-mode variant of each traversal diagram, with the
  traversal path colorized `--primary` blue, instead of the original
  black.
* Apply the same colorizations to the light-mode diagrams, but
  using the light-theme `--primary` blue.
* Add SPDX license/copyright comments to all eight SVG files.
* Add new files to documentation configs in `glib.toml.in`.
* Update documentation comment in `gnode.c` to embed both color
  variants via picture tags, instead of markdown image embeds.
* Add alt text to all four images.
* Add additional blank lines to documentation comment, so that
  a. First item in bulleted list does not get folded into
     preceding intro paragraph
  b. Intro paragraph and diagrams are not part of first paragraph
     in documentation. (This also gets them out of the top-level
     table-of-contents/index list, where they previously appeared
     in full.)
* (Accidental change I didn't realize I was making): Convert line
  endings in breadth-first diagram from CRLF to LF.
2024-07-15 15:58:45 +00:00
Philip Withnall
79c57b5b80 Merge branch 'gbsneto/doc-fixes-pt1' into 'main'
Port some GIO files to gi-docgen

See merge request GNOME/glib!4113
2024-07-11 21:01:39 +00:00
Georges Basile Stavracas Neto
a84c67da71 gio/appinfo: Directly link GdkAppLaunchContext
We can only use the [class@Foo.Bar] syntax for identifiers under
namespaces included by the current namespace. Naturally, we cannot
include the GDK namespace.

Use a direct link for this instead.
2024-07-11 16:23:06 +01:00
Georges Basile Stavracas Neto
ecc9904996 gio/applaunchcontext: Port docs to gi-docgen
This one is a little more self-contained, even though the class name
doesn't match the file it is in.
2024-07-11 15:33:41 +01:00
Georges Basile Stavracas Neto
b8db83940b gio/appinfo: Port docs to gi-docgen
Adjust all docs to use the gi-docgen referencing syntax, reindent
some of the comments, and add missing annotations to some async
methods.

The error arguments are not necessary with gi-docgen so they're
removed.

Confusingly enough, the docks for GAppInfo is spread between two
files.
2024-07-11 15:33:41 +01:00
Georges Basile Stavracas Neto
110e76a140 gio/actionmap: Port docs to gi-docgen 2024-07-11 15:33:41 +01:00
Georges Basile Stavracas Neto
3bd477b454 gio/actiongroupexporter: Port docs to gi-docgen 2024-07-11 15:33:41 +01:00
Marco Trevisan (Treviño)
27968f5226 gio: Use GCancellableChild to handle events with multiple sub-tasks
Simplify the code using cancellable child so that we don't have to
monitor for the main task cancellable manually
2024-05-05 17:29:33 +02:00
Marco Trevisan (Treviño)
c62001ec82 gio: Add GCancellableChild, a GCancellable with a GCancellable parent
Many times we need to manage multiple async operations inside an object
and each operation may need to be controlled via a different
GCancellable, however this implies keeping track of all of them.

It's instead at times convenient to just create a main object
cancellable to control a chain of GCancellable's that depend on it.

As per this introduce a GCancellable subtype that requires a parent
GCancellable object that is monitored for cancellation.

As per the way GCancellable's "cancelled" signal is defined we need to
disconnect on it outside the actual callback and we use an idle for
that. We avoid keeping references in the involved objects so in case the
idle functions may just do nothing.
2024-05-05 17:12:21 +02:00
Marco Trevisan (Treviño)
ab55a59005 tests/autoptr: Add GCancellable test 2024-05-05 17:01:44 +02:00
Marco Trevisan (Treviño)
b6969ef434 tests/cancellable: Initialize num_async_operations before tests using it 2024-05-05 17:01:44 +02:00
Marco Trevisan (Treviño)
fee2d30124 cancellable: Make possible to override the cancel function
Make possible for implementers of GCancellable to override the cancel
vfunc, to be able to customize some behaviors.

However, we need to ensure that in such case the real cancel function is
still called.
2024-05-05 17:01:43 +02:00
Marco Trevisan (Treviño)
cc6448b66b gcancellable: Reference the object before locking when doing signal emission
On g_cancellable_cancel() we were increasing the GCancellable ref count
before emitting the ::cancelled signal, this is a safe thing to do but
it was happening while the cancellable was locked, and this may have
potentially waken up some toggle notifications.

To prevent this, reference the GCancellable just before locking.
2024-05-05 17:00:00 +02:00
Marco Trevisan (Treviño)
1e1b082930 GCancellable: Use per-instance mutex logic instead of global critical sections
GCancellable is meant to be used in multi-thread operations but all the
cancellable instances were sharing a single mutex to synchronize them
which can be less optimal when many instances are in place.
Especially when we're doing a lock/unlock dances that may leave another
thread to take the control of a critical section in an unexpected way.

This in fact was leading to some races in GCancellableSources causing
leaks because we were assuming that the "cancelled" callback was always
called before our dispose implementation.

As per this, use per-instance mutexes.

The lock is also now used only to protect the calls that may interact
with cancelled state or that depends on that, as per this we can just
reduce it to the cancel and reset case, other than to the connect one to
prevent the race that we could have when connecting to a cancellable
that is reset from another thread.

We don't really need to release the locks during callbacks now as they
are per instance, and there's really no function that we allowed to call
during a ::cancelled signal callback that may require an unlocked state.
This could been done in case with a recursive lock, that is easy enough
to implement but not really needed for this case.

Fixes: #2309, #2313
2024-05-05 16:59:59 +02:00
Marco Trevisan (Treviño)
92e6602053 gio/tests: Ensure that a GCancellableSource can be used muliple times
Related to: #774
2024-05-05 16:59:59 +02:00
28 changed files with 4067 additions and 702 deletions

View File

@ -0,0 +1,138 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
SPDX-License-Identifier: CC0-1.0
SPDX-FileCopyrightText: Rory O'Kane at Wikimedia Commons
<https://commons.wikimedia.org/wiki/File:Sorted_binary_tree_breadth-first_traversal.svg>
SPDX-FileCopyrightText: 2024 Frank Dana
-->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="266px" height="212px" viewBox="0 0 266 212" enable-background="new 0 0 266 212" xml:space="preserve">
<g>
<g>
<line fill="none" stroke="#90c2ff" stroke-width="2" stroke-miterlimit="10" x1="27.23" y1="23.192" x2="29.23" y2="23.192"/>
<line fill="none" stroke="#90c2ff" stroke-width="2" stroke-miterlimit="10" stroke-dasharray="3.9562,3.9562" x1="33.187" y1="23.192" x2="236.932" y2="23.192"/>
<polyline fill="none" stroke="#90c2ff" stroke-width="2" stroke-miterlimit="10" points="238.91,23.192 240.91,23.192
238.97,23.677 "/>
<line fill="none" stroke="#90c2ff" stroke-width="2" stroke-miterlimit="10" stroke-dasharray="3.9907,3.9907" x1="235.099" y1="24.646" x2="27.971" y2="76.427"/>
<polyline fill="none" stroke="#90c2ff" stroke-width="2" stroke-miterlimit="10" points="26.035,76.912 24.095,77.396
26.095,77.396 "/>
<line fill="none" stroke="#90c2ff" stroke-width="2" stroke-miterlimit="10" stroke-dasharray="4.0154,4.0154" x1="30.11" y1="77.396" x2="236.902" y2="77.396"/>
<polyline fill="none" stroke="#90c2ff" stroke-width="2" stroke-miterlimit="10" points="238.91,77.396 240.91,77.396
238.97,77.881 "/>
<line fill="none" stroke="#90c2ff" stroke-width="2" stroke-miterlimit="10" stroke-dasharray="3.9907,3.9907" x1="235.099" y1="78.85" x2="27.971" y2="130.631"/>
<polyline fill="none" stroke="#90c2ff" stroke-width="2" stroke-miterlimit="10" points="26.035,131.114 24.095,131.6
26.095,131.6 "/>
<line fill="none" stroke="#90c2ff" stroke-width="2" stroke-miterlimit="10" stroke-dasharray="4.0154,4.0154" x1="30.11" y1="131.6" x2="236.902" y2="131.6"/>
<polyline fill="none" stroke="#90c2ff" stroke-width="2" stroke-miterlimit="10" points="238.91,131.6 240.91,131.6
238.97,132.085 "/>
<line fill="none" stroke="#90c2ff" stroke-width="2" stroke-miterlimit="10" stroke-dasharray="3.9907,3.9907" x1="235.099" y1="133.053" x2="27.971" y2="184.835"/>
<polyline fill="none" stroke="#90c2ff" stroke-width="2" stroke-miterlimit="10" points="26.035,185.318 24.095,185.804
26.095,185.804 "/>
<line fill="none" stroke="#90c2ff" stroke-width="2" stroke-miterlimit="10" stroke-dasharray="4.0086,4.0086" x1="30.104" y1="185.804" x2="228.53" y2="185.804"/>
<line fill="none" stroke="#90c2ff" stroke-width="2" stroke-miterlimit="10" x1="230.534" y1="185.804" x2="232.534" y2="185.804"/>
<g>
<rect fill="#90c2ff" x="24.095" y="19.892" width="6.602" height="6.602"/>
</g>
<g>
<path fill="#90c2ff" d="M238.569,185.804c-2.84,1.054-6.363,2.852-8.548,4.756l1.721-4.756l-1.721-4.755
C232.206,182.953,235.729,184.751,238.569,185.804z"/>
</g>
</g>
</g>
<g id="graph0">
<title>sorted_binary_tree</title>
<g id="node1">
<title>C</title>
<ellipse fill="#FFFFFF" stroke="#ffffff" cx="60.23" cy="185.804" rx="18.067" ry="18.067"/>
<text transform="matrix(1 0 0 1 55.5435 190.8223)" font-family="'Times-Roman'" font-size="14.0528">C</text>
</g>
<g id="node2">
<title>E</title>
<ellipse fill="#FFFFFF" stroke="#ffffff" cx="132.502" cy="185.804" rx="18.067" ry="18.067"/>
<text transform="matrix(1 0 0 1 128.21 190.8223)" font-family="'Times-Roman'" font-size="14.0528">E</text>
</g>
<g id="node3">
<title>H</title>
<ellipse fill="#FFFFFF" stroke="#ffffff" cx="204.773" cy="185.804" rx="17.064" ry="18.067"/>
<text transform="matrix(1 0 0 1 199.6992 190.8223)" font-family="'Times-Roman'" font-size="14.0528">H</text>
</g>
<g id="node4">
<title>A</title>
<ellipse fill="#FFFFFF" stroke="#ffffff" cx="24.094" cy="131.6" rx="17.064" ry="18.068"/>
<text transform="matrix(1 0 0 1 19.02 136.6191)" font-family="'Times-Roman'" font-size="14.0528">A</text>
</g>
<g id="node5">
<title>D</title>
<ellipse fill="#FFFFFF" stroke="#ffffff" cx="96.366" cy="131.6" rx="17.064" ry="18.068"/>
<text transform="matrix(1 0 0 1 91.292 136.6191)" font-family="'Times-Roman'" font-size="14.0528">D</text>
</g>
<g id="edge6">
<title>D-&gt;C</title>
<path fill="none" stroke="#ffffff" d="M86.328,147.66c-3.011,4.016-7.026,9.034-10.038,14.053"/>
<polygon fill="#ffffff" stroke="#ffffff" points="78.298,164.725 70.268,170.747 73.279,160.709 "/>
</g>
<g id="edge8">
<title>D-&gt;E</title>
<path fill="none" stroke="#ffffff" d="M106.404,147.66c3.011,4.016,7.026,9.034,10.038,14.053"/>
<polygon fill="#ffffff" stroke="#ffffff" points="119.453,160.709 122.464,170.747 114.434,164.725 "/>
</g>
<g id="node6">
<title>I</title>
<ellipse fill="#FFFFFF" stroke="#ffffff" cx="240.909" cy="131.6" rx="18.068" ry="18.068"/>
<text transform="matrix(1 0 0 1 238.5693 136.6191)" font-family="'Times-Roman'" font-size="14.0528">I</text>
</g>
<g id="edge12">
<title>I-&gt;H</title>
<path fill="none" stroke="#ffffff" d="M230.871,146.656c-3.011,5.02-6.021,10.038-10.037,15.057"/>
<polygon fill="#ffffff" stroke="#ffffff" points="223.846,163.721 214.812,169.743 217.822,159.705 "/>
</g>
<g id="node7">
<title>B</title>
<ellipse fill="#FFFFFF" stroke="#ffffff" cx="60.23" cy="77.396" rx="18.068" ry="18.068"/>
<text transform="matrix(1 0 0 1 55.5435 82.415)" font-family="'Times-Roman'" font-size="14.0528">B</text>
</g>
<g id="edge3">
<title>B-&gt;A</title>
<path fill="none" stroke="#ffffff" d="M50.192,92.453c-3.011,5.019-6.022,10.038-10.038,15.057"/>
<polygon fill="#ffffff" stroke="#ffffff" points="43.166,109.518 34.132,115.539 37.144,105.502 "/>
</g>
<g id="edge5">
<title>B-&gt;D</title>
<path fill="none" stroke="#ffffff" d="M70.268,92.453c3.011,5.019,6.022,10.038,10.038,15.057"/>
<polygon fill="#ffffff" stroke="#ffffff" points="83.317,105.502 86.328,115.539 77.294,109.518 "/>
</g>
<g id="node8">
<title>G</title>
<ellipse fill="#FFFFFF" stroke="#ffffff" cx="204.773" cy="77.396" rx="17.064" ry="18.068"/>
<text transform="matrix(1 0 0 1 199.6992 82.415)" font-family="'Times-Roman'" font-size="14.0528">G</text>
</g>
<g id="edge11">
<title>G-&gt;I</title>
<path fill="none" stroke="#ffffff" d="M214.812,93.457c3.011,4.015,7.026,9.034,10.038,14.053"/>
<polygon fill="#ffffff" stroke="#ffffff" points="227.86,106.506 230.871,116.543 222.842,110.521 "/>
</g>
<g id="node9">
<title>F</title>
<ellipse fill="#FFFFFF" stroke="#ffffff" cx="132.502" cy="23.192" rx="18.068" ry="18.068"/>
<text transform="matrix(1 0 0 1 128.5942 28.2109)" font-family="'Times-Roman'" font-size="14.0528">F</text>
</g>
<g id="edge2">
<title>F-&gt;B</title>
<path fill="none" stroke="#ffffff" d="M117.445,34.234c-11.042,8.03-23.087,17.064-34.128,26.098"/>
<polygon fill="#ffffff" stroke="#ffffff" points="85.325,63.343 75.287,66.354 81.31,57.321 "/>
</g>
<g id="edge10">
<title>F-&gt;G</title>
<path fill="none" stroke="#ffffff" d="M147.559,34.234c11.041,8.03,23.087,17.064,34.129,26.098"/>
<polygon fill="#ffffff" stroke="#ffffff" points="183.694,57.321 189.717,66.354 179.68,63.343 "/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 7.2 KiB

View File

@ -1,134 +1,138 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="266px" height="212px" viewBox="0 0 266 212" enable-background="new 0 0 266 212" xml:space="preserve">
<g>
<g>
<line fill="none" stroke="#000000" stroke-width="2" stroke-miterlimit="10" x1="27.23" y1="23.192" x2="29.23" y2="23.192"/>
<line fill="none" stroke="#000000" stroke-width="2" stroke-miterlimit="10" stroke-dasharray="3.9562,3.9562" x1="33.187" y1="23.192" x2="236.932" y2="23.192"/>
<polyline fill="none" stroke="#000000" stroke-width="2" stroke-miterlimit="10" points="238.91,23.192 240.91,23.192
238.97,23.677 "/>
<line fill="none" stroke="#000000" stroke-width="2" stroke-miterlimit="10" stroke-dasharray="3.9907,3.9907" x1="235.099" y1="24.646" x2="27.971" y2="76.427"/>
<polyline fill="none" stroke="#000000" stroke-width="2" stroke-miterlimit="10" points="26.035,76.912 24.095,77.396
26.095,77.396 "/>
<line fill="none" stroke="#000000" stroke-width="2" stroke-miterlimit="10" stroke-dasharray="4.0154,4.0154" x1="30.11" y1="77.396" x2="236.902" y2="77.396"/>
<polyline fill="none" stroke="#000000" stroke-width="2" stroke-miterlimit="10" points="238.91,77.396 240.91,77.396
238.97,77.881 "/>
<line fill="none" stroke="#000000" stroke-width="2" stroke-miterlimit="10" stroke-dasharray="3.9907,3.9907" x1="235.099" y1="78.85" x2="27.971" y2="130.631"/>
<polyline fill="none" stroke="#000000" stroke-width="2" stroke-miterlimit="10" points="26.035,131.114 24.095,131.6
26.095,131.6 "/>
<line fill="none" stroke="#000000" stroke-width="2" stroke-miterlimit="10" stroke-dasharray="4.0154,4.0154" x1="30.11" y1="131.6" x2="236.902" y2="131.6"/>
<polyline fill="none" stroke="#000000" stroke-width="2" stroke-miterlimit="10" points="238.91,131.6 240.91,131.6
238.97,132.085 "/>
<line fill="none" stroke="#000000" stroke-width="2" stroke-miterlimit="10" stroke-dasharray="3.9907,3.9907" x1="235.099" y1="133.053" x2="27.971" y2="184.835"/>
<polyline fill="none" stroke="#000000" stroke-width="2" stroke-miterlimit="10" points="26.035,185.318 24.095,185.804
26.095,185.804 "/>
<line fill="none" stroke="#000000" stroke-width="2" stroke-miterlimit="10" stroke-dasharray="4.0086,4.0086" x1="30.104" y1="185.804" x2="228.53" y2="185.804"/>
<line fill="none" stroke="#000000" stroke-width="2" stroke-miterlimit="10" x1="230.534" y1="185.804" x2="232.534" y2="185.804"/>
<g>
<rect x="24.095" y="19.892" width="6.602" height="6.602"/>
</g>
<g>
<path d="M238.569,185.804c-2.84,1.054-6.363,2.852-8.548,4.756l1.721-4.756l-1.721-4.755
C232.206,182.953,235.729,184.751,238.569,185.804z"/>
</g>
</g>
</g>
<g id="graph0">
<title>sorted_binary_tree</title>
<g id="node1">
<title>C</title>
<ellipse fill="#FFFFFF" stroke="#000000" cx="60.23" cy="185.804" rx="18.067" ry="18.067"/>
<text transform="matrix(1 0 0 1 55.5435 190.8223)" font-family="'Times-Roman'" font-size="14.0528">C</text>
</g>
<g id="node2">
<title>E</title>
<ellipse fill="#FFFFFF" stroke="#000000" cx="132.502" cy="185.804" rx="18.067" ry="18.067"/>
<text transform="matrix(1 0 0 1 128.21 190.8223)" font-family="'Times-Roman'" font-size="14.0528">E</text>
</g>
<g id="node3">
<title>H</title>
<ellipse fill="#FFFFFF" stroke="#000000" cx="204.773" cy="185.804" rx="17.064" ry="18.067"/>
<text transform="matrix(1 0 0 1 199.6992 190.8223)" font-family="'Times-Roman'" font-size="14.0528">H</text>
</g>
<g id="node4">
<title>A</title>
<ellipse fill="#FFFFFF" stroke="#000000" cx="24.094" cy="131.6" rx="17.064" ry="18.068"/>
<text transform="matrix(1 0 0 1 19.02 136.6191)" font-family="'Times-Roman'" font-size="14.0528">A</text>
</g>
<g id="node5">
<title>D</title>
<ellipse fill="#FFFFFF" stroke="#000000" cx="96.366" cy="131.6" rx="17.064" ry="18.068"/>
<text transform="matrix(1 0 0 1 91.292 136.6191)" font-family="'Times-Roman'" font-size="14.0528">D</text>
</g>
<g id="edge6">
<title>D-&gt;C</title>
<path fill="none" stroke="#000000" d="M86.328,147.66c-3.011,4.016-7.026,9.034-10.038,14.053"/>
<polygon stroke="#000000" points="78.298,164.725 70.268,170.747 73.279,160.709 "/>
</g>
<g id="edge8">
<title>D-&gt;E</title>
<path fill="none" stroke="#000000" d="M106.404,147.66c3.011,4.016,7.026,9.034,10.038,14.053"/>
<polygon stroke="#000000" points="119.453,160.709 122.464,170.747 114.434,164.725 "/>
</g>
<g id="node6">
<title>I</title>
<ellipse fill="#FFFFFF" stroke="#000000" cx="240.909" cy="131.6" rx="18.068" ry="18.068"/>
<text transform="matrix(1 0 0 1 238.5693 136.6191)" font-family="'Times-Roman'" font-size="14.0528">I</text>
</g>
<g id="edge12">
<title>I-&gt;H</title>
<path fill="none" stroke="#000000" d="M230.871,146.656c-3.011,5.02-6.021,10.038-10.037,15.057"/>
<polygon stroke="#000000" points="223.846,163.721 214.812,169.743 217.822,159.705 "/>
</g>
<g id="node7">
<title>B</title>
<ellipse fill="#FFFFFF" stroke="#000000" cx="60.23" cy="77.396" rx="18.068" ry="18.068"/>
<text transform="matrix(1 0 0 1 55.5435 82.415)" font-family="'Times-Roman'" font-size="14.0528">B</text>
</g>
<g id="edge3">
<title>B-&gt;A</title>
<path fill="none" stroke="#000000" d="M50.192,92.453c-3.011,5.019-6.022,10.038-10.038,15.057"/>
<polygon stroke="#000000" points="43.166,109.518 34.132,115.539 37.144,105.502 "/>
</g>
<g id="edge5">
<title>B-&gt;D</title>
<path fill="none" stroke="#000000" d="M70.268,92.453c3.011,5.019,6.022,10.038,10.038,15.057"/>
<polygon stroke="#000000" points="83.317,105.502 86.328,115.539 77.294,109.518 "/>
</g>
<g id="node8">
<title>G</title>
<ellipse fill="#FFFFFF" stroke="#000000" cx="204.773" cy="77.396" rx="17.064" ry="18.068"/>
<text transform="matrix(1 0 0 1 199.6992 82.415)" font-family="'Times-Roman'" font-size="14.0528">G</text>
</g>
<g id="edge11">
<title>G-&gt;I</title>
<path fill="none" stroke="#000000" d="M214.812,93.457c3.011,4.015,7.026,9.034,10.038,14.053"/>
<polygon stroke="#000000" points="227.86,106.506 230.871,116.543 222.842,110.521 "/>
</g>
<g id="node9">
<title>F</title>
<ellipse fill="#FFFFFF" stroke="#000000" cx="132.502" cy="23.192" rx="18.068" ry="18.068"/>
<text transform="matrix(1 0 0 1 128.5942 28.2109)" font-family="'Times-Roman'" font-size="14.0528">F</text>
</g>
<g id="edge2">
<title>F-&gt;B</title>
<path fill="none" stroke="#000000" d="M117.445,34.234c-11.042,8.03-23.087,17.064-34.128,26.098"/>
<polygon stroke="#000000" points="85.325,63.343 75.287,66.354 81.31,57.321 "/>
</g>
<g id="edge10">
<title>F-&gt;G</title>
<path fill="none" stroke="#000000" d="M147.559,34.234c11.041,8.03,23.087,17.064,34.129,26.098"/>
<polygon stroke="#000000" points="183.694,57.321 189.717,66.354 179.68,63.343 "/>
</g>
</g>
</svg>
<?xml version="1.0" encoding="utf-8"?>
<!--
SPDX-License-Identifier: CC0-1.0
SPDX-FileCopyrightText: Rory O'Kane at Wikimedia Commons
<https://commons.wikimedia.org/wiki/File:Sorted_binary_tree_breadth-first_traversal.svg>
SPDX-FileCopyrightText: 2024 Frank Dana
-->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="266px" height="212px" viewBox="0 0 266 212" enable-background="new 0 0 266 212" xml:space="preserve">
<g>
<g>
<line fill="none" stroke="#1c76e4" stroke-width="2" stroke-miterlimit="10" x1="27.23" y1="23.192" x2="29.23" y2="23.192"/>
<line fill="none" stroke="#1c76e4" stroke-width="2" stroke-miterlimit="10" stroke-dasharray="3.9562,3.9562" x1="33.187" y1="23.192" x2="236.932" y2="23.192"/>
<polyline fill="none" stroke="#1c76e4" stroke-width="2" stroke-miterlimit="10" points="238.91,23.192 240.91,23.192
238.97,23.677 "/>
<line fill="none" stroke="#1c76e4" stroke-width="2" stroke-miterlimit="10" stroke-dasharray="3.9907,3.9907" x1="235.099" y1="24.646" x2="27.971" y2="76.427"/>
<polyline fill="none" stroke="#1c76e4" stroke-width="2" stroke-miterlimit="10" points="26.035,76.912 24.095,77.396
26.095,77.396 "/>
<line fill="none" stroke="#1c76e4" stroke-width="2" stroke-miterlimit="10" stroke-dasharray="4.0154,4.0154" x1="30.11" y1="77.396" x2="236.902" y2="77.396"/>
<polyline fill="none" stroke="#1c76e4" stroke-width="2" stroke-miterlimit="10" points="238.91,77.396 240.91,77.396
238.97,77.881 "/>
<line fill="none" stroke="#1c76e4" stroke-width="2" stroke-miterlimit="10" stroke-dasharray="3.9907,3.9907" x1="235.099" y1="78.85" x2="27.971" y2="130.631"/>
<polyline fill="none" stroke="#1c76e4" stroke-width="2" stroke-miterlimit="10" points="26.035,131.114 24.095,131.6
26.095,131.6 "/>
<line fill="none" stroke="#1c76e4" stroke-width="2" stroke-miterlimit="10" stroke-dasharray="4.0154,4.0154" x1="30.11" y1="131.6" x2="236.902" y2="131.6"/>
<polyline fill="none" stroke="#1c76e4" stroke-width="2" stroke-miterlimit="10" points="238.91,131.6 240.91,131.6
238.97,132.085 "/>
<line fill="none" stroke="#1c76e4" stroke-width="2" stroke-miterlimit="10" stroke-dasharray="3.9907,3.9907" x1="235.099" y1="133.053" x2="27.971" y2="184.835"/>
<polyline fill="none" stroke="#1c76e4" stroke-width="2" stroke-miterlimit="10" points="26.035,185.318 24.095,185.804
26.095,185.804 "/>
<line fill="none" stroke="#1c76e4" stroke-width="2" stroke-miterlimit="10" stroke-dasharray="4.0086,4.0086" x1="30.104" y1="185.804" x2="228.53" y2="185.804"/>
<line fill="none" stroke="#1c76e4" stroke-width="2" stroke-miterlimit="10" x1="230.534" y1="185.804" x2="232.534" y2="185.804"/>
<g>
<rect fill="#1c76e4" x="24.095" y="19.892" width="6.602" height="6.602"/>
</g>
<g>
<path fill="#1c76e4" d="M238.569,185.804c-2.84,1.054-6.363,2.852-8.548,4.756l1.721-4.756l-1.721-4.755
C232.206,182.953,235.729,184.751,238.569,185.804z"/>
</g>
</g>
</g>
<g id="graph0">
<title>sorted_binary_tree</title>
<g id="node1">
<title>C</title>
<ellipse fill="#FFFFFF" stroke="#000000" cx="60.23" cy="185.804" rx="18.067" ry="18.067"/>
<text transform="matrix(1 0 0 1 55.5435 190.8223)" font-family="'Times-Roman'" font-size="14.0528">C</text>
</g>
<g id="node2">
<title>E</title>
<ellipse fill="#FFFFFF" stroke="#000000" cx="132.502" cy="185.804" rx="18.067" ry="18.067"/>
<text transform="matrix(1 0 0 1 128.21 190.8223)" font-family="'Times-Roman'" font-size="14.0528">E</text>
</g>
<g id="node3">
<title>H</title>
<ellipse fill="#FFFFFF" stroke="#000000" cx="204.773" cy="185.804" rx="17.064" ry="18.067"/>
<text transform="matrix(1 0 0 1 199.6992 190.8223)" font-family="'Times-Roman'" font-size="14.0528">H</text>
</g>
<g id="node4">
<title>A</title>
<ellipse fill="#FFFFFF" stroke="#000000" cx="24.094" cy="131.6" rx="17.064" ry="18.068"/>
<text transform="matrix(1 0 0 1 19.02 136.6191)" font-family="'Times-Roman'" font-size="14.0528">A</text>
</g>
<g id="node5">
<title>D</title>
<ellipse fill="#FFFFFF" stroke="#000000" cx="96.366" cy="131.6" rx="17.064" ry="18.068"/>
<text transform="matrix(1 0 0 1 91.292 136.6191)" font-family="'Times-Roman'" font-size="14.0528">D</text>
</g>
<g id="edge6">
<title>D-&gt;C</title>
<path fill="none" stroke="#000000" d="M86.328,147.66c-3.011,4.016-7.026,9.034-10.038,14.053"/>
<polygon stroke="#000000" points="78.298,164.725 70.268,170.747 73.279,160.709 "/>
</g>
<g id="edge8">
<title>D-&gt;E</title>
<path fill="none" stroke="#000000" d="M106.404,147.66c3.011,4.016,7.026,9.034,10.038,14.053"/>
<polygon stroke="#000000" points="119.453,160.709 122.464,170.747 114.434,164.725 "/>
</g>
<g id="node6">
<title>I</title>
<ellipse fill="#FFFFFF" stroke="#000000" cx="240.909" cy="131.6" rx="18.068" ry="18.068"/>
<text transform="matrix(1 0 0 1 238.5693 136.6191)" font-family="'Times-Roman'" font-size="14.0528">I</text>
</g>
<g id="edge12">
<title>I-&gt;H</title>
<path fill="none" stroke="#000000" d="M230.871,146.656c-3.011,5.02-6.021,10.038-10.037,15.057"/>
<polygon stroke="#000000" points="223.846,163.721 214.812,169.743 217.822,159.705 "/>
</g>
<g id="node7">
<title>B</title>
<ellipse fill="#FFFFFF" stroke="#000000" cx="60.23" cy="77.396" rx="18.068" ry="18.068"/>
<text transform="matrix(1 0 0 1 55.5435 82.415)" font-family="'Times-Roman'" font-size="14.0528">B</text>
</g>
<g id="edge3">
<title>B-&gt;A</title>
<path fill="none" stroke="#000000" d="M50.192,92.453c-3.011,5.019-6.022,10.038-10.038,15.057"/>
<polygon stroke="#000000" points="43.166,109.518 34.132,115.539 37.144,105.502 "/>
</g>
<g id="edge5">
<title>B-&gt;D</title>
<path fill="none" stroke="#000000" d="M70.268,92.453c3.011,5.019,6.022,10.038,10.038,15.057"/>
<polygon stroke="#000000" points="83.317,105.502 86.328,115.539 77.294,109.518 "/>
</g>
<g id="node8">
<title>G</title>
<ellipse fill="#FFFFFF" stroke="#000000" cx="204.773" cy="77.396" rx="17.064" ry="18.068"/>
<text transform="matrix(1 0 0 1 199.6992 82.415)" font-family="'Times-Roman'" font-size="14.0528">G</text>
</g>
<g id="edge11">
<title>G-&gt;I</title>
<path fill="none" stroke="#000000" d="M214.812,93.457c3.011,4.015,7.026,9.034,10.038,14.053"/>
<polygon stroke="#000000" points="227.86,106.506 230.871,116.543 222.842,110.521 "/>
</g>
<g id="node9">
<title>F</title>
<ellipse fill="#FFFFFF" stroke="#000000" cx="132.502" cy="23.192" rx="18.068" ry="18.068"/>
<text transform="matrix(1 0 0 1 128.5942 28.2109)" font-family="'Times-Roman'" font-size="14.0528">F</text>
</g>
<g id="edge2">
<title>F-&gt;B</title>
<path fill="none" stroke="#000000" d="M117.445,34.234c-11.042,8.03-23.087,17.064-34.128,26.098"/>
<polygon stroke="#000000" points="85.325,63.343 75.287,66.354 81.31,57.321 "/>
</g>
<g id="edge10">
<title>F-&gt;G</title>
<path fill="none" stroke="#000000" d="M147.559,34.234c11.041,8.03,23.087,17.064,34.129,26.098"/>
<polygon stroke="#000000" points="183.694,57.321 189.717,66.354 179.68,63.343 "/>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 7.1 KiB

After

Width:  |  Height:  |  Size: 7.0 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 36 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 36 KiB

After

Width:  |  Height:  |  Size: 36 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 36 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 36 KiB

After

Width:  |  Height:  |  Size: 36 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 36 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 36 KiB

After

Width:  |  Height:  |  Size: 36 KiB

View File

@ -99,6 +99,10 @@ content_images = [
"Sorted_binary_tree_inorder.svg",
"Sorted_binary_tree_postorder.svg",
"Sorted_binary_tree_preorder.svg",
"Sorted_binary_tree_breadth-first_traversal-dark.svg",
"Sorted_binary_tree_inorder-dark.svg",
"Sorted_binary_tree_postorder-dark.svg",
"Sorted_binary_tree_preorder-dark.svg",
]
[[object]]

View File

@ -34,7 +34,7 @@
/**
* GActionGroupExporter:
*
* These functions support exporting a [class@Gio.ActionGroup] on D-Bus.
* These functions support exporting a [iface@Gio.ActionGroup] on D-Bus.
* The D-Bus interface that is used is a private implementation
* detail.
*
@ -556,10 +556,9 @@ g_action_group_exporter_free (GActionGroupExporter *exporter)
/**
* g_dbus_connection_export_action_group:
* @connection: a #GDBusConnection
* @connection: the D-Bus connection
* @object_path: a D-Bus object path
* @action_group: a #GActionGroup
* @error: a pointer to a %NULL #GError, or %NULL
* @action_group: an action group
*
* Exports @action_group on @connection at @object_path.
*
@ -571,7 +570,7 @@ g_action_group_exporter_free (GActionGroupExporter *exporter)
* returned (with @error set accordingly).
*
* You can unexport the action group using
* g_dbus_connection_unexport_action_group() with the return value of
* [method@Gio.DBusConnection.unexport_action_group] with the return value of
* this function.
*
* The thread default main context is taken at the time of this call.
@ -641,15 +640,15 @@ g_dbus_connection_export_action_group (GDBusConnection *connection,
/**
* g_dbus_connection_unexport_action_group:
* @connection: a #GDBusConnection
* @export_id: the ID from g_dbus_connection_export_action_group()
* @connection: the D-Bus connection
* @export_id: the ID from [method@Gio.DBusConnection.export_action_group]
*
* Reverses the effect of a previous call to
* g_dbus_connection_export_action_group().
* [method@Gio.DBusConnection.export_action_group].
*
* It is an error to call this function with an ID that wasn't returned
* from g_dbus_connection_export_action_group() or to call it with the
* same ID more than once.
* It is an error to call this function with an ID that wasnt returned from
* [method@Gio.DBusConnection.export_action_group] or to call it with the same
* ID more than once.
*
* Since: 2.32
**/

View File

@ -37,7 +37,7 @@
* One useful application of this interface is to map the
* names of actions from various action groups to unique,
* prefixed names (e.g. by prepending "app." or "win.").
* This is the motivation for the 'Map' part of the interface
* This is the motivation for the Map part of the interface
* name.
*
* Since: 2.32
@ -45,11 +45,14 @@
/**
* GActionMapInterface:
* @lookup_action: the virtual function pointer for g_action_map_lookup_action()
* @add_action: the virtual function pointer for g_action_map_add_action()
* @remove_action: the virtual function pointer for g_action_map_remove_action()
* @lookup_action: the virtual function pointer for
* [method@Gio.ActionMap.lookup_action]
* @add_action: the virtual function pointer for
* [method@Gio.ActionMap.add_action]
* @remove_action: the virtual function pointer for
* [method@Gio.ActionMap.remove_action]
*
* The virtual function table for #GActionMap.
* The virtual function table for [iface@Gio.ActionMap].
*
* Since: 2.32
**/
@ -63,14 +66,14 @@ g_action_map_default_init (GActionMapInterface *iface)
/**
* g_action_map_lookup_action:
* @action_map: a #GActionMap
* @action_map: an action map
* @action_name: the name of an action
*
* Looks up the action with the name @action_name in @action_map.
*
* If no such action exists, returns %NULL.
* If no such action exists, returns `NULL`.
*
* Returns: (nullable) (transfer none): a #GAction, or %NULL
* Returns: (nullable) (transfer none): a [iface@Gio.Action]
*
* Since: 2.32
*/
@ -84,8 +87,8 @@ g_action_map_lookup_action (GActionMap *action_map,
/**
* g_action_map_add_action:
* @action_map: a #GActionMap
* @action: a #GAction
* @action_map: an action map
* @action: a [iface@Gio.Action]
*
* Adds an action to the @action_map.
*
@ -105,7 +108,7 @@ g_action_map_add_action (GActionMap *action_map,
/**
* g_action_map_remove_action:
* @action_map: a #GActionMap
* @action_map: an action map
* @action_name: the name of the action
*
* Removes the named action from the action map.
@ -124,51 +127,49 @@ g_action_map_remove_action (GActionMap *action_map,
/**
* GActionEntry:
* @name: the name of the action
* @activate: the callback to connect to the "activate" signal of the
* action. Since GLib 2.40, this can be %NULL for stateful
* actions, in which case the default handler is used. For
* boolean-stated actions with no parameter, this is a
* toggle. For other state types (and parameter type equal
* to the state type) this will be a function that
* just calls @change_state (which you should provide).
* @activate: the callback to connect to the "activate" signal of the action.
* Since GLib 2.40, this can be `NULL` for stateful actions, in which case
* the default handler is used. For boolean-stated actions with no
* parameter, this is a toggle. For other state types (and parameter type
* equal to the state type) this will be a function that just calls
* @change_state (which you should provide).
* @parameter_type: the type of the parameter that must be passed to the
* activate function for this action, given as a single
* GVariant type string (or %NULL for no parameter)
* activate function for this action, given as a single GVariant type string
* (or `NULL` for no parameter)
* @state: the initial state for this action, given in
* [GVariant text format](gvariant-text-format.html). The state is parsed
* with no extra type information, so type tags must be added to
* the string if they are necessary. Stateless actions should
* give %NULL here.
* @change_state: the callback to connect to the "change-state" signal
* of the action. All stateful actions should provide a
* handler here; stateless actions should not.
* [GVariant text format](gvariant-text-format.html). The state is parsed
* with no extra type information, so type tags must be added to the string
* if they are necessary. Stateless actions should give `NULL` here.
* @change_state: the callback to connect to the "change-state" signal of the
* action. All stateful actions should provide a handler here; stateless
* actions should not.
*
* This struct defines a single action. It is for use with
* g_action_map_add_action_entries().
* [method@Gio.ActionMap.add_action_entries].
*
* The order of the items in the structure are intended to reflect
* frequency of use. It is permissible to use an incomplete initialiser
* in order to leave some of the later values as %NULL. All values
* in order to leave some of the later values as `NULL`. All values
* after @name are optional. Additional optional fields may be added in
* the future.
*
* See g_action_map_add_action_entries() for an example.
* See [method@Gio.ActionMap.add_action_entries] for an example.
**/
/**
* g_action_map_add_action_entries:
* @action_map: a #GActionMap
* @action_map: an action map
* @entries: (array length=n_entries) (element-type GActionEntry): a pointer to
* the first item in an array of #GActionEntry structs
* @n_entries: the length of @entries, or -1 if @entries is %NULL-terminated
* the first item in an array of [struct@Gio.ActionEntry] structs
* @n_entries: the length of @entries, or -1 if @entries is `NULL`-terminated
* @user_data: the user data for signal connections
*
* A convenience function for creating multiple #GSimpleAction instances
* and adding them to a #GActionMap.
* A convenience function for creating multiple [class@Gio.SimpleAction]
* instances and adding them to a [iface@Gio.ActionMap].
*
* Each action is constructed as per one #GActionEntry.
* Each action is constructed as per one [struct@Gio.ActionEntry].
*
* |[<!-- language="C" -->
* ```c
* static void
* activate_quit (GSimpleAction *simple,
* GVariant *parameter,
@ -199,7 +200,7 @@ g_action_map_remove_action (GActionMap *action_map,
*
* return G_ACTION_GROUP (group);
* }
* ]|
* ```
*
* Since: 2.32
*/
@ -278,16 +279,16 @@ g_action_map_add_action_entries (GActionMap *action_map,
/**
* g_action_map_remove_action_entries:
* @action_map: The #GActionMap
* @action_map: The [iface@Gio.ActionMap]
* @entries: (array length=n_entries) (element-type GActionEntry): a pointer to
* the first item in an array of #GActionEntry structs
* @n_entries: the length of @entries, or -1 if @entries is %NULL-terminated
* the first item in an array of [struct@Gio.ActionEntry] structs
* @n_entries: the length of @entries, or -1 if @entries is `NULL`-terminated
*
* Remove actions from a #GActionMap. This is meant as the reverse of
* g_action_map_add_action_entries().
* Remove actions from a [iface@Gio.ActionMap]. This is meant as the reverse of
* [method@Gio.ActionMap.add_action_entries].
*
*
* |[<!-- language="C" -->
* ```c
* static const GActionEntry entries[] = {
* { "quit", activate_quit },
* { "print-string", activate_print_string, "s" }
@ -304,7 +305,7 @@ g_action_map_add_action_entries (GActionMap *action_map,
* {
* g_action_map_remove_action_entries (map, entries, G_N_ELEMENTS (entries));
* }
* ]|
* ```
*
* Since: 2.78
*/

View File

@ -56,15 +56,19 @@
* As of GLib 2.20, URIs will always be converted to POSIX paths
* (using [method@Gio.File.get_path]) when using [method@Gio.AppInfo.launch]
* even if the application requested an URI and not a POSIX path. For example
* for a desktop-file based application with Exec key `totem
* %U` and a single URI, `sftp://foo/file.avi`, then
* `/home/user/.gvfs/sftp on foo/file.avi` will be passed. This will
* only work if a set of suitable GIO extensions (such as GVfs 2.26
* compiled with FUSE support), is available and operational; if this
* is not the case, the URI will be passed unmodified to the application.
* Some URIs, such as `mailto:`, of course cannot be mapped to a POSIX
* path (in GVfs there's no FUSE mount for it); such URIs will be
* passed unmodified to the application.
* for a desktop-file based application with the following Exec key:
*
* ```
* Exec=totem %U
* ```
*
* and a single URI, `sftp://foo/file.avi`, then
* `/home/user/.gvfs/sftp on foo/file.avi` will be passed. This will only work
* if a set of suitable GIO extensions (such as GVfs 2.26 compiled with FUSE
* support), is available and operational; if this is not the case, the URI
* will be passed unmodified to the application. Some URIs, such as `mailto:`,
* of course cannot be mapped to a POSIX path (in GVfs theres no FUSE mount
* for it); such URIs will be passed unmodified to the application.
*
* Specifically for GVfs 2.26 and later, the POSIX URI will be mapped
* back to the GIO URI in the [iface@Gio.File] constructors (since GVfs
@ -94,7 +98,7 @@
*
* This code will work when both `cdda://sr0/Track 1.wav` and
* `/home/user/.gvfs/cdda on sr0/Track 1.wav` is passed to the
* application. It should be noted that it's generally not safe
* application. It should be noted that its generally not safe
* for applications to rely on the format of a particular URIs.
* Different launcher applications (e.g. file managers) may have
* different ideas of what a given URI means.
@ -115,9 +119,9 @@ g_app_info_default_init (GAppInfoInterface *iface)
/**
* g_app_info_dup:
* @appinfo: a #GAppInfo.
* @appinfo: the app info
*
* Creates a duplicate of a #GAppInfo.
* Creates a duplicate of a [iface@Gio.AppInfo].
*
* Returns: (transfer full): a duplicate of @appinfo.
**/
@ -135,16 +139,16 @@ g_app_info_dup (GAppInfo *appinfo)
/**
* g_app_info_equal:
* @appinfo1: the first #GAppInfo.
* @appinfo2: the second #GAppInfo.
* @appinfo1: the first [iface@Gio.AppInfo].
* @appinfo2: the second [iface@Gio.AppInfo].
*
* Checks if two #GAppInfos are equal.
* Checks if two [iface@Gio.AppInfo]s are equal.
*
* Note that the check *may not* compare each individual
* field, and only does an identity check. In case detecting changes in the
* contents is needed, program code must additionally compare relevant fields.
* Note that the check *may not* compare each individual field, and only does
* an identity check. In case detecting changes in the contents is needed,
* program code must additionally compare relevant fields.
*
* Returns: %TRUE if @appinfo1 is equal to @appinfo2. %FALSE otherwise.
* Returns: `TRUE` if @appinfo1 is equal to @appinfo2. `FALSE` otherwise.
**/
gboolean
g_app_info_equal (GAppInfo *appinfo1,
@ -165,17 +169,16 @@ g_app_info_equal (GAppInfo *appinfo1,
/**
* g_app_info_get_id:
* @appinfo: a #GAppInfo.
* @appinfo: the app info
*
* Gets the ID of an application. An id is a string that
* identifies the application. The exact format of the id is
* platform dependent. For instance, on Unix this is the
* desktop file id from the xdg menu specification.
* Gets the ID of an application. An id is a string that identifies the
* application. The exact format of the id is platform dependent. For instance,
* on Unix this is the desktop file id from the xdg menu specification.
*
* Note that the returned ID may be %NULL, depending on how
* the @appinfo has been constructed.
* Note that the returned ID may be `NULL`, depending on how the @appinfo has
* been constructed.
*
* Returns: (nullable): a string containing the application's ID.
* Returns: (nullable): a string containing the applications ID.
**/
const char *
g_app_info_get_id (GAppInfo *appinfo)
@ -191,7 +194,7 @@ g_app_info_get_id (GAppInfo *appinfo)
/**
* g_app_info_get_name:
* @appinfo: a #GAppInfo.
* @appinfo: the app info
*
* Gets the installed name of the application.
*
@ -211,7 +214,7 @@ g_app_info_get_name (GAppInfo *appinfo)
/**
* g_app_info_get_display_name:
* @appinfo: a #GAppInfo.
* @appinfo: the app info
*
* Gets the display name of the application. The display name is often more
* descriptive to the user than the name itself.
@ -238,12 +241,12 @@ g_app_info_get_display_name (GAppInfo *appinfo)
/**
* g_app_info_get_description:
* @appinfo: a #GAppInfo.
* @appinfo: the app info
*
* Gets a human-readable description of an installed application.
*
* Returns: (nullable): a string containing a description of the
* application @appinfo, or %NULL if none.
* application @appinfo, or `NULL` if none.
**/
const char *
g_app_info_get_description (GAppInfo *appinfo)
@ -259,15 +262,15 @@ g_app_info_get_description (GAppInfo *appinfo)
/**
* g_app_info_get_executable: (virtual get_executable)
* @appinfo: a #GAppInfo
* @appinfo: the app info
*
* Gets the executable's name for the installed application.
* Gets the executables name for the installed application.
*
* This is intended to be used for debugging or labelling what program is going
* to be run. To launch the executable, use g_app_info_launch() and related
* to be run. To launch the executable, use [method@Gio.AppInfo.launch] and related
* functions, rather than spawning the return value from this function.
*
* Returns: (type filename): a string containing the @appinfo's application
* Returns: (type filename): a string containing the @appinfos application
* binaries name
**/
const char *
@ -285,13 +288,13 @@ g_app_info_get_executable (GAppInfo *appinfo)
/**
* g_app_info_get_commandline: (virtual get_commandline)
* @appinfo: a #GAppInfo
* @appinfo: the app info
*
* Gets the commandline with which the application will be
* started.
*
* Returns: (nullable) (type filename): a string containing the @appinfo's commandline,
* or %NULL if this information is not available
* Returns: (nullable) (type filename): a string containing the @appinfos
* commandline, or `NULL` if this information is not available
*
* Since: 2.20
**/
@ -312,13 +315,12 @@ g_app_info_get_commandline (GAppInfo *appinfo)
/**
* g_app_info_set_as_default_for_type:
* @appinfo: a #GAppInfo.
* @appinfo: the app info
* @content_type: the content type.
* @error: a #GError.
*
* Sets the application as the default handler for a given type.
*
* Returns: %TRUE on success, %FALSE on error.
* Returns: `TRUE` on success, `FALSE` on error.
**/
gboolean
g_app_info_set_as_default_for_type (GAppInfo *appinfo,
@ -342,16 +344,15 @@ g_app_info_set_as_default_for_type (GAppInfo *appinfo,
/**
* g_app_info_set_as_last_used_for_type:
* @appinfo: a #GAppInfo.
* @appinfo: the app info
* @content_type: the content type.
* @error: a #GError.
*
* Sets the application as the last used application for a given type.
* This will make the application appear as first in the list returned
* by g_app_info_get_recommended_for_type(), regardless of the default
* Sets the application as the last used application for a given type. This
* will make the application appear as first in the list returned by
* [func@Gio.AppInfo.get_recommended_for_type], regardless of the default
* application for that content type.
*
* Returns: %TRUE on success, %FALSE on error.
* Returns: `TRUE` on success, `FALSE` on error.
**/
gboolean
g_app_info_set_as_last_used_for_type (GAppInfo *appinfo,
@ -375,14 +376,13 @@ g_app_info_set_as_last_used_for_type (GAppInfo *appinfo,
/**
* g_app_info_set_as_default_for_extension:
* @appinfo: a #GAppInfo.
* @extension: (type filename): a string containing the file extension
* (without the dot).
* @error: a #GError.
* @appinfo: the app info
* @extension: (type filename): a string containing the file extension (without
* the dot).
*
* Sets the application as the default handler for the given file extension.
*
* Returns: %TRUE on success, %FALSE on error.
* Returns: `TRUE` on success, `FALSE` on error.
**/
gboolean
g_app_info_set_as_default_for_extension (GAppInfo *appinfo,
@ -407,14 +407,13 @@ g_app_info_set_as_default_for_extension (GAppInfo *appinfo,
/**
* g_app_info_add_supports_type:
* @appinfo: a #GAppInfo.
* @appinfo: the app info
* @content_type: a string.
* @error: a #GError.
*
* Adds a content type to the application information to indicate the
* application is capable of opening files with the given content type.
*
* Returns: %TRUE on success, %FALSE on error.
* Returns: `TRUE` on success, `FALSE` on error.
**/
gboolean
g_app_info_add_supports_type (GAppInfo *appinfo,
@ -441,12 +440,12 @@ g_app_info_add_supports_type (GAppInfo *appinfo,
/**
* g_app_info_can_remove_supports_type:
* @appinfo: a #GAppInfo.
* @appinfo: the app info
*
* Checks if a supported content type can be removed from an application.
*
* Returns: %TRUE if it is possible to remove supported
* content types from a given @appinfo, %FALSE if not.
* Returns: `TRUE` if it is possible to remove supported content types from a
* given @appinfo, `FALSE` if not.
**/
gboolean
g_app_info_can_remove_supports_type (GAppInfo *appinfo)
@ -466,13 +465,12 @@ g_app_info_can_remove_supports_type (GAppInfo *appinfo)
/**
* g_app_info_remove_supports_type:
* @appinfo: a #GAppInfo.
* @appinfo: the app info
* @content_type: a string.
* @error: a #GError.
*
* Removes a supported type from an application, if possible.
*
* Returns: %TRUE on success, %FALSE on error.
* Returns: `TRUE` on success, `FALSE` on error.
**/
gboolean
g_app_info_remove_supports_type (GAppInfo *appinfo,
@ -498,17 +496,18 @@ g_app_info_remove_supports_type (GAppInfo *appinfo,
/**
* g_app_info_get_supported_types:
* @appinfo: a #GAppInfo that can handle files
* @appinfo: an app info that can handle files
*
* Retrieves the list of content types that @app_info claims to support.
* If this information is not provided by the environment, this function
* will return %NULL.
* will return `NULL`.
*
* This function does not take in consideration associations added with
* g_app_info_add_supports_type(), but only those exported directly by
* [method@Gio.AppInfo.add_supports_type], but only those exported directly by
* the application.
*
* Returns: (transfer none) (array zero-terminated=1) (element-type utf8):
* a list of content types.
* a list of content types.
*
* Since: 2.34
*/
@ -530,12 +529,12 @@ g_app_info_get_supported_types (GAppInfo *appinfo)
/**
* g_app_info_get_icon:
* @appinfo: a #GAppInfo.
* @appinfo: the app info
*
* Gets the icon for the application.
*
* Returns: (nullable) (transfer none): the default #GIcon for @appinfo or %NULL
* if there is no default icon.
* Returns: (nullable) (transfer none): the default [iface@Gio.Icon] for
* @appinfo or `NULL` if there is no default icon.
**/
GIcon *
g_app_info_get_icon (GAppInfo *appinfo)
@ -552,17 +551,16 @@ g_app_info_get_icon (GAppInfo *appinfo)
/**
* g_app_info_launch:
* @appinfo: a #GAppInfo
* @files: (nullable) (element-type GFile): a #GList of #GFile objects
* @context: (nullable): a #GAppLaunchContext or %NULL
* @error: a #GError
* @appinfo: the app info
* @files: (nullable) (element-type GFile): a list of [iface@Gio.File] objects
* @context: (nullable): the launch context
*
* Launches the application. Passes @files to the launched application
* as arguments, using the optional @context to get information
* about the details of the launcher (like what screen it is on).
* On error, @error will be set accordingly.
*
* To launch the application without arguments pass a %NULL @files list.
* To launch the application without arguments pass a `NULL` @files list.
*
* Note that even if the launch is successful the application launched
* can fail to start if it runs into problems during startup. There is
@ -571,11 +569,11 @@ g_app_info_get_icon (GAppInfo *appinfo)
* Some URIs can be changed when passed through a GFile (for instance
* unsupported URIs with strange formats like mailto:), so if you have
* a textual URI you want to pass in as argument, consider using
* g_app_info_launch_uris() instead.
* [method@Gio.AppInfo.launch_uris] instead.
*
* The launched application inherits the environment of the launching
* process, but it can be modified with g_app_launch_context_setenv()
* and g_app_launch_context_unsetenv().
* process, but it can be modified with [method@Gio.AppLaunchContext.setenv]
* and [method@Gio.AppLaunchContext.unsetenv].
*
* On UNIX, this function sets the `GIO_LAUNCHED_DESKTOP_FILE`
* environment variable with the path of the launched desktop file and
@ -585,7 +583,7 @@ g_app_info_get_icon (GAppInfo *appinfo)
* `XDG_ACTIVATION_TOKEN` and `DESKTOP_STARTUP_ID` environment
* variables are also set, based on information provided in @context.
*
* Returns: %TRUE on successful launch, %FALSE otherwise.
* Returns: `TRUE` on successful launch, `FALSE` otherwise.
**/
gboolean
g_app_info_launch (GAppInfo *appinfo,
@ -605,11 +603,11 @@ g_app_info_launch (GAppInfo *appinfo,
/**
* g_app_info_supports_uris:
* @appinfo: a #GAppInfo.
* @appinfo: the app info
*
* Checks if the application supports reading files and directories from URIs.
*
* Returns: %TRUE if the @appinfo supports URIs.
* Returns: `TRUE` if the @appinfo supports URIs.
**/
gboolean
g_app_info_supports_uris (GAppInfo *appinfo)
@ -626,11 +624,11 @@ g_app_info_supports_uris (GAppInfo *appinfo)
/**
* g_app_info_supports_files:
* @appinfo: a #GAppInfo.
* @appinfo: the app info
*
* Checks if the application accepts files as arguments.
*
* Returns: %TRUE if the @appinfo supports files.
* Returns: `TRUE` if the @appinfo supports files.
**/
gboolean
g_app_info_supports_files (GAppInfo *appinfo)
@ -647,10 +645,9 @@ g_app_info_supports_files (GAppInfo *appinfo)
/**
* g_app_info_launch_uris:
* @appinfo: a #GAppInfo
* @uris: (nullable) (element-type utf8): a #GList containing URIs to launch.
* @context: (nullable): a #GAppLaunchContext or %NULL
* @error: a #GError
* @appinfo: the app info
* @uris: (nullable) (element-type utf8): a list of URIs to launch.
* @context: (nullable): the launch context
*
* Launches the application. This passes the @uris to the launched application
* as arguments, using the optional @context to get information
@ -659,13 +656,13 @@ g_app_info_supports_files (GAppInfo *appinfo)
* one URI per invocation as part of their command-line, multiple instances
* of the application will be spawned.
*
* To launch the application without arguments pass a %NULL @uris list.
* To launch the application without arguments pass a `NULL` @uris list.
*
* Note that even if the launch is successful the application launched
* can fail to start if it runs into problems during startup. There is
* no way to detect this.
*
* Returns: %TRUE on successful launch, %FALSE otherwise.
* Returns: `TRUE` on successful launch, `FALSE` otherwise.
**/
gboolean
g_app_info_launch_uris (GAppInfo *appinfo,
@ -684,19 +681,20 @@ g_app_info_launch_uris (GAppInfo *appinfo,
/**
* g_app_info_launch_uris_async:
* @appinfo: a #GAppInfo
* @uris: (nullable) (element-type utf8): a #GList containing URIs to launch.
* @context: (nullable): a #GAppLaunchContext or %NULL
* @cancellable: (nullable): a #GCancellable
* @callback: (nullable): a #GAsyncReadyCallback to call when the request is done
* @appinfo: the app info
* @uris: (nullable) (element-type utf8): a list of URIs to launch.
* @context: (nullable): the launch context
* @cancellable: (nullable): a [class@Gio.Cancellable]
* @callback: (scope async) (nullable): a [type@Gio.AsyncReadyCallback] to call
* when the request is done
* @user_data: (nullable): data to pass to @callback
*
* Async version of g_app_info_launch_uris().
* Async version of [method@Gio.AppInfo.launch_uris].
*
* The @callback is invoked immediately after the application launch, but it
* waits for activation in case of D-Busactivated applications and also provides
* extended error information for sandboxed applications, see notes for
* g_app_info_launch_default_for_uri_async().
* [func@Gio.AppInfo.launch_default_for_uri_async].
*
* Since: 2.60
**/
@ -733,13 +731,12 @@ g_app_info_launch_uris_async (GAppInfo *appinfo,
/**
* g_app_info_launch_uris_finish:
* @appinfo: a #GAppInfo
* @result: a #GAsyncResult
* @error: (nullable): a #GError
* @appinfo: the app info
* @result: the async result
*
* Finishes a g_app_info_launch_uris_async() operation.
* Finishes a [method@Gio.AppInfo.launch_uris_async] operation.
*
* Returns: %TRUE on successful launch, %FALSE otherwise.
* Returns: `TRUE` on successful launch, `FALSE` otherwise.
*
* Since: 2.60
*/
@ -765,12 +762,12 @@ g_app_info_launch_uris_finish (GAppInfo *appinfo,
/**
* g_app_info_should_show:
* @appinfo: a #GAppInfo.
* @appinfo: the app info
*
* Checks if the application info should be shown in menus that
* list available applications.
*
* Returns: %TRUE if the @appinfo should be shown, %FALSE otherwise.
* Returns: `TRUE` if the @appinfo should be shown, `FALSE` otherwise.
**/
gboolean
g_app_info_should_show (GAppInfo *appinfo)
@ -821,14 +818,16 @@ get_default_for_type_thread (GTask *task,
/**
* g_app_info_get_default_for_type_async:
* @content_type: the content type to find a #GAppInfo for
* @must_support_uris: if %TRUE, the #GAppInfo is expected to
* support URIs
* @cancellable: optional #GCancellable object, %NULL to ignore
* @callback: (nullable): a #GAsyncReadyCallback to call when the request is done
* @content_type: the content type to find a [iface@Gio.AppInfo] for
* @must_support_uris: if `TRUE`, the [iface@Gio.AppInfo] is expected to
* support URIs
* @cancellable: (nullable): a [class@Gio.Cancellable]
* @callback: (scope async) (nullable): a [type@Gio.AsyncReadyCallback] to call
* when the request is done
* @user_data: (nullable): data to pass to @callback
*
* Asynchronously gets the default #GAppInfo for a given content type.
* Asynchronously gets the default [iface@Gio.AppInfo] for a given content
* type.
*
* Since: 2.74
*/
@ -882,14 +881,15 @@ get_default_for_scheme_thread (GTask *task,
/**
* g_app_info_get_default_for_uri_scheme_async:
* @uri_scheme: a string containing a URI scheme.
* @cancellable: optional #GCancellable object, %NULL to ignore
* @callback: (nullable): a #GAsyncReadyCallback to call when the request is done
* @cancellable: (nullable): a [class@Gio.Cancellable]
* @callback: (scope async) (nullable): a [type@Gio.AsyncReadyCallback] to call
* when the request is done
* @user_data: (nullable): data to pass to @callback
*
* Asynchronously gets the default application for handling URIs with
* the given URI scheme. A URI scheme is the initial part
* of the URI, up to but not including the ':', e.g. "http",
* "ftp" or "sip".
* of the URI, up to but not including the `:`, e.g. `http`,
* `ftp` or `sip`.
*
* Since: 2.74
*/
@ -914,16 +914,16 @@ g_app_info_get_default_for_uri_scheme_async (const char *uri_scheme,
/**
* g_app_info_get_default_for_uri_scheme_finish:
* @result: a #GAsyncResult
* @error: (nullable): a #GError
* @result: the async result
*
* Finishes a default #GAppInfo lookup started by
* g_app_info_get_default_for_uri_scheme_async().
* Finishes a default [iface@Gio.AppInfo] lookup started by
* [func@Gio.AppInfo.get_default_for_uri_scheme_async].
*
* If no #GAppInfo is found, then @error will be set to %G_IO_ERROR_NOT_FOUND.
* If no [iface@Gio.AppInfo] is found, then @error will be set to
* [error@Gio.IOErrorEnum.NOT_FOUND].
*
* Returns: (transfer full): #GAppInfo for given @uri_scheme or
* %NULL on error.
* Returns: (transfer full): [iface@Gio.AppInfo] for given @uri_scheme or
* `NULL` on error.
*
* Since: 2.74
*/
@ -941,16 +941,16 @@ g_app_info_get_default_for_uri_scheme_finish (GAsyncResult *result,
/**
* g_app_info_get_default_for_type_finish:
* @result: a #GAsyncResult
* @error: (nullable): a #GError
* @result: the async result
*
* Finishes a default #GAppInfo lookup started by
* g_app_info_get_default_for_type_async().
* Finishes a default [iface@Gio.AppInfo] lookup started by
* [func@Gio.AppInfo.get_default_for_type_async].
*
* If no #GAppInfo is found, then @error will be set to %G_IO_ERROR_NOT_FOUND.
* If no #[iface@Gio.AppInfo] is found, then @error will be set to
* [error@Gio.IOErrorEnum.NOT_FOUND].
*
* Returns: (transfer full): #GAppInfo for given @content_type or
* %NULL on error.
* Returns: (transfer full): [iface@Gio.AppInfo] for given @content_type or
* `NULL` on error.
*
* Since: 2.74
*/
@ -969,19 +969,17 @@ g_app_info_get_default_for_type_finish (GAsyncResult *result,
/**
* g_app_info_launch_default_for_uri:
* @uri: the uri to show
* @context: (nullable): an optional #GAppLaunchContext
* @error: (nullable): return location for an error, or %NULL
* @context: (nullable): optional launch context
*
* Utility function that launches the default application
* registered to handle the specified uri. Synchronous I/O
* is done on the uri to detect the type of the file if
* required.
* Utility function that launches the default application registered to handle
* the specified uri. Synchronous I/O is done on the uri to detect the type of
* the file if required.
*
* The D-Busactivated applications don't have to be started if your application
* The D-Busactivated applications dont have to be started if your application
* terminates too soon after this function. To prevent this, use
* g_app_info_launch_default_for_uri_async() instead.
* [func@Gio.AppInfo.launch_default_for_uri_async] instead.
*
* Returns: %TRUE on success, %FALSE on error.
* Returns: `TRUE` on success, `FALSE` on error.
**/
gboolean
g_app_info_launch_default_for_uri (const char *uri,
@ -1197,17 +1195,17 @@ launch_default_app_for_uri_cb (GObject *object,
/**
* g_app_info_launch_default_for_uri_async:
* @uri: the uri to show
* @context: (nullable): an optional #GAppLaunchContext
* @cancellable: (nullable): a #GCancellable
* @callback: (nullable): a #GAsyncReadyCallback to call when the request is done
* @context: (nullable): optional launch context
* @cancellable: (nullable): a [class@Gio.Cancellable]
* @callback: (scope async) (nullable): a [type@Gio.AsyncReadyCallback] to call
* when the request is done
* @user_data: (nullable): data to pass to @callback
*
* Async version of g_app_info_launch_default_for_uri().
* Async version of [func@Gio.AppInfo.launch_default_for_uri].
*
* This version is useful if you are interested in receiving
* error information in the case where the application is
* sandboxed and the portal may present an application chooser
* dialog to the user.
* This version is useful if you are interested in receiving error information
* in the case where the application is sandboxed and the portal may present an
* application chooser dialog to the user.
*
* This is also useful if you want to be sure that the D-Busactivated
* applications are really started before termination and if you are interested
@ -1258,12 +1256,11 @@ g_app_info_launch_default_for_uri_async (const char *uri,
/**
* g_app_info_launch_default_for_uri_finish:
* @result: a #GAsyncResult
* @error: (nullable): return location for an error, or %NULL
* @result: the async result
*
* Finishes an asynchronous launch-default-for-uri operation.
*
* Returns: %TRUE if the launch was successful, %FALSE if @error is set
* Returns: `TRUE` if the launch was successful, `FALSE` if @error is set
*
* Since: 2.50
*/
@ -1278,12 +1275,12 @@ g_app_info_launch_default_for_uri_finish (GAsyncResult *result,
/**
* g_app_info_can_delete:
* @appinfo: a #GAppInfo
* @appinfo: the app info
*
* Obtains the information whether the #GAppInfo can be deleted.
* See g_app_info_delete().
* Obtains the information whether the [iface@Gio.AppInfo] can be deleted.
* See [method@Gio.AppInfo.delete].
*
* Returns: %TRUE if @appinfo can be deleted
* Returns: `TRUE` if @appinfo can be deleted
*
* Since: 2.20
*/
@ -1305,15 +1302,15 @@ g_app_info_can_delete (GAppInfo *appinfo)
/**
* g_app_info_delete: (virtual do_delete)
* @appinfo: a #GAppInfo
* @appinfo: the app info
*
* Tries to delete a #GAppInfo.
* Tries to delete a [iface@Gio.AppInfo].
*
* On some platforms, there may be a difference between user-defined
* #GAppInfos which can be deleted, and system-wide ones which cannot.
* See g_app_info_can_delete().
* [iface@Gio.AppInfo]s which can be deleted, and system-wide ones which cannot.
* See [method@Gio.AppInfo.can_delete].
*
* Returns: %TRUE if @appinfo has been deleted
* Returns: `TRUE` if @appinfo has been deleted
*
* Since: 2.20
*/
@ -1348,9 +1345,10 @@ G_DEFINE_TYPE_WITH_PRIVATE (GAppLaunchContext, g_app_launch_context, G_TYPE_OBJE
* g_app_launch_context_new:
*
* Creates a new application launch context. This is not normally used,
* instead you instantiate a subclass of this, such as #GdkAppLaunchContext.
* instead you instantiate a subclass of this, such as
* [`GdkAppLaunchContext`](https://docs.gtk.org/gdk4/class.AppLaunchContext.html).
*
* Returns: a #GAppLaunchContext.
* Returns: a launch context.
**/
GAppLaunchContext *
g_app_launch_context_new (void)
@ -1380,9 +1378,9 @@ g_app_launch_context_class_init (GAppLaunchContextClass *klass)
* @context: the object emitting the signal
* @startup_notify_id: the startup notification id for the failed launch
*
* The #GAppLaunchContext::launch-failed signal is emitted when a #GAppInfo launch
* fails. The startup notification id is provided, so that the launcher
* can cancel the startup notification.
* The [signal@Gio.AppLaunchContext::launch-failed] signal is emitted when a
* [iface@Gio.AppInfo] launch fails. The startup notification id is provided,
* so that the launcher can cancel the startup notification.
*
* Because a launch operation may involve spawning multiple instances of the
* target application, you should expect this signal to be emitted multiple
@ -1400,14 +1398,14 @@ g_app_launch_context_class_init (GAppLaunchContextClass *klass)
/**
* GAppLaunchContext::launch-started:
* @context: the object emitting the signal
* @info: the #GAppInfo that is about to be launched
* @info: the [iface@Gio.AppInfo] that is about to be launched
* @platform_data: (nullable): additional platform-specific data for this launch
*
* The #GAppLaunchContext::launch-started signal is emitted when a #GAppInfo is
* about to be launched. If non-null the @platform_data is an
* GVariant dictionary mapping strings to variants (ie `a{sv}`), which
* contains additional, platform-specific data about this launch. On
* UNIX, at least the `startup-notification-id` keys will be
* The [signal@Gio.AppLaunchContext::launch-started] signal is emitted when a
* [iface@Gio.AppInfo] is about to be launched. If non-null the
* @platform_data is an GVariant dictionary mapping strings to variants
* (ie `a{sv}`), which contains additional, platform-specific data about this
* launch. On UNIX, at least the `startup-notification-id` keys will be
* present.
*
* The value of the `startup-notification-id` key (type `s`) is a startup
@ -1415,8 +1413,9 @@ g_app_launch_context_class_init (GAppLaunchContextClass *klass)
* specification](https://specifications.freedesktop.org/startup-notification-spec/startup-notification-0.1.txt).
* It allows tracking the progress of the launchee through startup.
*
* It is guaranteed that this signal is followed by either a #GAppLaunchContext::launched or
* #GAppLaunchContext::launch-failed signal.
* It is guaranteed that this signal is followed by either a
* [signal@Gio.AppLaunchContext::launched] or
* [signal@Gio.AppLaunchContext::launch-failed] signal.
*
* Because a launch operation may involve spawning multiple instances of the
* target application, you should expect this signal to be emitted multiple
@ -1439,11 +1438,11 @@ g_app_launch_context_class_init (GAppLaunchContextClass *klass)
/**
* GAppLaunchContext::launched:
* @context: the object emitting the signal
* @info: the #GAppInfo that was just launched
* @info: the [iface@Gio.AppInfo] that was just launched
* @platform_data: additional platform-specific data for this launch
*
* The #GAppLaunchContext::launched signal is emitted when a #GAppInfo is successfully
* launched.
* The [signal@Gio.AppLaunchContext::launched] signal is emitted when a
* [iface@Gio.AppInfo] is successfully launched.
*
* Because a launch operation may involve spawning multiple instances of the
* target application, you should expect this signal to be emitted multiple
@ -1454,14 +1453,15 @@ g_app_launch_context_class_init (GAppLaunchContextClass *klass)
* platform-specific data about this launch. On UNIX, at least the
* `pid` and `startup-notification-id` keys will be present.
*
* Since 2.72 the `pid` may be 0 if the process id wasn't known (for
* Since 2.72 the `pid` may be 0 if the process id wasnt known (for
* example if the process was launched via D-Bus). The `pid` may not be
* set at all in subsequent releases.
*
* On Windows, `pid` is guaranteed to be valid only for the duration of the
* #GAppLaunchContext::launched signal emission; after the signal is emitted,
* GLib will call g_spawn_close_pid(). If you need to keep the #GPid after the
* signal has been emitted, then you can duplicate `pid` using `DuplicateHandle()`.
* [signal@Gio.AppLaunchContext::launched] signal emission; after the signal
* is emitted, GLib will call [func@GLib.spawn_close_pid]. If you need to
* keep the [alias@GLib.Pid] after the signal has been emitted, then you can
* duplicate `pid` using `DuplicateHandle()`.
*
* Since: 2.36
*/
@ -1486,12 +1486,12 @@ g_app_launch_context_init (GAppLaunchContext *context)
/**
* g_app_launch_context_setenv:
* @context: a #GAppLaunchContext
* @context: the launch context
* @variable: (type filename): the environment variable to set
* @value: (type filename): the value for to set the variable to.
*
* Arranges for @variable to be set to @value in the child's
* environment when @context is used to launch an application.
* Arranges for @variable to be set to @value in the childs environment when
* @context is used to launch an application.
*
* Since: 2.32
*/
@ -1513,11 +1513,11 @@ g_app_launch_context_setenv (GAppLaunchContext *context,
/**
* g_app_launch_context_unsetenv:
* @context: a #GAppLaunchContext
* @context: the launch context
* @variable: (type filename): the environment variable to remove
*
* Arranges for @variable to be unset in the child's environment
* when @context is used to launch an application.
* Arranges for @variable to be unset in the childs environment when @context
* is used to launch an application.
*
* Since: 2.32
*/
@ -1537,15 +1537,15 @@ g_app_launch_context_unsetenv (GAppLaunchContext *context,
/**
* g_app_launch_context_get_environment:
* @context: a #GAppLaunchContext
* @context: the launch context
*
* Gets the complete environment variable list to be passed to
* the child process when @context is used to launch an application.
* This is a %NULL-terminated array of strings, where each string has
* This is a `NULL`-terminated array of strings, where each string has
* the form `KEY=VALUE`.
*
* Returns: (array zero-terminated=1) (element-type filename) (transfer full):
* the child's environment
* the childs environment
*
* Since: 2.32
*/
@ -1562,9 +1562,9 @@ g_app_launch_context_get_environment (GAppLaunchContext *context)
/**
* g_app_launch_context_get_display:
* @context: a #GAppLaunchContext
* @info: a #GAppInfo
* @files: (element-type GFile): a #GList of #GFile objects
* @context: the launch context
* @info: the app info
* @files: (element-type GFile): a list of [iface@Gio.File] objects
*
* Gets the display string for the @context. This is used to ensure new
* applications are started on the same display as the launching
@ -1592,9 +1592,9 @@ g_app_launch_context_get_display (GAppLaunchContext *context,
/**
* g_app_launch_context_get_startup_notify_id:
* @context: a #GAppLaunchContext
* @info: a #GAppInfo
* @files: (element-type GFile): a #GList of #GFile objects
* @context: the launch context
* @info: the app info
* @files: (element-type GFile): list of [iface@Gio.File] objects
*
* Initiates startup notification for the application and returns the
* `XDG_ACTIVATION_TOKEN` or `DESKTOP_STARTUP_ID` for the launched operation,
@ -1610,8 +1610,8 @@ g_app_launch_context_get_display (GAppLaunchContext *context,
*
* Support for the XDG Activation Protocol was added in GLib 2.76.
*
* Returns: (nullable): a startup notification ID for the application, or %NULL if
* not supported.
* Returns: (nullable): a startup notification ID for the application, or `NULL` if
* not supported.
**/
char *
g_app_launch_context_get_startup_notify_id (GAppLaunchContext *context,
@ -1634,11 +1634,13 @@ g_app_launch_context_get_startup_notify_id (GAppLaunchContext *context,
/**
* g_app_launch_context_launch_failed:
* @context: a #GAppLaunchContext.
* @startup_notify_id: the startup notification id that was returned by g_app_launch_context_get_startup_notify_id().
* @context: the launch context
* @startup_notify_id: the startup notification id that was returned by
* [method@Gio.AppLaunchContext.get_startup_notify_id].
*
* Called when an application has failed to launch, so that it can cancel
* the application startup notification started in g_app_launch_context_get_startup_notify_id().
* the application startup notification started in
* [method@Gio.AppLaunchContext.get_startup_notify_id].
*
**/
void
@ -1756,7 +1758,7 @@ g_app_info_monitor_class_init (GAppInfoMonitorClass *class)
* Gets the #GAppInfoMonitor for the current thread-default main
* context.
*
* The #GAppInfoMonitor will emit a "changed" signal in the
* The #GAppInfoMonitor will emit a changed signal in the
* thread-default main context whenever the list of installed
* applications (as reported by g_app_info_get_all()) may have changed.
*

View File

@ -49,32 +49,47 @@ typedef struct _GAppLaunchContextPrivate GAppLaunchContextPrivate;
/**
* GAppInfoIface:
* @g_iface: The parent interface.
* @dup: Copies a #GAppInfo.
* @equal: Checks two #GAppInfos for equality.
* @get_id: Gets a string identifier for a #GAppInfo.
* @get_name: Gets the name of the application for a #GAppInfo.
* @get_description: Gets a short description for the application described by the #GAppInfo.
* @get_executable: Gets the executable name for the #GAppInfo.
* @get_icon: Gets the #GIcon for the #GAppInfo.
* @launch: Launches an application specified by the #GAppInfo.
* @supports_uris: Indicates whether the application specified supports launching URIs.
* @supports_files: Indicates whether the application specified accepts filename arguments.
* @dup: Copies a [iface@Gio.AppInfo].
* @equal: Checks two [iface@Gio.AppInfo]s for equality.
* @get_id: Gets a string identifier for a [iface@Gio.AppInfo].
* @get_name: Gets the name of the application for a [iface@Gio.AppInfo].
* @get_description: Gets a short description for the application described by
* the [iface@Gio.AppInfo].
* @get_executable: Gets the executable name for the [iface@Gio.AppInfo].
* @get_icon: Gets the [iface@Gio.Icon] for the [iface@Gio.AppInfo].
* @launch: Launches an application specified by the [iface@Gio.AppInfo].
* @supports_uris: Indicates whether the application specified supports
* launching URIs.
* @supports_files: Indicates whether the application specified accepts
* filename arguments.
* @launch_uris: Launches an application with a list of URIs.
* @should_show: Returns whether an application should be shown (e.g. when getting a list of installed applications).
* [FreeDesktop.Org Startup Notification Specification](http://standards.freedesktop.org/startup-notification-spec/startup-notification-latest.txt).
* @set_as_default_for_type: Sets an application as default for a given content type.
* @set_as_default_for_extension: Sets an application as default for a given file extension.
* @add_supports_type: Adds to the #GAppInfo information about supported file types.
* @can_remove_supports_type: Checks for support for removing supported file types from a #GAppInfo.
* @remove_supports_type: Removes a supported application type from a #GAppInfo.
* @can_delete: Checks if a #GAppInfo can be deleted. Since 2.20
* @do_delete: Deletes a #GAppInfo. Since 2.20
* @get_commandline: Gets the commandline for the #GAppInfo. Since 2.20
* @get_display_name: Gets the display name for the #GAppInfo. Since 2.24
* @set_as_last_used_for_type: Sets the application as the last used. See g_app_info_set_as_last_used_for_type().
* @get_supported_types: Retrieves the list of content types that @app_info claims to support.
* @launch_uris_async: Asynchronously launches an application with a list of URIs. (Since: 2.60)
* @launch_uris_finish: Finishes an operation started with @launch_uris_async. (Since: 2.60)
* @should_show: Returns whether an application should be shown (e.g. when
* getting a list of installed applications).
* [FreeDesktop.Org Startup Notification Specification](http://standards.freedesktop.org/startup-notification-spec/startup-notification-latest.txt).
* @set_as_default_for_type: Sets an application as default for a given content
* type.
* @set_as_default_for_extension: Sets an application as default for a given
* file extension.
* @add_supports_type: Adds to the [iface@Gio.AppInfo] information about
* supported file types.
* @can_remove_supports_type: Checks for support for removing supported file
* types from a [iface@Gio.AppInfo].
* @remove_supports_type: Removes a supported application type from a
* [iface@Gio.AppInfo].
* @can_delete: Checks if a [iface@Gio.AppInfo] can be deleted. (Since 2.20)
* @do_delete: Deletes a [iface@Gio.AppInfo]. (Since 2.20)
* @get_commandline: Gets the commandline for the [iface@Gio.AppInfo].
* (Since 2.20)
* @get_display_name: Gets the display name for the [iface@Gio.AppInfo].
* (Since 2.24)
* @set_as_last_used_for_type: Sets the application as the last used. See
* [method@Gio.AppInfo.set_as_last_used_for_type].
* @get_supported_types: Retrieves the list of content types that @app_info
* claims to support.
* @launch_uris_async: Asynchronously launches an application with a list of
* URIs. (Since: 2.60)
* @launch_uris_finish: Finishes an operation started with @launch_uris_async.
* (Since: 2.60)
* Application Information interface, for operating system portability.
*/

View File

@ -1,6 +1,7 @@
/* GIO - GLib Input, Output and Streaming Library
*
* Copyright (C) 2006-2007 Red Hat, Inc.
* Copyright (C) 2022-2024 Canonical, Ltd.
*
* SPDX-License-Identifier: LGPL-2.1-or-later
*
@ -18,6 +19,7 @@
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
*
* Author: Alexander Larsson <alexl@redhat.com>
* Author: Marco Trevisan <marco.trevisan@canonical.com>
*/
#include "config.h"
@ -27,6 +29,7 @@
#include "gcancellable.h"
#include "glibintl.h"
static void g_cancellable_real_cancel (GCancellable *);
/**
* GCancellable:
@ -45,14 +48,12 @@ enum {
struct _GCancellablePrivate
{
/* Atomic so that g_cancellable_is_cancelled does not require holding the mutex. */
/* Atomic so that we don't require holding global mutexes for independent ops. */
gboolean cancelled;
/* Access to fields below is protected by cancellable_mutex. */
guint cancelled_running : 1;
guint cancelled_running_waiting : 1;
unsigned cancelled_emissions;
unsigned cancelled_emissions_waiting : 1;
int cancelled_running;
/* Access to fields below is protected by cancellable's mutex. */
GMutex mutex;
guint fd_refcount;
GWakeup *wakeup;
};
@ -62,7 +63,6 @@ static guint signals[LAST_SIGNAL] = { 0 };
G_DEFINE_TYPE_WITH_PRIVATE (GCancellable, g_cancellable, G_TYPE_OBJECT)
static GPrivate current_cancellable;
static GMutex cancellable_mutex;
static GCond cancellable_cond;
static void
@ -70,9 +70,15 @@ g_cancellable_finalize (GObject *object)
{
GCancellable *cancellable = G_CANCELLABLE (object);
/* We're at finalization phase, so only one thread can be here.
* Thus there's no need to lock. In case something is locking us, then we've
* a bug, and g_mutex_clear() will make this clear aborting.
*/
if (cancellable->priv->wakeup)
GLIB_PRIVATE_CALL (g_wakeup_free) (cancellable->priv->wakeup);
g_mutex_clear (&cancellable->priv->mutex);
G_OBJECT_CLASS (g_cancellable_parent_class)->finalize (object);
}
@ -81,6 +87,8 @@ g_cancellable_class_init (GCancellableClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
klass->cancel = g_cancellable_real_cancel;
gobject_class->finalize = g_cancellable_finalize;
/**
@ -154,6 +162,8 @@ static void
g_cancellable_init (GCancellable *cancellable)
{
cancellable->priv = g_cancellable_get_instance_private (cancellable);
g_mutex_init (&cancellable->priv->mutex);
}
/**
@ -265,28 +275,17 @@ g_cancellable_reset (GCancellable *cancellable)
g_return_if_fail (G_IS_CANCELLABLE (cancellable));
g_mutex_lock (&cancellable_mutex);
priv = cancellable->priv;
while (priv->cancelled_running || priv->cancelled_emissions > 0)
{
if (priv->cancelled_running)
priv->cancelled_running_waiting = TRUE;
g_mutex_lock (&priv->mutex);
if (priv->cancelled_emissions > 0)
priv->cancelled_emissions_waiting = TRUE;
g_cond_wait (&cancellable_cond, &cancellable_mutex);
}
if (g_atomic_int_exchange (&priv->cancelled, FALSE))
if (g_atomic_int_compare_and_exchange (&priv->cancelled, TRUE, FALSE))
{
if (priv->wakeup)
GLIB_PRIVATE_CALL (g_wakeup_acknowledge) (priv->wakeup);
}
g_mutex_unlock (&cancellable_mutex);
g_mutex_unlock (&priv->mutex);
}
/**
@ -404,26 +403,29 @@ g_cancellable_get_fd (GCancellable *cancellable)
gboolean
g_cancellable_make_pollfd (GCancellable *cancellable, GPollFD *pollfd)
{
GCancellablePrivate *priv;
g_return_val_if_fail (pollfd != NULL, FALSE);
if (cancellable == NULL)
return FALSE;
g_return_val_if_fail (G_IS_CANCELLABLE (cancellable), FALSE);
g_mutex_lock (&cancellable_mutex);
priv = cancellable->priv;
cancellable->priv->fd_refcount++;
g_mutex_lock (&priv->mutex);
if (cancellable->priv->wakeup == NULL)
if ((priv->fd_refcount++) == 0)
{
cancellable->priv->wakeup = GLIB_PRIVATE_CALL (g_wakeup_new) ();
priv->wakeup = GLIB_PRIVATE_CALL (g_wakeup_new) ();
if (g_atomic_int_get (&cancellable->priv->cancelled))
GLIB_PRIVATE_CALL (g_wakeup_signal) (cancellable->priv->wakeup);
if (g_atomic_int_get (&priv->cancelled))
GLIB_PRIVATE_CALL (g_wakeup_signal) (priv->wakeup);
}
GLIB_PRIVATE_CALL (g_wakeup_get_pollfd) (cancellable->priv->wakeup, pollfd);
g_assert (priv->wakeup);
GLIB_PRIVATE_CALL (g_wakeup_get_pollfd) (priv->wakeup, pollfd);
g_mutex_unlock (&cancellable_mutex);
g_mutex_unlock (&priv->mutex);
return TRUE;
}
@ -447,26 +449,22 @@ g_cancellable_make_pollfd (GCancellable *cancellable, GPollFD *pollfd)
void
g_cancellable_release_fd (GCancellable *cancellable)
{
GCancellablePrivate *priv;
if (cancellable == NULL)
return;
g_return_if_fail (G_IS_CANCELLABLE (cancellable));
priv = cancellable->priv;
g_mutex_lock (&cancellable->priv->mutex);
g_mutex_lock (&cancellable_mutex);
g_assert (priv->fd_refcount > 0);
g_assert (cancellable->priv->fd_refcount > 0);
priv->fd_refcount--;
if (priv->fd_refcount == 0)
if ((cancellable->priv->fd_refcount--) == 1)
{
GLIB_PRIVATE_CALL (g_wakeup_free) (priv->wakeup);
priv->wakeup = NULL;
GLIB_PRIVATE_CALL (g_wakeup_free) (cancellable->priv->wakeup);
cancellable->priv->wakeup = NULL;
}
g_mutex_unlock (&cancellable_mutex);
g_mutex_unlock (&cancellable->priv->mutex);
}
/**
@ -493,39 +491,52 @@ g_cancellable_release_fd (GCancellable *cancellable)
void
g_cancellable_cancel (GCancellable *cancellable)
{
GCancellablePrivate *priv;
if (cancellable == NULL || g_cancellable_is_cancelled (cancellable))
return;
G_CANCELLABLE_GET_CLASS (cancellable)->cancel (cancellable);
if (G_CANCELLABLE_GET_CLASS (cancellable)->cancel != g_cancellable_real_cancel)
{
if (!g_atomic_int_get (&cancellable->priv->cancelled))
{
g_critical ("Overridden cancel function did not call parent hook");
g_cancellable_real_cancel (cancellable);
}
}
}
static void
g_cancellable_real_cancel (GCancellable *cancellable)
{
GCancellablePrivate *priv;
priv = cancellable->priv;
g_mutex_lock (&cancellable_mutex);
/* We add a reference before locking, to avoid that potential toggle
* notifications on the object might happen while we're locked.
*/
g_object_ref (cancellable);
g_mutex_lock (&priv->mutex);
if (g_atomic_int_exchange (&priv->cancelled, TRUE))
if (!g_atomic_int_compare_and_exchange (&priv->cancelled, FALSE, TRUE))
{
g_mutex_unlock (&cancellable_mutex);
g_mutex_unlock (&priv->mutex);
g_object_unref (cancellable);
return;
}
priv->cancelled_running = TRUE;
g_atomic_int_inc (&priv->cancelled_running);
if (priv->wakeup)
GLIB_PRIVATE_CALL (g_wakeup_signal) (priv->wakeup);
g_mutex_unlock (&cancellable_mutex);
g_object_ref (cancellable);
g_signal_emit (cancellable, signals[CANCELLED], 0);
g_mutex_lock (&cancellable_mutex);
priv->cancelled_running = FALSE;
if (priv->cancelled_running_waiting)
if (g_atomic_int_dec_and_test (&priv->cancelled_running))
g_cond_broadcast (&cancellable_cond);
priv->cancelled_running_waiting = FALSE;
g_mutex_unlock (&cancellable_mutex);
g_mutex_unlock (&priv->mutex);
g_object_unref (cancellable);
}
@ -571,7 +582,7 @@ g_cancellable_connect (GCancellable *cancellable,
g_return_val_if_fail (G_IS_CANCELLABLE (cancellable), 0);
g_mutex_lock (&cancellable_mutex);
g_mutex_lock (&cancellable->priv->mutex);
if (g_atomic_int_get (&cancellable->priv->cancelled))
{
@ -581,23 +592,10 @@ g_cancellable_connect (GCancellable *cancellable,
_callback = (void *)callback;
id = 0;
cancellable->priv->cancelled_emissions++;
g_mutex_unlock (&cancellable_mutex);
_callback (cancellable, data);
if (data_destroy_func)
data_destroy_func (data);
g_mutex_lock (&cancellable_mutex);
if (cancellable->priv->cancelled_emissions_waiting)
g_cond_broadcast (&cancellable_cond);
cancellable->priv->cancelled_emissions--;
g_mutex_unlock (&cancellable_mutex);
}
else
{
@ -605,10 +603,9 @@ g_cancellable_connect (GCancellable *cancellable,
callback, data,
(GClosureNotify) data_destroy_func,
G_CONNECT_DEFAULT);
g_mutex_unlock (&cancellable_mutex);
}
g_mutex_unlock (&cancellable->priv->mutex);
return id;
}
@ -644,33 +641,26 @@ g_cancellable_disconnect (GCancellable *cancellable,
if (handler_id == 0 || cancellable == NULL)
return;
g_mutex_lock (&cancellable_mutex);
priv = cancellable->priv;
while (priv->cancelled_running || priv->cancelled_emissions)
{
if (priv->cancelled_running)
priv->cancelled_running_waiting = TRUE;
g_mutex_lock (&priv->mutex);
if (priv->cancelled_emissions)
priv->cancelled_emissions_waiting = TRUE;
while (g_atomic_int_get (&priv->cancelled_running) != 0)
g_cond_wait (&cancellable_cond, &priv->mutex);
g_cond_wait (&cancellable_cond, &cancellable_mutex);
}
g_mutex_unlock (&priv->mutex);
g_signal_handler_disconnect (cancellable, handler_id);
g_mutex_unlock (&cancellable_mutex);
}
typedef struct {
GSource source;
/* Atomic: */
GCancellable *cancellable;
gulong cancelled_handler;
/* Protected by cancellable_mutex: */
gboolean resurrected_during_cancellation;
/* Atomic: */
gboolean cancelled_callback_called;
} GCancellableSource;
/*
@ -691,26 +681,32 @@ cancellable_source_cancelled (GCancellable *cancellable,
GSource *source = user_data;
GCancellableSource *cancellable_source = (GCancellableSource *) source;
g_mutex_lock (&cancellable_mutex);
/* Drop the reference added in cancellable_source_dispose(); see the comment there.
* The reference must be dropped after unlocking @cancellable_mutex since
* it could be the final reference, and the dispose function takes
* @cancellable_mutex. */
if (cancellable_source->resurrected_during_cancellation)
{
cancellable_source->resurrected_during_cancellation = FALSE;
g_mutex_unlock (&cancellable_mutex);
g_source_unref (source);
return;
}
g_source_ref (source);
g_mutex_unlock (&cancellable_mutex);
g_source_set_ready_time (source, 0);
g_assert (g_atomic_int_compare_and_exchange (
&cancellable_source->cancelled_callback_called, FALSE, TRUE));
g_source_unref (source);
}
static gboolean
cancellable_source_prepare (GSource *source,
gint *timeout)
{
GCancellableSource *cancellable_source = (GCancellableSource *) source;
GCancellable *cancellable;
if (timeout)
*timeout = -1;
cancellable = g_atomic_pointer_get (&cancellable_source->cancellable);
if (cancellable && !g_atomic_int_get (&cancellable->priv->cancelled_running))
g_atomic_int_set (&cancellable_source->cancelled_callback_called, FALSE);
return FALSE;
}
static gboolean
cancellable_source_dispatch (GSource *source,
GSourceFunc callback,
@ -727,12 +723,13 @@ static void
cancellable_source_dispose (GSource *source)
{
GCancellableSource *cancellable_source = (GCancellableSource *)source;
GCancellable *cancellable;
g_mutex_lock (&cancellable_mutex);
cancellable = g_atomic_pointer_exchange (&cancellable_source->cancellable, NULL);
if (cancellable_source->cancellable)
if (cancellable)
{
if (cancellable_source->cancellable->priv->cancelled_running)
if (g_atomic_int_get (&cancellable->priv->cancelled_running))
{
/* There can be a race here: if thread A has called
* g_cancellable_cancel() and has got as far as committing to call
@ -744,21 +741,21 @@ cancellable_source_dispose (GSource *source)
* will then be left in a state where its committed to using a
* dangling GCancellableSource pointer.
*
* Eliminate that race by resurrecting the #GSource temporarily, and
* then dropping that reference in cancellable_source_cancelled(),
* which should be guaranteed to fire because were inside a
* @cancelled_running block.
* Eliminate that race by waiting to ensure that our cancelled
* callback has been called, keeping a temporary ref, so that
* there's no risk that we're unreffing something that is still
* going to be used.
*/
g_source_ref (source);
cancellable_source->resurrected_during_cancellation = TRUE;
while (!g_atomic_int_get (&cancellable_source->cancelled_callback_called))
;
g_source_unref (source);
}
g_clear_signal_handler (&cancellable_source->cancelled_handler,
cancellable_source->cancellable);
g_clear_object (&cancellable_source->cancellable);
g_clear_signal_handler (&cancellable_source->cancelled_handler, cancellable);
g_object_unref (cancellable);
}
g_mutex_unlock (&cancellable_mutex);
}
static gboolean
@ -787,7 +784,7 @@ cancellable_source_closure_callback (GCancellable *cancellable,
static GSourceFuncs cancellable_source_funcs =
{
NULL,
cancellable_source_prepare,
NULL,
cancellable_source_dispatch,
NULL,

View File

@ -54,6 +54,7 @@ struct _GCancellableClass
GObjectClass parent_class;
void (* cancelled) (GCancellable *cancellable);
void (* cancel) (GCancellable *cancellable);
/*< private >*/
/* Padding for future expansion */
@ -61,7 +62,6 @@ struct _GCancellableClass
void (*_g_reserved2) (void);
void (*_g_reserved3) (void);
void (*_g_reserved4) (void);
void (*_g_reserved5) (void);
};
GIO_AVAILABLE_IN_ALL

338
gio/gcancellablechild.c Normal file
View File

@ -0,0 +1,338 @@
/* GIO - GLib Input, Output and Streaming Library
*
* Copyright (C) 2022 Canonical Ltd.
*
* SPDX-License-Identifier: LGPL-2.1-or-later
*
* 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/>.
*
* Author: Marco Trevisan <marco.trevisan@canonical.com>
*/
#include "config.h"
#include "gcancellablechild.h"
#include "glibintl.h"
/**
* SECTION:gcancellablechild
* @short_description: Thread-safe Operation Cancellation Stack
* @include: gio/gio.h
*
* GCancellableChild is an implementation of #GCancellable that allows to
* monitor a parent cancellable to allow cancellation of synchronous and
* asynchronous operations when the parent cancellable gets cancelled.
*
* This allows to safely create a chain of cancellable's that are depending
* on a main #GCancellable cancellation.
*
* So for instance an object may hold a global cancellable to be cancelled
* when the object gets cancelled, while creating new child cancellable's
* for each single operation it needs to do that are depending on the
* main #GCancellable.
*/
typedef struct
{
GCancellableClass parent_class;
} GCancellableChildClass;
typedef struct {
GCancellableChild *self; /* atomic */
gulong id;
} ConnectionData;
struct _GCancellableChild
{
GCancellable parent_instance;
GCancellable *parent_cancellable;
ConnectionData *connection_data;
};
enum
{
PROP_0,
PROP_PARENT,
};
G_DEFINE_FINAL_TYPE (GCancellableChild, g_cancellable_child, G_TYPE_CANCELLABLE)
static void
connection_data_free (ConnectionData *data)
{
g_atomic_pointer_set (&data->self, NULL);
g_free (data);
}
typedef struct {
GWeakRef cancellable_ref;
gulong connection_id;
} IdleData;
static void
idle_data_free (IdleData *data)
{
g_weak_ref_clear (&data->cancellable_ref);
g_free (data);
}
static gboolean
on_parent_cancellable_cancel_idle (IdleData *idle_data)
{
GCancellable *cancellable = g_weak_ref_get (&idle_data->cancellable_ref);
if (cancellable)
{
g_cancellable_disconnect (cancellable, idle_data->connection_id);
g_object_unref (cancellable);
}
return G_SOURCE_REMOVE;
}
static void
disconnect_from_parent_idle (GCancellable *parent,
gulong connection_id)
{
IdleData *idle_data = g_new0 (IdleData, 1);
GSource *source = g_idle_source_new ();
g_weak_ref_init (&idle_data->cancellable_ref, parent);
idle_data->connection_id = connection_id;
g_source_set_priority (source, G_PRIORITY_HIGH_IDLE);
g_source_set_callback (source,
G_SOURCE_FUNC (on_parent_cancellable_cancel_idle),
idle_data,
(GDestroyNotify) idle_data_free);
g_source_set_static_name (source, "[gio] cancellable_child_disconnect_idle");
g_source_attach (source, g_main_context_get_thread_default ());
g_source_unref (source);
}
static void
on_parent_cancellable_cancelled (GCancellable *parent_cancellable,
gpointer data)
{
ConnectionData *connection_data = data;
GCancellableChild *self = NULL;
/* Let's avoid that any other thread may dispose us while processing this */
g_set_object (&self, g_atomic_pointer_exchange (&connection_data->self, NULL));
if (self &&
g_atomic_pointer_compare_and_exchange (&self->connection_data,
connection_data, NULL))
{
/* Cancellable may have been got cancelled in another thread, so we need
* to ensure that we act once the cancellable has been released.
*/
disconnect_from_parent_idle (parent_cancellable, connection_data->id);
g_cancellable_cancel (G_CANCELLABLE (self));
}
g_clear_object (&self);
}
/**
* g_cancellable_child_new:
* @parent: the parent #GCancellable to use
*
* Creates a new #GCancellableChild object with a @parent as
* parent cancellable that will be monitored for cancellation.
*
* If @parent is already cancelled, the newly created cancellable will
* be cancelled immediately.
*
* Returns: a #GCancellableChild.
*
* Since: 2.76
*/
GCancellableChild *
g_cancellable_child_new (GCancellable *parent)
{
g_return_val_if_fail (G_IS_CANCELLABLE (parent), NULL);
return g_object_new (G_TYPE_CANCELLABLE_CHILD,
"parent", parent,
NULL);
}
/**
* g_cancellable_child_get_parent:
* @cancellable_child: the parent #GCancellable to use
*
* Gets the parent @cancellable_child's #GCancellable
*
* Returns: (transfer none): @cancellable_child's parent #GCancellable
*
* Since: 2.76
*/
GCancellable *
g_cancellable_child_get_parent (GCancellableChild *cancellable_child)
{
g_return_val_if_fail (G_IS_CANCELLABLE_CHILD (cancellable_child), NULL);
return cancellable_child->parent_cancellable;
}
static void
g_cancellable_child_cancel (GCancellable *cancellable)
{
GCancellableChild *self = G_CANCELLABLE_CHILD (cancellable);
ConnectionData *connection_data;
connection_data = g_atomic_pointer_exchange (&self->connection_data, NULL);
if (connection_data)
g_cancellable_disconnect (self->parent_cancellable, connection_data->id);
G_CANCELLABLE_CLASS (g_cancellable_child_parent_class)->cancel (cancellable);
}
static void
g_cancellable_child_dispose (GObject *object)
{
GCancellableChild *self = G_CANCELLABLE_CHILD (object);
ConnectionData *connection_data;
connection_data = g_atomic_pointer_exchange (&self->connection_data, NULL);
if (connection_data &&
g_atomic_pointer_compare_and_exchange (&connection_data->self, self, NULL))
{
gulong connection_id = connection_data->id;
if (G_UNLIKELY (g_cancellable_is_cancelled (self->parent_cancellable)))
{
/* We do not disconnect from cancellation event if parent has been
* cancelled, as it means we're likely disposing this object during
* the parent cancellation callback. And in such case the parent is
* already cancelled but our callback handler has not been yet called.
* In such case we should still disconnect from the connection not to
* leak, but we're doing it at next idle cycle, while we won't keep
* any further reference on the parent cancellable.
*/
disconnect_from_parent_idle (self->parent_cancellable, connection_id);
}
else
{
g_cancellable_disconnect (self->parent_cancellable, connection_id);
}
}
g_clear_object (&self->parent_cancellable);
G_OBJECT_CLASS (g_cancellable_child_parent_class)->dispose (object);
}
static void
g_cancellable_child_constructed (GObject *object)
{
GCancellableChild *self = G_CANCELLABLE_CHILD (object);
if (g_cancellable_is_cancelled (self->parent_cancellable))
{
g_cancellable_cancel (G_CANCELLABLE (self));
}
else
{
ConnectionData *connection_data = g_new (ConnectionData, 1);
/* We need to keep a reference on data to avoid races during callback */
self->connection_data = connection_data;
self->connection_data->self = self;
self->connection_data->id =
g_cancellable_connect (self->parent_cancellable,
G_CALLBACK (on_parent_cancellable_cancelled),
g_steal_pointer (&connection_data),
(GDestroyNotify) connection_data_free);
}
}
static void
g_cancellable_child_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
GCancellableChild *self = G_CANCELLABLE_CHILD (object);
switch (prop_id)
{
case PROP_PARENT:
g_value_set_object (value, self->parent_cancellable);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
g_cancellable_child_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
GCancellableChild *self = G_CANCELLABLE_CHILD (object);
switch (prop_id)
{
case PROP_PARENT:
g_set_object (&self->parent_cancellable, g_value_get_object (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
g_cancellable_child_class_init (GCancellableChildClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
gobject_class->get_property = g_cancellable_child_get_property;
gobject_class->set_property = g_cancellable_child_set_property;
gobject_class->constructed = g_cancellable_child_constructed;
gobject_class->dispose = g_cancellable_child_dispose;
G_CANCELLABLE_CLASS (klass)->cancel = g_cancellable_child_cancel;
/**
* GCancellableChild:parent:
*
* The parent cancellable.
*/
g_object_class_install_property (gobject_class, PROP_PARENT,
g_param_spec_object ("parent",
P_("parent"),
P_("The Parent cancellable"),
G_TYPE_CANCELLABLE,
G_PARAM_CONSTRUCT_ONLY |
G_PARAM_READWRITE |
G_PARAM_STATIC_NAME |
G_PARAM_STATIC_BLURB |
G_PARAM_STATIC_NICK));
}
static void
g_cancellable_child_init (GCancellableChild *self)
{
}

57
gio/gcancellablechild.h Normal file
View File

@ -0,0 +1,57 @@
/* GIO - GLib Input, Output and Streaming Library
*
* Copyright (C) 2022 Canonical Ltd.
*
* SPDX-License-Identifier: LGPL-2.1-or-later
*
* 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/>.
*
* Author: Marco Trevisan <marco.trevisan@canonical.com>
*/
#ifndef __G_CANCELLABLE_CHILD_H__
#define __G_CANCELLABLE_CHILD_H__
#if !defined (__GIO_GIO_H_INSIDE__) && !defined (GIO_COMPILATION)
#error "Only <gio/gio.h> can be included directly."
#endif
#include <gio/gcancellable.h>
G_BEGIN_DECLS
#define G_TYPE_CANCELLABLE_CHILD (g_cancellable_child_get_type ())
#define G_CANCELLABLE_CHILD(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_CANCELLABLE_CHILD, GCancellableChild))
#define G_IS_CANCELLABLE_CHILD(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_CANCELLABLE_CHILD))
/**
* GCancellableChild:
*
* Allows actions to be cancelled monitoring a parent cancellable.
*/
typedef struct _GCancellableChild GCancellableChild;
GIO_AVAILABLE_IN_2_76
GType g_cancellable_child_get_type (void) G_GNUC_CONST;
GIO_AVAILABLE_IN_2_76
GCancellableChild *g_cancellable_child_new (GCancellable *parent);
GIO_AVAILABLE_IN_2_76
GCancellable *g_cancellable_child_get_parent (GCancellableChild *cancellable_child);
G_END_DECLS
#endif /* __G_CANCELLABLE_CHILD_H__ */

View File

@ -4486,17 +4486,18 @@ g_desktop_app_info_get_desktop_ids_for_content_type (const gchar *content_type,
/**
* g_app_info_get_recommended_for_type:
* @content_type: the content type to find a #GAppInfo for
* @content_type: the content type to find a [iface@Gio.AppInfo] for
*
* Gets a list of recommended [iface@Gio.AppInfo]s for a given content type,
* i.e. those applications which claim to support the given content type
* exactly, and not by MIME type subclassing.
*
* Gets a list of recommended #GAppInfos for a given content type, i.e.
* those applications which claim to support the given content type exactly,
* and not by MIME type subclassing.
* Note that the first application of the list is the last used one, i.e.
* the last one for which g_app_info_set_as_last_used_for_type() has been
* called.
* the last one for which [method@Gio.AppInfo.set_as_last_used_for_type] has
* been called.
*
* Returns: (element-type GAppInfo) (transfer full): #GList of #GAppInfos
* for given @content_type or %NULL on error.
* Returns: (element-type GAppInfo) (transfer full): list of
* [iface@Gio.AppInfo]s for given @content_type or `NULL` on error.
*
* Since: 2.28
**/
@ -4528,14 +4529,14 @@ g_app_info_get_recommended_for_type (const gchar *content_type)
/**
* g_app_info_get_fallback_for_type:
* @content_type: the content type to find a #GAppInfo for
* @content_type: the content type to find a [iface@Gio.AppInfo] for
*
* Gets a list of fallback #GAppInfos for a given content type, i.e.
* those applications which claim to support the given content type
* by MIME type subclassing and not directly.
* Gets a list of fallback [iface@Gio.AppInfo]s for a given content type, i.e.
* those applications which claim to support the given content type by MIME
* type subclassing and not directly.
*
* Returns: (element-type GAppInfo) (transfer full): #GList of #GAppInfos
* for given @content_type or %NULL on error.
* Returns: (element-type GAppInfo) (transfer full): list of [iface@Gio.AppInfo]s
* for given @content_type or `NULL` on error.
*
* Since: 2.28
**/
@ -4580,15 +4581,15 @@ g_app_info_get_fallback_for_type (const gchar *content_type)
/**
* g_app_info_get_all_for_type:
* @content_type: the content type to find a #GAppInfo for
* @content_type: the content type to find a [iface@Gio.AppInfo] for
*
* Gets a list of all #GAppInfos for a given content type,
* including the recommended and fallback #GAppInfos. See
* g_app_info_get_recommended_for_type() and
* g_app_info_get_fallback_for_type().
* Gets a list of all [iface@Gio.AppInfo]s for a given content type,
* including the recommended and fallback [iface@Gio.AppInfo]s. See
* [func@Gio.AppInfo.get_recommended_for_type] and
* [func@Gio.AppInfo.get_fallback_for_type].
*
* Returns: (element-type GAppInfo) (transfer full): #GList of #GAppInfos
* for given @content_type or %NULL on error.
* Returns: (element-type GAppInfo) (transfer full): list of
* [iface@Gio.AppInfo]s for given @content_type.
**/
GList *
g_app_info_get_all_for_type (const char *content_type)
@ -4621,10 +4622,10 @@ g_app_info_get_all_for_type (const char *content_type)
* @content_type: a content type
*
* Removes all changes to the type associations done by
* g_app_info_set_as_default_for_type(),
* g_app_info_set_as_default_for_extension(),
* g_app_info_add_supports_type() or
* g_app_info_remove_supports_type().
* [method@Gio.AppInfo.set_as_default_for_type],
* [method@Gio.AppInfo.set_as_default_for_extension],
* [method@Gio.AppInfo.add_supports_type] or
* [method@Gio.AppInfo.remove_supports_type].
*
* Since: 2.20
*/
@ -4638,14 +4639,14 @@ g_app_info_reset_type_associations (const char *content_type)
/**
* g_app_info_get_default_for_type:
* @content_type: the content type to find a #GAppInfo for
* @must_support_uris: if %TRUE, the #GAppInfo is expected to
* support URIs
* @content_type: the content type to find a [iface@Gio.AppInfo] for
* @must_support_uris: if `TRUE`, the [iface@Gio.AppInfo] is expected to
* support URIs
*
* Gets the default #GAppInfo for a given content type.
* Gets the default [iface@Gio.AppInfo] for a given content type.
*
* Returns: (transfer full) (nullable): #GAppInfo for given @content_type or
* %NULL on error.
* Returns: (transfer full) (nullable): [iface@Gio.AppInfo] for given
* @content_type or `NULL` on error.
*/
GAppInfo *
g_app_info_get_default_for_type (const char *content_type,
@ -4899,11 +4900,12 @@ g_desktop_app_info_search (const gchar *search_string)
*
* For desktop files, this includes applications that have
* `NoDisplay=true` set or are excluded from display by means
* of `OnlyShowIn` or `NotShowIn`. See g_app_info_should_show().
* of `OnlyShowIn` or `NotShowIn`. See [method@Gio.AppInfo.should_show].
* The returned list does not include applications which have
* the `Hidden` key set.
*
* Returns: (element-type GAppInfo) (transfer full): a newly allocated #GList of references to #GAppInfos.
* Returns: (element-type GAppInfo) (transfer full): a newly allocated
* list of references to [iface@Gio.AppInfo]s.
**/
GList *
g_app_info_get_all (void)

View File

@ -38,6 +38,7 @@ G_DEFINE_AUTOPTR_CLEANUP_FUNC(GBufferedInputStream, g_object_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GBufferedOutputStream, g_object_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GBytesIcon, g_object_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GCancellable, g_object_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GCancellableChild, g_object_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GCharsetConverter, g_object_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GConverter, g_object_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GConverterInputStream, g_object_unref)

View File

@ -40,6 +40,7 @@
#include <gio/gbufferedoutputstream.h>
#include <gio/gbytesicon.h>
#include <gio/gcancellable.h>
#include <gio/gcancellablechild.h>
#include <gio/gcharsetconverter.h>
#include <gio/gcontenttype.h>
#include <gio/gconverter.h>

View File

@ -24,6 +24,7 @@
#include "config.h"
#include <glib.h>
#include <gio.h>
#include "glibintl.h"
#include "giostream.h"
@ -692,8 +693,6 @@ typedef struct
GIOStream *stream2;
GIOStreamSpliceFlags flags;
gint io_priority;
GCancellable *cancellable;
gulong cancelled_id;
GCancellable *op1_cancellable;
GCancellable *op2_cancellable;
guint completed;
@ -705,10 +704,8 @@ splice_context_free (SpliceContext *ctx)
{
g_object_unref (ctx->stream1);
g_object_unref (ctx->stream2);
if (ctx->cancellable != NULL)
g_object_unref (ctx->cancellable);
g_object_unref (ctx->op1_cancellable);
g_object_unref (ctx->op2_cancellable);
g_clear_object (&ctx->op1_cancellable);
g_clear_object (&ctx->op2_cancellable);
g_clear_error (&ctx->error);
g_slice_free (SpliceContext, ctx);
}
@ -717,10 +714,6 @@ static void
splice_complete (GTask *task,
SpliceContext *ctx)
{
if (ctx->cancelled_id != 0)
g_cancellable_disconnect (ctx->cancellable, ctx->cancelled_id);
ctx->cancelled_id = 0;
if (ctx->error != NULL)
{
g_task_return_error (task, ctx->error);
@ -762,6 +755,7 @@ splice_cb (GObject *ostream,
gpointer user_data)
{
GTask *task = user_data;
GCancellable *task_cancellable = g_task_get_cancellable (task);
SpliceContext *ctx = g_task_get_task_data (task);
GError *error = NULL;
@ -772,8 +766,8 @@ splice_cb (GObject *ostream,
/* ignore cancellation error if it was not requested by the user */
if (error != NULL &&
g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED) &&
(ctx->cancellable == NULL ||
!g_cancellable_is_cancelled (ctx->cancellable)))
(task_cancellable == NULL ||
!g_cancellable_is_cancelled (task_cancellable)))
g_clear_error (&error);
/* Keep the first error that occurred */
@ -791,12 +785,11 @@ splice_cb (GObject *ostream,
}
else if (ctx->completed == 2)
{
if (ctx->cancellable == NULL ||
!g_cancellable_is_cancelled (ctx->cancellable))
{
g_cancellable_reset (ctx->op1_cancellable);
g_cancellable_reset (ctx->op2_cancellable);
}
if (ctx->op1_cancellable && !g_cancellable_is_cancelled (ctx->op1_cancellable))
g_cancellable_reset (ctx->op1_cancellable);
if (ctx->op2_cancellable && !g_cancellable_is_cancelled (ctx->op2_cancellable))
g_cancellable_reset (ctx->op2_cancellable);
/* Close the IO streams if needed */
if ((ctx->flags & G_IO_STREAM_SPLICE_CLOSE_STREAM1) != 0)
@ -827,17 +820,6 @@ splice_cb (GObject *ostream,
g_object_unref (task);
}
static void
splice_cancelled_cb (GCancellable *cancellable,
GTask *task)
{
SpliceContext *ctx;
ctx = g_task_get_task_data (task);
g_cancellable_cancel (ctx->op1_cancellable);
g_cancellable_cancel (ctx->op2_cancellable);
}
/**
* g_io_stream_splice_async:
* @stream1: a #GIOStream.
@ -886,8 +868,6 @@ g_io_stream_splice_async (GIOStream *stream1,
ctx->stream1 = g_object_ref (stream1);
ctx->stream2 = g_object_ref (stream2);
ctx->flags = flags;
ctx->op1_cancellable = g_cancellable_new ();
ctx->op2_cancellable = g_cancellable_new ();
ctx->completed = 0;
task = g_task_new (NULL, cancellable, callback, user_data);
@ -896,10 +876,8 @@ g_io_stream_splice_async (GIOStream *stream1,
if (cancellable != NULL)
{
ctx->cancellable = g_object_ref (cancellable);
ctx->cancelled_id = g_cancellable_connect (cancellable,
G_CALLBACK (splice_cancelled_cb), g_object_ref (task),
g_object_unref);
ctx->op1_cancellable = G_CANCELLABLE (g_cancellable_child_new (cancellable));
ctx->op2_cancellable = G_CANCELLABLE (g_cancellable_child_new (cancellable));
}
istream = g_io_stream_get_input_stream (stream1);

View File

@ -41,7 +41,7 @@
#include <gio/gproxyaddressenumerator.h>
#include <gio/gproxyaddress.h>
#include <gio/gtask.h>
#include <gio/gcancellable.h>
#include <gio/gcancellablechild.h>
#include <gio/gioerror.h>
#include <gio/gsocket.h>
#include <gio/gnetworkaddress.h>
@ -1496,8 +1496,6 @@ typedef struct
GSocketConnectable *connectable;
GSocketAddressEnumerator *enumerator;
GCancellable *enumeration_cancellable;
GCancellable *enumeration_parent_cancellable; /* (nullable) (owned) */
gulong enumeration_cancelled_id;
GSList *connection_attempts; /* (element-type ConnectionAttempt) (owned) */
GSList *successful_connections;
@ -1521,9 +1519,6 @@ g_socket_client_async_connect_data_free (GSocketClientAsyncConnectData *data)
g_clear_object (&data->connectable);
g_clear_object (&data->enumerator);
g_cancellable_disconnect (data->enumeration_parent_cancellable, data->enumeration_cancelled_id);
g_clear_object (&data->enumeration_parent_cancellable);
data->enumeration_cancelled_id = 0;
g_clear_object (&data->enumeration_cancellable);
g_slist_free_full (data->connection_attempts, connection_attempt_unref);
@ -1544,8 +1539,6 @@ typedef struct
GSource *delay_timeout_source; /* (owned) */
gboolean delay_reached;
GCancellable *cancellable;
GCancellable *task_cancellable; /* (owned); this is equal to g_task_get_cancellable (ConnectionAttempt.data->task), but with a longer lifetime */
gulong cancelled_id;
grefcount ref;
} ConnectionAttempt;
@ -1573,9 +1566,6 @@ connection_attempt_unref (gpointer pointer)
g_clear_object (&attempt->address);
g_clear_object (&attempt->socket);
g_clear_object (&attempt->connection);
g_cancellable_disconnect (attempt->task_cancellable, attempt->cancelled_id);
g_clear_object (&attempt->task_cancellable);
attempt->cancelled_id = 0;
g_clear_object (&attempt->cancellable);
g_clear_object (&attempt->proxy_addr);
if (attempt->delay_timeout_source)
@ -2001,15 +1991,6 @@ on_connection_attempt_delay_reached (gpointer data)
return G_SOURCE_REMOVE;
}
static void
on_connection_cancelled (GCancellable *cancellable,
gpointer data)
{
GCancellable *linked_cancellable = G_CANCELLABLE (data);
g_cancellable_cancel (linked_cancellable);
}
static void
g_socket_client_enumerator_callback (GObject *object,
GAsyncResult *result,
@ -2079,7 +2060,6 @@ g_socket_client_enumerator_callback (GObject *object,
attempt->data = data;
attempt->socket = socket;
attempt->address = address;
attempt->cancellable = g_cancellable_new ();
attempt->connection = (GIOStream *)g_socket_connection_factory_create_connection (socket);
attempt->delay_timeout_source = g_timeout_source_new (HAPPY_EYEBALLS_CONNECTION_ATTEMPT_DELAY_MS);
@ -2095,10 +2075,8 @@ g_socket_client_enumerator_callback (GObject *object,
if (g_task_get_cancellable (data->task))
{
attempt->task_cancellable = g_object_ref (g_task_get_cancellable (data->task));
attempt->cancelled_id =
g_cancellable_connect (attempt->task_cancellable, G_CALLBACK (on_connection_cancelled),
g_object_ref (attempt->cancellable), g_object_unref);
attempt->cancellable =
G_CANCELLABLE (g_cancellable_child_new (g_task_get_cancellable (data->task)));
}
g_socket_connection_set_cached_remote_address ((GSocketConnection *)attempt->connection, address);
@ -2206,14 +2184,8 @@ g_socket_client_connect_async (GSocketClient *client,
g_task_set_source_tag (data->task, g_socket_client_connect_async);
g_task_set_task_data (data->task, data, (GDestroyNotify)g_socket_client_async_connect_data_free);
data->enumeration_cancellable = g_cancellable_new ();
if (cancellable)
{
data->enumeration_parent_cancellable = g_object_ref (cancellable);
data->enumeration_cancelled_id =
g_cancellable_connect (cancellable, G_CALLBACK (on_connection_cancelled),
g_object_ref (data->enumeration_cancellable), g_object_unref);
}
data->enumeration_cancellable = G_CANCELLABLE (g_cancellable_child_new (cancellable));
g_debug ("%s: starting new g_socket_client_connect_async() with GTask %p "
"and GSocketClientAsyncConnectData %p",

View File

@ -488,6 +488,7 @@ gio_base_sources = files(
'gbufferedoutputstream.c',
'gbytesicon.c',
'gcancellable.c',
'gcancellablechild.c',
'gcharsetconverter.c',
'gcontextspecificgroup.c',
'gconverter.c',
@ -646,6 +647,7 @@ gio_headers = files(
'gbufferedoutputstream.h',
'gbytesicon.h',
'gcancellable.h',
'gcancellablechild.h',
'gcontenttype.h',
'gcharsetconverter.h',
'gconverter.h',

View File

@ -5,11 +5,15 @@ test_autoptr (void)
{
g_autoptr(GFile) p = g_file_new_for_path ("/blah");
g_autoptr(GInetAddress) a = g_inet_address_new_from_string ("127.0.0.1");
g_autoptr(GCancellable) cancellable = g_cancellable_new ();
g_autoptr(GCancellableChild) cancellable_child = g_cancellable_child_new (cancellable);
g_autofree gchar *path = g_file_get_path (p);
g_autofree gchar *istr = g_inet_address_to_string (a);
g_assert_cmpstr (path, ==, G_DIR_SEPARATOR_S "blah");
g_assert_cmpstr (istr, ==, "127.0.0.1");
g_assert_false (g_cancellable_is_cancelled (cancellable));
g_assert_true (g_cancellable_child_get_parent (cancellable_child) == cancellable);
}
int

View File

@ -1,6 +1,7 @@
/* GIO - GLib Input, Output and Streaming Library
*
* Copyright (C) 2011 Collabora Ltd.
* Copyright (C) 2022 Canonical Ltd.
*
* SPDX-License-Identifier: LGPL-2.1-or-later
*
@ -18,6 +19,7 @@
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
*
* Author: Stef Walter <stefw@collabora.co.uk>
* Marco Trevisan <marco.trevisan@canonical.com>
*/
#include <locale.h>
@ -32,6 +34,14 @@
static gint num_async_operations = 0;
static gulong
get_first_cancelled_signal_handler_id (GCancellable *cancellable)
{
guint signal_id = g_signal_lookup ("cancelled", G_TYPE_CANCELLABLE);
return g_signal_handler_find (cancellable, G_SIGNAL_MATCH_ID, signal_id, 0, NULL, NULL, NULL);
}
typedef struct
{
guint iterations_requested; /* construct-only */
@ -190,6 +200,7 @@ test_cancel_multiple_concurrent (void)
}
cancellable = g_cancellable_new ();
num_async_operations = 0;
for (i = 0; i < 45; i++)
{
@ -262,11 +273,6 @@ threaded_dispose_thread_cb (gpointer user_data)
static void
test_cancellable_source_threaded_dispose (void)
{
#ifdef _GLIB_ADDRESS_SANITIZER
g_test_incomplete ("FIXME: Leaks lots of GCancellableSource objects, see glib#2309");
(void) cancelled_cb;
(void) threaded_dispose_thread_cb;
#else
ThreadedDisposeData data;
GThread *thread = NULL;
guint i;
@ -276,6 +282,10 @@ test_cancellable_source_threaded_dispose (void)
"(in one thread) and cancelling the GCancellable it refers "
"to (in another thread)");
g_test_bug ("https://gitlab.gnome.org/GNOME/glib/issues/1841");
#ifdef _GLIB_ADDRESS_SANITIZER
g_test_message ("We also ensure that no GCancellableSource are leaked");
g_test_bug ("https://gitlab.gnome.org/GNOME/glib/issues/2309");
#endif
#ifdef ENABLE_VALGRIND
if (RUNNING_ON_VALGRIND)
@ -341,7 +351,6 @@ test_cancellable_source_threaded_dispose (void)
g_cond_clear (&data.cond);
g_ptr_array_unref (cancellables_pending_unref);
#endif
}
static void
@ -796,6 +805,528 @@ test_cancellable_cancel_reset_connect_races (void)
g_object_unref (cancellable);
}
static gboolean
source_cancelled_counter_cb (GCancellable *cancellable,
gpointer user_data)
{
guint *n_calls = user_data;
*n_calls = *n_calls + 1;
return G_SOURCE_CONTINUE;
}
static void
test_cancellable_source_can_be_fired_multiple_times (void)
{
GCancellable *cancellable;
GSource *source;
guint n_calls = 0;
g_test_summary ("Test a cancellable source callback can be called multiple times");
g_test_bug ("https://gitlab.gnome.org/GNOME/glib/issues/774");
cancellable = g_cancellable_new ();
source = g_cancellable_source_new (cancellable);
g_source_set_callback (source, G_SOURCE_FUNC (source_cancelled_counter_cb),
&n_calls, NULL);
g_source_attach (source, NULL);
g_cancellable_cancel (cancellable);
g_assert_cmpuint (n_calls, ==, 0);
while (g_main_context_pending (NULL))
g_main_context_iteration (NULL, TRUE);
g_assert_cmpuint (n_calls, ==, 1);
g_cancellable_cancel (cancellable);
g_timeout_add_once (100, g_free, NULL);
while (g_main_context_pending (NULL))
g_main_context_iteration (NULL, TRUE);
g_assert_cmpuint (n_calls, ==, 1);
g_cancellable_reset (cancellable);
g_cancellable_cancel (cancellable);
g_assert_cmpuint (n_calls, ==, 1);
while (g_main_context_pending (NULL))
g_main_context_iteration (NULL, TRUE);
g_assert_cmpuint (n_calls, ==, 2);
g_source_unref (source);
g_object_unref (cancellable);
}
struct _BadCancellable
{
GCancellable parent;
};
G_DECLARE_FINAL_TYPE (BadCancellable, bad_cancellable, BAD, CANCELLABLE, GCancellable);
G_DEFINE_TYPE (BadCancellable, bad_cancellable, G_TYPE_CANCELLABLE)
static void
bad_cancellable_cancel (GCancellable *cancellable)
{
}
static void
bad_cancellable_class_init (BadCancellableClass *klass)
{
G_CANCELLABLE_CLASS (klass)->cancel = bad_cancellable_cancel;
}
static void
bad_cancellable_init (BadCancellable *bad_cancellable)
{
}
static void
test_cancellable_wrong_cancel_override (void)
{
BadCancellable *bad = g_object_new (bad_cancellable_get_type (), NULL);
g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL,
"*Overridden cancel function*");
g_cancellable_cancel (G_CANCELLABLE (bad));
g_test_assert_expected_messages ();
g_assert_true (g_cancellable_is_cancelled (G_CANCELLABLE (bad)));
g_object_unref (bad);
}
static void
test_cancellable_child_invalid (void)
{
GCancellableChild *child;
g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL,
"*G_IS_CANCELLABLE*failed*");
child = g_cancellable_child_new (NULL);
g_test_assert_expected_messages ();
g_assert_null (child);
}
static void
test_cancellable_child (void)
{
GCancellable *parent;
GCancellable *parent_prop;
GCancellableChild *child;
parent = g_cancellable_new ();
child = g_cancellable_child_new (parent);
g_assert_true (G_IS_CANCELLABLE (child));
g_assert_true (g_cancellable_child_get_parent (child) == parent);
g_assert_false (g_cancellable_is_cancelled (G_CANCELLABLE (child)));
g_assert_cmpuint (get_first_cancelled_signal_handler_id (parent), !=, 0);
g_object_get (G_OBJECT (child), "parent", &parent_prop, NULL);
g_assert_true (parent_prop == parent);
g_object_unref (child);
g_assert_cmpuint (get_first_cancelled_signal_handler_id (parent), ==, 0);
g_object_unref (parent);
g_object_unref (parent_prop);
}
static void
test_cancellable_child_chain (void)
{
GCancellable *root;
GCancellable *parent;
GCancellableChild *child;
GCancellableChild *last_child = NULL;
const guint iterations = 20;
guint i;
root = g_cancellable_new ();
parent = root;
for (i = 0; i < iterations; i++)
{
child = g_cancellable_child_new (parent);
parent = G_CANCELLABLE (child);
g_set_object (&last_child, child);
g_object_unref (child);
}
child = last_child;
for (i = 0; i < iterations - 1; i++)
{
parent = g_cancellable_child_get_parent (child);
g_assert_true (G_IS_CANCELLABLE_CHILD (parent));
child = G_CANCELLABLE_CHILD (parent);
}
parent = g_cancellable_child_get_parent (child);
g_assert_false (G_IS_CANCELLABLE_CHILD (parent));
g_assert_true (parent == root);
g_cancellable_cancel (root);
child = last_child;
for (i = 0; i < iterations - 1; i++)
{
g_assert_true (g_cancellable_is_cancelled (G_CANCELLABLE (child)));
parent = g_cancellable_child_get_parent (child);
child = G_CANCELLABLE_CHILD (parent);
}
parent = g_cancellable_child_get_parent (child);
g_cancellable_is_cancelled (parent);
g_assert_true (parent == root);
g_object_unref (root);
g_object_unref (last_child);
}
static void
test_cancellable_child_already_cancelled (void)
{
GCancellable *parent;
GCancellableChild *child;
parent = g_cancellable_new ();
g_cancellable_cancel (parent);
g_assert_true (g_cancellable_is_cancelled (parent));
child = g_cancellable_child_new (parent);
g_assert_true (g_cancellable_is_cancelled (G_CANCELLABLE (child)));
g_assert_cmpuint (get_first_cancelled_signal_handler_id (parent), ==, 0);
g_object_unref (child);
g_object_unref (parent);
}
static void
test_cancellable_child_cancellation (void)
{
GCancellable *parent;
GCancellableChild *child;
parent = g_cancellable_new ();
child = g_cancellable_child_new (parent);
g_assert_false (g_cancellable_is_cancelled (G_CANCELLABLE (child)));
g_assert_cmpuint (get_first_cancelled_signal_handler_id (parent), !=, 0);
g_cancellable_cancel (G_CANCELLABLE (child));
g_assert_true (g_cancellable_is_cancelled (G_CANCELLABLE (child)));
g_assert_false (g_cancellable_is_cancelled (parent));
g_assert_cmpuint (get_first_cancelled_signal_handler_id (parent), ==, 0);
g_object_unref (child);
g_object_unref (parent);
}
static void
test_cancellable_child_parent_cancellation (void)
{
GCancellable *parent;
GCancellableChild *child;
parent = g_cancellable_new ();
child = g_cancellable_child_new (parent);
g_assert_false (g_cancellable_is_cancelled (G_CANCELLABLE (child)));
g_cancellable_cancel (parent);
g_assert_true (g_cancellable_is_cancelled (parent));
g_assert_true (g_cancellable_is_cancelled (G_CANCELLABLE (child)));
g_assert_cmpuint (get_first_cancelled_signal_handler_id (parent), !=, 0);
g_object_unref (child);
g_assert_cmpuint (get_first_cancelled_signal_handler_id (parent), !=, 0);
while (get_first_cancelled_signal_handler_id (parent))
g_main_context_iteration (NULL, TRUE);
g_object_unref (parent);
}
static void
test_cancellable_child_parent_cancellation_and_finalization (void)
{
GCancellable *parent;
GCancellableChild *child;
GWeakRef parent_ref;
parent = g_cancellable_new ();
child = g_cancellable_child_new (parent);
g_assert_false (g_cancellable_is_cancelled (G_CANCELLABLE (child)));
g_cancellable_cancel (parent);
g_assert_true (g_cancellable_is_cancelled (parent));
g_assert_true (g_cancellable_is_cancelled (G_CANCELLABLE (child)));
g_assert_cmpuint (get_first_cancelled_signal_handler_id (parent), !=, 0);
g_object_unref (child);
g_assert_cmpuint (get_first_cancelled_signal_handler_id (parent), !=, 0);
g_weak_ref_init (&parent_ref, parent);
g_object_unref (parent);
g_assert_null (g_weak_ref_get (&parent_ref));
/* Ensure we handle the cancellation idle properly */
g_timeout_add_once (100, (GSourceOnceFunc) g_nullify_pointer, &parent);
while (parent != NULL)
g_main_context_iteration (NULL, TRUE);
}
static void
test_cancellable_child_cancel_multiple_concurrent_from_parent (void)
{
GCancellable *parent;
guint i, iterations;
parent = g_cancellable_new ();
num_async_operations = 0;
for (i = 0; i < 45; i++)
{
GCancellableChild *cancellable_child = g_cancellable_child_new (parent);
iterations = i + 10;
mock_operation_async (iterations, g_random_boolean (),
G_CANCELLABLE (cancellable_child),
on_mock_operation_ready, GUINT_TO_POINTER (iterations));
num_async_operations++;
g_object_unref (cancellable_child);
}
g_assert_cmpint (num_async_operations, ==, 45);
if (g_test_verbose ())
g_test_message ("CANCEL: %d operations", num_async_operations);
g_cancellable_cancel (parent);
g_assert_true (g_cancellable_is_cancelled (parent));
/* Wait for all operations to be cancelled */
while (num_async_operations != 0 ||
get_first_cancelled_signal_handler_id (parent) != 0)
g_main_context_iteration (NULL, TRUE);
g_assert_cmpint (num_async_operations, ==, 0);
g_assert_cmpuint (get_first_cancelled_signal_handler_id (parent), ==, 0);
g_object_unref (parent);
}
static void
test_cancellable_child_cancel_multiple_concurrent_from_root (void)
{
GCancellable *root;
GCancellable *parent;
guint i, iterations;
root = g_cancellable_new ();
parent = g_object_ref (root);
num_async_operations = 0;
for (i = 0; i < 45; i++)
{
GCancellableChild *cancellable_child = g_cancellable_child_new (parent);
iterations = i + 10;
mock_operation_async (iterations, g_random_boolean (),
G_CANCELLABLE (cancellable_child),
on_mock_operation_ready, GUINT_TO_POINTER (iterations));
num_async_operations++;
g_set_object (&parent, G_CANCELLABLE (cancellable_child));
g_object_unref (cancellable_child);
}
g_assert_cmpint (num_async_operations, ==, 45);
if (g_test_verbose ())
g_test_message ("CANCEL: %d operations", num_async_operations);
g_cancellable_cancel (root);
g_assert_true (g_cancellable_is_cancelled (root));
/* Wait for all operations to be cancelled */
while (num_async_operations != 0 ||
get_first_cancelled_signal_handler_id (parent) != 0)
g_main_context_iteration (NULL, TRUE);
g_assert_cmpint (num_async_operations, ==, 0);
g_assert_cmpuint (get_first_cancelled_signal_handler_id (parent), ==, 0);
g_object_unref (root);
g_object_unref (parent);
}
static void
on_parent_cancelled_before_dispose (GCancellable *parent,
GCancellableChild **child_ptr)
{
GCancellableChild *child = *child_ptr;
GWeakRef child_ref;
g_weak_ref_init (&child_ref, child);
g_assert_false (g_cancellable_is_cancelled (G_CANCELLABLE (child)));
g_object_unref (child);
g_assert_null (g_weak_ref_get (&child_ref));
*child_ptr = NULL;
}
static void
test_cancellable_child_dispose_before_parent_cancellation (void)
{
GCancellable *parent;
GCancellableChild *child = NULL;
gulong id;
parent = g_cancellable_new ();
id = g_cancellable_connect (parent,
G_CALLBACK (on_parent_cancelled_before_dispose),
&child, NULL);
child = g_cancellable_child_new (parent);
g_assert_false (g_cancellable_is_cancelled (G_CANCELLABLE (child)));
g_cancellable_cancel (parent);
g_assert_null (child);
g_cancellable_disconnect (parent, id);
g_assert_cmpuint (get_first_cancelled_signal_handler_id (parent), !=, 0);
while (get_first_cancelled_signal_handler_id (parent))
g_main_context_iteration (NULL, TRUE);
g_object_unref (parent);
}
static void
on_parent_cancelled_after_dispose (GCancellable *parent,
GCancellableChild **child_ptr)
{
GCancellableChild *child = *child_ptr;
GWeakRef child_ref;
g_weak_ref_init (&child_ref, child);
g_assert_true (g_cancellable_is_cancelled (G_CANCELLABLE (child)));
g_object_unref (child);
g_assert_null (g_weak_ref_get (&child_ref));
*child_ptr = NULL;
}
static void
test_cancellable_child_dispose_after_parent_cancellation (void)
{
GCancellable *parent;
GCancellableChild *child = NULL;
gulong id;
parent = g_cancellable_new ();
child = g_cancellable_child_new (parent);
g_assert_false (g_cancellable_is_cancelled (G_CANCELLABLE (child)));
id = g_cancellable_connect (parent,
G_CALLBACK (on_parent_cancelled_after_dispose),
&child, NULL);
g_cancellable_cancel (parent);
g_assert_null (child);
g_cancellable_disconnect (parent, id);
g_assert_cmpuint (get_first_cancelled_signal_handler_id (parent), !=, 0);
while (get_first_cancelled_signal_handler_id (parent))
g_main_context_iteration (NULL, TRUE);
g_object_unref (parent);
}
static void
on_parent_cancelled_before_cancel (GCancellable *parent,
GCancellableChild *child)
{
g_assert_false (g_cancellable_is_cancelled (G_CANCELLABLE (child)));
g_cancellable_cancel (G_CANCELLABLE (child));
}
static void
test_cancellable_child_cancel_before_parent_cancellation (void)
{
GCancellable *parent;
GCancellableChild *child = NULL;
gulong id;
parent = g_cancellable_new ();
id = g_cancellable_connect (parent,
G_CALLBACK (on_parent_cancelled_before_cancel),
child, NULL);
child = g_cancellable_child_new (parent);
g_assert_false (g_cancellable_is_cancelled (G_CANCELLABLE (child)));
g_cancellable_cancel (parent);
g_assert_true (g_cancellable_is_cancelled (G_CANCELLABLE (child)));
g_cancellable_disconnect (parent, id);
g_assert_cmpuint (get_first_cancelled_signal_handler_id (parent), !=, 0);
while (get_first_cancelled_signal_handler_id (parent))
g_main_context_iteration (NULL, TRUE);
g_object_unref (child);
g_object_unref (parent);
}
static void
on_parent_cancelled_after_cancel (GCancellable *parent,
GCancellableChild *child)
{
g_assert_true (g_cancellable_is_cancelled (G_CANCELLABLE (child)));
g_cancellable_cancel (G_CANCELLABLE (child));
}
static void
test_cancellable_child_cancel_after_parent_cancellation (void)
{
GCancellable *parent;
GCancellableChild *child = NULL;
gulong id;
parent = g_cancellable_new ();
child = g_cancellable_child_new (parent);
g_assert_false (g_cancellable_is_cancelled (G_CANCELLABLE (child)));
id = g_cancellable_connect (parent,
G_CALLBACK (on_parent_cancelled_after_cancel),
child, NULL);
g_cancellable_cancel (parent);
g_assert_true (g_cancellable_is_cancelled (G_CANCELLABLE (child)));
g_cancellable_disconnect (parent, id);
g_assert_cmpuint (get_first_cancelled_signal_handler_id (parent), !=, 0);
while (get_first_cancelled_signal_handler_id (parent))
g_main_context_iteration (NULL, TRUE);
g_object_unref (child);
g_object_unref (parent);
}
int
main (int argc, char *argv[])
{
@ -810,7 +1341,22 @@ main (int argc, char *argv[])
g_test_add_func ("/cancellable/poll-fd-cancelled-threaded", test_cancellable_cancelled_poll_fd_threaded);
g_test_add_func ("/cancellable/cancel-reset-races", test_cancellable_cancel_reset_races);
g_test_add_func ("/cancellable/cancel-reset-connect-races", test_cancellable_cancel_reset_connect_races);
g_test_add_func ("/cancellable/invalid-cancel-override", test_cancellable_wrong_cancel_override);
g_test_add_func ("/cancellable-source/threaded-dispose", test_cancellable_source_threaded_dispose);
g_test_add_func ("/cancellable-source/can-be-fired-multiple-times", test_cancellable_source_can_be_fired_multiple_times);
g_test_add_func ("/cancellable-child/invalid", test_cancellable_child_invalid);
g_test_add_func ("/cancellable-child/basic", test_cancellable_child);
g_test_add_func ("/cancellable-child/chain", test_cancellable_child_chain);
g_test_add_func ("/cancellable-child/already-cancelled", test_cancellable_child_already_cancelled);
g_test_add_func ("/cancellable-child/child-cancellation", test_cancellable_child_cancellation);
g_test_add_func ("/cancellable-child/parent-cancellation", test_cancellable_child_parent_cancellation);
g_test_add_func ("/cancellable-child/parent-cancellation-and-finalization", test_cancellable_child_parent_cancellation_and_finalization);
g_test_add_func ("/cancellable-child/threaded-cancellation", test_cancellable_child_cancel_multiple_concurrent_from_parent);
g_test_add_func ("/cancellable-child/threaded-cancellation-chain", test_cancellable_child_cancel_multiple_concurrent_from_root);
g_test_add_func ("/cancellable-child/disposition-before-parent-cancellation", test_cancellable_child_dispose_before_parent_cancellation);
g_test_add_func ("/cancellable-child/disposition-after-parent-cancellation", test_cancellable_child_dispose_after_parent_cancellation);
g_test_add_func ("/cancellable-child/cancellation-before-parent-cancellation", test_cancellable_child_cancel_before_parent_cancellation);
g_test_add_func ("/cancellable-child/cancellation-after-parent-cancellation", test_cancellable_child_cancel_after_parent_cancellation);
return g_test_run ();
}

View File

@ -75,6 +75,7 @@ test_type (gconstpointer data)
if (g_type_is_a (type, G_TYPE_BINDING) ||
g_type_is_a (type, G_TYPE_BUFFERED_INPUT_STREAM) ||
g_type_is_a (type, G_TYPE_BUFFERED_OUTPUT_STREAM) ||
g_type_is_a (type, G_TYPE_CANCELLABLE_CHILD) ||
g_type_is_a (type, G_TYPE_CHARSET_CONVERTER) ||
g_type_is_a (type, G_TYPE_DBUS_ACTION_GROUP) ||
g_type_is_a (type, G_TYPE_DBUS_CONNECTION) ||

View File

@ -1007,17 +1007,16 @@ test_dbus_roundtrip (void)
static void
test_dbus_peer_roundtrip (void)
{
#ifdef _GLIB_ADDRESS_SANITIZER
g_test_incomplete ("FIXME: Leaks a GCancellableSource, see glib#2313");
(void) peer_connection_up;
(void) peer_connection_down;
#else
PeerConnection peer;
#ifdef _GLIB_ADDRESS_SANITIZER
g_test_message ("Ensure that no GCancellableSource are leaked");
g_test_bug ("https://gitlab.gnome.org/GNOME/glib/issues/2313");
#endif
peer_connection_up (&peer);
do_roundtrip (peer.server_connection, peer.client_connection);
peer_connection_down (&peer);
#endif
}
static gint items_changed_count;
@ -1146,17 +1145,16 @@ test_dbus_subscriptions (void)
static void
test_dbus_peer_subscriptions (void)
{
#ifdef _GLIB_ADDRESS_SANITIZER
g_test_incomplete ("FIXME: Leaks a GCancellableSource, see glib#2313");
(void) peer_connection_up;
(void) peer_connection_down;
#else
PeerConnection peer;
#ifdef _GLIB_ADDRESS_SANITIZER
g_test_message ("Ensure that no GCancellableSource are leaked");
g_test_bug ("https://gitlab.gnome.org/GNOME/glib/issues/2313");
#endif
peer_connection_up (&peer);
do_subscriptions (peer.server_connection, peer.client_connection);
peer_connection_down (&peer);
#endif
}
static void

View File

@ -800,16 +800,38 @@ g_node_depth_traverse_level (GNode *node,
* efficient than the other orders.
*
* Specifies the type of traversal performed by g_tree_traverse(),
* g_node_traverse() and g_node_find(). The different orders are
* illustrated here:
* g_node_traverse() and g_node_find().
*
* The different orders are illustrated here:
*
* - In order: A, B, C, D, E, F, G, H, I
* ![](Sorted_binary_tree_inorder.svg)
* <picture>
* <source srcset="Sorted_binary_tree_inorder-dark.svg"
* media="(prefers-color-scheme: dark)">
* <img src="Sorted_binary_tree_inorder.svg"
* alt="Sorted binary tree, in-order traversal">
* </picture>
* - Pre order: F, B, A, D, C, E, G, I, H
* ![](Sorted_binary_tree_preorder.svg)
* <picture>
* <source srcset="Sorted_binary_tree_preorder-dark.svg"
* media="(prefers-color-scheme: dark)">
* <img src="Sorted_binary_tree_preorder.svg"
* alt="Sorted binary tree, pre-order traversal">
* </picture>
* - Post order: A, C, E, D, B, H, I, G, F
* ![](Sorted_binary_tree_postorder.svg)
* <picture>
* <source srcset="Sorted_binary_tree_postorder-dark.svg"
* media="(prefers-color-scheme: dark)">
* <img src="Sorted_binary_tree_postorder.svg"
* alt="Sorted binary tree, post-order traversal">
* </picture>
* - Level order: F, B, G, A, D, I, C, E, H
* ![](Sorted_binary_tree_breadth-first_traversal.svg)
* <picture>
* <source srcset="Sorted_binary_tree_breadth-first_traversal-dark.svg"
* media="(prefers-color-scheme: dark)">
* <img src="Sorted_binary_tree_breadth-first_traversal.svg"
* alt="Sorted binary tree, breadth-first level order traversal">
* </picture>
*/
/**