Browse Source

add a first batch of graphics to implementation chapter

master
Constantin Fürst 11 months ago
parent
commit
5d5a3a6649
  1. 16
      thesis/content/50_implementation.tex
  2. BIN
      thesis/images/sequenzdiagramm-waitoncompletion.png
  3. 145
      thesis/images/sequenzdiagramm-waitoncompletion.xml
  4. 44
      thesis/images/structo-cachedata-waitoncompletion.nsd
  5. BIN
      thesis/images/structo-cachedata-waitoncompletion.png

16
thesis/content/50_implementation.tex

@ -40,7 +40,21 @@ The choice made in \ref{subsec:design:cache-entry-reuse} requires thread safe sh
It was therefore decided to implement atomic reference counting for \texttt{CacheData} which means providing a custom constructor and destructor wherein a shared (through a standard pointer however) atomic integer is either incremented or decremented using atomic fetch sub and add operations \cite{cppreference:atomic-operations} to increase or deacrease the reference counter and, in case of decrease in the destructor signals that the destructor is called for the last reference, perform actual destruction. The invalid state of \texttt{CacheData} achievable is also avoided. To achieve this, the waiting algorithm requires the handlers to be contained in an atomic pointer and the pointer to the cache memory be atomic too. Through this we may use the atomic wait operation which is guaranteed by the standard to be more efficient than simply spinning on Compare-And-Swap \cite{cppreference:atomic-wait}. Some standard implementations achieve this by yielding after a short spin cycle \cite{atomic-wait-details}. \par
Designing the wait to work from any thread was complicated. In the first implementation, a thread would check if the handlers are available and if not atomically wait \cite{cppreference:atomic-wait} on a value change from nullptr. As the handlers are only available after submission, a situation could arise where only one copy of \texttt{CacheData} is capable of actually waiting on them. Lets assume that three threads \(T_1\), \(T_2\) and \(T_3\) wish to access the same resource. \(T_1\) now is the first to call \texttt{CacheData::Access} and therefore adds it to the cache state and will perform the work submission. Before \(T_1\) may submit the work, it is interrupted and \(T_2\) and \(T_3\) obtain access to the incomplete \texttt{CacheData} on which they wait, causing them to see a nullptr for the handlers but invalid cache pointer, leading to atomic wait on the cache pointer. Now \(T_1\) submits the work and sets the handlers, while \(T_2\) and \(T_3\) continue to wait. Now only \(T_1\) can trigger the waiting and is therefore capable of keeping \(T_2\) and \(T_3\) from progressing. This is undesirable as it can lead to deadlocking if by some reason \(T_1\) does not wait and at least may lead to unnecessary delay for \(T_2\) and \(T_3\) if \(T_1\) does not wait immediately. \par
\begin{figure}[H]
\centering
\includegraphics[width=0.9\textwidth]{images/sequenzdiagramm-waitoncompletion.png}
\caption{Sequence diagram for threading scenario in \texttt{CacheData::WaitOnCompletion}}
\label{fig:impl-cachedata-threadseq-waitoncompletion}
\end{figure}
Designing the wait to work from any thread was complicated. In the first implementation, a thread would check if the handlers are available and if not atomically wait \cite{cppreference:atomic-wait} on a value change from nullptr. As the handlers are only available after submission, a situation could arise where only one copy of \texttt{CacheData} is capable of actually waiting on them. Lets assume that three threads \(T_1\), \(T_2\) and \(T_3\) wish to access the same resource. \(T_1\) now is the first to call \texttt{CacheData::Access} and therefore adds it to the cache state and will perform the work submission. Before \(T_1\) may submit the work, it is interrupted and \(T_2\) and \(T_3\) obtain access to the incomplete \texttt{CacheData} on which they wait, causing them to see a nullptr for the handlers but invalid cache pointer, leading to atomic wait on the cache pointer (marked blue lines in \ref{fig:impl-cachedata-threadseq-waitoncompletion}). Now \(T_1\) submits the work and sets the handlers (marked red lines in \ref{fig:impl-cachedata-threadseq-waitoncompletion}), while \(T_2\) and \(T_3\) continue to wait. Now only \(T_1\) can trigger the waiting and is therefore capable of keeping \(T_2\) and \(T_3\) from progressing. This is undesirable as it can lead to deadlocking if by some reason \(T_1\) does not wait and at the very least may lead to unnecessary delay for \(T_2\) and \(T_3\) if \(T_1\) does not wait immediately. \par
\begin{figure}[H]
\centering
\includegraphics[width=0.9\textwidth]{images/structo-cachedata-waitoncompletion.png}
\caption{Code Flow Diagram for \texttt{CacheData::WaitOnCompletion}}
\label{fig:impl-cachedata-waitoncompletion}
\end{figure}
To solve this, a different and more complicated order of waiting operations is required. When waiting, the threads now immediately check whether the cache pointer contains a valid value and return if it does, as nothing has to be waited for in this case. Let's take the same example as before to illustrate the second part of the waiting procedure. \(T_2\) and \(T_3\) now both arrive in this latter section as the cache was invalid at the point in time when waiting was called for. They now atomically wait on the handlers pointer to change, instead of doing it the other way around as before. Now when \(T_1\) supplies the handlers, it also uses \texttt{std::atomic<T>::notify\_one} \cite{cppreference:atomic-notify-one} to wake at least one thread waiting on value change of the handlers pointer, if there are any. Through this the exclusion that was observable in the first implementation is already avoided. If nobody is waiting, then the handlers will be set to a valid pointer and a thread may pass the atomic wait instruction later on. Following this wait, the handlers pointer is atomically exchanged \cite{cppreference:atomic-exchange} with nullptr, invalidating it. Now each thread again checks whether it has received a valid local pointer to the handlers from the exchange, if it has then the atomic operation guarantees that is now in sole possession of the pointer. The owning thread is tasked with actually waiting. All other threads will now regress and call \texttt{CacheData::WaitOnCompletion} again. The solo thread may proceed to wait on the handlers and should update the cache pointer. \par

BIN
thesis/images/sequenzdiagramm-waitoncompletion.png

After

Width: 622  |  Height: 322  |  Size: 26 KiB

145
thesis/images/sequenzdiagramm-waitoncompletion.xml

@ -0,0 +1,145 @@
<mxfile host="app.diagrams.net" modified="2024-01-18T16:52:07.664Z" agent="Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36" etag="amrg-pTHBEpKrWOlSLRH" version="22.1.21" type="device">
<diagram name="Page-1" id="2YBvvXClWsGukQMizWep">
<mxGraphModel dx="1533" dy="382" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="850" pageHeight="1100" math="0" shadow="0">
<root>
<mxCell id="0" />
<mxCell id="1" parent="0" />
<mxCell id="aM9ryv3xv72pqoxQDRHE-1" value="CacheData" style="shape=umlLifeline;perimeter=lifelinePerimeter;whiteSpace=wrap;html=1;container=0;dropTarget=0;collapsible=0;recursiveResize=0;outlineConnect=0;portConstraint=eastwest;newEdgeStyle={&quot;edgeStyle&quot;:&quot;elbowEdgeStyle&quot;,&quot;elbow&quot;:&quot;vertical&quot;,&quot;curved&quot;:0,&quot;rounded&quot;:0};" parent="1" vertex="1">
<mxGeometry x="40" y="40" width="100" height="300" as="geometry" />
</mxCell>
<mxCell id="aM9ryv3xv72pqoxQDRHE-2" value="" style="html=1;points=[];perimeter=orthogonalPerimeter;outlineConnect=0;targetShapes=umlLifeline;portConstraint=eastwest;newEdgeStyle={&quot;edgeStyle&quot;:&quot;elbowEdgeStyle&quot;,&quot;elbow&quot;:&quot;vertical&quot;,&quot;curved&quot;:0,&quot;rounded&quot;:0};" parent="aM9ryv3xv72pqoxQDRHE-1" vertex="1">
<mxGeometry x="45" y="70" width="10" height="210" as="geometry" />
</mxCell>
<mxCell id="aM9ryv3xv72pqoxQDRHE-5" value="Thread 1" style="shape=umlLifeline;perimeter=lifelinePerimeter;whiteSpace=wrap;html=1;container=0;dropTarget=0;collapsible=0;recursiveResize=0;outlineConnect=0;portConstraint=eastwest;newEdgeStyle={&quot;edgeStyle&quot;:&quot;elbowEdgeStyle&quot;,&quot;elbow&quot;:&quot;vertical&quot;,&quot;curved&quot;:0,&quot;rounded&quot;:0};" parent="1" vertex="1">
<mxGeometry x="220" y="40" width="100" height="300" as="geometry" />
</mxCell>
<mxCell id="aM9ryv3xv72pqoxQDRHE-6" value="" style="html=1;points=[];perimeter=orthogonalPerimeter;outlineConnect=0;targetShapes=umlLifeline;portConstraint=eastwest;newEdgeStyle={&quot;edgeStyle&quot;:&quot;elbowEdgeStyle&quot;,&quot;elbow&quot;:&quot;vertical&quot;,&quot;curved&quot;:0,&quot;rounded&quot;:0};" parent="aM9ryv3xv72pqoxQDRHE-5" vertex="1">
<mxGeometry x="45" y="80" width="10" height="20" as="geometry" />
</mxCell>
<mxCell id="RjI6kM-8N4aADmqfwwHL-28" value="" style="html=1;points=[];perimeter=orthogonalPerimeter;outlineConnect=0;targetShapes=umlLifeline;portConstraint=eastwest;newEdgeStyle={&quot;edgeStyle&quot;:&quot;elbowEdgeStyle&quot;,&quot;elbow&quot;:&quot;vertical&quot;,&quot;curved&quot;:0,&quot;rounded&quot;:0};" vertex="1" parent="aM9ryv3xv72pqoxQDRHE-5">
<mxGeometry x="45" y="230" width="10" height="20" as="geometry" />
</mxCell>
<mxCell id="aM9ryv3xv72pqoxQDRHE-9" value="WaitOnCompletion" style="html=1;verticalAlign=bottom;endArrow=classicThin;edgeStyle=elbowEdgeStyle;elbow=vertical;curved=0;rounded=0;endFill=1;" parent="1" source="aM9ryv3xv72pqoxQDRHE-6" target="aM9ryv3xv72pqoxQDRHE-2" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="175" y="160" as="sourcePoint" />
<Array as="points">
<mxPoint x="190" y="130" />
</Array>
<mxPoint x="100" y="120" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="RjI6kM-8N4aADmqfwwHL-1" value="Thread 2" style="shape=umlLifeline;perimeter=lifelinePerimeter;whiteSpace=wrap;html=1;container=0;dropTarget=0;collapsible=0;recursiveResize=0;outlineConnect=0;portConstraint=eastwest;newEdgeStyle={&quot;edgeStyle&quot;:&quot;elbowEdgeStyle&quot;,&quot;elbow&quot;:&quot;vertical&quot;,&quot;curved&quot;:0,&quot;rounded&quot;:0};" vertex="1" parent="1">
<mxGeometry x="340" y="40" width="100" height="300" as="geometry" />
</mxCell>
<mxCell id="RjI6kM-8N4aADmqfwwHL-2" value="" style="html=1;points=[];perimeter=orthogonalPerimeter;outlineConnect=0;targetShapes=umlLifeline;portConstraint=eastwest;newEdgeStyle={&quot;edgeStyle&quot;:&quot;elbowEdgeStyle&quot;,&quot;elbow&quot;:&quot;vertical&quot;,&quot;curved&quot;:0,&quot;rounded&quot;:0};" vertex="1" parent="RjI6kM-8N4aADmqfwwHL-1">
<mxGeometry x="45" y="110" width="10" height="20" as="geometry" />
</mxCell>
<mxCell id="RjI6kM-8N4aADmqfwwHL-29" value="" style="html=1;points=[];perimeter=orthogonalPerimeter;outlineConnect=0;targetShapes=umlLifeline;portConstraint=eastwest;newEdgeStyle={&quot;edgeStyle&quot;:&quot;elbowEdgeStyle&quot;,&quot;elbow&quot;:&quot;vertical&quot;,&quot;curved&quot;:0,&quot;rounded&quot;:0};" vertex="1" parent="RjI6kM-8N4aADmqfwwHL-1">
<mxGeometry x="45" y="260" width="10" height="20" as="geometry" />
</mxCell>
<mxCell id="RjI6kM-8N4aADmqfwwHL-3" value="Thread 3" style="shape=umlLifeline;perimeter=lifelinePerimeter;whiteSpace=wrap;html=1;container=0;dropTarget=0;collapsible=0;recursiveResize=0;outlineConnect=0;portConstraint=eastwest;newEdgeStyle={&quot;edgeStyle&quot;:&quot;elbowEdgeStyle&quot;,&quot;elbow&quot;:&quot;vertical&quot;,&quot;curved&quot;:0,&quot;rounded&quot;:0};" vertex="1" parent="1">
<mxGeometry x="460" y="40" width="100" height="300" as="geometry" />
</mxCell>
<mxCell id="RjI6kM-8N4aADmqfwwHL-4" value="" style="html=1;points=[];perimeter=orthogonalPerimeter;outlineConnect=0;targetShapes=umlLifeline;portConstraint=eastwest;newEdgeStyle={&quot;edgeStyle&quot;:&quot;elbowEdgeStyle&quot;,&quot;elbow&quot;:&quot;vertical&quot;,&quot;curved&quot;:0,&quot;rounded&quot;:0};" vertex="1" parent="RjI6kM-8N4aADmqfwwHL-3">
<mxGeometry x="45" y="80" width="10" height="150" as="geometry" />
</mxCell>
<mxCell id="RjI6kM-8N4aADmqfwwHL-5" value="WaitOnCompletion" style="html=1;verticalAlign=bottom;endArrow=classicThin;edgeStyle=elbowEdgeStyle;elbow=vertical;curved=0;rounded=0;endFill=1;" edge="1" parent="1" source="RjI6kM-8N4aADmqfwwHL-2" target="aM9ryv3xv72pqoxQDRHE-2">
<mxGeometry relative="1" as="geometry">
<mxPoint x="270" y="160" as="sourcePoint" />
<Array as="points">
<mxPoint x="190" y="160" />
</Array>
<mxPoint x="100" y="160" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="RjI6kM-8N4aADmqfwwHL-10" value="Add Handlers" style="html=1;verticalAlign=bottom;endArrow=classicThin;edgeStyle=elbowEdgeStyle;elbow=vertical;curved=0;rounded=0;endFill=1;fillColor=#f8cecc;strokeColor=#b85450;" edge="1" parent="1" source="RjI6kM-8N4aADmqfwwHL-4" target="aM9ryv3xv72pqoxQDRHE-2">
<mxGeometry relative="1" as="geometry">
<mxPoint x="390" y="189.76" as="sourcePoint" />
<Array as="points">
<mxPoint x="195" y="189.76" />
</Array>
<mxPoint x="100" y="189.76" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="RjI6kM-8N4aADmqfwwHL-12" value="WaitOnCompletion" style="html=1;verticalAlign=bottom;endArrow=classicThin;edgeStyle=elbowEdgeStyle;elbow=vertical;curved=0;rounded=0;endFill=1;fillColor=#f8cecc;strokeColor=#b85450;" edge="1" parent="1" target="aM9ryv3xv72pqoxQDRHE-2">
<mxGeometry relative="1" as="geometry">
<mxPoint x="505" y="220.24" as="sourcePoint" />
<Array as="points" />
<mxPoint x="100" y="220.3809523809524" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="RjI6kM-8N4aADmqfwwHL-15" value="atomic wait on cache update" style="rounded=1;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="-40" y="70" width="90" height="30" as="geometry" />
</mxCell>
<mxCell id="RjI6kM-8N4aADmqfwwHL-17" value="" style="endArrow=none;dashed=1;html=1;rounded=0;entryX=0.5;entryY=1;entryDx=0;entryDy=0;" edge="1" parent="1" target="RjI6kM-8N4aADmqfwwHL-15">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="5" y="340" as="sourcePoint" />
<mxPoint x="160" y="200" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="RjI6kM-8N4aADmqfwwHL-19" value="&lt;p style=&quot;line-height: 101%;&quot;&gt;&lt;br&gt;&lt;/p&gt;" style="html=1;verticalAlign=bottom;endArrow=classicThin;edgeStyle=elbowEdgeStyle;elbow=vertical;curved=0;rounded=0;endFill=1;fillColor=#dae8fc;strokeColor=#6c8ebf;" edge="1" parent="1" source="aM9ryv3xv72pqoxQDRHE-2">
<mxGeometry x="-0.064" y="80" relative="1" as="geometry">
<mxPoint x="90" y="130" as="sourcePoint" />
<Array as="points">
<mxPoint x="50" y="140" />
</Array>
<mxPoint x="10" y="140" as="targetPoint" />
<mxPoint as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="RjI6kM-8N4aADmqfwwHL-18" value="" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry y="120" width="10" height="160" as="geometry" />
</mxCell>
<mxCell id="RjI6kM-8N4aADmqfwwHL-20" value="&lt;p style=&quot;line-height: 101%;&quot;&gt;&lt;br&gt;&lt;/p&gt;" style="html=1;verticalAlign=bottom;endArrow=classicThin;edgeStyle=elbowEdgeStyle;elbow=vertical;curved=0;rounded=0;endFill=1;entryX=1.033;entryY=0.142;entryDx=0;entryDy=0;entryPerimeter=0;fillColor=#dae8fc;strokeColor=#6c8ebf;" edge="1" parent="1">
<mxGeometry x="-0.064" y="80" relative="1" as="geometry">
<mxPoint x="85" y="170" as="sourcePoint" />
<Array as="points">
<mxPoint x="50" y="170" />
</Array>
<mxPoint x="10" y="170" as="targetPoint" />
<mxPoint as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="RjI6kM-8N4aADmqfwwHL-22" value="return" style="html=1;verticalAlign=bottom;endArrow=classicThin;edgeStyle=elbowEdgeStyle;elbow=vertical;curved=0;rounded=0;endFill=1;dashed=1;" edge="1" parent="1" source="aM9ryv3xv72pqoxQDRHE-2" target="RjI6kM-8N4aADmqfwwHL-4">
<mxGeometry relative="1" as="geometry">
<mxPoint x="510" y="250.34" as="sourcePoint" />
<Array as="points">
<mxPoint x="370" y="250" />
</Array>
<mxPoint x="100" y="250.1" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="RjI6kM-8N4aADmqfwwHL-23" value="return" style="html=1;verticalAlign=bottom;endArrow=classicThin;edgeStyle=elbowEdgeStyle;elbow=vertical;curved=0;rounded=0;endFill=1;dashed=1;" edge="1" parent="1" source="aM9ryv3xv72pqoxQDRHE-2" target="RjI6kM-8N4aADmqfwwHL-28">
<mxGeometry relative="1" as="geometry">
<mxPoint x="100" y="280" as="sourcePoint" />
<Array as="points" />
<mxPoint x="270" y="280" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="RjI6kM-8N4aADmqfwwHL-24" value="return T1" style="html=1;verticalAlign=bottom;endArrow=classicThin;edgeStyle=elbowEdgeStyle;elbow=vertical;curved=0;rounded=0;endFill=1;dashed=1;exitX=1;exitY=0.75;exitDx=0;exitDy=0;fillColor=#fff2cc;strokeColor=#d6b656;" edge="1" parent="1" source="RjI6kM-8N4aADmqfwwHL-18" target="aM9ryv3xv72pqoxQDRHE-2">
<mxGeometry relative="1" as="geometry">
<mxPoint x="15" y="240" as="sourcePoint" />
<Array as="points" />
<mxPoint x="80" y="240" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="RjI6kM-8N4aADmqfwwHL-26" value="return T2" style="html=1;verticalAlign=bottom;endArrow=classicThin;edgeStyle=elbowEdgeStyle;elbow=vertical;curved=0;rounded=0;endFill=1;dashed=1;exitX=1;exitY=0.75;exitDx=0;exitDy=0;fillColor=#fff2cc;strokeColor=#d6b656;" edge="1" parent="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="10" y="270.1" as="sourcePoint" />
<Array as="points" />
<mxPoint x="85" y="270.1" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="RjI6kM-8N4aADmqfwwHL-27" value="return" style="html=1;verticalAlign=bottom;endArrow=classicThin;edgeStyle=elbowEdgeStyle;elbow=vertical;curved=0;rounded=0;endFill=1;dashed=1;" edge="1" parent="1" source="aM9ryv3xv72pqoxQDRHE-2" target="RjI6kM-8N4aADmqfwwHL-29">
<mxGeometry relative="1" as="geometry">
<mxPoint x="100" y="310.1" as="sourcePoint" />
<Array as="points">
<mxPoint x="240" y="310" />
</Array>
<mxPoint x="275" y="310.1" as="targetPoint" />
</mxGeometry>
</mxCell>
</root>
</mxGraphModel>
</diagram>
</mxfile>

44
thesis/images/structo-cachedata-waitoncompletion.nsd

@ -0,0 +1,44 @@
<?xml version="1.0" encoding="UTF-8"?>
<root xmlns:nsd="https://structorizer.fisch.lu" version="3.32-15" preRepeat="until " postFor="to" preReturn="return" postForIn="in" preWhile="while " output="OUTPUT" input="INPUT" preFor="for" preExit="exit" preLeave="leave" ignoreCase="true" preThrow="throw" preForIn="foreach" stepFor="by" namespace="" author="user" created="2024-01-18" changedby="user" changed="2024-01-18" origin="Structorizer 3.32-15" text="&#34;void CacheData::WaitOnCompletion()&#34;" comment="&#34;&#34;" color="ffffff" type="program" style="nice">
<children>
<alternative text="&#34;is cache valid (not nullptr)&#34;" comment="&#34;&#34;" color="ffffff" disabled="0">
<qTrue>
<instruction text="&#34;return immediately&#34;" comment="&#34;&#34;" color="ffffff" rotated="0" disabled="0"></instruction>
</qTrue>
<qFalse>
<while text="&#34;while handlers is not valid (equals nullptr)&#34;" comment="&#34;&#34;" color="ffffff" disabled="0">
<qWhile>
<instruction text="&#34;handlers.wait()&#34;" comment="&#34;&#34;" color="ffffff" rotated="0" disabled="0"></instruction>
</qWhile>
</while>
<instruction text="&#34;local_handlers &#60;- handlers.exchange(uint64::max)&#34;,&#34;handlers &#60;- uint64::max&#34;" comment="&#34;&#34;" color="ffffff" rotated="0" disabled="0"></instruction>
<alternative text="&#34;is local_handlers valid (not nullptr and not uint64::max)&#34;" comment="&#34;&#34;" color="ffffff" disabled="0">
<qTrue>
<for text="&#34;foreach handler in local_handlers&#34;" comment="&#34;&#34;" counterVar="handler" startValue="" stepConst="" style="TRAVERSAL" insep="in" color="ffffff" disabled="0">
<qFor>
<instruction text="&#34;status &#60;- handler.wait()&#34;" comment="&#34;&#34;" color="ffffff" rotated="0" disabled="0"></instruction>
</qFor>
</for>
<alternative text="&#34;status contains error&#34;" comment="&#34;&#34;" color="ffffff" disabled="0">
<qTrue>
<instruction text="&#34;cache &#60;- source pointer&#34;" comment="&#34;&#34;" color="ffffff" rotated="0" disabled="0"></instruction>
<instruction text="&#34;free(destination pointer)&#34;" comment="&#34;&#34;" color="ffffff" rotated="0" disabled="0"></instruction>
</qTrue>
<qFalse>
<instruction text="&#34;cache &#60;- destination pointer&#34;" comment="&#34;&#34;" color="ffffff" rotated="0" disabled="0"></instruction>
</qFalse>
</alternative>
<instruction text="&#34;signal threads waiting on cache or handlers&#34;" comment="&#34;&#34;" color="ffffff" rotated="0" disabled="0"></instruction>
</qTrue>
<qFalse>
<while text="&#34;while cache is not valid (equals nullptr)&#34;" comment="&#34;&#34;" color="ffffff" disabled="0">
<qWhile>
<instruction text="&#34;cache.wait()&#34;" comment="&#34;&#34;" color="ffffff" rotated="0" disabled="0"></instruction>
</qWhile>
</while>
</qFalse>
</alternative>
</qFalse>
</alternative>
</children>
</root>

BIN
thesis/images/structo-cachedata-waitoncompletion.png

After

Width: 739  |  Height: 511  |  Size: 46 KiB

Loading…
Cancel
Save