Compare commits
17 Commits
7cde931af4
...
82b0b2c933
Author | SHA1 | Date | |
---|---|---|---|
|
82b0b2c933 | ||
|
d3cb9d638d | ||
|
a931a75c4f | ||
|
79c57b5b80 | ||
|
a84c67da71 | ||
|
ecc9904996 | ||
|
b8db83940b | ||
|
110e76a140 | ||
|
3bd477b454 | ||
|
27968f5226 | ||
|
c62001ec82 | ||
|
ab55a59005 | ||
|
b6969ef434 | ||
|
fee2d30124 | ||
|
cc6448b66b | ||
|
1e1b082930 | ||
|
92e6602053 |
@ -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->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->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->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->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->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->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->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->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 |
@ -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->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->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->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->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->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->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->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->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->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->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->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->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->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->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->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->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 |
758
docs/reference/glib/Sorted_binary_tree_inorder-dark.svg
Normal file
After Width: | Height: | Size: 36 KiB |
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 36 KiB |
755
docs/reference/glib/Sorted_binary_tree_postorder-dark.svg
Normal file
After Width: | Height: | Size: 36 KiB |
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 36 KiB |
755
docs/reference/glib/Sorted_binary_tree_preorder-dark.svg
Normal file
After Width: | Height: | Size: 36 KiB |
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 36 KiB |
@ -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]]
|
||||
|
@ -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 wasn’t returned from
|
||||
* [method@Gio.DBusConnection.export_action_group] or to call it with the same
|
||||
* ID more than once.
|
||||
*
|
||||
* Since: 2.32
|
||||
**/
|
||||
|
@ -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
|
||||
*/
|
||||
|
392
gio/gappinfo.c
@ -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 there’s 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 it’s 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 application’s 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 executable’s 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 @appinfo’s 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 @appinfo’s
|
||||
* 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-Bus–activated 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-Bus–activated applications don't have to be started if your application
|
||||
* The D-Bus–activated applications don’t 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-Bus–activated
|
||||
* 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 wasn’t 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 child’s 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 child’s 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 child’s 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.
|
||||
*
|
||||
|
@ -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.
|
||||
*/
|
||||
|
@ -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 it’s 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 we’re 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,
|
||||
|
@ -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
@ -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
@ -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__ */
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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>
|
||||
|
@ -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);
|
||||
|
@ -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",
|
||||
|
@ -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',
|
||||
|
@ -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
|
||||
|
@ -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 ();
|
||||
}
|
||||
|
@ -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) ||
|
||||
|
@ -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
|
||||
|
34
glib/gnode.c
@ -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>
|
||||
*/
|
||||
|
||||
/**
|
||||
|