[libgda] GdaThreadWrapper: rewrite
- From: Vivien Malerba <vivien src gnome org>
- To: svn-commits-list gnome org
- Cc:
- Subject: [libgda] GdaThreadWrapper: rewrite
- Date: Sun, 2 Aug 2009 20:22:36 +0000 (UTC)
commit 47482eec48cf0fafcc45a1622e99bd2b38291c07
Author: Vivien Malerba <malerba gnome-db org>
Date: Sun Aug 2 22:20:36 2009 +0200
GdaThreadWrapper: rewrite
* simplified the design
* corrected bugs
* added gda_thread_wrapper_cancel
doc/C/Makefile.am | 4 +-
doc/C/libgda-sections.txt | 1 +
doc/C/thread-wrapper.png | Bin 0 -> 69647 bytes
doc/C/thread-wrapper.svg | 821 ++++++++++++++++++++++++++
doc/C/tmpl/gda-thread-wrapper.sgml | 48 ++-
libgda/libgda.symbols | 1 +
libgda/thread-wrapper/gda-thread-wrapper.c | 593 +++++++++++--------
libgda/thread-wrapper/gda-thread-wrapper.h | 1 +
tests/multi-threading/check_wrapper.c | 95 ++--
tests/multi-threading/multi_check_wrapper.sh | 13 +
10 files changed, 1288 insertions(+), 289 deletions(-)
---
diff --git a/doc/C/Makefile.am b/doc/C/Makefile.am
index f8eafa5..227d13b 100644
--- a/doc/C/Makefile.am
+++ b/doc/C/Makefile.am
@@ -62,7 +62,7 @@ HTML_IMAGES = DataModels.png \
gda-sql-graph.png howto-exec.png \
parser_gen.png parser_prov.png \
tree-overview.png tree-overview2.png \
- SqlIdentifiers.png \
+ SqlIdentifiers.png thread-wrapper.png \
vi-basic-form.png vi-combo.png vi-data-entry.png
# Extra options to supply to gtkdoc-fixref
@@ -76,7 +76,7 @@ EXTRA_DIST += examples/full_example.c installation.xml limitations.xml migration
prov-writing.xml packaging.xml packaging_ui.xml i_s_doc.xml howto.xml gda-sql-manual.xml data_validation.xml data_select.xml \
DataModels.svg \
architecture.svg parts.svg stmt-unknown.svg stmt-select.svg stmt-insert1.svg stmt-insert2.svg \
- stmt-update.svg stmt-compound.svg information_schema.svg howto-exec.svg \
+ stmt-update.svg stmt-compound.svg information_schema.svg howto-exec.svg thread-wrapper.svg \
version.xml.in \
visual_index.xml
diff --git a/doc/C/libgda-sections.txt b/doc/C/libgda-sections.txt
index acd49b4..8a7a215 100644
--- a/doc/C/libgda-sections.txt
+++ b/doc/C/libgda-sections.txt
@@ -1693,6 +1693,7 @@ GdaThreadWrapperFunc
gda_thread_wrapper_execute
GdaThreadWrapperVoidFunc
gda_thread_wrapper_execute_void
+gda_thread_wrapper_cancel
gda_thread_wrapper_iterate
gda_thread_wrapper_fetch_result
gda_thread_wrapper_get_waiting_size
diff --git a/doc/C/thread-wrapper.png b/doc/C/thread-wrapper.png
new file mode 100644
index 0000000..b2228ab
Binary files /dev/null and b/doc/C/thread-wrapper.png differ
diff --git a/doc/C/thread-wrapper.svg b/doc/C/thread-wrapper.svg
new file mode 100644
index 0000000..88857cf
--- /dev/null
+++ b/doc/C/thread-wrapper.svg
@@ -0,0 +1,821 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="210mm"
+ height="297mm"
+ id="svg2"
+ sodipodi:version="0.32"
+ inkscape:version="0.46"
+ sodipodi:docname="thread-wrapper.svg"
+ inkscape:output_extension="org.inkscape.output.svg.inkscape">
+ <defs
+ id="defs4">
+ <marker
+ inkscape:stockid="Arrow2Lend"
+ orient="auto"
+ refY="0.0"
+ refX="0.0"
+ id="Arrow2Lend"
+ style="overflow:visible;">
+ <path
+ id="path3420"
+ style="font-size:12.0;fill-rule:evenodd;stroke-width:0.62500000;stroke-linejoin:round;"
+ d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
+ transform="scale(1.1) rotate(180) translate(1,0)" />
+ </marker>
+ <inkscape:perspective
+ sodipodi:type="inkscape:persp3d"
+ inkscape:vp_x="0 : 526.18109 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_z="744.09448 : 526.18109 : 1"
+ inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
+ id="perspective10" />
+ <filter
+ inkscape:collect="always"
+ id="filter5378"
+ x="-4.2753608"
+ width="9.5507215"
+ y="-0.10678208"
+ height="1.2135642">
+ <feGaussianBlur
+ inkscape:collect="always"
+ stdDeviation="8.5507219"
+ id="feGaussianBlur5380" />
+ </filter>
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="1.4"
+ inkscape:cx="210.13568"
+ inkscape:cy="782.13507"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ showgrid="true"
+ inkscape:snap-global="false"
+ inkscape:window-width="1280"
+ inkscape:window-height="975"
+ inkscape:window-x="0"
+ inkscape:window-y="24">
+ <inkscape:grid
+ type="xygrid"
+ id="grid2385" />
+ </sodipodi:namedview>
+ <metadata
+ id="metadata7">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Calque 1"
+ inkscape:groupmode="layer"
+ id="layer1">
+ <path
+ style="opacity:0.50488597999999996;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#85a0dd;stroke-width:4.8;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;filter:url(#filter5378);marker-end:none"
+ d="M 311.63205,85.646196 L 311.63205,273.02949"
+ id="path5232"
+ inkscape:export-filename="/local/TESTS/V4/A/doc/C/thread-wrapper.png"
+ inkscape:export-xdpi="117.21"
+ inkscape:export-ydpi="117.21" />
+ <path
+ style="opacity:0.98235294;fill:#ffe200;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:0.80337358;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="M 226.06457,103.65075 L 225.8251,109.91475 L 69.721743,109.91475 C 69.247925,109.91475 68.866478,110.12018 68.866478,110.37533 L 68.866478,118.38957 C 68.866478,118.38957 69.247925,118.85016 69.721743,118.85016 L 225.75668,118.85016 L 225.89351,125.13258 L 243.40942,119.974 L 260.54898,114.46535 L 243.47782,108.90145 L 226.06457,103.65075 z"
+ id="rect2383"
+ sodipodi:nodetypes="cccccccccccc"
+ inkscape:export-filename="/local/TESTS/V4/A/doc/C/thread-wrapper.png"
+ inkscape:export-xdpi="117.21"
+ inkscape:export-ydpi="117.21" />
+ <g
+ id="g2405"
+ transform="translate(242.4366,-20.960666)"
+ inkscape:export-filename="/local/TESTS/V4/A/doc/C/thread-wrapper.png"
+ inkscape:export-xdpi="117.21"
+ inkscape:export-ydpi="117.21">
+ <rect
+ ry="0.78125"
+ rx="0.78125"
+ y="81.10051"
+ x="42.426407"
+ height="25.75889"
+ width="53.538086"
+ id="rect2407"
+ style="opacity:0.98235294;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ <text
+ id="text2409"
+ y="92.212181"
+ x="55.053314"
+ style="font-size:9px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
+ xml:space="preserve"><tspan
+ y="92.212181"
+ x="55.053314"
+ id="tspan2411"
+ sodipodi:role="line">Worker</tspan><tspan
+ id="tspan2413"
+ y="103.46218"
+ x="55.053314"
+ sodipodi:role="line">thread</tspan></text>
+ </g>
+ <g
+ id="g3320"
+ inkscape:export-filename="/local/TESTS/V4/A/doc/C/thread-wrapper.png"
+ inkscape:export-xdpi="117.21"
+ inkscape:export-ydpi="117.21">
+ <rect
+ ry="0.78125"
+ rx="0.78125"
+ y="127.9819"
+ x="96.729729"
+ height="24.930155"
+ width="33.515465"
+ id="rect2415"
+ style="opacity:0.98235293999999995;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:0.81858264999999997;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ <text
+ id="text2417"
+ y="135.08116"
+ x="108.12164"
+ style="font-size:6.3742671px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
+ xml:space="preserve"><tspan
+ y="135.08116"
+ x="108.12164"
+ id="tspan2419"
+ sodipodi:role="line">Job</tspan></text>
+ <path
+ id="path2421"
+ d="M 94.616735,138.78983 L 94.691583,141.71095 L 135.35732,141.71095 C 135.50543,141.71095 135.62465,141.80675 135.62465,141.92574 L 135.62465,145.66306 C 135.62465,145.78205 135.50543,145.87784 135.35732,145.87784 L 94.712979,145.87784 L 94.670208,148.80755 L 89.19535,146.40193 L 83.838125,143.83305 L 89.173967,141.23842 L 94.616735,138.78983 z"
+ style="opacity:0.98235293999999995;fill:#ffaaaa;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:0.30671608000000000;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ </g>
+ <g
+ id="g3326"
+ inkscape:export-filename="/local/TESTS/V4/A/doc/C/thread-wrapper.png"
+ inkscape:export-xdpi="117.21"
+ inkscape:export-ydpi="117.21">
+ <rect
+ ry="0.78125"
+ rx="0.78125"
+ y="127.72935"
+ x="157.20905"
+ height="24.930155"
+ width="33.515465"
+ id="rect2449"
+ style="opacity:0.98235293999999995;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:0.81858264999999997;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ <text
+ id="text2451"
+ y="134.8286"
+ x="168.60095"
+ style="font-size:6.37426709999999996px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
+ xml:space="preserve"><tspan
+ y="134.8286"
+ x="168.60095"
+ id="tspan2453"
+ sodipodi:role="line">Job</tspan></text>
+ <path
+ id="path2455"
+ d="M 155.09605,138.53728 L 155.1709,141.4584 L 195.83664,141.4584 C 195.98474,141.4584 196.10397,141.5542 196.10397,141.67319 L 196.10397,145.41051 C 196.10397,145.5295 195.98474,145.62529 195.83664,145.62529 L 155.19229,145.62529 L 155.14952,148.55501 L 149.67466,146.14938 L 144.31744,143.58051 L 149.65328,140.98587 L 155.09605,138.53728 z"
+ style="opacity:0.98235293999999995;fill:#afe9af;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:0.30671608000000000;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ </g>
+ <g
+ id="g3309"
+ transform="translate(-43.436559,99.500024)"
+ inkscape:export-filename="/local/TESTS/V4/A/doc/C/thread-wrapper.png"
+ inkscape:export-xdpi="117.21"
+ inkscape:export-ydpi="117.21">
+ <g
+ id="g3244">
+ <g
+ id="g3236">
+ <g
+ transform="translate(-25.001274,-35.860415)"
+ id="g2427">
+ <rect
+ style="opacity:0.98235294;fill:#ffaaaa;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="rect2391"
+ width="53.538086"
+ height="25.75889"
+ x="49.497475"
+ y="63.927917"
+ rx="0.78125"
+ ry="0.78125" />
+ <text
+ xml:space="preserve"
+ style="font-size:9px;font-style:normal;font-weight:normal;fill:#2b0000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
+ x="62.629459"
+ y="74.029434"
+ id="text2393"><tspan
+ sodipodi:role="line"
+ id="tspan2395"
+ x="62.629459"
+ y="74.029434">User</tspan><tspan
+ sodipodi:role="line"
+ x="62.629459"
+ y="85.279434"
+ id="tspan2397">thread</tspan></text>
+ </g>
+ <rect
+ ry="0.78125"
+ rx="0.78125"
+ y="21.52487"
+ x="5.5791974"
+ height="85.816246"
+ width="91.372093"
+ id="rect2457"
+ style="opacity:0.98235294;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1.04671741;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ </g>
+ <path
+ style="opacity:0.98235294;fill:#ffaaaa;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:0.30671608;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="M 48.019881,93.769315 L 48.094729,96.690435 L 88.760466,96.690435 C 88.908576,96.690435 89.027796,96.786235 89.027796,96.905225 L 89.027796,100.64255 C 89.027796,100.76154 88.908576,100.85733 88.760466,100.85733 L 48.116125,100.85733 L 48.073354,103.78704 L 42.598496,101.38142 L 37.241271,98.812535 L 42.577113,96.217905 L 48.019881,93.769315 z"
+ id="path3234" />
+ </g>
+ </g>
+ <g
+ id="g3301"
+ transform="translate(-43.436559,-135.86551)"
+ inkscape:export-filename="/local/TESTS/V4/A/doc/C/thread-wrapper.png"
+ inkscape:export-xdpi="117.21"
+ inkscape:export-ydpi="117.21">
+ <rect
+ ry="0.78125"
+ rx="0.78125"
+ y="172.01425"
+ x="25.253813"
+ height="25.75889"
+ width="53.538086"
+ id="rect3260"
+ style="opacity:0.98235294;fill:#afe9af;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ <text
+ id="text3262"
+ y="182.11577"
+ x="38.385796"
+ style="font-size:9px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
+ xml:space="preserve"><tspan
+ y="182.11577"
+ x="38.385796"
+ id="tspan3264"
+ sodipodi:role="line">User</tspan><tspan
+ id="tspan3266"
+ y="193.36577"
+ x="38.385796"
+ sodipodi:role="line">thread</tspan></text>
+ <rect
+ ry="0.78125"
+ rx="0.78125"
+ y="165.4716"
+ x="6.3368092"
+ height="85.816246"
+ width="91.372093"
+ id="rect3268"
+ style="opacity:0.98235294;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1.04671741;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ <path
+ id="path3270"
+ d="M 48.777493,237.71606 L 48.852341,240.63718 L 89.518078,240.63718 C 89.666188,240.63718 89.785408,240.73298 89.785408,240.85197 L 89.785408,244.58929 C 89.785408,244.70828 89.666188,244.80407 89.518078,244.80407 L 48.873737,244.80407 L 48.830966,247.73378 L 43.356108,245.32816 L 37.998883,242.75928 L 43.334725,240.16465 L 48.777493,237.71606 z"
+ style="opacity:0.98235294;fill:#afe9af;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:0.30671608;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ </g>
+ <rect
+ style="opacity:0.98235294;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:0.96782947;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="rect3338"
+ width="200.04237"
+ height="215.69974"
+ x="62.108295"
+ y="10.878822"
+ rx="0.78125"
+ ry="0.78125"
+ inkscape:export-filename="/local/TESTS/V4/A/doc/C/thread-wrapper.png"
+ inkscape:export-xdpi="117.21"
+ inkscape:export-ydpi="117.21" />
+ <g
+ id="g3350"
+ transform="translate(199.88037,-18.435293)"
+ inkscape:export-filename="/local/TESTS/V4/A/doc/C/thread-wrapper.png"
+ inkscape:export-xdpi="117.21"
+ inkscape:export-ydpi="117.21">
+ <rect
+ ry="0.78125"
+ rx="0.78125"
+ y="127.9819"
+ x="96.729729"
+ height="24.930155"
+ width="33.515465"
+ id="rect3352"
+ style="opacity:0.98235294;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:0.81858265;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ <text
+ id="text3354"
+ y="135.08116"
+ x="108.12164"
+ style="font-size:6.3742671px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
+ xml:space="preserve"><tspan
+ y="135.08116"
+ x="108.12164"
+ id="tspan3356"
+ sodipodi:role="line">Job</tspan></text>
+ <path
+ id="path3358"
+ d="M 94.616735,138.78983 L 94.691583,141.71095 L 135.35732,141.71095 C 135.50543,141.71095 135.62465,141.80675 135.62465,141.92574 L 135.62465,145.66306 C 135.62465,145.78205 135.50543,145.87784 135.35732,145.87784 L 94.712979,145.87784 L 94.670208,148.80755 L 89.19535,146.40193 L 83.838125,143.83305 L 89.173967,141.23842 L 94.616735,138.78983 z"
+ style="opacity:0.98235294;fill:#ffaaaa;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:0.30671608;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ </g>
+ <text
+ xml:space="preserve"
+ style="font-size:8px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
+ x="75.761444"
+ y="160.90256"
+ id="text3360"
+ inkscape:export-filename="/local/TESTS/V4/A/doc/C/thread-wrapper.png"
+ inkscape:export-xdpi="117.21"
+ inkscape:export-ydpi="117.21"><tspan
+ sodipodi:role="line"
+ id="tspan3362"
+ x="75.761444"
+ y="160.90256">Jobs are pushed into the Queue</tspan><tspan
+ sodipodi:role="line"
+ x="75.761444"
+ y="170.90256"
+ id="tspan3364">when executing a function</tspan><tspan
+ sodipodi:role="line"
+ x="75.761444"
+ y="180.90256"
+ id="tspan3366">is requested</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:9px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
+ x="115.66247"
+ y="23.016739"
+ id="text3368"
+ inkscape:export-filename="/local/TESTS/V4/A/doc/C/thread-wrapper.png"
+ inkscape:export-xdpi="117.21"
+ inkscape:export-ydpi="117.21"><tspan
+ sodipodi:role="line"
+ id="tspan3370"
+ x="115.66247"
+ y="23.016739">GdaThreadWrapper</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:8px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
+ x="302.54068"
+ y="163.93301"
+ id="text3372"
+ inkscape:export-filename="/local/TESTS/V4/A/doc/C/thread-wrapper.png"
+ inkscape:export-xdpi="117.21"
+ inkscape:export-ydpi="117.21"><tspan
+ sodipodi:role="line"
+ id="tspan3374"
+ x="302.54068"
+ y="163.93301">Jobs are poped</tspan><tspan
+ sodipodi:role="line"
+ x="302.54068"
+ y="173.93301"
+ id="tspan3376">from Queue, then their</tspan><tspan
+ sodipodi:role="line"
+ x="302.54068"
+ y="183.93301"
+ id="tspan3378">function is executed</tspan><tspan
+ sodipodi:role="line"
+ x="302.54068"
+ y="193.93303"
+ id="tspan3380">and they are pushed back</tspan><tspan
+ sodipodi:role="line"
+ x="302.54068"
+ y="203.93303"
+ id="tspan3382">to the job's reply queue</tspan></text>
+ <g
+ id="g3384"
+ transform="translate(121.59355,-0.7576231)"
+ style="opacity:0.39413681"
+ inkscape:export-filename="/local/TESTS/V4/A/doc/C/thread-wrapper.png"
+ inkscape:export-xdpi="117.21"
+ inkscape:export-ydpi="117.21">
+ <rect
+ ry="0.78125"
+ rx="0.78125"
+ y="127.9819"
+ x="96.729729"
+ height="24.930155"
+ width="33.515465"
+ id="rect3386"
+ style="opacity:0.98235293999999995;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:0.81858264999999986;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ <text
+ id="text3388"
+ y="135.08116"
+ x="108.12164"
+ style="font-size:6.37426709999999996px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
+ xml:space="preserve"><tspan
+ y="135.08116"
+ x="108.12164"
+ id="tspan3390"
+ sodipodi:role="line">Job</tspan></text>
+ <path
+ id="path3392"
+ d="M 94.616735,138.78983 L 94.691583,141.71095 L 135.35732,141.71095 C 135.50543,141.71095 135.62465,141.80675 135.62465,141.92574 L 135.62465,145.66306 C 135.62465,145.78205 135.50543,145.87784 135.35732,145.87784 L 94.712979,145.87784 L 94.670208,148.80755 L 89.19535,146.40193 L 83.838125,143.83305 L 89.173967,141.23842 L 94.616735,138.78983 z"
+ style="opacity:0.98235293999999995;fill:#ffaaaa;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:0.30671608000000000;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ </g>
+ <path
+ style="fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;marker-end:url(#Arrow2Lend);stroke-opacity:1"
+ d="M 259.95281,138.87387 L 281.48489,127.85939"
+ id="path3394"
+ inkscape:export-filename="/local/TESTS/V4/A/doc/C/thread-wrapper.png"
+ inkscape:export-xdpi="117.21"
+ inkscape:export-ydpi="117.21" />
+ <path
+ style="fill:none;fill-opacity:0.75000000000000000;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-end:url(#Arrow2Lend)"
+ d="M 289.91378,233.12847 C 289.91378,233.12847 241.71304,252.83728 160.10918,252.32137 C 92.818992,251.89595 48.992398,198.78328 48.992398,198.78328"
+ id="path4691"
+ sodipodi:nodetypes="csc"
+ inkscape:export-filename="/local/TESTS/V4/A/doc/C/thread-wrapper.png"
+ inkscape:export-xdpi="117.21"
+ inkscape:export-ydpi="117.21" />
+ <text
+ xml:space="preserve"
+ style="font-size:8px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
+ x="-22.223356"
+ y="-18.399523"
+ id="text5218"
+ inkscape:export-filename="/local/TESTS/V4/A/doc/C/thread-wrapper.png"
+ inkscape:export-xdpi="117.21"
+ inkscape:export-ydpi="117.21"><tspan
+ sodipodi:role="line"
+ x="-22.223356"
+ y="-18.399523"
+ id="tspan5222">Arrow are GASyncQueue objects, here 3 are created:</tspan><tspan
+ sodipodi:role="line"
+ x="-22.223356"
+ y="-8.3995228"
+ id="tspan5226">* one for the communication to the worker thread (in yellow)</tspan><tspan
+ sodipodi:role="line"
+ x="-22.223356"
+ y="1.6004776"
+ id="tspan5228">* one for each user thread using the GdaThreadWrapper object</tspan><tspan
+ sodipodi:role="line"
+ x="-22.223356"
+ y="11.600477"
+ id="tspan5230"> (in red and green)</tspan></text>
+ <g
+ id="g6420"
+ transform="translate(197.86006,81.064733)"
+ style="opacity:0.39413686"
+ inkscape:export-filename="/local/TESTS/V4/A/doc/C/thread-wrapper.png"
+ inkscape:export-xdpi="117.21"
+ inkscape:export-ydpi="117.21">
+ <rect
+ ry="0.78125"
+ rx="0.78125"
+ y="127.9819"
+ x="96.729729"
+ height="24.930155"
+ width="33.515465"
+ id="rect6422"
+ style="opacity:0.98235294;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:0.81858265;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ <text
+ id="text6424"
+ y="135.08116"
+ x="108.12164"
+ style="font-size:6.3742671px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
+ xml:space="preserve"><tspan
+ y="135.08116"
+ x="108.12164"
+ id="tspan6426"
+ sodipodi:role="line">Job</tspan></text>
+ <path
+ id="path6428"
+ d="M 94.616735,138.78983 L 94.691583,141.71095 L 135.35732,141.71095 C 135.50543,141.71095 135.62465,141.80675 135.62465,141.92574 L 135.62465,145.66306 C 135.62465,145.78205 135.50543,145.87784 135.35732,145.87784 L 94.712979,145.87784 L 94.670208,148.80755 L 89.19535,146.40193 L 83.838125,143.83305 L 89.173967,141.23842 L 94.616735,138.78983 z"
+ style="opacity:0.98235294;fill:#ffaaaa;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:0.30671608;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ </g>
+ <g
+ id="g6430"
+ transform="translate(-80.942042,83.085038)"
+ inkscape:export-filename="/local/TESTS/V4/A/doc/C/thread-wrapper.png"
+ inkscape:export-xdpi="117.21"
+ inkscape:export-ydpi="117.21">
+ <rect
+ ry="0.78125"
+ rx="0.78125"
+ y="127.9819"
+ x="96.729729"
+ height="24.930155"
+ width="33.515465"
+ id="rect6432"
+ style="opacity:0.98235294;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:0.81858265;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ <text
+ id="text6434"
+ y="135.08116"
+ x="108.12164"
+ style="font-size:6.3742671px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
+ xml:space="preserve"><tspan
+ y="135.08116"
+ x="108.12164"
+ id="tspan6436"
+ sodipodi:role="line">Job</tspan></text>
+ <path
+ id="path6438"
+ d="M 94.616735,138.78983 L 94.691583,141.71095 L 135.35732,141.71095 C 135.50543,141.71095 135.62465,141.80675 135.62465,141.92574 L 135.62465,145.66306 C 135.62465,145.78205 135.50543,145.87784 135.35732,145.87784 L 94.712979,145.87784 L 94.670208,148.80755 L 89.19535,146.40193 L 83.838125,143.83305 L 89.173967,141.23842 L 94.616735,138.78983 z"
+ style="opacity:0.98235294;fill:#ffaaaa;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:0.30671608;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ </g>
+ <g
+ id="g6464"
+ transform="translate(31.314729,219.70818)"
+ inkscape:export-filename="/local/TESTS/V4/A/doc/C/thread-wrapper.png"
+ inkscape:export-xdpi="117.21"
+ inkscape:export-ydpi="117.21">
+ <text
+ id="text6440"
+ y="-30.833584"
+ x="107.62464"
+ style="font-size:13px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
+ xml:space="preserve"><tspan
+ y="-30.833584"
+ x="107.62464"
+ id="tspan6442"
+ sodipodi:role="line">1</tspan></text>
+ <path
+ d="M 122.73354,-35.572109 A 10.859139,10.606602 0 1 1 101.01526,-35.572109 A 10.859139,10.606602 0 1 1 122.73354,-35.572109 z"
+ sodipodi:ry="10.606602"
+ sodipodi:rx="10.859139"
+ sodipodi:cy="-35.572109"
+ sodipodi:cx="111.8744"
+ id="path6444"
+ style="opacity:0.50488598;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#85a0dd;stroke-width:4.80000019;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ sodipodi:type="arc" />
+ </g>
+ <g
+ id="g6469"
+ transform="translate(127.7843,142.93658)"
+ inkscape:export-filename="/local/TESTS/V4/A/doc/C/thread-wrapper.png"
+ inkscape:export-xdpi="117.21"
+ inkscape:export-ydpi="117.21">
+ <text
+ id="text6446"
+ y="-34.874195"
+ x="149.29343"
+ style="font-size:13px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
+ xml:space="preserve"><tspan
+ y="-34.874195"
+ x="149.29343"
+ id="tspan6448"
+ sodipodi:role="line">2</tspan></text>
+ <path
+ transform="translate(41.66879,-4.0406099)"
+ d="M 122.73354,-35.572109 A 10.859139,10.606602 0 1 1 101.01526,-35.572109 A 10.859139,10.606602 0 1 1 122.73354,-35.572109 z"
+ sodipodi:ry="10.606602"
+ sodipodi:rx="10.859139"
+ sodipodi:cy="-35.572109"
+ sodipodi:cx="111.8744"
+ id="path6450"
+ style="opacity:0.50488598;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#85a0dd;stroke-width:4.80000019;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ sodipodi:type="arc" />
+ </g>
+ <g
+ id="g6474"
+ transform="translate(89.398505,284.35794)"
+ inkscape:export-filename="/local/TESTS/V4/A/doc/C/thread-wrapper.png"
+ inkscape:export-xdpi="117.21"
+ inkscape:export-ydpi="117.21">
+ <text
+ id="text6452"
+ y="-23.257441"
+ x="181.61832"
+ style="font-size:13px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
+ xml:space="preserve"><tspan
+ y="-23.257441"
+ x="181.61832"
+ id="tspan6454"
+ sodipodi:role="line">3</tspan></text>
+ <path
+ transform="translate(73.993671,7.576144)"
+ d="M 122.73354,-35.572109 A 10.859139,10.606602 0 1 1 101.01526,-35.572109 A 10.859139,10.606602 0 1 1 122.73354,-35.572109 z"
+ sodipodi:ry="10.606602"
+ sodipodi:rx="10.859139"
+ sodipodi:cy="-35.572109"
+ sodipodi:cx="111.8744"
+ id="path6456"
+ style="opacity:0.50488598;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#85a0dd;stroke-width:4.80000019;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ sodipodi:type="arc" />
+ </g>
+ <g
+ id="g6479"
+ transform="translate(-235.36554,256.07367)"
+ inkscape:export-filename="/local/TESTS/V4/A/doc/C/thread-wrapper.png"
+ inkscape:export-xdpi="117.21"
+ inkscape:export-ydpi="117.21">
+ <text
+ id="text6458"
+ y="-27.298048"
+ x="218.99396"
+ style="font-size:13px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
+ xml:space="preserve"><tspan
+ y="-27.298048"
+ x="218.99396"
+ id="tspan6460"
+ sodipodi:role="line">4</tspan></text>
+ <path
+ transform="translate(111.36932,3.5355339)"
+ d="M 122.73354,-35.572109 A 10.859139,10.606602 0 1 1 101.01526,-35.572109 A 10.859139,10.606602 0 1 1 122.73354,-35.572109 z"
+ sodipodi:ry="10.606602"
+ sodipodi:rx="10.859139"
+ sodipodi:cy="-35.572109"
+ sodipodi:cx="111.8744"
+ id="path6462"
+ style="opacity:0.50488598;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#85a0dd;stroke-width:4.80000019;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ sodipodi:type="arc" />
+ </g>
+ <text
+ xml:space="preserve"
+ style="font-size:8px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
+ x="-54.150032"
+ y="243.51823"
+ id="text6484"
+ inkscape:export-filename="/local/TESTS/V4/A/doc/C/thread-wrapper.png"
+ inkscape:export-xdpi="117.21"
+ inkscape:export-ydpi="117.21"><tspan
+ sodipodi:role="line"
+ x="-54.150032"
+ y="243.51823"
+ id="tspan6490">Completed jobs os signals </tspan><tspan
+ sodipodi:role="line"
+ x="-54.150032"
+ y="243.51823"
+ id="tspan4377">are waiting to be</tspan><tspan
+ sodipodi:role="line"
+ x="-54.150032"
+ y="253.51823"
+ id="tspan6496">analysed by the user thread</tspan></text>
+ <g
+ id="g3261"
+ transform="translate(-208.5965,124.24876)"
+ inkscape:export-filename="/local/TESTS/V4/A/doc/C/thread-wrapper.png"
+ inkscape:export-xdpi="117"
+ inkscape:export-ydpi="117">
+ <rect
+ ry="0.78125"
+ rx="0.78125"
+ y="218.48126"
+ x="390.92905"
+ height="33.335033"
+ width="46.972092"
+ id="rect2477"
+ style="opacity:1;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ <text
+ id="text3255"
+ y="231.10817"
+ x="401.53561"
+ style="font-size:8px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
+ xml:space="preserve"><tspan
+ y="231.10817"
+ x="401.53561"
+ id="tspan3257"
+ sodipodi:role="line">Some</tspan><tspan
+ id="tspan3259"
+ y="241.10817"
+ x="401.53561"
+ sodipodi:role="line">object</tspan></text>
+ </g>
+ <path
+ style="fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#000000;stroke-width:0.55331099px;stroke-linecap:butt;stroke-linejoin:miter;marker-end:url(#Arrow2Lend);stroke-opacity:1"
+ d="M 230.58237,358.61073 L 251.54218,346.59377 L 241.76094,356.93395 L 262.16183,347.99109"
+ id="path3267"
+ inkscape:export-filename="/local/TESTS/V4/A/doc/C/thread-wrapper.png"
+ inkscape:export-xdpi="117"
+ inkscape:export-ydpi="117" />
+ <g
+ id="g3819"
+ transform="translate(-31.314728,64.649762)"
+ inkscape:export-filename="/local/TESTS/V4/A/doc/C/thread-wrapper.png"
+ inkscape:export-xdpi="117"
+ inkscape:export-ydpi="117">
+ <rect
+ ry="0.78125"
+ rx="0.78125"
+ y="248.94766"
+ x="355.19894"
+ height="24.930155"
+ width="33.515465"
+ id="rect3786"
+ style="opacity:0.98235294;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:0.81858265;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ <text
+ id="text3788"
+ y="255.54185"
+ x="363.05533"
+ style="font-size:6.3742671px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
+ xml:space="preserve"><tspan
+ y="255.54185"
+ x="363.05533"
+ id="tspan3790"
+ sodipodi:role="line">Job</tspan><tspan
+ id="tspan3798"
+ y="263.50967"
+ x="363.05533"
+ sodipodi:role="line">as</tspan><tspan
+ id="tspan3800"
+ y="271.47751"
+ x="363.05533"
+ sodipodi:role="line">signal</tspan></text>
+ </g>
+ <g
+ id="g3812"
+ transform="translate(-67.175143,1.5152291)"
+ inkscape:export-filename="/local/TESTS/V4/A/doc/C/thread-wrapper.png"
+ inkscape:export-xdpi="117"
+ inkscape:export-ydpi="117">
+ <path
+ id="path3792"
+ d="M 333.38797,360.26577 L 333.46282,363.18689 L 374.12856,363.18689 C 374.27667,363.18689 374.39589,363.28269 374.39589,363.40168 L 374.39589,367.139 C 374.39589,367.25799 374.27667,367.35378 374.12856,367.35378 L 333.48422,367.35378 L 333.44145,370.28349 L 327.96659,367.87787 L 322.60936,365.30899 L 327.94521,362.71436 L 333.38797,360.26577 z"
+ style="opacity:0.98235294;fill:#ffaaaa;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:0.30671608;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ <rect
+ ry="0.78125"
+ rx="0.78125"
+ y="320.16827"
+ x="329.98148"
+ height="58.255516"
+ width="38.052467"
+ id="rect3804"
+ style="opacity:1;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ <text
+ id="text3806"
+ y="333.63867"
+ x="334.3605"
+ style="font-size:8px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
+ xml:space="preserve"><tspan
+ y="333.63867"
+ x="334.3605"
+ id="tspan3808"
+ sodipodi:role="line">signal</tspan><tspan
+ id="tspan3810"
+ y="343.63867"
+ x="334.3605"
+ sodipodi:role="line">handler</tspan></text>
+ </g>
+ <path
+ style="fill:none;fill-opacity:0.75000000000000000;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-end:url(#Arrow2Lend)"
+ d="M 305.06607,358.38739 C 305.06607,358.38739 331.45452,370.15749 339.41126,363.43815 C 344.622,359.03776 339.91633,340.70972 339.91633,340.70972"
+ id="path3826"
+ sodipodi:nodetypes="csc"
+ inkscape:export-filename="/local/TESTS/V4/A/doc/C/thread-wrapper.png"
+ inkscape:export-xdpi="117"
+ inkscape:export-ydpi="117" />
+ <path
+ style="fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="M 318.70313,313.4356 C 318.70313,313.4356 190.80669,297.55357 141.92643,274.54473 C 109.81338,259.42853 48.487322,198.78329 48.487322,198.78329"
+ id="path4347"
+ sodipodi:nodetypes="csc"
+ inkscape:export-filename="/local/TESTS/V4/A/doc/C/thread-wrapper.png"
+ inkscape:export-xdpi="117"
+ inkscape:export-ydpi="117" />
+ <text
+ xml:space="preserve"
+ style="font-size:8px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
+ x="99.39315"
+ y="314.22894"
+ id="text4349"
+ inkscape:export-filename="/local/TESTS/V4/A/doc/C/thread-wrapper.png"
+ inkscape:export-xdpi="117.21"
+ inkscape:export-ydpi="117.21"><tspan
+ sodipodi:role="line"
+ x="99.39315"
+ y="314.22894"
+ id="tspan4355">When a signal is connected , to be caught</tspan><tspan
+ sodipodi:role="line"
+ x="99.39315"
+ y="324.22894"
+ id="tspan4361">by the worker thread, it is handled by</tspan><tspan
+ sodipodi:role="line"
+ x="99.39315"
+ y="334.22894"
+ id="tspan4363">a signal handler which creates</tspan><tspan
+ sodipodi:role="line"
+ x="99.39315"
+ y="344.22894"
+ id="tspan4365">a new Job as signal</tspan><tspan
+ sodipodi:role="line"
+ x="99.39315"
+ y="354.22894"
+ id="tspan4367">and pushed to</tspan><tspan
+ sodipodi:role="line"
+ x="99.39315"
+ y="364.22894"
+ id="tspan4369">the queue</tspan><tspan
+ sodipodi:role="line"
+ x="99.39315"
+ y="374.22894"
+ id="tspan4371">specified when</tspan><tspan
+ sodipodi:role="line"
+ x="99.39315"
+ y="384.22894"
+ id="tspan4373">the signal was</tspan><tspan
+ sodipodi:role="line"
+ x="99.39315"
+ y="394.22894"
+ id="tspan4375">connected</tspan></text>
+ </g>
+</svg>
diff --git a/doc/C/tmpl/gda-thread-wrapper.sgml b/doc/C/tmpl/gda-thread-wrapper.sgml
index 2e832bd..63eb1e4 100644
--- a/doc/C/tmpl/gda-thread-wrapper.sgml
+++ b/doc/C/tmpl/gda-thread-wrapper.sgml
@@ -7,18 +7,50 @@ Execute functions in a sub thread
<!-- ##### SECTION Long_Description ##### -->
<para>
The purpose of the #GdaThreadWrapper object is to execute functions in an isolated sub thread. As the
- #GdaThreadWrapper is thread safe, one is able to isolate some code's execution is a <emphasis>private</emphasis> thread, and
- make a non thread safe code thread safe.
+ #GdaThreadWrapper is thread safe, one is able to isolate some code's execution is a <emphasis>private</emphasis>
+ <emphasis>worker</emphasis> thread, and make a non thread safe code thread safe.
</para>
<para>
The downside of this is that the actual execution of the code will be slower as it requires
threads to be synchronized.
</para>
<para>
+ The #GdaThreadWrapper implements its own locking mechanism and can safely be used from multiple
+ threads at once without needing further locking.
+</para>
+<para>
Each thread using a #GdaThreadWrapper object can use it as if it was the only user: the #GdaThreadWrapper will
- simply dispatch all the execution requests to its <emphasis>private</emphasis> sub thread and report the
+ simply dispatch all the execution requests to its private <emphasis>worker</emphasis> thread and report the
execution's status only to the thread which made the request.
</para>
+<para>
+ The user can also specify a callback function to be called when an object exmits a signal while being
+ used by the worker thread, see the gda_thread_wrapper_connect_raw() method.
+</para>
+<para>
+ The following diagram illustrates the conceptual working of the #GdaThreadWrapper object: here two user threads
+ are represented (assigned a red and green colors), both using a single #GdaThreadWrapper, so in this diagram, 3 threads
+ are present. The communication between the threads are handled by some #GAsyncQueue objects (in a transparent way for
+ the user, presented here only for illustration purposes). The queue represented in yellow is where jobs are
+ pushed by each user thread (step 1), and popped by the worker thread (step 2). Once the user thread has finished
+ with a job, it stores the result along with the job and pushes it to the queue dedicated to the user thread
+ (step 3) in this example the red queue (because the job was issued from the thread represented in red). The last
+ step is when the user fetches the result (in its user thread), step 4.
+</para>
+<para>
+ If, when the worker thread is busy with a job, a signal is emitted, and if the user has set up a signal handler
+ using gda_thread_wrapper_connect_raw(),
+ then a "job as signal" is created by the worker thread and pushed to the user thread as illustrated
+ at the bottom of the diagram.
+ <mediaobject>
+ <imageobject role="html">
+ <imagedata fileref="thread-wrapper.png" format="PNG" contentwidth="170mm"/>
+ </imageobject>
+ <textobject>
+ <phrase>GdaThreadWrapper's conceptual working</phrase>
+ </textobject>
+ </mediaobject>
+</para>
<!-- ##### SECTION See_Also ##### -->
<para>
@@ -87,6 +119,16 @@ Execute functions in a sub thread
@Returns:
+<!-- ##### FUNCTION gda_thread_wrapper_cancel ##### -->
+<para>
+
+</para>
+
+ wrapper:
+ id:
+ Returns:
+
+
<!-- ##### FUNCTION gda_thread_wrapper_iterate ##### -->
<para>
diff --git a/libgda/libgda.symbols b/libgda/libgda.symbols
index 6cc559d..f094e77 100644
--- a/libgda/libgda.symbols
+++ b/libgda/libgda.symbols
@@ -717,6 +717,7 @@
gda_string_to_binary
gda_string_to_blob
gda_text_to_alphanum
+ gda_thread_wrapper_cancel
gda_thread_wrapper_connect_raw
gda_thread_wrapper_disconnect
gda_thread_wrapper_execute
diff --git a/libgda/thread-wrapper/gda-thread-wrapper.c b/libgda/thread-wrapper/gda-thread-wrapper.c
index 2500149..04c0620 100644
--- a/libgda/thread-wrapper/gda-thread-wrapper.c
+++ b/libgda/thread-wrapper/gda-thread-wrapper.c
@@ -28,97 +28,64 @@
#include <libgda/gda-debug-macros.h>
#include <libgda/gda-value.h>
+/* this GPrivate holds a pointer to the GAsyncQueue used by the job being currently treated
+ * by the worker thread. It is used to avoid creating signal data for threads for which
+ * no job is being performed
+ */
+GStaticPrivate worker_thread_current_queue = G_STATIC_PRIVATE_INIT;
+
typedef struct _ThreadData ThreadData;
-typedef struct _SharedData SharedData;
typedef struct _Job Job;
-typedef struct _Result Result;
typedef struct _SignalSpec SignalSpec;
-typedef struct _SignalEmissionData SignalEmissionData;
struct _GdaThreadWrapperPrivate {
GdaMutex *mutex;
guint next_job_id;
- GThread *sub_thread;
- GAsyncQueue *to_sub_thread;
+ GThread *worker_thread;
+ GAsyncQueue *to_worker_thread;
- GHashTable *threads_hash; /* key = a GThread, value = a #ThreadData pointer */
+ GHashTable *threads_hash; /* key = a GThread, value = a #ThreadData pointer */
};
-/*
- * Data shared between the GdaThreadWrapper (and the threads which use it) and its sub thread.
- * It implements its own locking mechanism.
- */
-struct _SharedData {
- gint nb_waiting;
- Job *current_job;
- GThread *wrapper_sub_thread;
-
- gint ref_count;
- GMutex *mutex;
-};
-
-SharedData *
-shared_data_new (void)
-{
- SharedData *shd = g_new0 (SharedData, 1);
- shd->ref_count = 1;
- shd->nb_waiting = 0;
- shd->mutex = g_mutex_new ();
- return shd;
-}
-
-static SharedData *
-shared_data_ref (SharedData *shd)
-{
- g_mutex_lock (shd->mutex);
- shd->ref_count++;
- g_mutex_unlock (shd->mutex);
- return shd;
-}
-static void
-shared_data_unref (SharedData *shd)
-{
- g_mutex_lock (shd->mutex);
- shd->ref_count--;
- if (shd->ref_count == 0) {
- g_mutex_unlock (shd->mutex);
- g_mutex_free (shd->mutex);
- g_free (shd);
- }
- else
- g_mutex_unlock (shd->mutex);
-}
-
-static void
-shared_data_add_nb_waiting (SharedData *shd, gint to_add)
-{
- g_mutex_lock (shd->mutex);
- shd->nb_waiting += to_add;
- g_mutex_unlock (shd->mutex);
-}
-
-
/*
- * Jobs.
+ * One instance for each job to execute (and its result) and
+ * one instance for each emitted signal
+ *
* Created and destroyed exclusively by the thread(s) using the GdaThreadWrapper object,
* except for the job where job->type == JOB_TYPE_DESTROY which is destroyed by the sub thread.
*
- * Passed to the sub job through obj->to_sub_thread
+ * Passed to the sub job through obj->to_worker_thread
*/
typedef enum {
JOB_TYPE_EXECUTE,
- JOB_TYPE_DESTROY
+ JOB_TYPE_DESTROY,
+ JOB_TYPE_SIGNAL
} JobType;
struct _Job {
JobType type;
+ gboolean processed; /* TRUE when worker thread has started to work on it */
+ gboolean cancelled; /* TRUE when job has been cancelled before being executed */
guint job_id;
GdaThreadWrapperFunc func;
GdaThreadWrapperVoidFunc void_func;
gpointer arg;
GDestroyNotify arg_destroy_func;
GAsyncQueue *reply_queue; /* holds a ref to it */
- SharedData *shared; /* holds a ref to it */
+
+ /* result part */
+ union {
+ struct {
+ gpointer result;
+ GError *error;
+ } exe;
+ struct {
+ SignalSpec *spec;
+ guint n_param_values;
+ GValue *param_values; /* array of GValue structures */
+ } signal;
+ } u;
};
+#define JOB(x) ((Job*)(x))
static void
job_free (Job *job)
{
@@ -126,8 +93,25 @@ job_free (Job *job)
job->arg_destroy_func (job->arg);
if (job->reply_queue)
g_async_queue_unref (job->reply_queue);
- if (job->shared)
- shared_data_unref (job->shared);
+
+ if (job->type == JOB_TYPE_EXECUTE) {
+ if (job->u.exe.error)
+ g_error_free (job->u.exe.error);
+ }
+ else if (job->type == JOB_TYPE_SIGNAL) {
+ gint i;
+ for (i = 0; i < job->u.signal.n_param_values; i++) {
+ GValue *value = job->u.signal.param_values + i;
+ if (G_VALUE_TYPE (value) != GDA_TYPE_NULL)
+ g_value_reset (value);
+ }
+ g_free (job->u.signal.param_values);
+ }
+ else if (job->type == JOB_TYPE_DESTROY) {
+ /* nothing to do here */
+ }
+ else
+ g_assert_not_reached ();
g_free (job);
}
@@ -138,72 +122,52 @@ job_free (Job *job)
*/
struct _SignalSpec {
GSignalQuery sigprop; /* must be first */
+
+ GThread *worker_thread;
+ GAsyncQueue *reply_queue; /* a ref is held here */
+
gpointer instance;
gulong signal_id;
GdaThreadWrapperCallback callback;
gpointer data;
- ThreadData *td;
+ GMutex *mutex;
+ guint ref_count;
};
-struct _SignalEmissionData {
- guint n_param_values;
- GValue *param_values; /* array of GValue structures */
-};
+#define signal_spec_lock(x) g_mutex_lock(((SignalSpec*)x)->mutex);
+#define signal_spec_unlock(x) g_mutex_unlock(((SignalSpec*)x)->mutex);
+/*
+ * call signal_spec_lock() before calling this function
+ */
static void
-signal_emission_data_free (SignalEmissionData *sd)
+signal_spec_unref (SignalSpec *sigspec)
{
- gint i;
- for (i = 0; i < sd->n_param_values; i++) {
- GValue *value = sd->param_values + i;
- if (G_VALUE_TYPE (value) != GDA_TYPE_NULL)
- g_value_reset (value);
+ sigspec->ref_count --;
+ if (sigspec->ref_count == 0) {
+ signal_spec_unlock (sigspec);
+ g_mutex_free (sigspec->mutex);
+ if (sigspec->instance && (sigspec->signal_id > 0))
+ g_signal_handler_disconnect (sigspec->instance, sigspec->signal_id);
+ if (sigspec->reply_queue)
+ g_async_queue_unref (sigspec->reply_queue);
+ g_free (sigspec);
}
- g_free (sd->param_values);
- g_free (sd);
+ else
+ signal_spec_unlock (sigspec);
}
/*
- * Result of a job executed by the sub thread.
- * Created exclusively by the sub thread, and destroyed by the thread(s) using the GdaThreadWrapper object
- *
- * Passed from the sub thread through obj->from_sub_thread
+ * call signal_spec_unlock() after this function
*/
-typedef enum {
- RESULT_TYPE_EXECUTE,
- RESULT_TYPE_SIGNAL
-} ResultType;
-struct _Result {
- Job *job;
- ResultType type;
- union {
- struct {
- gpointer result;
- GError *error;
- } exe;
- struct {
- SignalSpec *spec;
- SignalEmissionData *data;
- } signal;
- } u;
-};
-#define RESULT(x) ((Result*)(x))
-static void
-result_free (Result *res)
+static SignalSpec *
+signal_spec_ref (SignalSpec *sigspec)
{
- if (res->job)
- job_free (res->job);
- if (res->type == RESULT_TYPE_EXECUTE) {
- if (res->u.exe.error)
- g_error_free (res->u.exe.error);
- }
- else if (res->type == RESULT_TYPE_SIGNAL)
- signal_emission_data_free (res->u.signal.data);
- else
- g_assert_not_reached ();
- g_free (res);
+ signal_spec_lock (sigspec);
+ sigspec->ref_count ++;
+ return sigspec;
}
/*
@@ -211,11 +175,12 @@ result_free (Result *res)
* Each new job increases the ref count
*/
struct _ThreadData {
- GThread *owner;
- GSList *signals_list; /* list of SignalSpec pointers, owns all the structures */
- GAsyncQueue *from_sub_thread; /* holds a ref to it */
- GSList *results; /* list of Result pointers */
- SharedData *shared; /* number of jobs waiting from that thread, holds a ref to it */
+ GThread *owner;
+ GSList *signals_list; /* list of SignalSpec pointers, owns all the structures */
+ GAsyncQueue *from_worker_thread; /* holds a ref to it */
+
+ GSList *jobs; /* list of Job pointers not yet handled, or being handled (ie not yet poped from @from_worker_thread) */
+ GSList *results; /* list of Job pointers to completed jobs (ie. poped from @from_worker_thread) */
};
#define THREAD_DATA(x) ((ThreadData*)(x))
@@ -224,39 +189,47 @@ get_thread_data (GdaThreadWrapper *wrapper, GThread *thread)
{
ThreadData *td;
+ gda_mutex_lock (wrapper->priv->mutex);
td = g_hash_table_lookup (wrapper->priv->threads_hash, thread);
if (!td) {
td = g_new0 (ThreadData, 1);
td->owner = thread;
- td->from_sub_thread = g_async_queue_new ();
+ td->from_worker_thread = g_async_queue_new_full ((GDestroyNotify) job_free);
td->results = NULL;
- td->shared = shared_data_new ();
- td->shared->wrapper_sub_thread = wrapper->priv->sub_thread;
g_hash_table_insert (wrapper->priv->threads_hash, thread, td);
}
+ gda_mutex_unlock (wrapper->priv->mutex);
return td;
}
static void
thread_data_free (ThreadData *td)
{
- g_async_queue_unref (td->from_sub_thread);
+ g_async_queue_unref (td->from_worker_thread);
+ g_assert (!td->jobs);
if (td->results) {
- g_slist_foreach (td->results, (GFunc) result_free, NULL);
+ g_slist_foreach (td->results, (GFunc) job_free, NULL);
g_slist_free (td->results);
}
if (td->signals_list) {
GSList *list;
for (list = td->signals_list; list; list = list->next) {
- /* free each SignalSpec */
+ /* clear SignalSpec */
SignalSpec *sigspec = (SignalSpec*) list->data;
+
+ signal_spec_lock (sigspec);
g_signal_handler_disconnect (sigspec->instance, sigspec->signal_id);
- g_free (sigspec);
+ sigspec->instance = NULL;
+ sigspec->signal_id = 0;
+ g_async_queue_unref (sigspec->reply_queue);
+ sigspec->reply_queue = NULL;
+ sigspec->callback = NULL;
+ sigspec->data = NULL;
+ signal_spec_unref (sigspec);
}
g_slist_free (td->signals_list);
}
- shared_data_unref (td->shared);
g_free (td);
}
@@ -306,44 +279,49 @@ gda_thread_wrapper_class_init (GdaThreadWrapperClass *klass)
/*
* Executed in the sub thread:
- * takes a Job in (from the wrapper->priv->to_sub_thread queue) and creates a new Result which
+ * takes a Job in (from the wrapper->priv->to_worker_thread queue) and creates a new Result which
* it pushed to Job->reply_queue
*/
static gpointer
-sub_thread_entry_point (GAsyncQueue *to_sub_thread)
+worker_thread_entry_point (GAsyncQueue *to_worker_thread)
{
GAsyncQueue *in;
- in = to_sub_thread;
+ in = to_worker_thread;
- /* don't use @priv anymore */
while (1) {
Job *job;
- job = g_async_queue_pop (in);
+ /* pop next job and mark it as being processed */
+ g_static_private_set (&worker_thread_current_queue, NULL, NULL);
+ g_async_queue_lock (in);
+ job = g_async_queue_pop_unlocked (in);
+ job->processed = TRUE;
+ g_static_private_set (&worker_thread_current_queue, job->reply_queue, NULL);
+ g_async_queue_unlock (in);
+
+ if (job->cancelled) {
+ job_free (job);
+ continue;
+ }
if (job->type == JOB_TYPE_DESTROY) {
g_assert (! job->arg_destroy_func);
job_free (job);
/*g_print ("... exit sub thread for wrapper\n");*/
+
/* exit sub thread */
break;
}
else if (job->type == JOB_TYPE_EXECUTE) {
- Result *res = g_new0 (Result, 1);
- res->job = job;
- res->type = RESULT_TYPE_EXECUTE;
- job->shared->current_job = job;
if (job->func)
- res->u.exe.result = job->func (job->arg, &(res->u.exe.error));
+ job->u.exe.result = job->func (job->arg, &(job->u.exe.error));
else {
- res->u.exe.result = NULL;
- job->void_func (job->arg, &(res->u.exe.error));
+ job->u.exe.result = NULL;
+ job->void_func (job->arg, &(job->u.exe.error));
}
- job->shared->current_job = NULL;
- shared_data_add_nb_waiting (job->shared, -1);
/*g_print ("... done job %d\n", job->job_id);*/
- g_async_queue_push (job->reply_queue, res);
+ g_async_queue_push (job->reply_queue, job);
}
else
g_assert_not_reached ();
@@ -365,11 +343,42 @@ gda_thread_wrapper_init (GdaThreadWrapper *wrapper, GdaThreadWrapperClass *klass
wrapper->priv->threads_hash = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify) thread_data_free);
- wrapper->priv->to_sub_thread = g_async_queue_new ();
- wrapper->priv->sub_thread = g_thread_create ((GThreadFunc) sub_thread_entry_point,
- g_async_queue_ref (wrapper->priv->to_sub_thread), /* inc. ref for sub thread usage */
+ wrapper->priv->to_worker_thread = g_async_queue_new ();
+ wrapper->priv->worker_thread = g_thread_create ((GThreadFunc) worker_thread_entry_point,
+ g_async_queue_ref (wrapper->priv->to_worker_thread), /* inc. ref for sub thread usage */
FALSE, NULL);
- /*g_print ("... new wrapper %p, sub_thread=%p\n", wrapper, wrapper->priv->sub_thread);*/
+ /*g_print ("... new wrapper %p, worker_thread=%p\n", wrapper, wrapper->priv->worker_thread);*/
+}
+
+static gboolean
+thread_data_remove_jobs_func (GThread *key, ThreadData *td, gpointer data)
+{
+ if (td->jobs) {
+ GSList *list;
+ for (list = td->jobs; list; list = list->next) {
+ Job *job = JOB (list->data);
+ if (job->processed) {
+ /* we can't free that job because it is probably being used by the
+ * worker thread, so just emit a warning
+ */
+ if (job->arg_destroy_func) {
+ g_warning ("The argument of Job ID %d will be destroyed by sub thread",
+ job->job_id);
+ }
+ }
+ else {
+ /* cancel this job */
+ job->cancelled = TRUE;
+ if (job->arg && job->arg_destroy_func) {
+ job->arg_destroy_func (job->arg);
+ job->arg = NULL;
+ }
+ }
+ }
+ g_slist_free (td->jobs);
+ td->jobs = NULL;
+ }
+ return TRUE; /* remove this ThreadData */
}
static void
@@ -382,14 +391,18 @@ gda_thread_wrapper_dispose (GObject *object)
if (wrapper->priv) {
Job *job = g_new0 (Job, 1);
job->type = JOB_TYPE_DESTROY;
- g_async_queue_push (wrapper->priv->to_sub_thread, job);
+ g_async_queue_push (wrapper->priv->to_worker_thread, job);
/*g_print ("... pushed JOB_TYPE_DESTROY for wrapper %p\n", wrapper);*/
-
- g_async_queue_unref (wrapper->priv->to_sub_thread);
- wrapper->priv->sub_thread = NULL; /* side note: don't wait for sub thread to terminate */
- if (wrapper->priv->threads_hash)
+ g_async_queue_lock (wrapper->priv->to_worker_thread);
+ if (wrapper->priv->threads_hash) {
+ g_hash_table_foreach_remove (wrapper->priv->threads_hash,
+ (GHRFunc) thread_data_remove_jobs_func, NULL);
g_hash_table_destroy (wrapper->priv->threads_hash);
+ }
+ g_async_queue_unlock (wrapper->priv->to_worker_thread);
+ g_async_queue_unref (wrapper->priv->to_worker_thread);
+ wrapper->priv->worker_thread = NULL; /* side note: don't wait for sub thread to terminate */
gda_mutex_free (wrapper->priv->mutex);
@@ -527,10 +540,11 @@ gda_thread_wrapper_execute (GdaThreadWrapper *wrapper, GdaThreadWrapperFunc func
g_return_val_if_fail (func, 0);
td = get_thread_data (wrapper, g_thread_self());
- shared_data_add_nb_waiting (td->shared, 1);
job = g_new0 (Job, 1);
job->type = JOB_TYPE_EXECUTE;
+ job->processed = FALSE;
+ job->cancelled = FALSE;
gda_mutex_lock (wrapper->priv->mutex);
job->job_id = wrapper->priv->next_job_id++;
gda_mutex_unlock (wrapper->priv->mutex);
@@ -538,29 +552,25 @@ gda_thread_wrapper_execute (GdaThreadWrapper *wrapper, GdaThreadWrapperFunc func
job->void_func = NULL;
job->arg = arg;
job->arg_destroy_func = arg_destroy_func;
- job->reply_queue = g_async_queue_ref (td->from_sub_thread);
- job->shared = shared_data_ref (td->shared);
+ job->reply_queue = g_async_queue_ref (td->from_worker_thread);
id = job->job_id;
/* g_print ("... submitted job %d from thread %p\n", id, g_thread_self()); */
- if (g_thread_self () == wrapper->priv->sub_thread) {
- Result *res = g_new0 (Result, 1);
- res->job = job;
- res->type = RESULT_TYPE_EXECUTE;
- job->shared->current_job = job;
+ td->jobs = g_slist_append (td->jobs, job);
+
+ if (g_thread_self () == wrapper->priv->worker_thread) {
+ job->processed = TRUE;
if (job->func)
- res->u.exe.result = job->func (job->arg, &(res->u.exe.error));
+ job->u.exe.result = job->func (job->arg, &(job->u.exe.error));
else {
- res->u.exe.result = NULL;
- job->void_func (job->arg, &(res->u.exe.error));
+ job->u.exe.result = NULL;
+ job->void_func (job->arg, &(job->u.exe.error));
}
- job->shared->current_job = NULL;
- shared_data_add_nb_waiting (job->shared, -1);
- g_async_queue_push (job->reply_queue, res);
+ g_async_queue_push (job->reply_queue, job);
}
else
- g_async_queue_push (wrapper->priv->to_sub_thread, job);
+ g_async_queue_push (wrapper->priv->to_worker_thread, job);
return id;
}
@@ -603,10 +613,11 @@ gda_thread_wrapper_execute_void (GdaThreadWrapper *wrapper, GdaThreadWrapperVoid
g_return_val_if_fail (func, 0);
td = get_thread_data (wrapper, g_thread_self());
- shared_data_add_nb_waiting (td->shared, 1);
job = g_new0 (Job, 1);
job->type = JOB_TYPE_EXECUTE;
+ job->processed = FALSE;
+ job->cancelled = FALSE;
gda_mutex_lock (wrapper->priv->mutex);
job->job_id = wrapper->priv->next_job_id++;
gda_mutex_unlock (wrapper->priv->mutex);
@@ -614,34 +625,89 @@ gda_thread_wrapper_execute_void (GdaThreadWrapper *wrapper, GdaThreadWrapperVoid
job->void_func = func;
job->arg = arg;
job->arg_destroy_func = arg_destroy_func;
- job->reply_queue = g_async_queue_ref (td->from_sub_thread);
- job->shared = shared_data_ref (td->shared);
+ job->reply_queue = g_async_queue_ref (td->from_worker_thread);
id = job->job_id;
/*g_print ("... submitted VOID job %d\n", id);*/
- if (g_thread_self () == wrapper->priv->sub_thread) {
- Result *res = g_new0 (Result, 1);
- res->job = job;
- res->type = RESULT_TYPE_EXECUTE;
- job->shared->current_job = job;
+ td->jobs = g_slist_append (td->jobs, job);
+
+ if (g_thread_self () == wrapper->priv->worker_thread) {
+ job->processed = TRUE;
if (job->func)
- res->u.exe.result = job->func (job->arg, &(res->u.exe.error));
+ job->u.exe.result = job->func (job->arg, &(job->u.exe.error));
else {
- res->u.exe.result = NULL;
- job->void_func (job->arg, &(res->u.exe.error));
+ job->u.exe.result = NULL;
+ job->void_func (job->arg, &(job->u.exe.error));
}
- job->shared->current_job = NULL;
- shared_data_add_nb_waiting (job->shared, -1);
- g_async_queue_push (job->reply_queue, res);
+ g_async_queue_push (job->reply_queue, job);
}
else
- g_async_queue_push (wrapper->priv->to_sub_thread, job);
+ g_async_queue_push (wrapper->priv->to_worker_thread, job);
return id;
}
/**
+ * gda_thread_wrapper_cancel
+ * @wrapper: a #GdaThreadWrapper object
+ * @id: the ID of a job as returned by gda_thread_wrapper_execute() or gda_thread_wrapper_execute_void()
+ *
+ * Cancels a job not yet executed. This may fail for the following reasons:
+ * <itemizedlist>
+ * <listitem><para>the job @id could not be found, either because it has already been treated or because
+ * it does not exist or because it was created in another thread</para></listitem>
+ * <listitem><para>the job @id is currently being treated by the worker thread</para></listitem>
+ * </itemizedlist>
+ *
+ * Returns: %TRUE if the job has been cancelled, or %FALSE in any other case.
+ *
+ * Since: 4.2
+ */
+gboolean
+gda_thread_wrapper_cancel (GdaThreadWrapper *wrapper, guint id)
+{
+ GSList *list;
+ gboolean retval = FALSE; /* default if job not found */
+ ThreadData *td;
+
+ g_return_val_if_fail (GDA_IS_THREAD_WRAPPER (wrapper), FALSE);
+
+ gda_mutex_lock (wrapper->priv->mutex);
+
+ td = g_hash_table_lookup (wrapper->priv->threads_hash, g_thread_self());
+ if (!td) {
+ /* nothing to be done for this thread */
+ gda_mutex_unlock (wrapper->priv->mutex);
+ return FALSE;
+ }
+
+ g_async_queue_lock (wrapper->priv->to_worker_thread);
+ for (list = td->jobs; list; list = list->next) {
+ Job *job = JOB (list->data);
+ if (job->job_id == id) {
+ if (job->processed) {
+ /* can't cancel it as it's being treated */
+ break;
+ }
+
+ retval = TRUE;
+ job->cancelled = TRUE;
+ if (job->arg && job->arg_destroy_func) {
+ job->arg_destroy_func (job->arg);
+ job->arg = NULL;
+ }
+ td->jobs = g_slist_delete_link (td->jobs, list);
+ break;
+ }
+ }
+ g_async_queue_unlock (wrapper->priv->to_worker_thread);
+ gda_mutex_unlock (wrapper->priv->mutex);
+
+ return retval;
+}
+
+/**
* gda_thread_wrapper_iterate
* @wrapper: a #GdaThreadWrapper object
* @may_block: whether the call may block
@@ -657,7 +723,7 @@ void
gda_thread_wrapper_iterate (GdaThreadWrapper *wrapper, gboolean may_block)
{
ThreadData *td;
- Result *res;
+ Job *job;
g_return_if_fail (GDA_IS_THREAD_WRAPPER (wrapper));
g_return_if_fail (wrapper->priv);
@@ -672,28 +738,36 @@ gda_thread_wrapper_iterate (GdaThreadWrapper *wrapper, gboolean may_block)
again:
if (may_block)
- res = g_async_queue_pop (td->from_sub_thread);
+ job = g_async_queue_pop (td->from_worker_thread);
else
- res = g_async_queue_try_pop (td->from_sub_thread);
- if (res) {
+ job = g_async_queue_try_pop (td->from_worker_thread);
+ if (job) {
gboolean do_again = FALSE;
- if (res->type == RESULT_TYPE_EXECUTE) {
- if (!res->job->func) {
- result_free (res); /* ignore as there is no result */
+
+ td->jobs = g_slist_remove (td->jobs, job);
+
+ if (job->type == JOB_TYPE_EXECUTE) {
+ if (!job->func) {
+ job_free (job); /* ignore as there is no result */
do_again = TRUE;
}
else
- td->results = g_slist_append (td->results, res);
+ td->results = g_slist_append (td->results, job);
}
- else if (res->type == RESULT_TYPE_SIGNAL) {
+ else if (job->type == JOB_TYPE_SIGNAL) {
/* run callback now */
- SignalSpec *spec = res->u.signal.spec;
- SignalEmissionData *sd = res->u.signal.data;
+ SignalSpec *spec = job->u.signal.spec;
- spec->callback (wrapper, spec->instance, ((GSignalQuery*)spec)->signal_name,
- sd->n_param_values, sd->param_values, NULL,
- spec->data);
- result_free (res);
+ signal_spec_lock (spec);
+ if (spec->callback)
+ spec->callback (wrapper, spec->instance, ((GSignalQuery*)spec)->signal_name,
+ job->u.signal.n_param_values, job->u.signal.param_values, NULL,
+ spec->data);
+ else
+ g_print ("Not propagating signal %s\n", ((GSignalQuery*)spec)->signal_name);
+ signal_spec_unref (spec);
+ job->u.signal.spec = NULL;
+ job_free (job);
do_again = TRUE;
}
else
@@ -722,7 +796,7 @@ gpointer
gda_thread_wrapper_fetch_result (GdaThreadWrapper *wrapper, gboolean may_lock, guint exp_id, GError **error)
{
ThreadData *td;
- Result *res = NULL;
+ Job *job = NULL;
gpointer retval = NULL;
g_return_val_if_fail (GDA_IS_THREAD_WRAPPER (wrapper), NULL);
@@ -742,13 +816,13 @@ gda_thread_wrapper_fetch_result (GdaThreadWrapper *wrapper, gboolean may_lock, g
/* see if we have the result we want */
GSList *list;
for (list = td->results; list; list = list->next) {
- res = RESULT (list->data);
- if (res->job->job_id == exp_id) {
+ job = JOB (list->data);
+ if (job->job_id == exp_id) {
/* found it */
td->results = g_slist_delete_link (td->results, list);
- if (!(td->results) &&
- (td->shared->nb_waiting == 0) &&
- (g_async_queue_length (td->from_sub_thread) == 0) &&
+ if (!td->results &&
+ !td->jobs &&
+ (g_async_queue_length (td->from_worker_thread) == 0) &&
!td->signals_list) {
/* remove this ThreadData */
gda_mutex_lock (wrapper->priv->mutex);
@@ -767,22 +841,22 @@ gda_thread_wrapper_fetch_result (GdaThreadWrapper *wrapper, gboolean may_lock, g
len = g_slist_length (td->results);
gda_thread_wrapper_iterate (wrapper, FALSE);
if (g_slist_length (td->results) == len) {
- res = NULL;
+ job = NULL;
break;
}
}
} while (1);
out:
- if (res) {
- g_assert (res->type == RESULT_TYPE_EXECUTE);
- if (res->u.exe.error) {
- g_propagate_error (error, res->u.exe.error);
- res->u.exe.error = NULL;
+ if (job) {
+ g_assert (job->type == JOB_TYPE_EXECUTE);
+ if (job->u.exe.error) {
+ g_propagate_error (error, job->u.exe.error);
+ job->u.exe.error = NULL;
}
- retval = res->u.exe.result;
- res->u.exe.result = NULL;
- result_free (res);
+ retval = job->u.exe.result;
+ job->u.exe.result = NULL;
+ job_free (job);
}
return retval;
@@ -802,7 +876,8 @@ gda_thread_wrapper_fetch_result (GdaThreadWrapper *wrapper, gboolean may_lock, g
gint
gda_thread_wrapper_get_waiting_size (GdaThreadWrapper *wrapper)
{
- gint size;
+ GSList *list;
+ gint size = 0;
ThreadData *td;
g_return_val_if_fail (GDA_IS_THREAD_WRAPPER (wrapper), 0);
@@ -815,7 +890,14 @@ gda_thread_wrapper_get_waiting_size (GdaThreadWrapper *wrapper)
gda_mutex_unlock (wrapper->priv->mutex);
return 0;
}
- size = td->shared->nb_waiting;
+
+ /* lock job consuming queue to avoid that the worker thread "consume" a Job */
+ g_async_queue_lock (wrapper->priv->to_worker_thread);
+ for (size = 0, list = td->jobs; list; list = list->next) {
+ if (!JOB (list->data)->cancelled)
+ size++;
+ }
+ g_async_queue_unlock (wrapper->priv->to_worker_thread);
gda_mutex_unlock (wrapper->priv->mutex);
return size;
}
@@ -825,39 +907,40 @@ gda_thread_wrapper_get_waiting_size (GdaThreadWrapper *wrapper)
* pushes data into the queue
*/
static void
-sub_thread_closure_marshal (GClosure *closure,
- GValue *return_value,
- guint n_param_values,
- const GValue *param_values,
- gpointer invocation_hint,
- gpointer marshal_data)
+worker_thread_closure_marshal (GClosure *closure,
+ GValue *return_value,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint,
+ gpointer marshal_data)
{
SignalSpec *sigspec = (SignalSpec *) closure->data;
- /* if the signal is not emitted from the sub thread then don't do anything */
- if (g_thread_self () != sigspec->td->shared->wrapper_sub_thread)
+ /* if the signal is not emitted from the working thread then don't do anything */
+ if (g_thread_self () != sigspec->worker_thread)
return;
- /* if we are not currently doing a job, then don't do anything */
- if (!sigspec->td->shared->current_job)
+ /* check that the worker thread is working on a job for which job->reply_queue == sigspec->reply_queue */
+ if (g_static_private_get (&worker_thread_current_queue) != sigspec->reply_queue)
return;
gint i;
/*
- for (i = 1; i < n_param_values; i++) {
+ for (i = 1; i < n_param_values; i++) {
g_print ("\t%d => %s\n", i, gda_value_stringify (param_values + i));
}
*/
- SignalEmissionData *sdata;
- sdata = g_new0 (SignalEmissionData, 1);
- sdata->n_param_values = n_param_values - 1;
- sdata->param_values = g_new0 (GValue, sdata->n_param_values);
+ Job *job= g_new0 (Job, 1);
+ job->type = JOB_TYPE_SIGNAL;
+ job->u.signal.spec = signal_spec_ref (sigspec);
+ job->u.signal.n_param_values = n_param_values - 1;
+ job->u.signal.param_values = g_new0 (GValue, job->u.signal.n_param_values);
for (i = 1; i < n_param_values; i++) {
const GValue *src;
GValue *dest;
src = param_values + i;
- dest = sdata->param_values + i - 1;
+ dest = job->u.signal.param_values + i - 1;
if (G_VALUE_TYPE (src) != GDA_TYPE_NULL) {
g_value_init (dest, G_VALUE_TYPE (src));
@@ -865,13 +948,8 @@ sub_thread_closure_marshal (GClosure *closure,
}
}
- Result *res = g_new0 (Result, 1);
- g_assert (sigspec->td->shared);
- res->job = NULL;
- res->type = RESULT_TYPE_SIGNAL;
- res->u.signal.spec = sigspec;
- res->u.signal.data = sdata;
- g_async_queue_push (sigspec->td->shared->current_job->reply_queue, res);
+ g_async_queue_push (sigspec->reply_queue, job);
+ signal_spec_unlock (sigspec);
}
/**
@@ -882,7 +960,7 @@ sub_thread_closure_marshal (GClosure *closure,
* @callback: a #GdaThreadWrapperCallback function
* @data: data to pass to @callback's calls
*
- * Connects a callbeck function to a signal for a particular object. The difference with g_signal_connect() and
+ * Connects a callback function to a signal for a particular object. The difference with g_signal_connect() and
* similar functions are:
* <itemizedlist>
* <listitem><para>the @callback argument is not a #GCallback function, so the callback signature is not
@@ -893,9 +971,14 @@ sub_thread_closure_marshal (GClosure *closure,
* <listitem><para>the @callback function will be called only if the signal has been emitted by @instance
* while being used in @wrapper's private sub thread (ie. used when @wrapper is executing some functions
* specified by
- * gda_thread_wrapper_execute() or gda_thread_wrapper_execute_void() have been used)</para></listitem>
+ * gda_thread_wrapper_execute() or gda_thread_wrapper_execute_void())</para></listitem>
* </itemizedlist>
*
+ * Also note that signal handling is done asynchronously: when emitted in the worker thread, it
+ * will be "queued" to be processed in the user thread when it has the chance (when gda_thread_wrapper()
+ * is called directly or indirectly). The side effect is that the callback function is usually
+ * called long after the object emitting the signal has finished emitting it.
+ *
* To disconnect a signal handler, don't use any of the g_signal_handler_*() functions but the
* gda_thread_wrapper_disconnect() method.
*
@@ -936,16 +1019,19 @@ gda_thread_wrapper_connect_raw (GdaThreadWrapper *wrapper,
g_free (sigspec);
return 0;
}
+ sigspec->worker_thread = wrapper->priv->worker_thread;
+ sigspec->reply_queue = g_async_queue_ref (td->from_worker_thread);
sigspec->instance = instance;
sigspec->callback = callback;
sigspec->data = data;
+ sigspec->mutex = g_mutex_new ();
+ sigspec->ref_count = 1;
GClosure *cl;
cl = g_closure_new_simple (sizeof (GClosure), sigspec);
- g_closure_set_marshal (cl, (GClosureMarshal) sub_thread_closure_marshal);
+ g_closure_set_marshal (cl, (GClosureMarshal) worker_thread_closure_marshal);
sigspec->signal_id = g_signal_connect_closure (instance, sig_name, cl, FALSE);
- sigspec->td = td;
td->signals_list = g_slist_append (td->signals_list, sigspec);
gda_mutex_unlock (wrapper->priv->mutex);
@@ -971,7 +1057,11 @@ find_signal_r_func (GThread *thread, ThreadData *td, gulong *id)
* @wrapper: a #GdaThreadWrapper object
* @id: a handler ID, as returned by gda_thread_wrapper_connect_raw()
*
- * Disconnects the emission of a signal, does the opposite of gda_thread_wrapper_connect_raw()
+ * Disconnects the emission of a signal, does the opposite of gda_thread_wrapper_connect_raw().
+ *
+ * As soon as this method returns, the callback function set when gda_thread_wrapper_connect_raw()
+ * was called will not be called anymore (even if the object has emitted the signal in the worker
+ * thread and this signal has not been handled in the user thread).
*
* Since: 4.2
*/
@@ -1012,13 +1102,21 @@ gda_thread_wrapper_disconnect (GdaThreadWrapper *wrapper, gulong id)
return;
}
+ signal_spec_lock (sigspec);
+ /*g_print ("Disconnecting signal %s\n", ((GSignalQuery*)sigspec)->signal_name);*/
td->signals_list = g_slist_remove (td->signals_list, sigspec);
g_signal_handler_disconnect (sigspec->instance, sigspec->signal_id);
- g_free (sigspec);
-
- if (!(td->results) &&
- (td->shared->nb_waiting == 0) &&
- (g_async_queue_length (td->from_sub_thread) == 0) &&
+ sigspec->instance = NULL;
+ sigspec->signal_id = NULL;
+ g_async_queue_unref (sigspec->reply_queue);
+ sigspec->reply_queue = 0;
+ sigspec->callback = NULL;
+ sigspec->data = NULL;
+ signal_spec_unref (sigspec);
+
+ if (!td->results &&
+ !td->jobs &&
+ (g_async_queue_length (td->from_worker_thread) == 0) &&
!td->signals_list) {
/* remove this ThreadData */
g_hash_table_remove (wrapper->priv->threads_hash, g_thread_self());
@@ -1065,7 +1163,6 @@ gda_thread_wrapper_steal_signal (GdaThreadWrapper *wrapper, gulong id)
SignalSpec *sigspec = (SignalSpec*) list->data;
if (sigspec->signal_id == id) {
new_td = get_thread_data (wrapper, g_thread_self ());
- sigspec->td = new_td;
new_td->signals_list = g_slist_prepend (new_td->signals_list, sigspec);
old_td->signals_list = g_slist_remove (old_td->signals_list, sigspec);
break;
diff --git a/libgda/thread-wrapper/gda-thread-wrapper.h b/libgda/thread-wrapper/gda-thread-wrapper.h
index 997d5b0..7256a2b 100644
--- a/libgda/thread-wrapper/gda-thread-wrapper.h
+++ b/libgda/thread-wrapper/gda-thread-wrapper.h
@@ -73,6 +73,7 @@ guint gda_thread_wrapper_execute (GdaThreadWrapper *w
gpointer arg, GDestroyNotify arg_destroy_func, GError **error);
guint gda_thread_wrapper_execute_void (GdaThreadWrapper *wrapper, GdaThreadWrapperVoidFunc func,
gpointer arg, GDestroyNotify arg_destroy_func, GError **error);
+gboolean gda_thread_wrapper_cancel (GdaThreadWrapper *wrapper, guint id);
void gda_thread_wrapper_iterate (GdaThreadWrapper *wrapper, gboolean may_block);
gpointer gda_thread_wrapper_fetch_result (GdaThreadWrapper *wrapper, gboolean may_lock,
guint exp_id, GError **error);
diff --git a/tests/multi-threading/check_wrapper.c b/tests/multi-threading/check_wrapper.c
index fb5aebb..0056e03 100644
--- a/tests/multi-threading/check_wrapper.c
+++ b/tests/multi-threading/check_wrapper.c
@@ -220,12 +220,22 @@ main_thread_func (gpointer int_id)
typedef struct {
DummyObject *dummy;
GSList *signals_sent; /* list of TestSignal structures */
+ GMutex *mutex;
} t2ExecData;
+static void add_to_signals_sent (t2ExecData *data, gpointer what_to_add);
static gpointer t2_main_thread_func (DummyObject *dummy);
static gpointer t2_exec_in_wrapped_thread (t2ExecData *data, GError **error);
static void t2_exec_in_wrapped_thread_v (t2ExecData *data, GError **error);
#define INT_TOKEN 1034
+static void
+add_to_signals_sent (t2ExecData *data, gpointer what_to_add)
+{
+ g_mutex_lock (data->mutex);
+ data->signals_sent = g_slist_append (data->signals_sent, what_to_add);
+ g_mutex_unlock (data->mutex);
+}
+
static gint
test2 (void)
{
@@ -349,12 +359,28 @@ compare_signals_lists (GSList *explist, GSList *gotlist)
}
if (elist) {
- g_print ("Error: Some signals have not been received\n");
+ g_print ("Error: Some signals have not been received:\n");
+ for (; elist; elist = elist->next) {
+ TestSignal *es = (TestSignal*) elist->data;
+ gint i;
+ g_print ("\tSignal: %s", es->name);
+ for (i = 0; i < es->n_param_values; i++)
+ g_print (" %s", gda_value_stringify (es->param_values + i));
+ g_print ("\n");
+ }
return FALSE;
}
if (glist) {
- g_print ("Error: Received too many signals\n");
+ g_print ("Error: Received too many signals:\n");
+ for (; glist; glist = glist->next) {
+ TestSignal *gs = (TestSignal*) glist->data;
+ gint i;
+ g_print ("\tSignal: %s", gs->name);
+ for (i = 0; i < gs->n_param_values; i++)
+ g_print (" %s", gda_value_stringify (gs->param_values + i));
+ g_print ("\n");
+ }
return FALSE;
}
@@ -368,50 +394,43 @@ t2_exec_in_wrapped_thread (t2ExecData *data, GError **error)
g_print ("TH %p ** emit sig0 (dummy=>%p)\n", g_thread_self (), data->dummy);
#endif
g_signal_emit_by_name (data->dummy, "sig0", NULL);
- data->signals_sent = g_slist_append (data->signals_sent,
- test_signal_new ("sig0", 0));
+ add_to_signals_sent (data, test_signal_new ("sig0", 0));
#ifdef PRINT_CALLS
g_print ("TH %p ** emit sig1 (dummy=>%p, i=>123)\n", g_thread_self (), data->dummy);
#endif
g_signal_emit_by_name (data->dummy, "sig1", 123, NULL);
- data->signals_sent = g_slist_append (data->signals_sent,
- test_signal_new ("sig1", 1, G_TYPE_INT, 123));
+ add_to_signals_sent (data, test_signal_new ("sig1", 1, G_TYPE_INT, 123));
#ifdef PRINT_CALLS
g_print ("TH %p ** emit sig0 (dummy=>%p)\n", g_thread_self (), data->dummy);
#endif
g_signal_emit_by_name (data->dummy, "sig0", NULL);
- data->signals_sent = g_slist_append (data->signals_sent,
- test_signal_new ("sig0", 0));
+ add_to_signals_sent (data, test_signal_new ("sig0", 0));
#ifdef PRINT_CALLS
g_print ("TH %p ** emit sig2 (dummy=>%p, i=>456 str=>Hello)\n", g_thread_self (), data->dummy);
#endif
g_signal_emit_by_name (data->dummy, "sig2", 456, "Hello", NULL);
- data->signals_sent = g_slist_append (data->signals_sent,
- test_signal_new ("sig2", 2, G_TYPE_INT, 456, G_TYPE_STRING, "Hello"));
+ add_to_signals_sent (data, test_signal_new ("sig2", 2, G_TYPE_INT, 456, G_TYPE_STRING, "Hello"));
#ifdef PRINT_CALLS
g_print ("TH %p ** emit sig0 (dummy=>%p)\n", g_thread_self (), data->dummy);
#endif
g_signal_emit_by_name (data->dummy, "sig0", NULL);
- data->signals_sent = g_slist_append (data->signals_sent,
- test_signal_new ("sig0", 0));
+ add_to_signals_sent (data, test_signal_new ("sig0", 0));
#ifdef PRINT_CALLS
g_print ("TH %p ** emit sig1 (dummy=>%p, i=>789)\n", g_thread_self (), data->dummy);
#endif
g_signal_emit_by_name (data->dummy, "sig1", 789, NULL);
- data->signals_sent = g_slist_append (data->signals_sent,
- test_signal_new ("sig1", 1, G_TYPE_INT, 789));
+ add_to_signals_sent (data, test_signal_new ("sig1", 1, G_TYPE_INT, 789));
#ifdef PRINT_CALLS
g_print ("TH %p ** emit sig2 (dummy=>%p, i=>32 str=>World)\n", g_thread_self (), data->dummy);
#endif
g_signal_emit_by_name (data->dummy, "sig2", 32, "World", NULL);
- data->signals_sent = g_slist_append (data->signals_sent,
- test_signal_new ("sig2", 2, G_TYPE_INT, 32, G_TYPE_STRING, "World"));
+ add_to_signals_sent (data, test_signal_new ("sig2", 2, G_TYPE_INT, 32, G_TYPE_STRING, "World"));
return GINT_TO_POINTER (INT_TOKEN);
}
@@ -423,50 +442,43 @@ t2_exec_in_wrapped_thread_v (t2ExecData *data, GError **error)
g_print ("TH %p ** emit sig0 (dummy=>%p)\n", g_thread_self (), data->dummy);
#endif
g_signal_emit_by_name (data->dummy, "sig0", NULL);
- data->signals_sent = g_slist_append (data->signals_sent,
- test_signal_new ("sig0", 0));
+ add_to_signals_sent (data, test_signal_new ("sig0", 0));
#ifdef PRINT_CALLS
g_print ("TH %p ** emit sig1 (dummy=>%p, i=>321)\n", g_thread_self (), data->dummy);
#endif
g_signal_emit_by_name (data->dummy, "sig1", 321, NULL);
- data->signals_sent = g_slist_append (data->signals_sent,
- test_signal_new ("sig1", 1, G_TYPE_INT, 321));
+ add_to_signals_sent (data, test_signal_new ("sig1", 1, G_TYPE_INT, 321));
#ifdef PRINT_CALLS
g_print ("TH %p ** emit sig0 (dummy=>%p)\n", g_thread_self (), data->dummy);
#endif
g_signal_emit_by_name (data->dummy, "sig0", NULL);
- data->signals_sent = g_slist_append (data->signals_sent,
- test_signal_new ("sig0", 0));
+ add_to_signals_sent (data, test_signal_new ("sig0", 0));
#ifdef PRINT_CALLS
g_print ("TH %p ** emit sig2 (dummy=>%p, i=>654 str=>Thread)\n", g_thread_self (), data->dummy);
#endif
g_signal_emit_by_name (data->dummy, "sig2", 654, "Thread", NULL);
- data->signals_sent = g_slist_append (data->signals_sent,
- test_signal_new ("sig2", 2, G_TYPE_INT, 654, G_TYPE_STRING, "Thread"));
+ add_to_signals_sent (data, test_signal_new ("sig2", 2, G_TYPE_INT, 654, G_TYPE_STRING, "Thread"));
#ifdef PRINT_CALLS
g_print ("TH %p ** emit sig0 (dummy=>%p)\n", g_thread_self (), data->dummy);
#endif
g_signal_emit_by_name (data->dummy, "sig0", NULL);
- data->signals_sent = g_slist_append (data->signals_sent,
- test_signal_new ("sig0", 0));
+ add_to_signals_sent (data, test_signal_new ("sig0", 0));
#ifdef PRINT_CALLS
g_print ("TH %p ** emit sig1 (dummy=>%p, i=>987)\n", g_thread_self (), data->dummy);
#endif
g_signal_emit_by_name (data->dummy, "sig1", 987, NULL);
- data->signals_sent = g_slist_append (data->signals_sent,
- test_signal_new ("sig1", 1, G_TYPE_INT, 987));
+ add_to_signals_sent (data, test_signal_new ("sig1", 1, G_TYPE_INT, 987));
#ifdef PRINT_CALLS
g_print ("TH %p ** emit sig2 (dummy=>%p, i=>23 str=>Thread)\n", g_thread_self (), data->dummy);
#endif
g_signal_emit_by_name (data->dummy, "sig2", 23, "Thread", NULL);
- data->signals_sent = g_slist_append (data->signals_sent,
- test_signal_new ("sig2", 2, G_TYPE_INT, 23, G_TYPE_STRING, "Thread"));
+ add_to_signals_sent (data, test_signal_new ("sig2", 2, G_TYPE_INT, 23, G_TYPE_STRING, "Thread"));
}
static void
@@ -474,7 +486,8 @@ wrapper_callback (GdaThreadWrapper *wrapper, gpointer instance, const gchar *sig
gint n_param_values, const GValue *param_values, gpointer gda_reserved, GSList **sig_list)
{
gint i;
- /*g_print ("RECEIVED signal '%s' on thread %p\n", signame, g_thread_self ());
+ /*
+ g_print ("RECEIVED signal '%s' on thread %p\n", signame, g_thread_self ());
for (i = 0; i < n_param_values; i++) {
gchar *str = gda_value_stringify (param_values + i);
g_print ("\tParam %d: %s\n", i, str);
@@ -525,6 +538,7 @@ t2_main_thread_func (DummyObject *dummy)
edata.dummy = dummy;
edata.signals_sent = NULL;
+ edata.mutex = g_mutex_new ();
ids = g_new0 (guint, NCALLS);
for (i = 0; i < NCALLS; i++) {
@@ -583,20 +597,26 @@ t2_main_thread_func (DummyObject *dummy)
nfailed ++;
}
#ifdef PRINT_CALLS
- g_print ("<-- Thread %p jobID %u arg %d\n", g_thread_self (), id, INT_TOKEN);
+ g_print ("<-- Thread %p jobID %u arg %d\n", g_thread_self (), ids[i], INT_TOKEN);
#endif
if (rand () >= RAND_MAX / 2.)
g_thread_yield ();
g_signal_emit_by_name (dummy, "sig1", 666, NULL); /* this signal should be ignored */
}
- gda_thread_wrapper_iterate (wrapper, FALSE);
+ while (gda_thread_wrapper_get_waiting_size (wrapper) > 0) {
+ gda_thread_wrapper_iterate (wrapper, FALSE);
+ g_usleep (10000);
+ }
+
+ g_mutex_lock (edata.mutex);
if (! compare_signals_lists (edata.signals_sent, received_list))
nfailed++;
+ g_mutex_unlock (edata.mutex);
#ifdef PRINT_CALLS
- g_print ("Disconnected signals\n");
+ g_print ("Disconnecting signals\n");
#endif
gda_thread_wrapper_disconnect (wrapper, sigid[0]);
gda_thread_wrapper_disconnect (wrapper, sigid[1]);
@@ -621,8 +641,11 @@ t2_main_thread_func (DummyObject *dummy)
}
total_jobs ++;
- gda_thread_wrapper_iterate (wrapper, FALSE);
- g_usleep (100000);
+ while (gda_thread_wrapper_get_waiting_size (wrapper) > 0) {
+ gda_thread_wrapper_iterate (wrapper, FALSE);
+ g_usleep (10000);
+ }
+ g_mutex_free (edata.mutex);
if (received_list) {
g_print ("Error: signals should not be received anymore...\n");
diff --git a/tests/multi-threading/multi_check_wrapper.sh b/tests/multi-threading/multi_check_wrapper.sh
new file mode 100755
index 0000000..b88e180
--- /dev/null
+++ b/tests/multi-threading/multi_check_wrapper.sh
@@ -0,0 +1,13 @@
+#!/bin/sh
+
+runs=200
+for i in `seq 1 $runs`
+do
+ echo "RUNNING TEST $i"
+ ./check_wrapper
+ if test $? != 0; then
+ echo "FAILED!!!"
+ exit 1
+ fi
+done
+echo "All $runs OK"
\ No newline at end of file
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]