[libgda] GdaThreadWrapper: rewrite



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]