tag:blogger.com,1999:blog-293316752009-06-05T15:17:16.795+02:00The Delphi Geekrandom ramblings on Delphi, programming, Delphi programming, and all the restgabrhttp://www.blogger.com/profile/06903558857617342477noreply@blogger.comBlogger143125tag:blogger.com,1999:blog-29331675.post-12908492902544406362009-04-30T00:43:00.001+02:002009-04-30T00:43:31.219+02:00TDM Rerun #13: Shared Events, Part 2: Redesign<blockquote> <p><em>Now we can already guess where the general sluggishness of the shared event system comes from. The trouble lies in the constant XML loading and saving. Most of the shared event system tables are quite static, but that doesn’t hold for the Event Queue table, where new entries are inserted, modified and deleted all the time.</em></p> <p><em>- Shared Events, Part 2: Redesign, The Delphi Magazine 102, February 2004</em></p> </blockquote> <p><a href="http://17slon.com/blogs/gabr/files/TDMRerun13SharedEventsPart2Redesign_6F6/image.png" target="_blank"><img title="shared table memory snapshot" style="border-top-width: 0px; display: inline; border-left-width: 0px; border-bottom-width: 0px; margin-left: 0px; margin-right: 0px; border-right-width: 0px" height="84" alt="shared table memory snapshot" src="http://17slon.com/blogs/gabr/files/TDMRerun13SharedEventsPart2Redesign_6F6/image_thumb.png" width="244" align="right" border="0" /></a>My second article on shared events architecture first addressed speed issues (original code was quite slow), then discussed internals of shared counters, shared linked lists and shared tables (all of which are used in the shared events system) and at the end returned to fine-tuning by fixing some remaining speed issues. As you can expect, the basis for the tuning was hard data from the profiler, not some wave-of-hand ideas about where the problem <em>maybe</em> lies.</p> <p>Links: <a title="TDM 102: Shared Events, Part 2: Redesign [article]" href="http://www.17slon.com/blogs/gabr/TDM/tdm102-gp.pdf" target="_blank">article</a> (PDF, 99 KB), <a title="TDM 102: Shared Events, Part 2: Redesign [source code]" href="http://www.17slon.com/blogs/gabr/TDM/tdm102-gp.zip" target="_blank">source code</a> (ZIP, 1,9 MB), <a title="GpSharedEvents 1.03" href="http://gp.17slon.com/gp/gpsharedevents.htm" target="_blank">current version</a>.</p> <div class="blogger-post-footer"><font size=-2>---<br/>Published under the <a href="http://creativecommons.org/licenses/by/3.0/">Creative Commons Attribution 3.0</a> license</font><img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29331675-1290849290254440636?l=17slon.com%2Fblogs%2Fgabr%2Fblogger.html'/></div>gabrhttp://www.blogger.com/profile/06903558857617342477noreply@blogger.com0tag:blogger.com,1999:blog-29331675.post-40960401545149521092009-04-30T00:28:00.001+02:002009-04-30T00:40:22.368+02:00TDM Rerun #12: Shared Events<blockquote> <p><em>Shared event system, as I nicknamed this approach, is implemented as a shared set of in-memory tables, which are accessed and manipulated by producers and listeners. The important part is that there is no dedicated server: housekeeping is distributed between the producers and listeners.</em></p> <p><em>- Shared Events, The Delphi Magazine 97, September 2003</em></p> </blockquote> <p><a href="http://17slon.com/blogs/gabr/files/TDMRerun12_14DD4/image.png" target="_blank"><img title="image" style="border-right: 0px; border-top: 0px; display: inline; margin-left: 0px; border-left: 0px; margin-right: 0px; border-bottom: 0px" height="145" alt="image" src="http://17slon.com/blogs/gabr/files/TDMRerun12_14DD4/image_thumb.png" width="324" align="right" border="0" /></a>Shared events mechanism was definitely the most complicated architecture based on shared memory I ever put together. The system allowed multiple programs (running on the same computer) to cooperate using an event-based system. First program would <em>publish </em>an event (or more events) and others would <em>subscribe</em> to those events. First program would then <em>broadcast</em> the event, which would trigger notifications in all subscribed programs. The best trick was that there was no privileged part – no server, service or manager. Publishers and consumers shared all the work – tables were created as needed, housekeeping was done on both sides and so on.</p> <p>Underlying architecture was largely redesigned after this article was published. Original source files are included only for historical reference.</p> <p>Links: <a title="TDM 97: Shared Events [article]" href="http://www.17slon.com/blogs/gabr/TDM/tdm97-gp.pdf" target="_blank">article</a> (PDF, 496 KB), <a title="TDM 97: Shared Events [source code]" href="http://www.17slon.com/blogs/gabr/TDM/tdm97-gp.zip" target="_blank">source code</a> (ZIP, 1,9 MB), <a title="GpSharedEvents 1.03" href="http://gp.17slon.com/gp/gpsharedevents.htm" target="_blank">current version</a>.</p> <div class="blogger-post-footer"><font size=-2>---<br/>Published under the <a href="http://creativecommons.org/licenses/by/3.0/">Creative Commons Attribution 3.0</a> license</font><img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29331675-4096040154514952109?l=17slon.com%2Fblogs%2Fgabr%2Fblogger.html'/></div>gabrhttp://www.blogger.com/profile/06903558857617342477noreply@blogger.com0tag:blogger.com,1999:blog-29331675.post-65691270946690048772009-04-02T23:58:00.002+02:002009-04-03T00:02:17.915+02:00Fluent XML [2]<p><a title="Fluent XML [1]" href="http://17slon.com/blogs/gabr/2009/04/fluent-xml-1.html" target="_blank">Yesterday</a> I described my approach to more fluent XML writing. Today I’ll describe the GpFluentXML unit where the ‘fluid’ implementation is stored. If you skipped yesterday’s post you’re strongly encourage to read it now.</p> <p>Let’s start with the current version of the fluent XML builder interface, which is not completely identical to the yesterday’s version.</p> <h2>Interface</h2> <pre class="pas-source"><span class="pas-kwd">uses</span><br/> OmniXML_Types,<br/> OmniXML;<br/><br/><span class="pas-kwd">type</span><br/> IGpFluentXmlBuilder = <span class="pas-kwd">interface</span> [<span class="pas-str">'{91F596A3-F5E3-451C-A6B9-C5FF3F23ECCC}'</span>]<br/> <span class="pas-kwd">function</span> GetXml: IXmlDocument;<br/> <span class="pas-comment">//</span><br/> <span class="pas-kwd">function</span> AddChild(<span class="pas-kwd">const</span> <span class="pas-kwd">name</span>: XmlString): IGpFluentXmlBuilder;<br/> <span class="pas-kwd">function</span> AddComment(<span class="pas-kwd">const</span> comment: XmlString): IGpFluentXmlBuilder;<br/> <span class="pas-kwd">function</span> AddProcessingInstruction(<span class="pas-kwd">const</span> target, data: XmlString): IGpFluentXmlBuilder;<br/> <span class="pas-kwd">function</span> AddSibling(<span class="pas-kwd">const</span> <span class="pas-kwd">name</span>: XmlString): IGpFluentXmlBuilder;<br/> <span class="pas-kwd">function</span> Anchor(<span class="pas-kwd">var</span> node: IXMLNode): IGpFluentXmlBuilder;<br/> <span class="pas-kwd">function</span> Mark: IGpFluentXmlBuilder;<br/> <span class="pas-kwd">function</span> Return: IGpFluentXmlBuilder;<br/> <span class="pas-kwd">function</span> SetAttrib(<span class="pas-kwd">const</span> <span class="pas-kwd">name</span>, value: XmlString): IGpFluentXmlBuilder;<br/> <span class="pas-kwd">function</span> Up: IGpFluentXmlBuilder;<br/> <span class="pas-kwd">property</span> Attrib[<span class="pas-kwd">const</span> <span class="pas-kwd">name</span>, value: XmlString]: IGpFluentXmlBuilder<br/> <span class="pas-kwd">read</span> SetAttrib; <span class="pas-kwd">default</span>;<br/> <span class="pas-kwd">property</span> Xml: IXmlDocument <span class="pas-kwd">read</span> GetXml;<br/> <span class="pas-kwd">end</span>; <span class="pas-comment">{ IGpFluentXmlBuilder }</span><br/><br/><span class="pas-kwd">function</span> CreateFluentXml: IGpFluentXmlBuilder;</pre><p>The fluent XML builder is designed around the concept of the <em>active node</em>, which represents the point where changes are made. When you call the factory function <em>CreateFluentXml</em>, it creates a new IXMLDocument interface and sets active node to this interface (IXMLDocument is IXMLNode so that is not a problem). When you call other functions, active node may change or it may not, depending on a function.</p><p><em>AddProcessingInstruction</em> and <em>AddComment</em> just create a processing instruction (<em>&lt;?xml … ?&gt;</em> line at the beginning of the XML document) and comment and don’t affect the active node.</p><p><em>AddChild</em> creates a new XML node and makes it a child of the current active node.</p><p><em>Up</em> sets active node to the parent of the active node. Unless, of course, if active node is already at the topmost level in which case it will raise an exception. In the yesterday post this method was called <em>Parent</em>.</p><p><em>AddSibling</em> creates a new XML node and makes it a child of the current active node’s parent. In other words, <em>AddSibling</em> is a shorter version of <em>Up</em> followed by the <em>AddChild</em>.</p><p><em>SetAttrib</em> or it’s shorthand, the default property <em>Attrib</em>, sets value of an attribute.</p><p><em>Mark</em> and <em>Return </em>are always used in pairs. <em>Mark</em> pushes active node on the top of the internal (to the xml builder) stack. <em>Return</em> pops a node from the top of the stack and sets it as the active node. Yesterday this pair was named <em>Here</em>/<em>Back</em>.</p><p><em>Anchor</em> copies the active node into its parameter. That allows you to generate the template code with the fluent xml and store few nodes for later use. Then you can use those nodes to insert programmatically generated XML at those points.</p><p>At the end, there’s the <em>Xml</em> property which returns the internal <em>IXMLDocument</em> interface, the one that was created in the <em>CreateFluentXml</em>.</p><p>And now let’s move to the implementation.</p><h2>Implementation</h2><p>Class <em>TGpFluentXmlBuilder</em> implements the <em>IGpFluentXmlBuilder</em> interface. In addition to the methods from this interface, it declares function <em>ActiveNode </em>and three fields – <em>fxbActiveNode</em> stores the active node, <em>fxbMarkedNodes</em> is a stack of nodes stored with the <em>Mark</em> method and <em>fxbXmlDoc</em> is the XML document.</p><pre class="pas-source"><span class="pas-kwd">type</span><br/> TGpFluentXmlBuilder = <span class="pas-kwd">class</span>(TInterfacedObject, IGpFluentXmlBuilder)<br/> strict <span class="pas-kwd">private</span><br/> fxbActiveNode : IXMLNode;<br/> fxbMarkedNodes: IInterfaceList;<br/> fxbXmlDoc : IXMLDocument;<br/> strict <span class="pas-kwd">protected</span><br/> <span class="pas-kwd">function</span> ActiveNode: IXMLNode;<br/> <span class="pas-kwd">protected</span><br/> <span class="pas-kwd">function</span> GetXml: IXmlDocument;<br/> <span class="pas-kwd">public</span><br/> <span class="pas-kwd">constructor</span> Create;<br/> <span class="pas-kwd">destructor</span> Destroy; <span class="pas-kwd">override</span>;<br/> <span class="pas-kwd">function</span> AddChild(<span class="pas-kwd">const</span> <span class="pas-kwd">name</span>: XmlString): IGpFluentXmlBuilder;<br/> <span class="pas-kwd">function</span> AddComment(<span class="pas-kwd">const</span> comment: XmlString): IGpFluentXmlBuilder;<br/> <span class="pas-kwd">function</span> AddProcessingInstruction(<span class="pas-kwd">const</span> target, data: XmlString): IGpFluentXmlBuilder;<br/> <span class="pas-kwd">function</span> AddSibling(<span class="pas-kwd">const</span> <span class="pas-kwd">name</span>: XmlString): IGpFluentXmlBuilder;<br/> <span class="pas-kwd">function</span> Anchor(<span class="pas-kwd">var</span> node: IXMLNode): IGpFluentXmlBuilder;<br/> <span class="pas-kwd">function</span> Mark: IGpFluentXmlBuilder;<br/> <span class="pas-kwd">function</span> Return: IGpFluentXmlBuilder;<br/> <span class="pas-kwd">function</span> SetAttrib(<span class="pas-kwd">const</span> <span class="pas-kwd">name</span>, value: XmlString): IGpFluentXmlBuilder;<br/> <span class="pas-kwd">function</span> Up: IGpFluentXmlBuilder;<br/> <span class="pas-kwd">end</span>; <span class="pas-comment">{ TGpFluentXmlBuilder }</span></pre><p>Some functions are pretty much trivial – one line to execute the action and another to return Self so another fluent XML action can be chained onto result of the function. Of course, some of those functions are simple because they use wrappers from the OmniXMLUtils unit, not from MS-compatible OmniXML.pas. [By the way, you can download OmniXML at <a href="http://www.omnixml.com">www.omnixml.com</a>.]</p><pre class="pas-source"><span class="pas-kwd">function</span> TGpFluentXmlBuilder.AddChild(<span class="pas-kwd">const</span> <span class="pas-kwd">name</span>: XmlString): IGpFluentXmlBuilder;<br/><span class="pas-kwd">begin</span><br/> fxbActiveNode := AppendNode(ActiveNode, <span class="pas-kwd">name</span>);<br/> Result := Self;<br/><span class="pas-kwd">end</span>; <span class="pas-comment">{ TGpFluentXmlBuilder.AddChild }</span><br/><br/><span class="pas-kwd">function</span> TGpFluentXmlBuilder.AddComment(<span class="pas-kwd">const</span> comment: XmlString): IGpFluentXmlBuilder;<br/><span class="pas-kwd">begin</span><br/> ActiveNode.AppendChild(fxbXmlDoc.CreateComment(comment));<br/> Result := Self;<br/><span class="pas-kwd">end</span>; <span class="pas-comment">{ TGpFluentXmlBuilder.AddComment }</span><br/><br/><span class="pas-kwd">function</span> TGpFluentXmlBuilder.AddProcessingInstruction(<span class="pas-kwd">const</span> target, data: XmlString):<br/> IGpFluentXmlBuilder;<br/><span class="pas-kwd">begin</span><br/> ActiveNode.AppendChild(fxbXmlDoc.CreateProcessingInstruction(target, data));<br/> Result := Self;<span class="pas-kwd">end</span>; <span class="pas-comment">{ TGpFluentXmlBuilder.AddProcessingInstruction }</span><br/><br/><span class="pas-kwd">function</span> TGpFluentXmlBuilder.AddSibling(<span class="pas-kwd">const</span> <span class="pas-kwd">name</span>: XmlString): IGpFluentXmlBuilder;<br/><span class="pas-kwd">begin</span><br/> Result := Up;<br/> fxbActiveNode := AppendNode(ActiveNode, <span class="pas-kwd">name</span>);<br/><span class="pas-kwd">end</span>; <span class="pas-comment">{ TGpFluentXmlBuilder.AddSibling }</span><br/><br/><span class="pas-kwd">function</span> TGpFluentXmlBuilder.GetXml: IXmlDocument;<br/><span class="pas-kwd">begin</span><br/> Result := fxbXmlDoc;<br/><span class="pas-kwd">end</span>; <span class="pas-comment">{ TGpFluentXmlBuilder.GetXml }</span><br/><br/><span class="pas-kwd">function</span> TGpFluentXmlBuilder.Mark: IGpFluentXmlBuilder;<br/><span class="pas-kwd">begin</span><br/> fxbMarkedNodes.Add(ActiveNode);<br/> Result := Self;<br/><span class="pas-kwd">end</span>; <span class="pas-comment">{ TGpFluentXmlBuilder.Mark }</span><br/><span class="pas-kwd">function</span> TGpFluentXmlBuilder.Return: IGpFluentXmlBuilder;<br/><span class="pas-kwd">begin</span><br/> fxbActiveNode := fxbMarkedNodes.Last <span class="pas-kwd">as</span> IXMLNode;<br/> fxbMarkedNodes.Delete(fxbMarkedNodes.Count - <span class="pas-num">1</span>);<br/> Result := Self;<br/><span class="pas-kwd">end</span>; <span class="pas-comment">{ TGpFluentXmlBuilder.Return }</span><br/><br/><span class="pas-kwd">function</span> TGpFluentXmlBuilder.SetAttrib(<span class="pas-kwd">const</span> <span class="pas-kwd">name</span>, value: XmlString): IGpFluentXmlBuilder;<br/><span class="pas-kwd">begin</span><br/> SetNodeAttrStr(ActiveNode, <span class="pas-kwd">name</span>, value);<br/> Result := Self;<br/><span class="pas-kwd">end</span>; <span class="pas-comment">{ TGpFluentXmlBuilder.SetAttrib }</span></pre><p>OK, <em>Return</em> has three lines, not two. That makes it medium complicated :)</p><p>In fact <em>Up</em> is also very simple, except that it checks validity of the active node before returning its parent.</p><pre class="pas-source"><span class="pas-kwd">function</span> TGpFluentXmlBuilder.Up: IGpFluentXmlBuilder;<br/><span class="pas-kwd">begin</span><br/> <span class="pas-kwd">if</span> <span class="pas-kwd">not</span> assigned(fxbActiveNode) <span class="pas-kwd">then</span><br/> <span class="pas-kwd">raise</span> Exception.Create(<span class="pas-str">'Cannot access a parent at the root level'</span>)<br/> <span class="pas-kwd">else</span> <span class="pas-kwd">if</span> fxbActiveNode = DocumentElement(fxbXmlDoc) <span class="pas-kwd">then</span><br/> <span class="pas-kwd">raise</span> Exception.Create(<span class="pas-str">'Cannot create a parent at the document element level'</span>)<br/> <span class="pas-kwd">else</span><br/> fxbActiveNode := ActiveNode.ParentNode;<br/> Result := Self;<br/><span class="pas-kwd">end</span>; <span class="pas-comment">{ TGpFluentXmlBuilder.Up }</span></pre><p>A little more trickstery is hidden inside the ActiveNode helper function. It returns active node when it is set; if not it returns XML document’s <em>document element</em>&#160; or the XML doc itself if document element is not set. I don’t think the second option (document element) can ever occur. That part is just there to future-proof the code.</p><pre class="pas-source"><span class="pas-kwd">function</span> TGpFluentXmlBuilder.ActiveNode: IXMLNode;<br/><span class="pas-kwd">begin</span><br/> <span class="pas-kwd">if</span> assigned(fxbActiveNode) <span class="pas-kwd">then</span><br/> Result := fxbActiveNode<br/> <span class="pas-kwd">else</span> <span class="pas-kwd">begin</span><br/> Result := DocumentElement(fxbXmlDoc);<br/> <span class="pas-kwd">if</span> <span class="pas-kwd">not</span> assigned(Result) <span class="pas-kwd">then</span><br/> Result := fxbXmlDoc;<br/> <span class="pas-kwd">end</span>;<br/><span class="pas-kwd">end</span>; <span class="pas-comment">{ TGpFluentXmlBuilder.ActiveNode }</span></pre><p>Believe it or not, that’s all. The whole GpFluentXml unit with comments and everything is only 177 lines long. </p><p>Full GpFluentXML source is available at <a href="http://17slon.com/blogs/gabr/files/GpFluentXml.pas">http://17slon.com/blogs/gabr/files/GpFluentXml.pas</a>.</p><div class="blogger-post-footer"><font size=-2>---<br/>Published under the <a href="http://creativecommons.org/licenses/by/3.0/">Creative Commons Attribution 3.0</a> license</font><img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29331675-6569127094669004877?l=17slon.com%2Fblogs%2Fgabr%2Fblogger.html'/></div>gabrhttp://www.blogger.com/profile/06903558857617342477noreply@blogger.com3tag:blogger.com,1999:blog-29331675.post-64671894873055668552009-04-01T21:04:00.002+02:002009-04-01T21:08:08.569+02:00Fluent XML [1]<p>Few days ago I was writing a very boring piece of code that should generate some XML document. It was full of function calls that created nodes in the XML document and set attributes. Boooooring stuff. But even worse than that – the structure of the XML document was totally lost in the code. It was hard to tell which node is child of which and how it’s all structured.</p> <p>Then I did what every programmer does when he/she should write some boring code – I wrote a tool to simplify the process. [That process usually takes more time than the original approach but at least it is interesting ;) .]</p> <p>I started by writing the endcode. In other words, I started thinking about how I want to create this XML document at all. Quickly I decided on the <a title="Fluent interface @ Wikipedia" href="http://en.wikipedia.org/wiki/Fluent_interface" target="_blank">fluent interface</a> approach. I perused it in the OmniThreadLibrary where it proved to be quite useful.</p> <p>That’s how the first draft looked (Actually, it was much longer but that’s the important part.):</p><pre class="pas-source">xmlWsdl := CreateFluentXml<br/> .AddProcessingInstruction(<span class="pas-str">'xml'</span>, <span class="pas-str">'version="1.0" encoding="UTF-8"'</span>)<br/> .AddChild(<span class="pas-str">'definitions'</span>)<br/> .SetAttr(<span class="pas-str">'xmlns'</span>, <span class="pas-str">'http://schemas.xmlsoap.org/wsdl/'</span>)<br/> .SetAttr(<span class="pas-str">'xmlns:xs'</span>, <span class="pas-str">'http://www.w3.org/2001/XMLSchema'</span>)<br/> .SetAttr(<span class="pas-str">'xmlns:soap'</span>, <span class="pas-str">'http://schemas.xmlsoap.org/wsdl/soap/'</span>)<br/> .SetAttr(<span class="pas-str">'xmlns:soapenc'</span>, <span class="pas-str">'http://schemas.xmlsoap.org/soap/encoding/'</span>)<br/> .SetAttr(<span class="pas-str">'xmlns:mime'</span>, <span class="pas-str">'http://schemas.xmlsoap.org/wsdl/mime/'</span>);</pre><p>This short fragment looks quite nice but in the full version (about 50 lines) all those SetAttr calls visually merged together with AddChild calls and the result was still unreadable (although shorter than the original code with explicit calls to XML interface).</p><p>My first idea was to merge at least some SetAttr calls into the AddChild by introducing two versions – one which takes only a node name and another which takes node name, attribute name and attribute value – but that didn’t help the code at all. Even worse – it was hard to see which AddChild calls were setting attributes and which not :(</p><p>That got me started in a new direction. If the main problem is visual clutter, I had to do something to make setting attributes stand out. Briefly I considered a complicated scheme which would use smart records and operator overloading but I couldn’t imagine a XML creating code which would use operators and be more readable than this so I rejected this approach. [It may still be a valid approach – it’s just that I cannot make it work in my head.]</p><p>Then I thought about arrays. In “classical” code I could easily add array-like support to attributes so that I could write xmlNode[attrName] := ‘some value’, but how can I make this conforming my fluent architecture?</p><h2>To get or not to get</h2><p>In order to be able to chain anything after the [], the indexed property hiding behind must return Self, i.e. the same interface it is living in. And because I want to use attribute name/value pairs, this property has to have two indices.</p><pre class="pas-source"><span class="pas-kwd">property</span> Attrib[<span class="pas-kwd">const</span> <span class="pas-kwd">name</span>, value: XmlString]: IGpFluentXmlBuilder <br> <span class="pas-kwd">read</span> GetAttrib; <span class="pas-kwd">default</span>;</pre><p>That would allow me to write such code:</p><pre class="pas-source">.AddSibling(<span class="pas-str">'service'</span>)[<span class="pas-str">'name'</span>, serviceName]<br/> .AddChild(<span class="pas-str">'port'</span>)<br/> [<span class="pas-str">'name'</span>, portName]<br/> [<span class="pas-str">'binding'</span>, <span class="pas-str">'fs:'</span> + bindingName]<br/> .AddChild(<span class="pas-str">'soap:address'</span>)[<span class="pas-str">'location'</span>, serviceLocation];</pre><p>As you can see, attributes can be chained and I can write attribute assignment in the same line as node creation and it is still obvious which is which and who is who.</p><p>But … assignment? In a getter? Why not! You can do anything in the property getter. To make this more obvious, my code calls this ‘getter’ SetAttrib. As a nice side effect, SetAttrib is completely the same as it was defined in the first draft and can even be used insted of the [] approach.</p><p>I’ll end today’s instalment with the complete 'fluent xml builder’ interface and with sample code that uses this interface to build an XML document. Tomorrow I’ll wrap things up by describing the interface and its implementation in all boring detail.</p><pre class="pas-source"><span class="pas-kwd">type</span><br/> IGpFluentXmlBuilder = <span class="pas-kwd">interface</span> [<span class="pas-str">'{91F596A3-F5E3-451C-A6B9-C5FF3F23ECCC}'</span>]<br/> <span class="pas-kwd">function</span> GetXml: IXmlDocument;<br/> <span class="pas-comment">//</span><br/> <span class="pas-kwd">function</span> Anchor(<span class="pas-kwd">var</span> node: IXMLNode): IGpFluentXmlBuilder;<br/> <span class="pas-kwd">function</span> AddChild(<span class="pas-kwd">const</span> <span class="pas-kwd">name</span>: XmlString): IGpFluentXmlBuilder;<br/> <span class="pas-kwd">function</span> AddComment(<span class="pas-kwd">const</span> comment: XmlString): IGpFluentXmlBuilder;<br/> <span class="pas-kwd">function</span> AddSibling(<span class="pas-kwd">const</span> <span class="pas-kwd">name</span>: XmlString): IGpFluentXmlBuilder;<br/> <span class="pas-kwd">function</span> AddProcessingInstruction(<span class="pas-kwd">const</span> target, data: XmlString): IGpFluentXmlBuilder;<br/> <span class="pas-kwd">function</span> Back: IGpFluentXmlBuilder;<br/> <span class="pas-kwd">function</span> Here: IGpFluentXmlBuilder;<br/> <span class="pas-kwd">function</span> Parent: IGpFluentXmlBuilder;<br/> <span class="pas-kwd">function</span> SetAttrib(<span class="pas-kwd">const</span> <span class="pas-kwd">name</span>, value: XmlString): IGpFluentXmlBuilder;<br/> <span class="pas-kwd">property</span> Attrib[<span class="pas-kwd">const</span> <span class="pas-kwd">name</span>, value: XmlString]: IGpFluentXmlBuilder<br/> <span class="pas-kwd">read</span> SetAttrib; <span class="pas-kwd">default</span>;<br/> <span class="pas-kwd">property</span> Xml: IXmlDocument <span class="pas-kwd">read</span> GetXml;<br/> <span class="pas-kwd">end</span>; <span class="pas-comment">{ IGpFluentXmlBuilder }</span></pre><pre class="pas-source">&nbsp;</pre><pre class="pas-source"> xmlWsdl := CreateFluentXml<br/> .AddProcessingInstruction(<span class="pas-str">'xml'</span>, <span class="pas-str">'version="1.0" encoding="UTF-8"'</span>)<br/> .AddChild(<span class="pas-str">'definitions'</span>)<br/> [<span class="pas-str">'xmlns'</span>, <span class="pas-str">'http://schemas.xmlsoap.org/wsdl/'</span>]<br/> [<span class="pas-str">'xmlns:xs'</span>, <span class="pas-str">'http://www.w3.org/2001/XMLSchema'</span>]<br/> [<span class="pas-str">'xmlns:soap'</span>, <span class="pas-str">'http://schemas.xmlsoap.org/wsdl/soap/'</span>]<br/> [<span class="pas-str">'xmlns:soapenc'</span>, <span class="pas-str">'http://schemas.xmlsoap.org/soap/encoding/'</span>]<br/> [<span class="pas-str">'xmlns:mime'</span>, <span class="pas-str">'http://schemas.xmlsoap.org/wsdl/mime/'</span>]<br/> [<span class="pas-str">'name'</span>, serviceName]<br/> [<span class="pas-str">'xmlns:ns1'</span>, <span class="pas-str">'urn:'</span> + intfName]<br/> [<span class="pas-str">'xmlns:fs'</span>, <span class="pas-str">'http://fab-online.com/soap/'</span>]<br/> [<span class="pas-str">'targetNamespace'</span>, <span class="pas-str">'http://fab-online.com/soap/'</span>]<br/> .AddChild(<span class="pas-str">'message'</span>)[<span class="pas-str">'name'</span>, <span class="pas-str">'fs:'</span> + baseName + <span class="pas-str">'Request'</span>].Anchor(nodeRequest)<br/> .AddSibling(<span class="pas-str">'message'</span>)[<span class="pas-str">'name'</span>, <span class="pas-str">'fs:'</span> + baseName + <span class="pas-str">'Response'</span>].Anchor(nodeResponse)<br/> .AddSibling(<span class="pas-str">'portType'</span>)[<span class="pas-str">'name'</span>, baseName]<br/> .Here<br/> .AddChild(<span class="pas-str">'operation'</span>)[<span class="pas-str">'name'</span>, baseName]<br/> .AddChild(<span class="pas-str">'input'</span>)[<span class="pas-str">'message'</span>, <span class="pas-str">'fs:'</span> + baseName + <span class="pas-str">'Request'</span>]<br/> .AddSibling(<span class="pas-str">'output'</span>)[<span class="pas-str">'message'</span>, <span class="pas-str">'fs:'</span> + baseName + <span class="pas-str">'Response'</span>]<br/> .Back<br/> .AddSibling(<span class="pas-str">'binding'</span>)<br/> .Here<br/> [<span class="pas-str">'name'</span>, bindingName]<br/> [<span class="pas-str">'type'</span>, <span class="pas-str">'fs:'</span> + intfName]<br/> .AddChild(<span class="pas-str">'soap:binding'</span>)<br/> [<span class="pas-str">'style'</span>, <span class="pas-str">'rpc'</span>]<br/> [<span class="pas-str">'transport'</span>, <span class="pas-str">'http://schemas.xmlsoap.og/soap/http'</span>]<br/> .AddChild(<span class="pas-str">'operation'</span>)[<span class="pas-str">'name'</span>, baseName]<br/> .AddChild(<span class="pas-str">'soap:operation'</span>)<br/> [<span class="pas-str">'soapAction'</span>, <span class="pas-str">'urn:'</span> + baseName]<br/> [<span class="pas-str">'style'</span>, <span class="pas-str">'rpc'</span>]<br/> .AddSibling(<span class="pas-str">'input'</span>)<br/> .AddChild(<span class="pas-str">'soap:body'</span>)<br/> [<span class="pas-str">'use'</span>, <span class="pas-str">'encoded'</span>]<br/> [<span class="pas-str">'encodingStyle'</span>, <span class="pas-str">'http://schemas.xmlsoap.org/soap/encoding/'</span>]<br/> [<span class="pas-str">'namespace'</span>, <span class="pas-str">'urn:'</span> + intfName + <span class="pas-str">'-'</span> + baseName]<br/> .Parent<br/> .AddSibling(<span class="pas-str">'output'</span>)<br/> .AddChild(<span class="pas-str">'soap:body'</span>)<br/> [<span class="pas-str">'use'</span>, <span class="pas-str">'encoded'</span>]<br/> [<span class="pas-str">'encodingStyle'</span>, <span class="pas-str">'http://schemas.xmlsoap.org/soap/encoding/'</span>]<br/> [<span class="pas-str">'namespace'</span>, <span class="pas-str">'urn:'</span> + intfName + <span class="pas-str">'-'</span> + baseName]<br/> .Back<br/> .AddSibling(<span class="pas-str">'service'</span>)[<span class="pas-str">'name'</span>, serviceName]<br/> .AddChild(<span class="pas-str">'port'</span>)<br/> [<span class="pas-str">'name'</span>, portName]<br/> [<span class="pas-str">'binding'</span>, <span class="pas-str">'fs:'</span> + bindingName]<br/> .AddChild(<span class="pas-str">'soap:address'</span>)[<span class="pas-str">'location'</span>, serviceLocation];</pre><p>What do <strong>you</strong> think? Does my approach make any sense?</p><div class="blogger-post-footer"><font size=-2>---<br/>Published under the <a href="http://creativecommons.org/licenses/by/3.0/">Creative Commons Attribution 3.0</a> license</font><img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29331675-6467189487305566855?l=17slon.com%2Fblogs%2Fgabr%2Fblogger.html'/></div>gabrhttp://www.blogger.com/profile/06903558857617342477noreply@blogger.com34tag:blogger.com,1999:blog-29331675.post-71738693577001350822009-02-10T20:21:00.002+01:002009-02-10T20:22:57.654+01:00OmniThreadLibrary 1.03<p>OmniThreadLibrary 1.03 was silently released two days ago. Even without the announcement, it was downloaded 133 times to this point. Awesome!</p> <p>The main new feature is per-thread initialized data in thread pool. That allows you to create a connection pool with OTL. There’s a simple demo included in the distribution (24_ConnectionPool). I wrote few words about it <a title="Building a connection pool" href="http://17slon.com/blogs/gabr/2009/02/building-connection-pool.html" target="_blank">yesterday</a>.</p> <p>No bugs were fixed so you don’t have to upgrade if you don’t need new thread pool functionality.</p> <p>As usual, you can get it via <a href="http://omnithreadlibrary.googlecode.com/svn/tags/release-1.02" target="_blank">SVN</a> or as a <a href="http://code.google.com/p/omnithreadlibrary/downloads/list" target="_blank">ZIP archive</a>.</p> <p>Other important OTL links:</p> <ul> <li><a href="http://otl.17slon.com/" target="_blank">Home page</a> </li> <li><a href="http://otl.17slon.com/forum/" target="_blank">Web discussion forum</a> </li> <li><a href="http://code.google.com/p/omnithreadlibrary/issues/list" target="_blank">Issue tracker</a> </li> <li><a href="http://code.google.com/p/omnithreadlibrary/source/checkout" target="_blank">SVN checkout instructions</a> </li> </ul> <div class="blogger-post-footer"><font size="-2">--- <br />Published under the <a href="http://creativecommons.org/licenses/by/3.0/">Creative Commons Attribution 3.0</a> license</font></div><div class="blogger-post-footer"><font size=-2>---<br/>Published under the <a href="http://creativecommons.org/licenses/by/3.0/">Creative Commons Attribution 3.0</a> license</font><img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29331675-7173869357700135082?l=17slon.com%2Fblogs%2Fgabr%2Fblogger.html'/></div>gabrhttp://www.blogger.com/profile/06903558857617342477noreply@blogger.com0tag:blogger.com,1999:blog-29331675.post-21088438104824421692009-02-09T23:18:00.002+01:002009-02-09T23:23:29.591+01:00Building a connection pool<p>Recently, an OTL user asked me <a title="OTL Connection Pool System possible?" href="http://otl.17slon.com/forum/index.php?topic=30.msg159" target="_blank">in the forum</a> how to build a connection pool with the OTL. The answer, at the time, was – not possible. There was a <a title="Adding connection pool mechanism to OmniThreadLibrary" href="http://17slon.com/blogs/gabr/2009/02/adding-connection-pool-mechanism-to.html" target="_blank">crucial component missing</a>.</p> <p>It turned out that implementing thread-global data was not really hard to do so here it is – a tutorial on how to build a connection pool with the OTL (also included in the latest release as a demo <em>24_ConnectionPool</em>). To run this code you’ll need <a title="OmniThreadLibrary v1.03" href="http://omnithreadlibrary.googlecode.com/files/OmniThreadLibrary-1.03.zip" target="_blank">OTL 1.03</a>.</p> <p>Let’s say we want to build a pool of some entities that take some time to initialize (database connections, for example). In a traditional sense, one would build a list of objects managing those entities and would then allocate them to the threads running the code. In practice, we can run in a big problem if such entities expect to always run from the thread in which they were created. (I had such problem once with TDBIB and Firebird Embedded.) To solve this, we would have to associate entities with threads and we’ll also have to monitor thread lifecycle (to deallocate entities when a thread is terminated).</p> <p>With OTL, the logic is reversed. Threads will be managed by a thread pool and there will be no need for us to create/destroy them. We’ll just create a task and submit it into a thread pool. Thread pool will initialize the pool entity (database connection), associate it with a thread and pass it to all tasks that will run in this thread so that they can use it.</p> <p>Furthermore, this solution allows you to use all the functionality of the OTL thread pool. You can set maximum number of concurrent tasks, idle thread timeout, maximum time the task will wait for execution and more and more.</p> <p>So let’s see how we can code this in the OTL. All code was extracted from the demo <em>24_ConnectionPool</em>.</p> <h2>Connection pool demo</h2> <p>In the <em>OnCreate</em> event the code creates a thread pool, assigns it a name and thread data factory. The latter is a function that will create and initialize new connection for each new thread. In the <em>OnClose</em> event the code terminates all waiting tasks (if any), allowing the application to shutdown gracefully. <em>FConnectionPool</em> is an interface and its lifetime is managed automatically so we don’t have to do anything explicit with it.</p> <pre class="pas-source"><span class="pas-kwd">procedure</span> TfrmConnectionPoolDemo.FormCreate(Sender: TObject);<br/><span class="pas-kwd">begin</span><br/> FConnectionPool := CreateThreadPool(<span class="pas-str">'Connection pool'</span>);<br/> FConnectionPool.ThreadDataFactory := CreateThreadData;<br/><span class="pas-kwd">end</span>;<br/><br/><span class="pas-kwd">procedure</span> TfrmConnectionPoolDemo.FormClose(Sender: TObject; <span class="pas-kwd">var</span> Action: TCloseAction);<br/><span class="pas-kwd">begin</span><br/> FConnectionPool.CancelAll;<br/><span class="pas-kwd">end</span>;</pre><p>The magic <em>CreateThreadData</em> factory just creates a connection object (which would in a real program establish a database connection, for example).</p><pre class="pas-source"><span class="pas-kwd">function</span> CreateThreadData: IInterface;<br/><span class="pas-kwd">begin</span><br/> Result := TConnectionPoolData.Create;<br/><span class="pas-kwd">end</span>;</pre><p>There’s no black magic behind this connection object. It is an object which implements an interface. Any interface. This interface will be used only in your code. In this demo, <em>TConnectionPoolData</em> contains only one field – unique ID, which will help us follow the program execution.</p><pre class="pas-source"><span class="pas-kwd">type</span><br/> IConnectionPoolData = <span class="pas-kwd">interface</span> [<span class="pas-str">'{F604640D-6D4E-48B4-9A8C-483CA9635C71}'</span>]<br/> <span class="pas-kwd">function</span> ConnectionID: integer;<br/> <span class="pas-kwd">end</span>;<br/><br/> TConnectionPoolData = <span class="pas-kwd">class</span>(TInterfacedObject, IConnectionPoolData)<br/> strict <span class="pas-kwd">private</span><br/> cpID: integer;<br/> <span class="pas-kwd">public</span><br/> <span class="pas-kwd">constructor</span> Create;<br/> <span class="pas-kwd">destructor</span> Destroy; <span class="pas-kwd">override</span>;<br/> <span class="pas-kwd">function</span> ConnectionID: integer;<br/> <span class="pas-kwd">end</span>; <span class="pas-comment">{ TConnectionPoolData }</span></pre><p>As this is not a code from a real world application, I didn’t bother connecting it to any specific database. <em>TConnectionPoolData</em> constructor will just notify the main form that it has begun its job, generate new ID and sleep for 5 seconds (to emulate establishing a slow connection). The destructor is even simpler, it just sends a notification to the main form.</p><pre class="pas-source"><span class="pas-kwd">constructor</span> TConnectionPoolData.Create;<br/><span class="pas-kwd">begin</span><br/> PostToForm(WM_USER, MSG_CREATING_CONNECTION, integer(GetCurrentThreadID));<br/> cpID := GConnPoolID.Increment;<br/> Sleep(<span class="pas-num">5000</span>);<br/> PostToForm(WM_USER, MSG_CREATED_CONNECTION, cpID);<br/><span class="pas-kwd">end</span>;<br/><br/><span class="pas-kwd">destructor</span> TConnectionPoolData.Destroy;<br/><span class="pas-kwd">begin</span><br/> PostToForm(WM_USER, MSG_DESTROY_CONNECTION, cpID);<br/><span class="pas-kwd">end</span>;</pre><p>Creating and running a task is really simple with the OTL:</p><pre class="pas-source"><span class="pas-kwd">procedure</span> TfrmConnectionPoolDemo.btnScheduleClick(Sender: TObject);<br/><span class="pas-kwd">begin</span><br/> Log(<span class="pas-str">'Creating task'</span>);<br/> CreateTask(TaskProc).MonitorWith(OTLMonitor).Schedule(FConnectionPool);<br/><span class="pas-kwd">end</span>;</pre><p>We are monitoring the task with the<em> TOmniEventMonitor</em> component because a) we want to know when the task will terminate and b) otherwise we would have to keep reference to the IOmniTaskControl interface returned from the <em>CreateTask</em>. </p><p>The task worker procedure <em>TaskProc</em> is again really simple. First it pulls the connection data from the <em>task</em> interface (<em>task.ThreadData as IConnectionPoolData</em>), retrieves the connection ID and sends task and connection ID to the main form (for logging purposes) and then it sleeps for three seconds, indicating some heavy database activity.</p><pre class="pas-source"><span class="pas-kwd">procedure</span> TaskProc(<span class="pas-kwd">const</span> task: IOmniTask);<br/><span class="pas-kwd">begin</span><br/> PostToForm(WM_USER + <span class="pas-num">1</span>, task.UniqueID,<br/> (task.ThreadData <span class="pas-kwd">as</span> IConnectionPoolData).ConnectionID);<br/> Sleep(<span class="pas-num">3000</span>);<br/><span class="pas-kwd">end</span>;</pre><p>Then … but wait! There’s no more! Believe it or not, that’s all. OK, there is some infrastructure code that is used only for logging but that you can look up by yourself.</p><p>There is also a code assigned to the second button (“Schedule and wait”) but it only demonstrates how you can schedule a task and wait on its execution. Useful if you’re running the task from a background thread (for example, Indy thread, as specified by the author of the original question).</p><h2>Running the demo</h2><p>Let’s run the demo and click on the <em>Schedule </em>key. </p><p><a href="http://17slon.com/blogs/gabr/files/Buildingconnectionpool_14686/image.png"><img title="image" style="border-right: 0px; border-top: 0px; display: inline; border-left: 0px; border-bottom: 0px" height="399" alt="image" src="http://17slon.com/blogs/gabr/files/Buildingconnectionpool_14686/image_thumb.png" width="583" border="0" /></a></p><p>What happened here?</p><ul><li>Task was created.</li><li>Immediately, it was scheduled for execution and thread pool called our thread data factory.</li><li>Thread data waited for five seconds and returned.</li><li>Thread pool immediately started executing the task.</li><li>Task waited for three seconds and exited.</li></ul><p>OK, nothing special. Let’s click the <em>Schedule</em> button again.</p><p><a href="http://17slon.com/blogs/gabr/files/Buildingconnectionpool_14686/image_3.png"><img title="image" style="border-right: 0px; border-top: 0px; display: inline; border-left: 0px; border-bottom: 0px" height="399" alt="image" src="http://17slon.com/blogs/gabr/files/Buildingconnectionpool_14686/image_thumb_3.png" width="583" border="0" /></a></p><p>Now a new task was created (with ID 4), was scheduled for execution in the same thread as the previous task and reused the connection that was created when the first task was scheduled. There is no 5 second wait, just the 3 second wait implemented in the task worker procedure.</p><p>If you now leave the program running for 10 seconds, a message <em>Destroying connection 1</em> will appear. The reason for this is that the default thread idle timeout in the OTL thread pool is 10 seconds. In other words, if a thread does nothing for 10 seconds, it will be stopped. You are, of course, free to set this value to any number or even to 0, which would disable the idle thread termination mechanism.</p><p>If you now click the <em>Schedule </em>button again, new thread will be created in the thread pool and new connection will be created in our factory function (spending 5 seconds doing nothing). </p><p><a href="http://17slon.com/blogs/gabr/files/Buildingconnectionpool_14686/image_4.png"><img title="image" style="border-right: 0px; border-top: 0px; display: inline; border-left: 0px; border-bottom: 0px" height="399" alt="image" src="http://17slon.com/blogs/gabr/files/Buildingconnectionpool_14686/image_thumb_4.png" width="583" border="0" /></a></p><p>Let’s try something else. I was running the demo on my laptop with a dual core CPU, which caused the OTL thread pool to limit maximum number of currently executing threads to two. By default, OTL thread pool uses as much threads as there are cores in the system, but again you can override the value. At the moment, you are limited by a maximum 60 concurrent threads, which should not cause any problems in the next few years, I hope. (The 60 thread limit is not an arbitrary number but is caused by the Windows limitation of allowing only up to 64 handles in the <em>WaitForMultipleObjects</em> function.) Yes, you are allowed to set this limitation to a value higher than the number of CPU cores in the system but still, running 60 active concurrent threads is really not recommended.</p><p>To recap – when running the demo, OTL thread pool was limited to two concurrent threads. When I clicked the <em>Schedule</em> button two times in a quick succession, first task was scheduled and first connection started being established (translation: entered the <em>Sleep</em> function). Then the second task was created (as the connection is being established from the worker thread, GUI is not blocked) and second connection started being established in the second thread. Five seconds later, connections are created and task start running (and wait three seconds, and exit).</p><p><a href="http://17slon.com/blogs/gabr/files/Buildingconnectionpool_14686/image_5.png"><img title="image" style="border-right: 0px; border-top: 0px; display: inline; border-left: 0px; border-bottom: 0px" height="399" alt="image" src="http://17slon.com/blogs/gabr/files/Buildingconnectionpool_14686/image_thumb_5.png" width="583" border="0" /></a></p><p>Then I clicked the <em>Schedule </em>button two more times. Two tasks were scheduled and they immediately started execution in two worker threads.</p><p><a href="http://17slon.com/blogs/gabr/files/Buildingconnectionpool_14686/image_6.png"><img title="image" style="border-right: 0px; border-top: 0px; display: inline; border-left: 0px; border-bottom: 0px" height="399" alt="image" src="http://17slon.com/blogs/gabr/files/Buildingconnectionpool_14686/image_thumb_6.png" width="583" border="0" /></a></p><p>For the third demo, I restarted the app and clicked the <em>Shedule</em> button three times. Only two worker threads were created and two connections established and two tasks started execution. The third task entered the thread pool queue and waited for the first task to terminate, after which it was immediately scheduled.</p><p><a href="http://17slon.com/blogs/gabr/files/Buildingconnectionpool_14686/image_7.png"><img title="image" style="border-right: 0px; border-top: 0px; display: inline; border-left: 0px; border-bottom: 0px" height="399" alt="image" src="http://17slon.com/blogs/gabr/files/Buildingconnectionpool_14686/image_thumb_7.png" width="583" border="0" /></a></p><p>So here you have it – a very simple way to build a connection pool. Have fun!</p><div class="blogger-post-footer"><font size=-2>---<br/>Published under the <a href="http://creativecommons.org/licenses/by/3.0/">Creative Commons Attribution 3.0</a> license</font><img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29331675-2108843810482442169?l=17slon.com%2Fblogs%2Fgabr%2Fblogger.html'/></div>gabrhttp://www.blogger.com/profile/06903558857617342477noreply@blogger.com2tag:blogger.com,1999:blog-29331675.post-67269539330454593702009-02-06T10:28:00.003+01:002009-02-06T10:46:20.062+01:00Adding connection pool mechanism to OmniThreadLibrary<p>I have a problem.</p> <p>I have this thread pool, which needs to be enhanced a little. And I don’t know how to do it.</p> <p>Well, actually I know. I have at least three approaches. I just can’t tell which one is the best :(</p> <p>I’m talking about the OmniThreadLibrary – I know you guessed that already. The problem is the thread pool in OTL doesn’t allow for per-thread resource initialization and that’s something that you need when you’re implementing a connection pool (<a title="OTL Connection Pool System possible?" href="http://otl.17slon.com/forum/index.php?topic=30.0" target="_blank">more background info here</a>). I started adding this functionality but soon found out that I don’t have a good idea on how to implement it. Oh, I have few ideas, they are just not very good :(</p> <h2>Current</h2> <p>At the moment, OTL thread pool functionality is exposed through an interface. This interface is pretty high-level and doesn’t allow the programmer to mess with the underlying thread management. All thread information is hidden in the implementation section. There’s a notification event that’s triggered when pool thread is created or destroyed, but it is only a notification and is triggered asynchronously (and possibly with a delay).</p> <p>In short:</p> <pre class="pas-source"><span class="pas-kwd">type</span><br /> TOTPWorkerThread = <span class="pas-kwd">class</span>(TThread)<br /> <span class="pas-kwd">end</span>;<br /><br /> TOmniThreadPool = <span class="pas-kwd">class</span>(TInterfacedObject, IOmniThreadPool)<br /> <span class="pas-kwd">end</span>;</pre><h2>Event handlers</h2><p>The first idea was to add OnThreadInitialization/OnThreadCleanup to the IOmniThreadPool. <em>[Actually something similar already exists - OnWorkerThreadCreated_Asy and OnWorkerThreadDestroyed_Asy – but those events are part of the previous implementation and will be removed very soon.]</em> Those two events would receive a TThread parameter and do the proper initialization there.</p><p>There are some big problems though. Let’s say you’ll be implementing database connection pool. You’ll have to open a database connection in OnThreadInitialization. Where would you store that info then? In an external structure, indexed by the TThread? Ugly! Even worse – how would you access the database info from the task that will be executing in the thread pool? By accessing that same structure? Eugh!</p><p>Rejected.</p><h2>Thread subclassing</h2><p>A better idea is to implement a subclassed thread class in your own code and then tell the thread pool to use this thread class when creating new thread object. You’d then manage database connection in overridden Initialize/Cleanup methods.</p><pre class="pas-source"><span class="pas-kwd">type</span><br /> TDBConnectionPoolThread = <span class="pas-kwd">class</span>(TOTPWorkerThread)<br /> strict <span class="pas-kwd">private</span><br /> FDBConnection: TDBConnectionInfo;<br /> <span class="pas-kwd">protected</span><br /> <span class="pas-kwd">function</span> Initialize: boolean; <span class="pas-kwd">override</span>;<br /> <span class="pas-kwd">procedure</span> Cleanup; <span class="pas-kwd">override</span>;<br /> <span class="pas-kwd">end</span>;<br /><br />GlobalOmniThreadPool.ThreadClass := TDBConnectionPoolThread;</pre><p>Looks much better but there’s again a problem – I’d have to expose TOTPWorkerThread object in the interface section and that’s just plain ugly. Worker thread mechanism should be hidden. Only few people would ever be interested in it.</p><h2>Thread data subclassing</h2><p>An even better idea is to add an empty</p><pre class="pas-source">TOTPWorkerThreadData = <span class="pas-kwd">class</span><br /><span class="pas-kwd">end</span>;</pre><p>definition to the interface section of the thread pool unit. IOmniThreadPool would contain a property ThreadDataClass which would point to this definition. And each worker thread would create/destroy an instance of this class in its Execute method.</p><p>You’d add database management as</p><pre class="pas-source"><span class="pas-kwd">type</span><br /> TDBConnectionPoolThreadData = <span class="pas-kwd">class</span>(TOTPWorkerThreadData)<br /> strict <span class="pas-kwd">private</span><br /> FDBConnection: TDBConnectionInfo;<br /> <span class="pas-kwd">protected</span><br /> <span class="pas-kwd">constructor</span> Create;<br /> <span class="pas-kwd">destructor</span> Destroy; <span class="pas-kwd">override</span>;<br /> <span class="pas-kwd">end</span>;<br /><br />GlobalOmniThreadPool.ThreadDataClass := TDBConnectionPoolThreadData;</pre><p><em>[Maybe the constructor has to be virtual here? I never know until I try.]</em></p><p>There’s still a question of accessing this information from the task (and it goes the same for the previous attempt – I just skipped the issue then). I’d have to extend the IOmniTask interface with a method to access per-thread data.</p><h2>Thread data with interfaces</h2><p>While writing this <em>mind dump</em> a new idea crossed my mind – what if thread data would be implemented as an interface, without the need for subclassing the thread or thread data? In a way it is a first idea just reimplemented to remove all its problems.</p><p>Task interface would be extended with thread data access definitions, approximately like this:</p><pre class="pas-source"><span class="pas-kwd">type</span><br /> IOtlThreadData = <span class="pas-kwd">interface</span><br /> <span class="pas-kwd">end</span>;<br /><br /> IOtlTask = <span class="pas-kwd">interface</span><br /> <span class="pas-kwd">property</span> ThreadData: IOtlThreadData;<br /> <span class="pas-kwd">end</span>;</pre><p>Thread pool would get a property containing a factory method.</p><pre class="pas-source"><span class="pas-kwd">type</span><br /> TCreateThreadDataProc = <span class="pas-kwd">function</span>: IOtlThreadData;<br /><br /> IOmniThreadPool = <span class="pas-kwd">interface</span><br /> <span class="pas-kwd">property</span> ThreadDataFactory: TCreateThreadDataProc;<br /> <span class="pas-kwd">end</span>;</pre><p>This factory method would be called when thread is created to initialize thread data. Each task would get assigned that same interface into its ThreadData property just before starting its execution in a selected thread. Task would then access ThreadData property to retrieve this information.</p><p>In the database connection pool scenario, you’d have to write a connection interface, object and factory.</p><pre class="pas-source"><span class="pas-kwd">type</span><br /> IDBConnectionPoolThreadData = <span class="pas-kwd">interface</span>(IOtlThreadData)<br /> <span class="pas-kwd">property</span> ConnectionInfo: TDBConnectionInfo <span class="pas-kwd">read</span> GetConnectionInfo;<br /> <span class="pas-kwd">end</span>;<br /><br /> TDBConnectionPoolThreadData = <span class="pas-kwd">class</span>(TInterfacedObject, IDBConnectionPoolThreadData )<br /> strict <span class="pas-kwd">private</span><br /> FDBConnection: TDBConnectionInfo;<br /> <span class="pas-kwd">protected</span><br /> <span class="pas-kwd">constructor</span> Create;<br /> <span class="pas-kwd">destructor</span> Destroy; <span class="pas-kwd">override</span>;<br /> <span class="pas-kwd">end</span>;<br /><br /> <span class="pas-kwd">function</span> CreateConnectionPoolThreadData: IDBCOnnectionPoolThreadData;<br /> <span class="pas-kwd">begin</span><br /> Result := TDBConnectionPoolThreadData.Create;<br /> <span class="pas-kwd">end</span>;<br /><br />GlobalThreadPool.ThreadDataFactory := CreateConnectionPoolThreadData;</pre><p>This approach requires slightly more work from the programmer but I like it most as it somehow seems the cleanest of them all (plus it is implemented with interfaces which is pretty much the approach used in all OTL code).</p><p>So, dear reader, what do you think? If you have better idea, or see a big problem with any of those implementations that I didn’t think of, please do tell in the comments!</p><div class="blogger-post-footer"><font size="-2">---<br /> <br />Published under the <a href="http://creativecommons.org/licenses/by/3.0/">Creative Commons Attribution 3.0</a> license</font></div><div class="blogger-post-footer"><font size=-2>---<br/>Published under the <a href="http://creativecommons.org/licenses/by/3.0/">Creative Commons Attribution 3.0</a> license</font><img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29331675-6726953933045459370?l=17slon.com%2Fblogs%2Fgabr%2Fblogger.html'/></div>gabrhttp://www.blogger.com/profile/06903558857617342477noreply@blogger.com2tag:blogger.com,1999:blog-29331675.post-53360241830926666552009-02-03T23:19:00.003+01:002009-02-06T10:50:05.491+01:00Hassle-free critical section<p align="justify">While writing multithreaded code I sometimes need a fine-grained critical section that will synchronize access to some small (very small) piece of code. In the OmniThreadLibrary, for example, there’s a class TOmniTaskExecutor which has some of its internals (for example a set of Option flags) exposed to both the task controller and the task itself (and those two by definition live in two different threads). Access to those internal fields is serialized with a critical section.</p> <p align="justify">Usually, I need two or more such critical sections. And because I’m lazy and I don’t want to write creation/destruction code every time I need a fine-grained lock, I usually create only one critical section and use if for all such accesses. In other words, when thread 1 is accessing field 1 (protected with that one critical section), thread 2 will be blocked from accessing field 2 (because it is protected with the same critical section). I can live with that, because the frequency of such accesses is very low (or I would not be reusing the same critical section). </p> <p align="justify">Still, I was not happy with this status quo but I didn’t know what to do (except creating more critical sections, of course). Then, while developing the new OTL thread pool, I got a great idea – records! Records need no explicit .Create. Let’s make this new critical section a record!</p> <p align="justify">Let’s start with the use scenario. I want to be able to declare the critical section object …</p> <pre class="pas-source"> TOmniTaskExecutor = <span class="pas-kwd">class</span><br/> strict <span class="pas-kwd">private</span><br/> oteInternalLock: TOmniCS;<br/> <span class="pas-comment">//...</span><br/> <span class="pas-kwd">end</span>;</pre><p align="justify">… and then use it without any initialization.</p><pre class="pas-source"> oteInternalLock.Acquire;<br/> <span class="pas-kwd">try</span><br/> <span class="pas-kwd">if</span> <span class="pas-kwd">not</span> assigned(oteCommList) <span class="pas-kwd">then</span><br/> oteCommList := TInterfaceList.Create;<br/> oteCommList.Add(comm);<br/> SetEvent(oteCommRebuildHandles);<br/> <span class="pas-kwd">finally</span> oteInternalLock.Release; <span class="pas-kwd">end</span>;</pre><p align="justify">There are only two problems to be solved. I had to make sure that critical section is created when the record is first used and destroyed when the owning object is destroyed. It turned out that this is quite a big <em>only</em> …</p><h2>Destruction</h2><p align="justify">Let’s start with the simpler problem – destruction. The solution to automatic record cleanup is well-documented (at least if you follow Delphi blogs where they talk about such things …). In general, Delphi compiler doesn’t guarantee what the initial state of record fields will be, but there are two exceptions to this rule – all strings are initialized to an empty string and all interfaces to nil (which in both cases means that the fields holding strings/interfaces are initialized to 0). In addition to that, the compiler will free memory allocated for string fields and destroy interfaces (well, decrease the reference count) when record goes <em>out of scope.</em> If the record is declared inside a method, this will happen when the method exits and if it is declared as a class field, the cleanup will occur when the class is destroyed. In any case, you can be sure that the compiler will take care for strings and interfaces.</p><p align="justify">So we already know something – TOmniCS record will contain an interface field and an instance of the object implementing this interface will do the actual critical section allocation and access.</p><pre class="pas-source"> IOmniCriticalSection = <span class="pas-kwd">interface</span> [<span class="pas-str">'{AA92906B-B92E-4C54-922C-7B87C23DABA9}'</span>]<br/> <span class="pas-kwd">procedure</span> Acquire;<br/> <span class="pas-kwd">procedure</span> Release;<br/> <span class="pas-kwd">function</span> GetSyncObj: TSynchroObject;<br/> <span class="pas-kwd">end</span>; <span class="pas-comment">{ IOmniCriticalSection }</span><br/><br/> TOmniCS = <span class="pas-kwd">record</span><br/> <span class="pas-kwd">private</span><br/> ocsSync: IOmniCriticalSection;<br/> <span class="pas-kwd">function</span> GetSyncObj: TSynchroObject;<br/> <span class="pas-kwd">public</span><br/> <span class="pas-kwd">procedure</span> Initialize;<br/> <span class="pas-kwd">procedure</span> Acquire; <span class="pas-kwd">inline</span>;<br/> <span class="pas-kwd">procedure</span> Release; <span class="pas-kwd">inline</span>;<br/> <span class="pas-kwd">property</span> SyncObj: TSynchroObject <span class="pas-kwd">read</span> GetSyncObj;<br/> <span class="pas-kwd">end</span>; <span class="pas-comment">{ TOmniCS }</span></pre><p align="justify">The implementation of the IOmniCriticalSection interface is trivial.</p><pre class="pas-source"> TOmniCriticalSection = <span class="pas-kwd">class</span>(TInterfacedObject, IOmniCriticalSection)<br/> strict <span class="pas-kwd">private</span><br/> ocsCritSect: TSynchroObject;<br/> <span class="pas-kwd">public</span><br/> <span class="pas-kwd">constructor</span> Create;<br/> <span class="pas-kwd">destructor</span> Destroy; <span class="pas-kwd">override</span>;<br/> <span class="pas-kwd">procedure</span> Acquire; <span class="pas-kwd">inline</span>;<br/> <span class="pas-kwd">function</span> GetSyncObj: TSynchroObject;<br/> <span class="pas-kwd">procedure</span> Release; <span class="pas-kwd">inline</span>;<br/> <span class="pas-kwd">end</span>; <span class="pas-comment">{ TOmniCriticalSection }</span></pre><br/><pre class="pas-source"><span class="pas-kwd">constructor</span> TOmniCriticalSection.Create;<br/><span class="pas-kwd">begin</span><br/> ocsCritSect := TCriticalSection.Create;<br/><span class="pas-kwd">end</span>; <span class="pas-comment">{ TOmniCriticalSection.Create }</span><br/><br/><span class="pas-kwd">destructor</span> TOmniCriticalSection.Destroy;<br/><span class="pas-kwd">begin</span><br/> FreeAndNil(ocsCritSect);<br/><span class="pas-kwd">end</span>; <span class="pas-comment">{ TOmniCriticalSection.Destroy }</span><br/><br/><span class="pas-kwd">procedure</span> TOmniCriticalSection.Acquire;<br/><span class="pas-kwd">begin</span><br/> ocsCritSect.Acquire;<br/><span class="pas-kwd">end</span>; <span class="pas-comment">{ TOmniCriticalSection.Acquire }</span><br/><br/><span class="pas-kwd">function</span> TOmniCriticalSection.GetSyncObj: TSynchroObject;<br/><span class="pas-kwd">begin</span><br/> Result := ocsCritSect;<br/><span class="pas-kwd">end</span>; <span class="pas-comment">{ TOmniCriticalSection.GetSyncObj }</span><br/><br/><span class="pas-kwd">procedure</span> TOmniCriticalSection.Release;<br/><span class="pas-kwd">begin</span><br/> ocsCritSect.Release;<br/><span class="pas-kwd">end</span>; <span class="pas-comment">{ TOmniCriticalSection.Release }</span></pre><pre class="pas-source"><span class="pas-kwd">function</span> CreateOmniCriticalSection: IOmniCriticalSection;<br/><span class="pas-kwd">begin</span><br/> Result := TOmniCriticalSection.Create;<br/><span class="pas-kwd">end</span>; <span class="pas-comment">{ CreateOmniCriticalSection }</span></pre><br/><h2>Creation</h2><p align="justify">The destruction part was trivial (once you know the trick, of course), but the creation is not. Delphi only guarantees that the <em>ocsSync</em> interface will be initialized to nil (or 0, if you prefer), nothing more than that.</p><p align="justify">The TOmniCS record offloads all hard work to the <em>Initialize</em> method. It is called from <em>Acquire</em> and <em>GetSyncObj</em> (a method that returns underlying critical section), but not from <em>Release</em> and that’s for a reason. If you call <em>Release</em> before first calling <em>Acquire</em>, it is clearly a programming error and program should crash – and it will because the <em>ocsSync</em> will be nil.</p><pre class="pas-source"><span class="pas-kwd">procedure</span> TOmniCS.Acquire;<br/><span class="pas-kwd">begin</span><br/> Initialize;<br/> ocsSync.Acquire;<br/><span class="pas-kwd">end</span>; <span class="pas-comment">{ TOmniCS.Acquire }</span><br/><br/><span class="pas-kwd">function</span> TOmniCS.GetSyncObj: TSynchroObject;<br/><span class="pas-kwd">begin</span><br/> Initialize;<br/> Result := ocsSync.GetSyncObj;<br/><span class="pas-kwd">end</span>; <span class="pas-comment">{ TOmniCS.GetSyncObj }</span><br/><br/><span class="pas-kwd">procedure</span> TOmniCS.Release;<br/><span class="pas-kwd">begin</span><br/> ocsSync.Release;<br/><span class="pas-kwd">end</span>; <span class="pas-comment">{ TOmniCS.Release }</span></pre><p align="justify">Let’s finally solve the hard work. Before the critical section can be used, <em>ocsSync</em> interface must be initialized. In a single-threaded world we would just create a <em>TOmniCriticalSection </em>object and store it in the <em>ocsSync</em> field. In the multi-threaded world this is not possible. </p><p align="justify">Let’s think about what can happen if two <em>Acquire</em> calls are made at the same time from two threads. Thread 1 checks if <em>ocsSync</em> is initialized, finds that it’s not and loses its CPU slice. Thread 2 checks if <em>ocsSync</em> is initialized, finds that it’s not, initializes it, calls <em>Acquire</em> and loses its CPU slice. Thread 1 creates another <em>TOmniCriticalSection</em> object, stores it in the <em>ocsSync</em> field (overwriting the previous value, which will get its reference count decremented, which will destroy the implementing object) and calls <em>Acquire</em>. Because this <em>Acquire</em> will be using a critical section different from the <em>Acquire</em> in thread 2, it will succeed and both threads will have access to the protected data. Bad!</p><p align="justify">The trick is to store <em>TOmniCriticalSecion</em> in the <em>ocsSync</em> field with an atomic operation that will succeed if and only if the <em>ocsSync</em> is empty (nil, zero). And that’s a job for the <a title="InterlockedCompareExchange @ MSDN" href="http://msdn.microsoft.com/en-us/library/ms683560.aspx" target="_blank">InterlockedCompareExchange</a> (ICE in short).</p><p align="justify">ICE takes three parameters – first is an address of the memory area we are trying to modify. Second is the new value and third is the expected value stored in the memory area we are trying to modify. The function returns the current value of the affected memory area. If this memory is not equal to the third parameter than ICE will do nothing.</p><p align="justify">Quite a mouthful, I know. That’s how it is used in practice:</p><pre class="pas-source"><span class="pas-kwd">procedure</span> TOmniCS.Initialize;<br/><span class="pas-kwd">var</span><br/> syncIntf: IOmniCriticalSection;<br/><span class="pas-kwd">begin</span><br/> Assert(cardinal(@ocsSync) <span class="pas-kwd">mod</span> <span class="pas-num">4</span> = <span class="pas-num">0</span>, <span class="pas-str">'TOmniCS.Initialize: ocsSync is not 4-aligned!'</span>);<br/> <span class="pas-kwd">while</span> <span class="pas-kwd">not</span> assigned(ocsSync) <span class="pas-kwd">do</span> <span class="pas-kwd">begin</span><br/> syncIntf := CreateOmniCriticalSection;<br/> <span class="pas-kwd">if</span> InterlockedCompareExchange(PInteger(@ocsSync)^, integer(syncIntf), <span class="pas-num">0</span>) = <span class="pas-num">0</span> <span class="pas-kwd">then</span> <span class="pas-kwd">begin</span><br/> pointer(syncIntf) := <span class="pas-kwd">nil</span>;<br/> Exit;<br/> <span class="pas-kwd">end</span>;<br/> DSiYield;<br/> <span class="pas-kwd">end</span>;<br/><span class="pas-kwd">end</span>; <span class="pas-comment">{ TOmniCS.Initialize }</span></pre><p align="justify"><em>Initialize</em> checks if <em>ocsSync</em> is allocated. If not, it will create a new instance of the <em>IOmniCriticalSection</em> interface and store it in the local variable. Then it tries to store it in the <em>ocsSync</em> field with a call to the ICE. The third parameter tells the ICE that we expect <em>ocsSync</em> to contain all zeroes. If this is so, interface will be stored in the <em>ocsSync</em> and ICE will return 0 (otherwise, it will return current value of the <em>ocsSync</em> field). If ICE succeeded, we have to clear the local variable without decrementing interface reference count and we can exit. If ICE failed, we’ll give the other thread a time slice (after all, the other thread just created the critical section, therefore we can assume it will <em>Acquire</em> it, therefore the current thread would not be able to <em>Acquire</em> it and it can sleep a little) and retry.</p><p align="justify">And that’s how you get a hassle-free critical section. Ugly, I know, but it works.</p><p align="justify">Just a word of warning – don’t try to pass a TOmniCS record around. Eventually you’ll do an assignment somewhere (newCS := oldCS) and that would screw things out. Just pass the critical section (TOmniCS.SyncObj) and all will be fine.</p><br /><div class="blogger-post-footer"><font size="-2">---<br /> <br />Published under the <a href="http://creativecommons.org/licenses/by/3.0/">Creative Commons Attribution 3.0</a> license</font></div><div class="blogger-post-footer"><font size=-2>---<br/>Published under the <a href="http://creativecommons.org/licenses/by/3.0/">Creative Commons Attribution 3.0</a> license</font><img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29331675-5336024183092666655?l=17slon.com%2Fblogs%2Fgabr%2Fblogger.html'/></div>gabrhttp://www.blogger.com/profile/06903558857617342477noreply@blogger.com5tag:blogger.com,1999:blog-29331675.post-7152827992310487742009-02-03T12:42:00.001+01:002009-02-03T12:42:59.286+01:00OmniThreadLibrary 1.02<p>If you haven’t noticed already – OmniThreadLibrary 1.02 has been released few days ago.</p> <p>The main new feature is reimplemented thread pool. Previous implementation was never meant to be a permanent solution anyway. New thread pool uses OTL as an internal implementation mechanism. Talk about recursion! :) A longer article on the new implementation will follow.</p> <p>There’s a new Enforced decorator which you can apply to the IOmniTask or IOmniTaskControl. In short – OTL always tries to execute your task. If you call taskControl.Terminate <strong>before</strong> the task has even started, OTL will set the termination signal <strong>and start executing task</strong>. This is not a good idea if the task was waiting in the thread pool queue and threadPool.CancelAll or threadPool.Terminate was executed. To bypass this auto-execute behaviour, you can call .Enforced(false).</p> <p>I’ve implement a critical section which you only have do declare and start using. No need for .Create or .Initialize or similar. An article will follow …</p> <p>There’s a new demo that shows how to use OTL for background file scanning.</p> <p>Few small and not so small bugs were fixed and 3rd party units were sync’d to fresh releases.</p> <p>As usual, you can get it via <a href="http://omnithreadlibrary.googlecode.com/svn/tags/release-1.02" target="_blank">SVN</a> or as a <a href="http://code.google.com/p/omnithreadlibrary/downloads/list" target="_blank">ZIP archive</a>.</p> <p>Other important OTL links:</p> <ul> <li><a href="http://otl.17slon.com/" target="_blank">Home page</a></li> <li><a href="http://otl.17slon.com/forum/" target="_blank">Web discussion forum</a></li> <li><a href="http://code.google.com/p/omnithreadlibrary/issues/list" target="_blank">Issue tracker</a></li> <li><a href="http://code.google.com/p/omnithreadlibrary/source/checkout" target="_blank">SVN checkout instructions</a></li> </ul> <div class="blogger-post-footer"><font size=-2>---<br/>Published under the <a href="http://creativecommons.org/licenses/by/3.0/">Creative Commons Attribution 3.0</a> license</font><img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29331675-715282799231048774?l=17slon.com%2Fblogs%2Fgabr%2Fblogger.html'/></div>gabrhttp://www.blogger.com/profile/06903558857617342477noreply@blogger.com2tag:blogger.com,1999:blog-29331675.post-47153886650220073042008-12-20T20:49:00.001+01:002008-12-20T20:49:40.282+01:00Christmas mystery<p>We were tracking down quite an interesting problem in the past few days …</p> <p>We have this service that accepts connections from multiple computers and reports some status back. And it does that via SOAP. And the SOAP implementation is our internal.</p> <p>So far so good – we are using this approach in quite some programs deployed in many places. Everything is working well.</p> <p>Everything, except one installation.</p> <p>That customer was reporting weird things. Mysterious things. Sometimes the client on one computer would for a moment display a same data as another computer. And then the display would flip back to the correct data. Mysterious.</p> <p>As expected, the guy who wrote the service pointed his finger to the guy that wrote the SOAP layer (me). And, as expected, I did the reverse. We were pretty sure that the problem was caused by the buggy code written by the other guy. We were both wrong.</p> <p>Of course we first spent about a working day logging various parts of the service and SOAP server. You know the drill – add logging, compile, connect VPN, upload the source to the customer, start two clients at two machines on the remote site via RDP, wait for the problem, download logs, analyze. Booooring. At the end, we were none smarter. We only knew that the server always returns correct data to all clients.</p> <p>Then we switched our attention to the client. Who’d say, the client always received the correct data. Except that it sometimes displayed wrong data. But that data was never received via the SOAP layer. As I said before – mystery.</p> <p>Of course, once we got to that point we knew that the problem lies inside the client software so we only have to dig in that direction. And, of course, we got the answer. And boy was it a surprising one!</p> <p>You see, our client is somewhat baroque. Layers of layers of code that were developed over the years. It’s a fate of all successful applications and this one is quite successful, at least in the vertical market we are working in. It is therefore not very surprising that the client is using temporary files in a somewhat strange arrangement. Instead of creating temporary files with unique names, it creates a temporary folder inside the %temp% and stores files there. This folder is guaranteed to be unique on the system as it uses a global counter as a part of the folder name. IOW, first client on the computer stores data in %temp%\data_1, second in %temp%\data_2. So the client on the first computer stored remote data (returned from the service) in %temp%\data_1\list.txt and the client on the second computer used %temp%\data_1\list.txt. They were both using ‘data_1’ subfolder as they were both the first (and only) client instance on that machine. A recipe for disaster? Not really as the %temp% folder is local to the computer.</p> <p>Except that it was not.</p> <p>Somebody at the customer’s site got a brilliant idea and configured temp folders for all domain clients to point to the domain controller. Don’t know who and surely don’t know why, but as the result %temp% pointed to the same folder (on the domain controller computer) on <strong>all client computers in the domain</strong>. So the first client downloaded its data to the %temp% (on the domain server), second client downloaded <strong>its</strong> data to the same location and then the first client displayed that data – data that was already modified and which belonged to the second client. A typical race condition if I ever saw one.</p> <p>The solution is, of course, to always use GetTempFileName. But this time we surely (re)learnt it in an interesting way.</p> <div class="blogger-post-footer"><font size=-2>---<br/>Published under the <a href="http://creativecommons.org/licenses/by/3.0/">Creative Commons Attribution 3.0</a> license</font><img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29331675-4715388665022007304?l=17slon.com%2Fblogs%2Fgabr%2Fblogger.html'/></div>gabrhttp://www.blogger.com/profile/06903558857617342477noreply@blogger.com3tag:blogger.com,1999:blog-29331675.post-79539591475541469382008-11-27T13:26:00.001+01:002008-11-27T13:26:52.974+01:00Advanced enumerators<p>The <a href="http://www.blaisepascal.eu/" target="_blank">Blaise Pascal magazine #4</a> is out. Inside you can find a second part of my “enumerators” series (<a title="Introduction to enumerators, first page" href="http://www.blaisepascal.eu/images/jpg/versie4/advanced_enum.jpg" target="_blank">first page</a>).</p> <div class="blogger-post-footer"><font size=-2>---<br/>Published under the <a href="http://creativecommons.org/licenses/by/3.0/">Creative Commons Attribution 3.0</a> license</font><img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29331675-7953959147554146938?l=17slon.com%2Fblogs%2Fgabr%2Fblogger.html'/></div>gabrhttp://www.blogger.com/profile/06903558857617342477noreply@blogger.com0tag:blogger.com,1999:blog-29331675.post-75742817547266293422008-11-03T22:31:00.002+01:002008-11-03T22:36:04.199+01:00Background file scanning with OmniThreadLibrary<p><a title="OmniThreadLibrary 1.01" href="http://17slon.com/blogs/gabr/2008/11/omnithreadlibrary-101.html" target="_blank">Yesterday</a>, a reader asked if I can create a demo for the background file searcher, so here it is. To get the demo source, update your SVN copy or download <a title="Background file search demo" href="http://omnithreadlibrary.googlecode.com/files/demo_23_background_file_search.zip">this file</a>.</p> <p>The app has a simple interface.</p> <p><img title="demo 23 - initial" style="border-right: 0px; border-top: 0px; display: block; float: none; margin-left: auto; border-left: 0px; margin-right: auto; border-bottom: 0px" height="285" alt="demo 23 - initial" src="http://17slon.com/blogs/gabr/files/BackgroundfilescanningwithOmniThreadLibr_12937/image.png" width="486" border="0" /></p> <p>Enter the path to be searched and the file mask in the edit field and click <em>Scan</em>.</p> <p><img title="demo 23 - scanning" style="border-right: 0px; border-top: 0px; display: block; float: none; margin-left: auto; border-left: 0px; margin-right: auto; border-bottom: 0px" height="285" alt="demo 23 - scanning" src="http://17slon.com/blogs/gabr/files/BackgroundfilescanningwithOmniThreadLibr_12937/image_3.png" width="486" border="0" /></p> <p></p> <p></p> <p>Program starts scanning and displays current folder and number of found files during the process. If you click the X button during the scan, it will be aborted and program will close.</p> <p>When background scanning completes, list of found files is displayed in the listbox.</p> <p><img title="demo 23 - final" style="border-right: 0px; border-top: 0px; display: block; float: none; margin-left: auto; border-left: 0px; margin-right: auto; border-bottom: 0px" height="285" alt="demo 23 - final" src="http://17slon.com/blogs/gabr/files/BackgroundfilescanningwithOmniThreadLibr_12937/image_4.png" width="486" border="0" /></p> <p>During the scanning, main thread is fully active. You can move the program around, resize it, minimize, maximize and so on.</p> <h2>Implementation</h2> <p>Let’s take a look at the application in design mode.</p> <p><img title="demo 23 - design" style="border-right: 0px; border-top: 0px; display: block; float: none; margin-left: auto; border-left: 0px; margin-right: auto; border-bottom: 0px" height="284" alt="demo 23 - design" src="http://17slon.com/blogs/gabr/files/BackgroundfilescanningwithOmniThreadLibr_12937/image_5.png" width="485" border="0" /></p> <p>Besides the components that are visible at runtime, there is also a TOmniEventMonitor component (named OTLMonitor) and a TTimer (tmrDisplayStatus) on the form.</p> <p>When the user clicks the <em>Scan</em> button, a background task is created.</p> <pre class="pas-source"><span class="pas-kwd">procedure</span> TfrmBackgroundFileSearchDemo.btnScanClick(Sender: TObject);<br/><span class="pas-kwd">begin</span><br/> FFileList := TStringList.Create;<br/> btnScan.Enabled := false;<br/> tmrDisplayStatus.Enabled := true;<br/> FScanTask := CreateTask(ScanFolders, <span class="pas-str">'ScanFolders'</span>)<br/> .MonitorWith(OTLMonitor)<br/> .SetParameter(<span class="pas-str">'FolderMask'</span>, inpFolderMask.Text)<br/> .Run;<br/><span class="pas-kwd">end</span>;</pre><p>ScanFolders is the method that will do the scanning (in a background thread). We’ll return to it later. Task will be monitored with the OTLMonitor component so that we will receive task messages. OTLMonitor will also tell us when the task will terminate. Input folder and mask is send to the task as a parameter <em>FolderMask</em> and task is started.</p><p>The FFileList field is a TStringList that will contain a list of all found files.</p><p>Let’s ignore the scanner details for the moment and skip to the end of the scanning process. When task has completed its job, OTLMonitor.OnTaskTerminated is called.</p><pre class="pas-source"><span class="pas-kwd">procedure</span> TfrmBackgroundFileSearchDemo.OTLMonitorTaskTerminated(<br /> <span class="pas-kwd">const</span> task: IOmniTaskControl);<br/><span class="pas-kwd">begin</span><br/> tmrDisplayStatus.Enabled := false;<br/> outScanning.Text := <span class="pas-str">''</span>;<br/> outFiles.Text := IntToStr(FFileList.Count);<br/> lbFiles.Clear;<br/> lbFiles.Items.AddStrings(FFileList);<br/> FreeAndNil(FFileList);<br/> FScanTask := <span class="pas-kwd">nil</span>;<br/> btnScan.Enabled := true;<br/><span class="pas-kwd">end</span>;</pre><p>At that point, number of found files is copied to the <em>outFiles</em> edit field and complete list is assigned to the listbox. Task reference <em>FScanTask</em> is then cleared, which causes the task object to be destroyed and <em>Scan</em> button is reenabled (it was disabled during the scanning process).</p><p>But what if the user closes the program by clicking the X button while the background scanner is active? Simple, we just catch the OnFormCloseQuery event and tell the task to terminate.</p><pre class="pas-source"><span class="pas-kwd">procedure</span> TfrmBackgroundFileSearchDemo.FormCloseQuery(Sender: TObject;<br/> <span class="pas-kwd">var</span> CanClose: boolean);<br/><span class="pas-kwd">begin</span><br/> <span class="pas-kwd">if</span> assigned(FScanTask) <span class="pas-kwd">then</span> <span class="pas-kwd">begin</span><br/> FScanTask.Terminate;<br/> FScanTask := <span class="pas-kwd">nil</span>;<br/> CanClose := true;<br/> <span class="pas-kwd">end</span>;<br/><span class="pas-kwd">end</span>;</pre><p>The <em>Terminate</em> method will do two things – tell the task to terminate and then wait for its termination. After that, we simply have to clear the task reference and allow the program to terminate.</p><h2>Scanner</h2><p>Let’s move to the scanning part now. The <em>ScanFolders</em> method (which is the main task method, the one we passed to the <em>CreateTask</em>) splits the value of the <em>FolderMask</em> parameter into folder and mask parts and passes them to the main worker <em>ScanFolder</em>.</p><pre class="pas-source"><span class="pas-kwd">procedure</span> ScanFolders(<span class="pas-kwd">const</span> task: IOmniTask);<br/><span class="pas-kwd">var</span><br/> folder: <span class="pas-kwd">string</span>;<br/> mask : <span class="pas-kwd">string</span>;<br/><span class="pas-kwd">begin</span><br/> mask := task.ParamByName[<span class="pas-str">'FolderMask'</span>];<br/> folder := ExtractFilePath(mask);<br/> Delete(mask, <span class="pas-num">1</span>, Length(folder));<br/> <span class="pas-kwd">if</span> folder &lt;&gt; <span class="pas-str">''</span> <span class="pas-kwd">then</span><br/> folder := IncludeTrailingPathDelimiter(folder);<br/> ScanFolder(task, folder, mask);<br/><span class="pas-kwd">end</span>;</pre><p><em>ScanFolder</em> first finds all subfolders of the selected folder and calls itself recursively for each subfolder. That means that we’ll first process deepest folders and then proceed to the top of the folder tree.</p><p>Then it sends a message <em>MSG_SCAN_FOLDER</em> to the main thread. As a parameter of this message it sends the name of the folder being processed. There’s nothing magical about this message – it is just an arbitrary numeric constant from range 0 .. 65534 (yes, number 65535 is reserved for internal use).</p><pre class="pas-source"><span class="pas-kwd">const</span><br/> MSG_SCAN_FOLDER = <span class="pas-num">1</span>;<br/> MSG_FOLDER_FILES = <span class="pas-num">2</span>;</pre><pre class="pas-source"><span class="pas-kwd">procedure</span> ScanFolder(<span class="pas-kwd">const</span> task: IOmniTask; <span class="pas-kwd">const</span> folder, mask: <span class="pas-kwd">string</span>);<br/><span class="pas-kwd">var</span><br/> err : integer;<br/> folderFiles: TStringList;<br/> S : TSearchRec;<br/><span class="pas-kwd">begin</span><br/> err := FindFirst(folder + <span class="pas-str">'*.*'</span>, faDirectory, S);<br/> <span class="pas-kwd">if</span> err = <span class="pas-num">0</span> <span class="pas-kwd">then</span> <span class="pas-kwd">try</span><br/> <span class="pas-kwd">repeat</span><br/> <span class="pas-kwd">if</span> ((S.Attr <span class="pas-kwd">and</span> faDirectory) &lt;&gt; <span class="pas-num">0</span>) <span class="pas-kwd">and</span> (S.<span class="pas-kwd">Name</span> &lt;&gt; <span class="pas-str">'.'</span>) <span class="pas-kwd">and</span> (S.<span class="pas-kwd">Name</span> &lt;&gt; <span class="pas-str">'..'</span>) <span class="pas-kwd">then</span><br/> ScanFolder(task, folder + S.<span class="pas-kwd">Name</span> + <span class="pas-str">'\'</span>, mask);<br/> err := FindNext(S);<br/> <span class="pas-kwd">until</span> task.Terminated <span class="pas-kwd">or</span> (err &lt;&gt; <span class="pas-num">0</span>);<br/> <span class="pas-kwd">finally</span> FindClose(S); <span class="pas-kwd">end</span>;<br/> task.Comm.Send(MSG_SCAN_FOLDER, folder);<br/> folderFiles := TStringList.Create;<br/> <span class="pas-kwd">try</span><br/> err := FindFirst(folder + mask, <span class="pas-num">0</span>, S);<br/> <span class="pas-kwd">if</span> err = <span class="pas-num">0</span> <span class="pas-kwd">then</span> <span class="pas-kwd">try</span><br/> <span class="pas-kwd">repeat</span><br/> folderFiles.Add(folder + S.<span class="pas-kwd">Name</span>);<br/> err := FindNext(S);<br/> <span class="pas-kwd">until</span> task.Terminated <span class="pas-kwd">or</span> (err &lt;&gt; <span class="pas-num">0</span>);<br/> <span class="pas-kwd">finally</span> FindClose(S); <span class="pas-kwd">end</span>;<br/> <span class="pas-kwd">finally</span> task.Comm.Send(MSG_FOLDER_FILES, folderFiles); <span class="pas-kwd">end</span>;<br/><span class="pas-kwd">end</span>;</pre><p><em>ScanFolder </em>then runs the FindFirst/FindNext/FindClose loop for the second time to search for files in the folder. [BTW, if you want to first scan folders nearer to the root, just change the two loops and scan for files first and for folders second.] Each file is added to an internal TStringList object which was created just a moment before. When folder scan is completed, this object is sent to the main thread as parameter of the MSG_FOLDER_FILES message.</p><p>This approach – sending data for one folder – is a compromise between returning the complete set (full scanned tree), which would not provide a good feedback, and returning each file as we detect it, which would unnecessarily put a high load on the system.</p><p>Both Find loops test the status of the <em>task.Terminated</em> function and exit immediately if it is <em>True</em>. That allows us to terminate the background task when the user closes the application and <em>OnFormCloseQuery</em> is called.</p><h2>Receiving messages</h2><p>That’s all that has to be done in the background task but we still have to process the messages in the main thread. For that, we write the OTLMonitor’s OnTaskMessage event.</p><pre class="pas-source"><span class="pas-kwd">procedure</span> TfrmBackgroundFileSearchDemo.OTLMonitorTaskMessage(<br/> <span class="pas-kwd">const</span> task: IOmniTaskControl);<br/><span class="pas-kwd">var</span><br/> folderFiles: TStringList;<br/> msg : TOmniMessage;<br/><span class="pas-kwd">begin</span><br/> task.Comm.Receive(msg);<br/> <span class="pas-kwd">if</span> msg.MsgID = MSG_SCAN_FOLDER <span class="pas-kwd">then</span><br/> FWaitingMessage := msg.MsgData<br/> <span class="pas-kwd">else</span> <span class="pas-kwd">if</span> msg.MsgID = MSG_FOLDER_FILES <span class="pas-kwd">then</span> <span class="pas-kwd">begin</span><br/> folderFiles := TStringList(msg.MsgData.AsObject);<br/> FFileList.AddStrings(folderFiles);<br/> FreeAndNil(folderFiles);<br/> FWaitingCount := IntToStr(FFileList.Count);<br/> <span class="pas-kwd">end</span>;<br/><span class="pas-kwd">end</span>;</pre><p>If the message is MSG_SCAN_FOLDER we just copy folder name to a local field. If the message is MSG_FOLDER_FILES, we copy file names from the parameter (which is a TStringList) to the global FFileList list and destroy the parameter. We also update a local field holding the number of currently found files.</p><p>So why don’t we directly update two edit fields on the form (one with current folder and another with number of found files)? Well, the background task can send many messages in one second (when processing folders will small number of files) and there’s no point in displaying them all – the user will never see what was displayed anyway. And it would slow down the GUI because Windows controls would be updated hundreds of times per second, which is never a good idea.</p><p>Instead of that we just store the strings to be displayed in two form fields and display them from a timer which is triggered three times per second. That will not show all scanned folders and all intermediate file count results, but will still provide the user with the sufficient feedback.</p><pre class="pas-source"><span class="pas-kwd">procedure</span> TfrmBackgroundFileSearchDemo.tmrDisplayStatusTimer(Sender: TObject);<br/><span class="pas-kwd">begin</span><br/> <span class="pas-kwd">if</span> FWaitingMessage &lt;&gt; <span class="pas-str">''</span> <span class="pas-kwd">then</span> <span class="pas-kwd">begin</span><br/> outScanning.Text := FWaitingMessage;<br/> FWaitingMessage := <span class="pas-str">''</span>;<br/> <span class="pas-kwd">end</span>;<br/> <span class="pas-kwd">if</span> FWaitingCount &lt;&gt; <span class="pas-str">''</span> <span class="pas-kwd">then</span> <span class="pas-kwd">begin</span><br/> outFiles.Text := FWaitingCount;<br/> FWaitingCount := <span class="pas-str">''</span>;<br/> <span class="pas-kwd">end</span>;<br/><span class="pas-kwd">end</span>;</pre><p>And that’s all. Fully functional background file scanner that could easily be repackaged into a TComponent. And it’s all yours.</p><div class="blogger-post-footer"><font size=-2>---<br/>Published under the <a href="http://creativecommons.org/licenses/by/3.0/">Creative Commons Attribution 3.0</a> license</font><img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29331675-7574281754726629342?l=17slon.com%2Fblogs%2Fgabr%2Fblogger.html'/></div>gabrhttp://www.blogger.com/profile/06903558857617342477noreply@blogger.com0tag:blogger.com,1999:blog-29331675.post-1844886326172157372008-11-02T18:31:00.001+01:002008-11-02T18:31:26.954+01:00OmniThreadLibrary 1.01<p>OmniThreadLibrary 1.01 has been released yesterday. It is available via SVN (<a href="http://omnithreadlibrary.googlecode.com/svn/tags/release-1.01">http://omnithreadlibrary.googlecode.com/svn/tags/release-1.01</a>) or as a <a title="OmniThreadLibrary-1.01.zip" href="http://omnithreadlibrary.googlecode.com/files/OmniThreadLibrary-1.01.zip" target="_blank">ZIP archive</a>.</p> <p>Changes since version 1.0a:</p> <ul> <li><strong>*** Breaking interface change ***</strong></li> <ul> <li>IOmniTask.Terminated renamed to IOmniTask.Stopped.</li> <li>New IOmniTask.Terminated that check whether the task <strong>has been requested to terminate</strong>. [demo 22]</li> </ul> <li>[GJ] Redesigned stack cotainer with better lock contention.</li> <li>[GJ] Totally redesigned queue container, which is no longer based on stack and allows multiple readers.</li> <li>Full D2009 support; D2009 packages, project files and Tests project group.</li> <li>Invoke-by-name and invoke-by-address messaging implemented [<a href="http://17slon.com/blogs/gabr/2008/10/erlangenizing-omnithreadlibrary.html">http://17slon.com/blogs/gabr/2008/10/erlangenizing-omnithreadlibrary.html</a>, <a href="http://17slon.com/blogs/gabr/2008/10/omnithreadlibrary-using-rtti-to-call.html">http://17slon.com/blogs/gabr/2008/10/omnithreadlibrary-using-rtti-to-call.html</a>, demos 18 and 19]</li> <li>Implemented CreateTask(reference to function (task: ITaskControl)). [D2009 only, demo 21]</li> <li>Implemented blocking wait (ReceiveWait). [demo 19]</li> <li>Added enumerator to the IOmniTaskGroup interface.</li> <li>Implemented IOmniTaskGroup.RegisterAllWithTask and .UnregisterAllFromTask.</li> <li>Added automatic comm unregistration for IOmniTaskGroup.RegisterAllCommWith.</li> <li>Implemented IOmniTaskGroup.SendToAll.</li> <li>IOmniTaskControl.Terminate now kills the task after the timeout.</li> <li>New/updated tests/demos:</li> <ul> <li>10_Containers</li> <ul> <li>2 -&gt; 2, 1 -&gt; 4 and 4 -&gt; 4 tests for stacks and queues.</li> <li>[1, 2, 4] -&gt; [1, 2, 4] full tests.</li> <li>Writes CSV file with cumulative test results.</li> </ul> <li>17_MsgWait: demo for the .MsgWait decorator.</li> <li>18_StringMsgDispatch: Invoke demo.</li> <li>19_StringMsgBenchmark: Invoke benchmark, ReceiveWait demo.</li> <li>20_QuickSort: Parallel quicksort demo.</li> <li>21_Anonymous_methods: Anonymous methods demo (D2009 only).</li> <li>22_TerminationTest: Task termination demo.</li> </ul> <li>Message ID $FFFF is now reserved for internal purposes.</li> <li>Better default queue length calculation that takes into account OtlContainers&#160; overhead and FastMM4 granulation.</li> <li>Bug fixed: TOmniValue.Null was not really initialized to Null.</li> <li>Bug fixed: Setting timer interval resets timer countdown.</li> <li>Bug fixed: TOmniTaskControl.Schedule always scheduled task to the global thread pool.</li> <li>Current versions of 3rd part units included.</li> </ul> <p>Known problems:</p> <ul> <li>Thread pool is not stable under high&#160; load.</li> </ul> <div class="blogger-post-footer"><font size=-2>---<br/>Published under the <a href="http://creativecommons.org/licenses/by/3.0/">Creative Commons Attribution 3.0</a> license</font><img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29331675-184488632617215737?l=17slon.com%2Fblogs%2Fgabr%2Fblogger.html'/></div>gabrhttp://www.blogger.com/profile/06903558857617342477noreply@blogger.com2tag:blogger.com,1999:blog-29331675.post-30786290528603339732008-10-30T20:45:00.001+01:002008-10-30T20:45:25.655+01:00When one and one makes one<p><a href="http://17slon.com/blogs/gabr/2008/10/bulk-update.html" target="_blank">Some time ago</a> I promised to write an article on TGpJoinedStream class. A time has come to fulfill the promise.</p> <p>TGpJoinedStream is a stream wrapper class – a class that wraps one or more existing streams and provides a TStream interface on the outside. My <a href="http://gp.17slon.com/gp/gpstreams.htm" target="_blank">GpStreams</a> unit contains several examples of such wrappers: <a title="Stream me a river" href="http://17slon.com/blogs/gabr/2007/05/stream-me-river.html" target="_blank">TGpStreamWindow</a> provides a stream access to a part of another stream, TGpScatteredStream provides contiguous access to a scattered data (hm, I never wrote an article about that one, didn’t I?), and TGpBufferedStream adds data caching to any stream.</p> <p>TGpJoinedStream’s role is simple. You pass it a bunch of streams and it provides stream access to concatenated data. IOW, if first stream contains numbers &lt;1, 2, 3&gt; and second stream contains &lt;4, 5&gt;, joined stream would function as if it contains &lt;1, 2, 3, 4, 5&gt;.</p> <p>Implementation is pretty straightforward and won’t describe it here. I'll rather show how TGpJoinedStream is used in practice and why I wrote it at all.</p> <p>We have a MPEG parser class that takes a TStream containing MPEG video data and extracts some information from it. It works fine when you need to extract information from a video file, but not so good if you want to use it inside a DirectX filter. From a viewpoint of a DirectX source or sink filter, video is not a stream but a bunch of buffers, which we must process one by one. The trouble is that buffers can almost never be fully processed because MPEG sequences don’t contain data length. </p> <p>In MPEG, each sequence starts with byte signature 00 00 01, followed by a byte that tells you what kind of data you’re processing, followed by some data. Unless you want to parse each and every sequence data and you know exactly how all well-formed and malformed sequences in the existence are built, you only know that you reached the end of one sequence when you reach another 00 00 01 signature. That’s why the last sequence in the buffer can never be processed (unless this is the last buffer of them all) until we find 00 00 01 in the next buffer. Uff. I hope somebody understands this at all.</p> <p>In short, we are processing data buffers. A variable-length part of each buffer will stay unprocessed and will have to be prepended to the next buffer before it is passed through the MPEG parser. And so on, until the end of data.</p> <p>And that’s where TGpJoinedStream comes to help. The unprocessed part is stored in a memory stream FLeftovers, which is empty at the beginning. Buffer parser concatenates FLeftovers with the current data and passes the result through the stream parser. When that one returns, .Position in the combined stream indicates the first unprocessed byte. The unprocessed data is then copied into the FLeftovers stream so it can be used next time the buffer parser is called.</p> <p>That’s how it looks in code (slightly simplified real code; I just removed some details that are not interesting for our story).</p> <pre class="pas-source"><span class="pas-kwd">procedure</span> TGpMPEGSequentialParser.ParseNext(buffer: pointer; <br /> bufferSize: integer);<br /><span class="pas-kwd">var</span><br /> combinedStream: TGpJoinedStream;<br /> mpegParser : TGpMPEGParser;<br /> newLeftovers : TMemoryStream;<br /> strBuffer : TGpFixedMemoryStream;<br /><span class="pas-kwd">begin</span><br /> strBuffer := TGpFixedMemoryStream.Create(buffer^, bufferSize);<br /> <span class="pas-kwd">try</span><br /> newLeftovers := TMemoryStream.Create;<br /> <span class="pas-kwd">try</span><br /> combinedStream := TGpJoinedStream.Create([FLeftovers, strBuffer]);<br /> <span class="pas-kwd">try</span><br /> mpegParser := TGpMPEGParser.Create;<br /> <span class="pas-kwd">try</span><br /> mpegParser.MPEGStream := combinedStream;<br /><br /> mpegParser.Parse(HandleMpegSequence);<br /><br /> <span class="pas-kwd">if</span> combinedStream.Position &lt; combinedStream.Size <span class="pas-kwd">then</span><br /> newLeftovers.CopyFrom(combinedStream, <br /> combinedStream.Size - combinedStream.Position);<br /> <span class="pas-kwd">finally</span> FreeAndNil(mpegParser); <span class="pas-kwd">end</span>;<br /> <span class="pas-kwd">finally</span> FreeAndNil(combinedStream); <span class="pas-kwd">end</span>;<br /> <span class="pas-kwd">finally</span><br /> FreeAndNil(FLeftovers);<br /> FLeftovers := newLeftovers;<br /> <span class="pas-kwd">end</span>;<br /> <span class="pas-kwd">finally</span> FreeAndNil(strBuffer); <span class="pas-kwd">end</span>;<br /><span class="pas-kwd">end</span>;</pre><br /><br /><p>Hope you like it!</p> <div class="blogger-post-footer"><font size=-2>---<br/>Published under the <a href="http://creativecommons.org/licenses/by/3.0/">Creative Commons Attribution 3.0</a> license</font><img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29331675-3078629052860333973?l=17slon.com%2Fblogs%2Fgabr%2Fblogger.html'/></div>gabrhttp://www.blogger.com/profile/06903558857617342477noreply@blogger.com0tag:blogger.com,1999:blog-29331675.post-35831987692643339622008-10-21T14:31:00.001+02:002008-10-21T14:31:39.782+02:00Internet, as predicted in 1946<p align="justify">“You know the logics setup. You got a logic in your house. It looks like a vision receiver used to, only it's got keys instead of dials and you punch the keys for what you wanna get. It's hooked in to the tank, which has the Carson Circuit all fixed up with relays. Say you punch &quot;Station SNAFU&quot; on your logic. Relays in the tank take over an' whatever vision-program SNAFU is telecastin' comes on your logic's screen. Or you punch &quot;Sally Hancock's Phone&quot; an' the screen blinks an' sputters an' you're hooked up with the logic in her house an' if somebody answers you got a vision-phone connection. But besides that, if you punch for the weather forecast or who won today's race at Hialeah or who was mistress of the White House durin' Garfield's administration or what is PDQ and R sellin' for today, that comes on the screen too. The relays in the tank do it. The tank is a big buildin' full of all the facts in creation an' all the recorded telecasts that ever was made—an' it's hooked in with all the other tanks all over the country—an' everything you wanna know or see or hear, you punch for it an' you get it. Very convenient. Also it does math for you, an' keeps books, an' acts as consultin' chemist, physicist, astronomer, an' tea-leaf reader, with a &quot;Advice to the Lovelorn&quot; thrown in. The only thing it won't do is tell you exactly what your wife meant when she said, &quot;Oh, you think so, do you?&quot; in that peculiar kinda voice. Logics don't work good on women. Only on things that make sense.”</p> <p align="right">- Murray Leinster, <a href="http://www.webscription.net/10.1125/Baen/0743499107/0743499107.htm">A Logic Named Joe</a></p> <p align="left">For Sci-Fi fans: <a href="http://www.baen.com/library/">Baen Free Library</a>.</p> <div class="blogger-post-footer"><font size=-2>---<br/>Published under the <a href="http://creativecommons.org/licenses/by/3.0/">Creative Commons Attribution 3.0</a> license</font><img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29331675-3583198769264333962?l=17slon.com%2Fblogs%2Fgabr%2Fblogger.html'/></div>gabrhttp://www.blogger.com/profile/06903558857617342477noreply@blogger.com5tag:blogger.com,1999:blog-29331675.post-37335801312972920942008-10-14T20:51:00.001+02:002008-10-14T20:51:59.226+02:00TDM Rerun #11: Shared Pools<blockquote> <p><em>Sometimes we run into problems because of a Windows feature, and most of the time this is a very good feature, that a file mapping (an essential part of my shared memory implementation) cannot exist on its own. A file mapping, like mutexes, events, and other Windows primitives, must have an owner. If all processes associated with a given file mapping die, the file mapping will be destroyed. Because in my shared memory implementation this file mapping is backed with a page file, its contents won’t be preserved on an accessible part of the disk.</em></p> <p><em>- Shared Pools, The Delphi Magazine 95, July 2003</em></p> </blockquote> <p><a href="http://17slon.com/blogs/gabr/files/TDMRerun11SharedPools_1201F/image.png" target="_blank"><img title="Shared pool architecture" style="border-right: 0px; border-top: 0px; display: inline; margin-left: 0px; border-left: 0px; margin-right: 0px; border-bottom: 0px" height="119" alt="Shared pool architecture" src="http://17slon.com/blogs/gabr/files/TDMRerun11SharedPools_1201F/image_thumb.png" width="244" align="right" border="0" /></a>The shared pool was one of my more baroque creations. In fact, it was so complicated that it was never used in a deployed application. Basically, the article described an architecture to implement a pool of shared memory objects, which multiple Writers could use to send data to one Reader (typically sitting in another process). The system also handled cleanup when a Reader task died and other management details.</p> <p>You really should not be playing with this code. There are better solutions.</p> <p>Links: <a title="TDM 95: Shared Pools [article]" href="http://www.17slon.com/blogs/gabr/TDM/tdm95-gp.pdf">article</a> (PDF, 193 KB), <a title="TDM 95: Shared Pools [source code]" href="http://www.17slon.com/blogs/gabr/TDM/tdm95-gp.zip">source code</a> (ZIP, 1.9 MB)</p> <div class="blogger-post-footer"><font size=-2>---<br/>Published under the <a href="http://creativecommons.org/licenses/by/3.0/">Creative Commons Attribution 3.0</a> license</font><img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29331675-3733580131297292094?l=17slon.com%2Fblogs%2Fgabr%2Fblogger.html'/></div>gabrhttp://www.blogger.com/profile/06903558857617342477noreply@blogger.com0tag:blogger.com,1999:blog-29331675.post-43196678365988583962008-10-07T21:59:00.004+02:002008-10-14T20:53:03.760+02:00OmniThreadLibrary: Using RTTI to call task methods<p align="justify">Yesterday I wrote <a title="Erlangenizing the OmniThreadLibrary" href="http://17slon.com/blogs/gabr/2008/10/erlangenizing-omnithreadlibrary.html" target="_blank">an article</a> on by-name and by-address invocations in the new OmniThreadLibrary (development version) but I didn’t finish the description of the OTL internal magic that makes those new calls work. Let’s fix that …</p> <p align="justify">I ended the story right at the point where <em>TOmniTaskExecutor.Asy_DispatchMessages</em> calls <em>DispatchOmniMessage.</em></p> <p align="justify">Most of the magic happens inside this method, so it’s only fair to display it in its full glory.</p> <pre class="pas-source"><span class="pas-kwd">procedure</span> TOmniTaskExecutor.DispatchOmniMessage(msg: TOmniMessage);<br/><span class="pas-kwd">var</span><br/> methodAddr : pointer;<br/> methodInfoObj : TObject;<br/> methodInfo : TOmniInvokeInfo <span class="pas-kwd">absolute</span> methodInfoObj;<br/> methodName : <span class="pas-kwd">string</span>;<br/> methodSignature: TOmniInvokeType;<br/> msgData : TOmniValue;<br/> obj : TObject;<br/><span class="pas-kwd">begin</span><br/> <span class="pas-kwd">if</span> msg.MsgID = COtlReservedMsgID <span class="pas-kwd">then</span> <span class="pas-kwd">begin</span><br/> Assert(assigned(WorkerIntf));<br/> GetMethodNameFromInternalMessage(msg, methodName, msgData);<br/> <span class="pas-kwd">if</span> methodName = <span class="pas-str">''</span> <span class="pas-kwd">then</span><br/> <span class="pas-kwd">raise</span> Exception.Create(<span class="pas-str">'TOmniTaskExecutor.DispatchOmniMessage: Method name not set'</span>);<br/> <span class="pas-kwd">if</span> <span class="pas-kwd">not</span> assigned(oteMethodHash) <span class="pas-kwd">then</span><br/> oteMethodHash := TGpStringObjectHash.Create(<span class="pas-num">17</span>, true); <span class="pas-comment">//usually there won't be many methods</span><br/> <span class="pas-kwd">if</span> <span class="pas-kwd">not</span> oteMethodHash.Find(methodName, methodInfoObj) <span class="pas-kwd">then</span> <span class="pas-kwd">begin</span><br/> GetMethodAddrAndSignature(methodName, methodAddr, methodSignature);<br/> methodInfo := TOmniInvokeInfo.Create(methodAddr, methodSignature);<br/> oteMethodHash.Add(methodName, methodInfo);<br/> <span class="pas-kwd">end</span>;<br/> <span class="pas-kwd">case</span> methodInfo.Signature <span class="pas-kwd">of</span><br/> itSelf:<br/> TOmniInvokeSignature_Self(methodInfo.Address)(WorkerIntf.Implementor);<br/> itSelfAndOmniValue:<br/> TOmniInvokeSignature_Self_OmniValue(methodInfo.Address)(WorkerIntf.Implementor, msgData);<br/> itSelfAndObject:<br/> <span class="pas-kwd">begin</span><br/> obj := msgData.AsObject;<br/> TOmniInvokeSignature_Self_Object(methodInfo.Address)(WorkerIntf.Implementor, obj);<br/> <span class="pas-kwd">end</span><br/> <span class="pas-kwd">else</span><br/> RaiseInvalidSignature(methodName);<br/> <span class="pas-kwd">end</span>; <span class="pas-comment">//case methodSignature</span><br/> <span class="pas-kwd">end</span><br/> <span class="pas-kwd">else</span><br/> WorkerIntf.DispatchMessage(msg);<br/><span class="pas-kwd">end</span>; <span class="pas-comment">{ TOmniTaskExecutor.DispatchMessage }</span></pre><p align="justify">Now let’s dissect it. The code first checks if&#160; the message ID contains the magic value. If not, the message is dispatched as before by using Delphi’s Dispatch. </p><pre class="pas-source"> <span class="pas-kwd">if</span> msg.MsgID = COtlReservedMsgID <span class="pas-kwd">then</span> <span class="pas-kwd">begin</span><br/> <span class="pas-comment">//...<br /></span> <span class="pas-kwd">end</span><br/> <span class="pas-kwd">else</span><br/> WorkerIntf.DispatchMessage(msg);<br/><span class="pas-kwd">end</span>; <span class="pas-comment">{ TOmniTaskExecutor.DispatchMessage }</span></pre><p align="justify">Not interesting. Let’s take a look at the other path – when msg.MsgID is an internal message ID.</p><p align="justify">In this case, <em>DispatchMessages</em> extracts the method name from the message. If the caller passed a method name to the <em>Invoke</em>, then the job is simple – it must only be extracted from the <em>TOmniInternalAddressMsg</em> object. If, on the other hand, method pointer was used, the code calls <em>MethodName</em> to convert it to a (who would guess?) method name.</p><pre class="pas-source"><span class="pas-kwd">procedure</span> TOmniTaskExecutor.GetMethodNameFromInternalMessage(<span class="pas-kwd">const</span> msg: TOmniMessage; <span class="pas-kwd">var</span><br/> msgName: <span class="pas-kwd">string</span>; <span class="pas-kwd">var</span> msgData: TOmniValue);<br/><span class="pas-kwd">var</span><br/> internalType: TOmniInternalMessageType;<br/> method : pointer;<br/><span class="pas-kwd">begin</span><br/> internalType := TOmniInternalMessage.InternalType(msg);<br/> <span class="pas-kwd">case</span> internalType <span class="pas-kwd">of</span><br/> imtStringMsg:<br/> TOmniInternalStringMsg.UnpackMessage(msg, msgName, msgData);<br/> imtAddressMsg:<br/> <span class="pas-kwd">begin</span><br/> TOmniInternalAddressMsg.UnpackMessage(msg, method, msgData);<br/> msgName := WorkerIntf.Implementor.MethodName(method);<br/> <span class="pas-kwd">if</span> msgName = <span class="pas-str">''</span> <span class="pas-kwd">then</span><br/> <span class="pas-kwd">raise</span> Exception.CreateFmt(<span class="pas-str">'TOmniTaskExecutor.GetMethodNameFromInternalMessage: '</span> +<br/> <span class="pas-str">'Cannot find method name for method %p'</span>, [method]);<br/> <span class="pas-kwd">end</span><br/> <span class="pas-kwd">else</span><br/> <span class="pas-kwd">raise</span> Exception.CreateFmt(<span class="pas-str">'TOmniTaskExecutor.GetMethodNameFromInternalMessage: '</span> +<br/> <span class="pas-str">'Internal message type %s is not supported'</span>,<br/> [GetEnumName(TypeInfo(TOmniInternalMessageType), Ord(internalType))]);<br/> <span class="pas-kwd">end</span>; <span class="pas-comment">//case internalType</span><br/><span class="pas-kwd">end</span>; <span class="pas-comment">{ TOmniTaskExecutor.GetMethodNameFromInternalMessage }</span></pre><p align="justify">Next, the code looks into an internal hash table and tries to fetch information on that method name.&#160; If there’s no such information (when some method is called for the first time), <em>GetMethodAddrAndSignature</em> is called to convert the name to the method’s address and signature. Then this information is stored in the hash table so it will be immediately ready next time. </p><pre class="pas-source"><span class="pas-kwd">if</span> <span class="pas-kwd">not</span> oteMethodHash.Find(methodName, methodInfoObj) <span class="pas-kwd">then</span> <span class="pas-kwd">begin</span><br/> GetMethodAddrAndSignature(methodName, methodAddr, methodSignature);<br/> methodInfo := TOmniInvokeInfo.Create(methodAddr, methodSignature);<br/> oteMethodHash.Add(methodName, methodInfo);<br/><span class="pas-kwd">end</span>;</pre><p align="justify">Method signature is then used to select the type of call that will be performed. Only three signatures are supported: (Self: TObject), (Self: TObject; const msgData: TOmniValue) and (Self: TObject; var obj: TObject). Demo 18 demonstrates the use of all three. What I find especially convenient is that the third option allows you to put any TObject descendant in the parameter list. In other words, you can do:</p><pre class="pas-source"><span class="pas-kwd">type</span><br/> TAsyncHello = <span class="pas-kwd">class</span>(TOmniWorker)<br/> <span class="pas-kwd">published</span><br/> <span class="pas-kwd">procedure</span> TheAnswer(<span class="pas-kwd">var</span> sl: TStringList);<br/> <span class="pas-kwd">end</span>;<br/><br/><span class="pas-kwd">procedure</span> TfrmTestStringMsgDispatch.btnSendObjectClick(Sender: TObject);<br/><span class="pas-kwd">var</span><br/> sl: TStringList;<br/><span class="pas-kwd">begin</span><br/> sl := TStringList.Create;<br/> sl.Text := <span class="pas-str">'42'</span>;<br/> FHelloTask.Invoke(@TAsyncHello.TheAnswer, sl);<br/><span class="pas-kwd">end</span>;<br/><br/><span class="pas-kwd">procedure</span> TAsyncHello.TheAnswer(<span class="pas-kwd">var</span> sl: TStringList);<br/><span class="pas-kwd">begin</span><br/> FreeAndNil(sl);<br/><span class="pas-kwd">end</span>;</pre><p align="justify">Convenient, huh?</p><h2>RTTI</h2><p>The above description of the <em>DispatchOmniMessage</em> looks very much like the famous Sydney Harris cartoon. In step one a method name is retrieved from the message. In step three the method is invoked using the right signature and method pointer. In step two, well …</p><p><a href="http://www.cartoonbank.com/product_details.asp?sid=40967"><img title="Sydney Harris" style="border-right: 0px; border-top: 0px; display: block; float: none; margin-left: auto; border-left: 0px; margin-right: auto; border-bottom: 0px" height="368" alt="Sydney Harris" src="http://17slon.com/blogs/gabr/files/UsingRTTItoappropriatemethod_8E6D/image.png" width="304" border="0" /></a> </p><p>Let’s be more explicit, then. We have a method name and we want to find the address for that method and some information about its parameters. Sounds like a job for RTTI, yes? Well, basic RTTI (the one that you enable with {$M+} or {$TYPEINFO ON} or simply by declaring a <em>published</em> property) only handles published properties, not methods. For methods, we need to use <em>extended RTTI</em>, which is enabled with {$METHODINFO ON}. I won’t describe it here as <a href="http://davidglassborow.blogspot.com">David Glassborow</a> and <a href="http://hallvards.blogspot.com">Hallvard Vassbotn</a> already did the job better than I could. If you’re interested in extended RTTI, I suggest that your research starts at Hallvard’s <a href="http://hallvards.blogspot.com/2006/05/david-glassborow-on-extended-rtti.html">David Glassborow on extended RTTI</a> article.</p><p>Great thanks for publishing all that info, guys, it helped a lot!</p><p><em>GetMethodAddrAndSignature </em>first uses Delphi’s <em>ObjAuto</em> unit to extract method information header. Then it uses <em>TObject’s MethodAddress</em> to convert method name into address. Of course, it doesn’t use <em>TObject </em>directly, as it has no idea where the methods belonging to the task object are stored – it must call <em>MethodAddress</em> on your task object directly.</p><pre class="pas-source"> methodInfoHeader := ObjAuto.GetMethodInfo(WorkerIntf.Implementor, methodName);<br/> methodAddress := WorkerIntf.Implementor.MethodAddress(methodName);</pre><p>After that, some sanity checks are run, which I won’t reprint here.</p><p>Then the method signature is checked. First we want to ensure that we’re dealing with a procedure, not a function.</p><pre class="pas-source"> <span class="pas-kwd">if</span> assigned(methodInfoHeader.ReturnInfo.ReturnType) <span class="pas-kwd">then</span><br/> <span class="pas-kwd">raise</span> Exception.CreateFmt(<span class="pas-str">'TOmniTaskExecutor.DispatchMessage: '</span> +<br/> <span class="pas-str">'Method %s.%s must not return result'</span>,<br/> [WorkerIntf.Implementor.ClassName, methodName]);</pre><p>At the end, a messy fragment of code walks over the parameter description list for this method and checks that first parameter is a class reference (because this method is a part of a class, it has a hidden <em>Self</em> parameter at the beginning) and that the second parameter, if present at all, is either <em>const data: TOmniValue</em> or <em>var obj: TObject</em> (or descendant of TObject). Detected signature is then returned in the <em>methodSignature</em> parameter.</p><pre class="pas-source"> <span class="pas-comment">// only limited subset of method signatures is allowed:</span><br/> <span class="pas-comment">// (Self), (Self, const TOmniValue), (Self, var TObject)</span><br/> headerEnd := cardinal(methodInfoHeader) + methodInfoHeader^.Len;<br/> params := PParamInfo(cardinal(methodInfoHeader) + SizeOf(methodInfoHeader^)<br/> - CShortLen + SizeOf(TReturnInfo) + Length(methodInfoHeader^.<span class="pas-kwd">Name</span>));<br/> paramNum := <span class="pas-num">0</span>;<br/> methodSignature := itUnknown;<br/> <span class="pas-comment">// Loop over the parameters</span><br/> <span class="pas-kwd">while</span> cardinal(params) &lt; headerEnd <span class="pas-kwd">do</span> <span class="pas-kwd">begin</span><br/> Inc(paramNum);<br/> paramType := params.ParamType^;<br/> <span class="pas-kwd">if</span> paramNum = <span class="pas-num">1</span> <span class="pas-kwd">then</span><br/> <span class="pas-kwd">if</span> (params^.Flags &lt;&gt; []) <span class="pas-kwd">or</span> (paramType^.Kind &lt;&gt; tkClass) <span class="pas-kwd">then</span><br/> RaiseInvalidSignature(methodName)<br/> <span class="pas-kwd">else</span><br/> methodSignature := itSelf<br/> <span class="pas-kwd">else</span> <span class="pas-kwd">if</span> paramNum = <span class="pas-num">2</span> <span class="pas-kwd">then</span><br/> <span class="pas-comment">//code says 'const' but GetMethodInfo says 'pfVar' :(</span><br/> <span class="pas-kwd">if</span> (params^.Flags * [pfConst, pfVar] &lt;&gt; []) <span class="pas-kwd">and</span> (paramType^.Kind = tkRecord) <span class="pas-kwd">and</span><br/> (SameText(paramType^.<span class="pas-kwd">Name</span>, <span class="pas-str">'TOmniValue'</span>))<br/> <span class="pas-kwd">then</span><br/> methodSignature := itSelfAndOmniValue<br/> <span class="pas-kwd">else</span> <span class="pas-kwd">if</span> (params^.Flags = [pfVar]) <span class="pas-kwd">and</span> (paramType^.Kind = tkClass) <span class="pas-kwd">then</span><br/> methodSignature := itSelfAndObject<br/> <span class="pas-kwd">else</span><br/> RaiseInvalidSignature(methodName)<br/> <span class="pas-kwd">else</span><br/> RaiseInvalidSignature(methodName);<br/> params := params.NextParam;<br/> <span class="pas-kwd">end</span>;</pre><p>It looks messy and it is messy, but it does the job. If you have problems understanding this code, I’d recommend stepping over it with the debugger.</p><h2>Benchmarks</h2><p>In test 19 I implemented some benchmarking code to find out how much this approach is slower than the standard <em>Comm.Send(msg, data)</em>. It turned out that not very much, thanks to the built-in caching.</p><p><img title="image" style="border-right: 0px; border-top: 0px; display: inline; border-left: 0px; border-bottom: 0px" height="326" alt="image" src="http://17slon.com/blogs/gabr/files/UsingRTTItoappropriatemethod_8E6D/image_3.png" width="616" border="0" /></p><p>Integer methods dispatching is about twice as fast as the string/pointer dispatching. That’s completely acceptable speed for something that is executed only few (thousand) times in the program’s lifetime. If your program model is based on sending millions and millions of <em>do computation</em> messages to the worker thread then it is doomed since the beginning anyway.</p><p>That would be enough for today, hope you liked it and stay well since the next time. Bye!</p><div class="blogger-post-footer"><font size=-2>---<br/>Published under the <a href="http://creativecommons.org/licenses/by/3.0/">Creative Commons Attribution 3.0</a> license</font><img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29331675-4319667836598858396?l=17slon.com%2Fblogs%2Fgabr%2Fblogger.html'/></div>gabrhttp://www.blogger.com/profile/06903558857617342477noreply@blogger.com0tag:blogger.com,1999:blog-29331675.post-39746615528296818752008-10-06T10:28:00.002+02:002008-10-06T10:32:18.567+02:00Erlangenizing the OmniThreadLibrary<p align="justify">Few days ago I “discovered” Erlang. <em>[Such things happen when you read </em><a href="http://stackoverflow.com/" target="_blank"><em>StackOverflow</em></a><em> obsessively (sigh).]</em> I started with <a title="Erlang (programming language)" href="http://en.wikipedia.org/wiki/Erlang_(programming_language)" target="_blank">Wikipedia</a> and proceeded with the <a title="Programming Erlang: Software for a Concurrent World" href="http://www.pragprog.com/titles/jaerlang/programming-erlang" target="_blank">Pragmatic Programmer book</a> – but that’s not really important. I wanted to talk about Erlang’s built-in concurrency support.</p> <p align="justify">Some things are very similar to the OTL (if we take into account that Erlang is a functional language and Delphi is not) and some are different but one immediately jumped to my attention. In Erlang, the message recipient doesn’t use numeric code to discover what message it has received; instead of that, whole message is matched to a programmer provided pattern (or patterns). The interesting thing is that usually (by convention) the first element in the message is an atom (a name, a sequence of characters, if you want). That got me thinking … Why do we send integer messages in the OTL, anyway?</p> <h2>The Windows Way</h2> <p align="justify">From the very beginnings, OmniThreadLibrary tried to make programmer’s life simple. Well, at least simpler. One of the helping hands it offered was was simplified message processing. In the traditional Windows thread programming you have to wait for various objects to become signaled with WaitForMultipleObjects and then proceed accordingly. In practice that means that thread’s main logic is centralized in one very big method that handles all those events, mutexes and other stuff that can be waited upon.</p> <p align="justify">OTL helps by implementing this logic internally (at least for workers that implement IOmniWorker interface). Instead of using kernel primitives, task owner sends messages to the task’s message queue. Messages are processed somewhere inside the OTL (specifically OtlTaskControl.pas/TOmniTaskExecutor.Asy_DispatchMessages) and are converted into method calls with Delphi’s Dispacth mechanism. That’s the same mechanism that makes sure that Windows messages are “converted” into Delphi methods and it requires that first two bytes of the dispatched message contain message ID. That’s why (until now) the recommended way to send a message to task was:</p> <pre class="pas-source"><span class="pas-kwd">const</span><br/> MSG_CHANGE_MESSAGE = <span class="pas-num">1</span>;<br/> MSG_SEND_MESSAGE = <span class="pas-num">2</span>;<br/><br/><span class="pas-kwd">type</span><br/> TAsyncHello = <span class="pas-kwd">class</span>(TOmniWorker)<br/> strict <span class="pas-kwd">private</span><br/> aiMessage: <span class="pas-kwd">string</span>;<br/> <span class="pas-kwd">public</span><br/> <span class="pas-kwd">function</span> Initialize: boolean; <span class="pas-kwd">override</span>;<br/> <span class="pas-kwd">procedure</span> OMChangeMessage(<span class="pas-kwd">var</span> msg: TOmniMessage); <span class="pas-kwd">message</span> MSG_CHANGE_MESSAGE;<br/> <span class="pas-kwd">procedure</span> OMSendMessage(<span class="pas-kwd">var</span> msg: TOmniMessage); <span class="pas-kwd">message</span> MSG_SEND_MESSAGE;<br/> <span class="pas-kwd">end</span>;<br/><br/>FHelloTask: IOmniTaskControl;<br/><br/>FHelloTask.Comm.Send(MSG_CHANGE_MESSAGE, <span class="pas-str">'Random '</span> + IntToStr(Random(<span class="pas-num">1234</span>)));</pre><p align="justify"><em>[You can read more about this approach in </em><a href="http://17slon.com/blogs/gabr/2008/07/omnithreadlibrary-example-4.html" target="_blank"><em>OmniThreadLibrary Example #4: Bidirectional communication, the OTL way</em></a><em>.]</em></p><h2>The Erlang Way</h2><p align="justify">This approach simplifies writing threaded code – at least the one that doesn’t depend heavily on shared data structures. But there’s still some room for improvement. For example, do we really have to use numeric messages, which have to be declared in advance. Why couldn’t the task controller just tell the task to execute the <em>OMChangeMessage</em> method?</p><p align="justify">To cut the long story short – this <strong>is</strong> now possible. Yesterday I committed a set of OTL modifications that allow you to do this:</p><pre class="pas-source"><span class="pas-kwd">type</span><br/> TAsyncHello = <span class="pas-kwd">class</span>(TOmniWorker)<br/> <span class="pas-kwd">published</span><br/> <span class="pas-kwd">procedure</span> Change(<span class="pas-kwd">const</span> data: TOmniValue);<br/> <span class="pas-kwd">end</span>;<br/><br/>FHelloTask: IOmniTaskControl;<br/><br/>FHelloTask.Invoke(<span class="pas-str">'Change'</span>, <span class="pas-str">'Random '</span> + IntToStr(Random(<span class="pas-num">1234</span>)));</pre><p align="justify">Yes, the <em>Change</em> method is <strong>published</strong> here. This is important.</p><p align="justify">Simple, huh? There’s a small problem, though – there are no compile-time checks. The code sends a string and compiler can do nothing to verify validity of this string. If the name was mistyped, you’d only notice it during the program execution.</p><p align="justify">To fix this problem, OTL allows another form of method invocation which uses a method address instead of&#160; the name.</p><pre class="pas-source">FHelloTask.Invoke(@TAsyncHello.Change, <span class="pas-str">'Random '</span> + IntToStr(Random(<span class="pas-num">1234</span>)));</pre><p align="justify">In this case the compiler can check your typing, but still it won’t catch all problems – for example, the following code will compile and then raise exception during the execution.</p><pre class="pas-source"><span class="pas-kwd">procedure</span> TfrmTestStringMsgDispatch.btnTestInvalidMsgClick(Sender: TObject);<br/><span class="pas-kwd">begin</span><br/> <span class="pas-kwd">if</span> cbStringMessages.Checked <span class="pas-kwd">then</span><br/> <span class="pas-comment">// will fail, FooBar method is not defined</span><br/> FHelloTask.Invoke(<span class="pas-str">'FooBar'</span>)<br/> <span class="pas-kwd">else</span><br/> <span class="pas-comment">// will fail, can only invoke methods from the task's class</span><br/> FHelloTask.Invoke(@Self.btnTestInvalidMsg);<br/><span class="pas-kwd">end</span>;</pre><p align="justify"><em>[All new functionality is exposed in new demo 18_StringMsgDispatch.]</em></p><h2>Implementation</h2><p align="justify">To understand how the <em>Invoke</em> is implemented, it’s best to trace one such call. First we see that <em>Invoke</em>&#160; gets converted into a normal message.</p><pre class="pas-source"><span class="pas-kwd">procedure</span> TOmniTaskControl.Invoke(<span class="pas-kwd">const</span> msgMethod: pointer; msgData: TOmniValue);<br/><span class="pas-kwd">begin</span><br/> Comm.Send(TOmniInternalAddressMsg.CreateMessage(msgMethod, msgData));<br/><span class="pas-kwd">end</span>; <span class="pas-comment">{ TOmniTaskControl.Invoke }</span><br/><br/><span class="pas-kwd">class</span> <span class="pas-kwd">function</span> TOmniInternalAddressMsg.CreateMessage(<span class="pas-kwd">const</span> msgMethod: pointer; msgData:<br/> TOmniValue): TOmniMessage;<br/><span class="pas-kwd">begin</span><br/> Result := TOmniMessage.Create(COtlReservedMsgID,<br/> TOmniInternalAddressMsg.Create(msgMethod, msgData));<br/><span class="pas-kwd">end</span>; <span class="pas-comment">{ TOmniInternalAddressMsg.CreateMessage }</span></pre><p align="justify">This message has message ID <em>COtlReservedMsgID</em> (which is equal to <em>$FFFF</em>, so from now on please don’t use this message for you purposes). Message data field contains object which wraps method name and message data that was passed to the <em>Invoke</em>. Similar code is executed when Invoke is called with the method pointer parameter.</p><p align="justify">OK, so that’s how method name travels from the task controller to the task itself by using standard communication channel. But that is only half of the story … the simpler part!</p><p align="justify">On the receiving side, <em>TOmniTaskExecutor.Asy_DispatchMessages</em> detects new message and calls <em>DispatchOmniMessage</em> to process it.</p><pre class="pas-source"><span class="pas-kwd">if</span> awaited = idxFirstMessage <span class="pas-kwd">then</span><br/> gotMsg := task.Comm.Receive(msg)<br/><span class="pas-kwd">else</span> <span class="pas-kwd">begin</span><br/> oteInternalLock.Acquire;<br/> <span class="pas-kwd">try</span><br/> gotMsg := (oteCommList[awaited - idxFirstMessage - <span class="pas-num">1</span>] <span class="pas-kwd">as</span> <br /> IOmniCommunicationEndpoint).Receive(msg);<br/> <span class="pas-kwd">finally</span> oteInternalLock.Release; <span class="pas-kwd">end</span>;<br/><span class="pas-kwd">end</span>;<br/><span class="pas-kwd">if</span> gotMsg <span class="pas-kwd">and</span> assigned(WorkerIntf) <span class="pas-kwd">then</span><br/> DispatchOmniMessage(msg);</pre><p align="justify">Now that’s where the things start to get really interesting as we have to use RTTI and even extended RTTI to call the appropriate method. It is also the point where I’ll cut the story short. This article is already very long, maybe even too long, and I have much more to say on the subject. Expect part II to be published in few days. <em>[Of course, if you’re curious you can just look into the code to see how DispatchOmniMessage is implemented!]</em></p><h2>Test it!</h2><p align="justify">The newest OmniThreadLibrary code is only available in the <a title="OmniThreadLibrary SVN repository" href="http://code.google.com/p/omnithreadlibrary/source/checkout" target="_blank">repository</a>. No snapshots this time.</p><p align="justify">I’d still be immensely grateful to anybody that will test the new functionality and provide me with his thoughts on this approach.</p><div class="blogger-post-footer"><font size=-2>---<br/>Published under the <a href="http://creativecommons.org/licenses/by/3.0/">Creative Commons Attribution 3.0</a> license</font><img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29331675-3974661552829681875?l=17slon.com%2Fblogs%2Fgabr%2Fblogger.html'/></div>gabrhttp://www.blogger.com/profile/06903558857617342477noreply@blogger.com0tag:blogger.com,1999:blog-29331675.post-55551797921047377442008-10-03T12:13:00.001+02:002008-10-03T12:13:43.612+02:00Bulk update<p>Delphi 2009 was released so now it’s time to publish updates to my various units … In this article I’m just publishing the short changelog; you can expect more details on some enhancements (TGpJoinedStream, 4- and 8- aligned integer, Unicode support in GpStructuredStorage) in the near future.</p> <h2><a href="http://gp.17slon.com/gp/dsiwin32.htm" target="_blank">DSiWin32 1.41</a></h2> <ul> <li>Compatible with Delphi 2009. </li> <li>Created DSiInterlocked*64 family of functions by copying the code from <a href="http://qc.borland.com/wc/qcmain.aspx?d=6212">http://qc.borland.com/wc/qcmain.aspx?d=6212</a>. Functions were written by Will DeWitt Jr and are included with permission. </li> <li>New functions DSiCopyFileAnimated, DSiConnectToNetworkResource, DSiIsHtmlFormatOnClipboard, DSiGetHtmlFormatFromClipboard, DSiCopyHtmlFormatToClipboard, DSiYield. </li> <li>Added constants FILE_LIST_DIRECTORY, FILE_SHARE_FULL, FILE_ACTION_ADDED, FILE_ACTION_REMOVED, FILE_ACTION_MODIFIED, FILE_ACTION_RENAMED_OLD_NAME, FILE_ACTION_RENAMED_NEW_NAME. </li> <li>Forced {$T-} as the code doesn't compile in {$T+} state. </li> <li>Bug fixed: It was not possible to use DSiTimeGetTime64 in parallel from multiple threads </li> </ul> <h2><a href="http://gp.17slon.com/gp/gphugefile.htm" target="_blank">GpHugeFile 5.05a</a></h2> <ul> <li>Optimization: Under some circumstances, lots of unnecessary SetFilePointer calls were made. </li> </ul> <h2><a href="http://gp.17slon.com/gp/gplists.htm" target="_blank">GpLists 1.41</a></h2> <ul> <li>Works with Delphi 2009. </li> </ul> <h2><a href="http://gp.17slon.com/gp/gpstreams.htm" target="_blank">GpStreams 1.25</a></h2> <ul> <li>Added TGpJoinedStream class. </li> <li>Span-storing class can now be modified via TGpScatteredStream.SpanClass. </li> <li>TGpScatteredStream's AddSpan and AddSpanOS now return span offset in the span list. </li> <li>Added bunch of BE_ overloads to the TGpStreamEnhancer class. </li> <li>Small optimization in KeepStreamPositionWrapper destructor. </li> </ul> <h2><a href="http://gp.17slon.com/gp/gpstructuredstorage.htm" target="_blank">GpStructuredStorage 2.0</a></h2> <ul> <li>Works with Delphi 2009.</li> <li>Added Unicode support to the underlying storage. File and folder names are stored in UTF-16, as are attribute names and values. API is still based on Delphi's 'string' type - meaning that values are converted to Unicode and back on the fly using the current locale.</li> <li>Existing structured storage files will be upgraded automatically if they are not open for readonly access. Applications, compiled with GpStructuredStorage 1.x will not be able to read new/upgraded files. Version is incremented to 2.0.0.0 when 1.x file is opened unless it is opened in readonly mode. Newly created storages have version 2.0.0.0.</li> </ul> <h2><a href="http://gp.17slon.com/gp/gpstuff.htm" target="_blank">GpStuff 1.13</a></h2> <ul> <li>Implemented 4-aligned integer, TGp4AlignedInt.</li> <li>Implemented 8-aligned integer, TGp8AlignedInt.</li> <li>Added function OpenArrayToVarArray, written by Thomas Schubbauer.</li> <li>ReverseWord/ReverseDWord rewritten in assembler (by GJ).</li> <li>Declared MaxInt64 constant.</li> </ul> <h2><a href="http://gp.17slon.com/gp/gpsync.htm" target="_blank">GpSync 1.21</a></h2> <ul> <li>Added optional external message counter to the message queue.</li> </ul> <h2><a href="http://gp.17slon.com/gp/gptextfile.htm" target="_blank">GpTextFile 4.01</a></h2> <ul> <li>Added TGpTextFile.Write(ws: WideString) and TGpTextFile.Writeln(s: string) overloads.</li> <li>TGpTextFile.Write[ln] string parameters made 'const'.</li> <li>Bug fixed [found by AKi]: TGpTextFile.Write was not working when using CP_UTF8 codepage.</li> </ul> <h2><a href="http://gp.17slon.com/gp/gptextstream.htm" target="_blank">GpTextStream 1.06</a></h2> <ul> <li>Works with Delphi 2009.</li> <li>Exported StringToWideString, WideStringToString, and GetDefaultAnsiCodepage. </li> </ul> <h2><a href="http://gp.17slon.com/gp/gpversion.htm" target="_blank">GpVersion 2.02</a></h2> <ul> <li>Added another CreateVersion overload.</li> <li>Extended IVersion interface with IsHigherThan and IsLowerThan.</li> </ul> <div class="blogger-post-footer"><font size=-2>---<br/>Published under the <a href="http://creativecommons.org/licenses/by/3.0/">Creative Commons Attribution 3.0</a> license</font><img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29331675-5555179792104737744?l=17slon.com%2Fblogs%2Fgabr%2Fblogger.html'/></div>gabrhttp://www.blogger.com/profile/06903558857617342477noreply@blogger.com1tag:blogger.com,1999:blog-29331675.post-82731602250615359552008-09-20T13:05:00.002+02:002008-09-20T13:08:56.004+02:00Processing Windows messages in OmniThreadLibrary<p align="justify">In all my ravings about the OmniThreadLibrary I’ve never discussed the .<em>MsgWait</em> decorator – and partly because of that it was not even working in the 1.0 release :-(. Time to fix that!</p> <p align="justify">Today I’ll show you how simple is to make a synchronous wrapper around the asynchronous component (using OTL, of course). The code itself is not an OTL test project but a unit I’m using to synchronously download web pages. Where’s the problem at all, you’ll say? Indy is synchronous and so is WinInet. True, but I’m using <a title="Internet Component Suite" href="http://www.overbyte.be/frame_index.html?redirTo=/products/ics.html" target="_blank">ICS</a>, which is a completely asynchronous solution.</p> <blockquote> <p align="justify">[For the readers who have no idea what I’m talking about: Synchronous means <em>program calls the code, code executes for some time and then returns with web page.</em> Asynchronous means: <em>program calls the code and the code returns immediately. Somewhere in the background, web page is being retrieved and when it is fully transferred, your program gets notified in some way.</em> Second way is more flexible, allows you to do more things in parallel, doesn’t block the UI but is more complicated to use.]</p> </blockquote> <p align="justify">Sometimes (usually for quick and dirty tests) I need to retrieve a web page without having to think about messages and events and what will be called in which moment. For such times I put together a simple unit which wraps ICS stuff in a thread. It is called <em>GpHttp</em> and will be available on my web in some time. For now, you can download it <a title="GpHttp unit" href="http:/17slon.com/krama/GpHttp.zip " target="_blank">here</a>. You’ll also need ICS v6 (and it should work with ICS v5 if you remove <em>Overbyte</em> prefix from used units and classes).</p> <h2>Interface</h2> <p align="justify">I wanted to keep the interface really simple. The <em>GpHttp</em> unit exports only two functions, one to execute GET and another POST request. Both accept username and password and both return status code (200 if everything went OK), status text and page contents. In case of a socket problem, WinSock error code is returned in <em>statusCode</em>. (HTTP codes are always below 1,000 and WinSock codes are always above 10,000 so there is no possibility for collision.)</p><pre class="pas-source"><span class="pas-kwd">function</span> GpHttpGet(<span class="pas-kwd">const</span> url, username, password: <span class="pas-kwd">string</span>; <br /> <span class="pas-kwd">var</span> statusCode: integer; <span class="pas-kwd">var</span> statusText, pageContents: <span class="pas-kwd">string</span>): boolean;<br/><br/><span class="pas-kwd">function</span> GpHttpPost(<span class="pas-kwd">const</span> url, username, password, postData: <span class="pas-kwd">string</span>;<br /> <span class="pas-kwd">var</span> statusCode: integer; <span class="pas-kwd">var</span> statusText, pageContents: <span class="pas-kwd">string</span>): boolean;</pre><p align="justify">Both functions are simple wrappers around the third (local) function that executes any HTTP request.</p><pre class="pas-source"><span class="pas-kwd">function</span> GpHttpGet(<span class="pas-kwd">const</span> url, username, password: <span class="pas-kwd">string</span>; <br /> <span class="pas-kwd">var</span> statusCode: integer; <span class="pas-kwd">var</span> statusText, pageContents: <span class="pas-kwd">string</span>): boolean;<br/><span class="pas-kwd">begin</span><br/> Result := GpHttpRequest(url, username, password, <span class="pas-str">'GET'</span>, <span class="pas-str">''</span>, statusCode, <br /> statusText, pageContents);<br/><span class="pas-kwd">end</span>; <span class="pas-comment">{ GpHttpGet }</span><br/><br/><span class="pas-kwd">function</span> GpHttpPost(<span class="pas-kwd">const</span> url, username, password, postData: <span class="pas-kwd">string</span>;<br /> <span class="pas-kwd">var</span> statusCode: integer; <span class="pas-kwd">var</span> statusText, pageContents: <span class="pas-kwd">string</span>): boolean;<br/><span class="pas-kwd">begin</span><br/> Result := GpHttpRequest(url, username, password, <span class="pas-str">'POST'</span>, postData, <br /> statusCode, statusText, pageContents);<br/><span class="pas-kwd">end</span>; <span class="pas-comment">{ GpHttpPost }</span></pre><p align="justify">The real work starts in the <em>GpHttpRequest</em> method. First it creates a worker object and passes request data to it. The reference to the object (or more correct – to the interface implemented by this object) is stored in the <em>worker</em> variable. We’ll need it at the end to retrieve the status code and page contents.</p><p align="justify">Next, the task is created and run. Notice the call to <em>.MsgWait</em> which instructs the internal thread loop to process Windows messages (more on that later).</p><p align="justify">Next, we wait for the task to terminate. We’ll only be waiting up to 30 seconds (a constant in code which could be easily changed into a caller-provided parameter). If task is not finished in 30 seconds, we’ll terminate it and return <em>False</em>. Otherwise, we’ll retrieve status code, text and page contents from the worker object via the <em><a title="OmniThreadLibrary patterns - How to (not) create a task" href="http://17slon.com/blogs/gabr/2008/09/omnithreadlibrary-patterns-how-to-not.html" target="_blank">Implementor</a></em> trick. (A better way would be to write a <em>IGpHttpRequest</em> interface supporting <em>StatusCode</em>, <em>StatusText</em> and <em>PageContents</em> properties, but sometimes I’m just too lazy …)</p><pre class="pas-source"><span class="pas-kwd">function</span> GpHttpRequest(<span class="pas-kwd">const</span> url, username, password, request, <br /> postData: <span class="pas-kwd">string</span>; <span class="pas-kwd">var</span> statusCode: integer; <span class="pas-kwd">var</span> statusText, <br /> pageContents: <span class="pas-kwd">string</span>): boolean;<br/><span class="pas-kwd">var</span><br/> task : IOmniTaskControl;<br/> worker: IOmniWorker;<br/><span class="pas-kwd">begin</span><br/> worker := TGpHttpRequest.Create(url, username, password, request, postData);<br/> task := CreateTask(worker, <span class="pas-str">'GpHttpRequest'</span>).MsgWait.Run;<br/> Result := task.WaitFor(CGpHttpRequestTimeout_sec * <span class="pas-num">1000</span>);<br/> <span class="pas-kwd">if</span> <span class="pas-kwd">not</span> Result <span class="pas-kwd">then</span><br/> task.Terminate<br/> <span class="pas-kwd">else</span> <span class="pas-kwd">begin</span><br/> statusCode := TGpHttpRequest(worker.Implementor).StatusCode;<br/> statusText := TGpHttpRequest(worker.Implementor).StatusText;<br/> pageContents := TGpHttpRequest(worker.Implementor).PageContents;<br/> <span class="pas-kwd">end</span>;<br/><span class="pas-kwd">end</span>; <span class="pas-comment">{ GpHttpRequest }</span></pre><h2>Background Task</h2><p align="justify">The real work is done in the background task <em>[as expected, doh, why are you even mentioning that??]</em>. In the <em>Initialize</em> method, to be more specific.</p><pre class="pas-source"><span class="pas-kwd">function</span> TGpHttpRequest.Initialize: boolean;<br/><span class="pas-kwd">begin</span><br/> hrHttpClient := THttpCli.Create(<span class="pas-kwd">nil</span>);<br/> <span class="pas-kwd">try</span><br/> hrHttpClient.NoCache := true;<br/> hrHttpClient.RequestVer := <span class="pas-str">'1.1'</span>;<br/> hrHttpClient.URL := hrURL;<br/> hrHttpClient.Username := hrUsername;<br/> hrHttpClient.Password := hrPassword;<br/> hrHttpClient.FollowRelocation := true;<br/> <span class="pas-kwd">if</span> hrUsername &lt;&gt; <span class="pas-str">''</span> <span class="pas-kwd">then</span><br/> hrHttpClient.ServerAuth := httpAuthBasic;<br/> hrHttpClient.SendStream := TStringStream.Create(hrPostData);<br/> hrHttpClient.RcvdStream := TStringStream.Create(<span class="pas-str">''</span>);<br/> hrHttpClient.OnRequestDone := HandleRequestDone;<br/> <span class="pas-kwd">if</span> SameText(hrRequest, <span class="pas-str">'GET'</span>) <span class="pas-kwd">then</span><br/> hrHttpClient.GetASync<br/> <span class="pas-kwd">else</span> <span class="pas-kwd">if</span> SameText(hrRequest, <span class="pas-str">'POST'</span>) <span class="pas-kwd">then</span><br/> hrHttpClient.PostASync<br/> <span class="pas-kwd">else</span><br/> <span class="pas-kwd">raise</span> Exception.CreateFmt(<br /> <span class="pas-str">'TGpHttpRequest.Initialize: Unknown request type %s'</span>, <br /> [hrRequest]);<br/> Result := true;<br/> <span class="pas-kwd">except</span><br/> <span class="pas-kwd">on</span> E:ESocketException <span class="pas-kwd">do</span> <span class="pas-kwd">begin</span><br/> hrStatusCode := -<span class="pas-num">1</span>;<br/> hrStatusText := E.<span class="pas-kwd">Message</span>;<br/> Result := false;<br/> <span class="pas-kwd">end</span>;<br/> <span class="pas-kwd">end</span>;<br/><span class="pas-kwd">end</span>; <span class="pas-comment">{ TGpHttpRequest.Initialize }</span></pre><p align="justify">The code first instantiates a <em>THttpCli</em> object. <em>THttpCli</em> is the ICS’s HTTP client class used to execute HTTP requests asynchronously or synchronously. Here I’m using asynchronous mode because a) <em>THttpCli.GetSync</em> doesn’t have configurable timeout and b) it calls <em>Application.ProcessMessages</em>, which is not a good idea if you want to run your requests from a background thread (which I did).</p><p align="justify">Next the code initializes bunch of <em>THttpCli</em> paramers and calls either <em>GetASync</em> or <em>PostASync</em>. If get or post cause an exception, it is remapped to status code and text and <em>Initialize </em>returns <em>False</em>. That signals the <em>IOmniWorker</em> main loop that it should not proceed with the task execution.</p><p align="justify">Now we have reached the asynchronous phase. ICS is working in the background and all other threads are just waiting for it to finish. Because ICS’s architecture is message based, somebody must process Windows messages which flow through the ICS itself. That’s why we had to use <em>.MsgWait </em>decorator when preparing the task.</p><p align="justify">Some time later (possibly before the timeout occurs), <em>THttpCli</em> will fully download the web page and call the <em>HandleRequestDone</em> event handler. Here we’ll just copy relevant data into task’s internal fields and then the code will tell the task (i.e. itself) to terminate.</p><pre class="pas-source"><span class="pas-kwd">procedure</span> TGpHttpRequest.HandleRequestDone(sender: TObject; <br /> rqType: THttpRequest; error: word);<br/><span class="pas-kwd">begin</span><br/> <span class="pas-kwd">if</span> error &lt;&gt; <span class="pas-num">0</span> <span class="pas-kwd">then</span> <span class="pas-kwd">begin</span><br/> hrStatusCode := error;<br/> hrStatusText := <span class="pas-str">'Socket error'</span>;<br/> <span class="pas-kwd">end</span><br/> <span class="pas-kwd">else</span> <span class="pas-kwd">begin</span><br/> hrPageContents := TStringStream(hrHttpClient.RcvdStream).DataString;<br/> hrStatusCode := hrHttpClient.StatusCode;<br/> hrStatusText := hrHttpClient.ReasonPhrase;<br/> <span class="pas-kwd">end</span>;<br/> Task.Terminate;<br/><span class="pas-kwd">end</span>; <span class="pas-comment">{ TGpHttpRequest.HandleRequestDone }</span></pre><p align="justify">In the cleanup code (which is, same as <em>Initialize</em>, called automatically from the <em>IOmniWorker</em> main loop) we just have to tear down internal objects.</p><pre class="pas-source"><span class="pas-kwd">procedure</span> TGpHttpRequest.Cleanup;<br/><span class="pas-kwd">begin</span><br/> <span class="pas-kwd">if</span> assigned(hrHttpClient) <span class="pas-kwd">then</span> <span class="pas-kwd">begin</span><br/> hrHttpClient.RcvdStream.Free;<br/> hrHttpClient.SendStream.Free;<br/> FreeAndNil(hrHttpClient);<br/> <span class="pas-kwd">end</span>;<br/><span class="pas-kwd">end</span>; <span class="pas-comment">{ TGpHttpRequest.Cleanup }</span></pre><p align="justify">And that’s all, folks. It really is that simple.</p><h2>MsgWait</h2><p align="justify">In case you want to learn more about OTL internals, read ahead …</p><p align="justify">Let’s take a short look at the <em>.MsgWait</em> implementation. The function itself just sets two internal fields and returns the object itself so that we can chain another method to it.</p><pre class="pas-source"><span class="pas-kwd">function</span> TOmniTaskControl.MsgWait(wakeMask: DWORD): IOmniTaskControl;<br/><span class="pas-kwd">begin</span><br/> Options := Options + [tcoMessageWait];<br/> otcExecutor.WakeMask := wakeMask;<br/> Result := Self;<br/><span class="pas-kwd">end</span>; <span class="pas-comment">{ TOmniTaskControl.MsgWait }</span></pre><p align="justify">The hard work is done in <em>TOmniTaskExecutor.Asy_DispatchMessages</em>. If the <em>tcoMessageWait</em> option is set, the MsgWaitForMultipleObjectsEx will also wait for Windows messages (in addition to everything else it does) because it will receive non-null <em>waitWakeMask. </em>When a message is detected, the code will call <em>ProcessThreadMessages </em>method which simply peeks and dispatches all Windows messages (and Delphi’s internal message dispatch mechanism takes care of all the rest).</p><pre class="pas-source"><span class="pas-kwd">if</span> tcoMessageWait <span class="pas-kwd">in</span> Options <span class="pas-kwd">then</span><br/> waitWakeMask := WakeMask<br/><span class="pas-kwd">else</span><br/> waitWakeMask := <span class="pas-num">0</span>;<br/><span class="pas-comment">//...</span><br/>awaited := MsgWaitForMultipleObjectsEx(numWaitHandles, waitHandles,<br/> cardinal(timeout_ms), waitWakeMask, flags);<br/><span class="pas-comment">//...</span><br/><span class="pas-kwd">else</span> <span class="pas-kwd">if</span> awaited = (WAIT_OBJECT_0 + numWaitHandles) <span class="pas-kwd">then</span> <span class="pas-comment">//message</span><br/> ProcessThreadMessages<br/><br/><span class="pas-kwd">procedure</span> TOmniTaskExecutor.ProcessThreadMessages;<br/><span class="pas-kwd">var</span><br/> msg: TMsg;<br/><span class="pas-kwd">begin</span><br/> <span class="pas-kwd">while</span> PeekMessage(Msg, <span class="pas-num">0</span>, <span class="pas-num">0</span>, <span class="pas-num">0</span>, PM_REMOVE) <span class="pas-kwd">and</span> (Msg.<span class="pas-kwd">Message</span> &lt;&gt; WM_QUIT) <span class="pas-kwd">do</span> <span class="pas-kwd">begin</span><br/> TranslateMessage(Msg);<br/> DispatchMessage(Msg);<br/> <span class="pas-kwd">end</span>;<br/><span class="pas-kwd">end</span>; <span class="pas-comment">{ TOmniTaskControl.ProcessThreadMessages }</span></pre><p align="justify">The <em>Asy_DispatchMessages </em>is probably the most complicated part of the OTL (once you understand the lock-free structures inside the OtlContainers ;) Once you understand how it works you’ll be fully prepared to write custom thread loops in highly specialized threaded code. But don’t worry, you can use OTL even if you don’t understand the magic hidden inside.</p><div class="blogger-post-footer"><font size=-2>---<br/>Published under the <a href="http://creativecommons.org/licenses/by/3.0/">Creative Commons Attribution 3.0</a> license</font><img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29331675-8273160225061535955?l=17slon.com%2Fblogs%2Fgabr%2Fblogger.html'/></div>gabrhttp://www.blogger.com/profile/06903558857617342477noreply@blogger.com1tag:blogger.com,1999:blog-29331675.post-55683498779012894112008-09-06T19:01:00.002+02:002008-09-06T20:59:06.171+02:00Interfacing with external programs<p>Say you have a program. A popular program (at least in some circles) that other people want to write add-ons for. Your program does some job and in some processing steps you want to be able to execute some external code and then proceed according to the result returned by that code. Similar to the way CGI code is executed when a HTTP server processes a HTTP request.</p> <p>The other day I was trying to enumerate all possible ways to do that. I found following solutions:</p> <ul> <li>Running external program. Data can be passed in program’s standard input and read from its standard output – just like when CGI program is launched from the HTTP server. Simple, stable (it is simple to protect against malfunctioning add-ons), but quite slow.</li> <li>Third-party DLL. Relatively simple, very fast, but can seriously destabilize the whole product. Complicated to upgrade the DLL (must shut down main application to upgrade add-on).</li> <li>[D]COM[+]. Not my bag of Swedish … sorry, wrong movie. Definitely not the way I’d like to pursue. Unstable. Leads to problems that nobody seems to be able to troubleshoot.</li> <li>Windows messages. Messy. Plus the main program runs as a service while add-on maybe wouldn’t.</li> <li>TCP. Implement add-on as a text-processing TCP/IP service (another HTTP server, if we continue the CGI analogy). Interesting idea, but not very simple to implement. Fast when both are running on the same machine. Flexible – each can be shutdown and upgraded independently; processing can be distributed over several computers. Complicated to configure when multiple add-ons are installed (each must be configured to a different port). Firewall and antivirus software may cause problems.</li> <li>Drop folder. Main app drops a file into some folder and waits for the result file. Clumsy, possibly faster than the external program solution (add-on can be always running), simple to implement and very stable.</li> <li>Message queues (as in the MSMQ). Interesting but possibly too complicated for most customers to install and manage.</li> </ul> <p>And now to the main point of my ramblings. What did I miss? Are there more possibilities? If you have any idea how to approach my problem from a different direction, leave me a comment. Don’t mention SOAP, BTW, it is implicitly included in the “add-on as a TCP server” solution.</p> <p>And thanks!</p><div class="blogger-post-footer"><font size=-2>---<br/>Published under the <a href="http://creativecommons.org/licenses/by/3.0/">Creative Commons Attribution 3.0</a> license</font><img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29331675-5568349877901289411?l=17slon.com%2Fblogs%2Fgabr%2Fblogger.html'/></div>gabrhttp://www.blogger.com/profile/06903558857617342477noreply@blogger.com17tag:blogger.com,1999:blog-29331675.post-19795837119730206232008-09-03T15:54:00.002+02:002008-09-04T00:06:39.965+02:00OmniThreadLibrary patterns - How to (not) create a task<p>From the first public release two months ago (or is it closer to three already?), <a title="OmniThreadLibrary home" href="http://otl.17slon.com" target="_blank">OmniThreadLibrary</a> has been constantly changing. Only with the 1.0 release it has reached a somewhat stable state. That flux was good as it was only through the development of the OTL that I discovered how to do some things properly (maybe I was not so wrong in <a href="http://17slon.com/blogs/gabr/2008/07/why-is-software-third-time-lucky.html" target="_blank">Why is software third time lucky?</a>, after all). But it was also bad as some introductionary topics that I wrote don't accurately reflect the current state anymore. Yes, I know, I should start writing good tutorials. Not fun :( I like writing blog articles more. Today I'll focus on task creation. </p> <p>OTL offers three different ways to create a task (and a fourth one will be introduced when Delphi 2009 is available). Those three ways are mapped to three overloads of the CreateTask method (OtlTaskControl.pas).</p><pre class="pas-source"> <span class="pas-kwd">function</span> CreateTask(worker: TOmniTaskProcedure; <br><span class="pas-kwd"><font color="#000000"> </font>const</span> taskName: <span class="pas-kwd">string</span> = <span class="pas-str">''</span>): IOmniTaskControl; <span class="pas-kwd">overload</span>;<br/> <span class="pas-kwd">function</span> CreateTask(worker: TOmniTaskMethod; <br> <span class="pas-kwd">const</span> taskName: <span class="pas-kwd">string</span> = <span class="pas-str">''</span>): IOmniTaskControl; <span class="pas-kwd">overload</span>;<br/> <span class="pas-kwd">function</span> CreateTask(<span class="pas-kwd">const</span> worker: IOmniWorker; <br> <span class="pas-kwd">const</span> taskName: <span class="pas-kwd">string</span> = <span class="pas-str">''</span>): IOmniTaskControl; <span class="pas-kwd">overload</span>;</pre><p>The simplest possible way (demoed in test application 2_TwoWayHello; see <a href="http://17slon.com/blogs/gabr/2008/07/omnithreadlibrary-example-3.html" target="_blank">OmniThreadLibrary Example #3: Bidirectional communication</a>) is to pass a name of a global procedure to the CreateTask. This global procedure must consume one parameter of type IOmniTask (it was described in the <a href="http://17slon.com/blogs/gabr/2008/07/omnithreadlibrary-internals-otltask.html" target="_blank">OmniThreadLibrary internals - OtlTask</a> article, which is still mostly valid).</p><pre class="pas-source">CreateTask(RunHelloWorld, <span class="pas-str">'HelloWorld'</span>).Run;<br/><br/><span class="pas-kwd">procedure</span> RunHelloWorld(<span class="pas-kwd">const</span> task: IOmniTask);<br/><span class="pas-kwd">begin</span><br/><span class="pas-kwd">end</span>;</pre><p>A variation on the theme is passing a name of a method to the CreateTask. This approach is used in the test application <a title="OmniThreadLibrary Example #2: Hello, world!" href="http://17slon.com/blogs/gabr/2008/07/omnithreadlibrary-example-2-hello-world.html" target="_blank">1_HelloWorld</a>. The interesting point to make here is that you can declare this method in the same class from which the CreateTask is called. That way you can access the class fields and methods from the threaded code. Just keep in mind that you'll be doing this from another thread so make sure you know what you're doing!</p><pre class="pas-source"><span class="pas-kwd">procedure</span> TfrmTestHelloWorld.RunHelloWorld(<span class="pas-kwd">const</span> task: IOmniTask);<br/><span class="pas-kwd">begin</span><br/><span class="pas-kwd">end</span>;</pre><p>For all except the simplest tasks, you'll use the third approach, because it will give you access to the true OTL power (<a href="http://17slon.com/blogs/gabr/2008/07/omnithreadlibrary-example-4.html" target="_blank">internal wait loop and message dispatching</a>). To use it, you have to create a worker object, which implements the IOmniWorker interface. An example from the 5_TwoWayHello_without_loop demo:</p><pre class="pas-source"><span class="pas-kwd">procedure</span> TfrmTestTwoWayHello.actStartHelloExecute(Sender: TObject);<br/><span class="pas-kwd">var</span><br/> worker: IOmniWorker;<br/><span class="pas-kwd">begin</span><br/> worker := TAsyncHello.Create;<br/> FHelloTask :=<br/> OmniEventMonitor1.Monitor(CreateTask(worker, <span class="pas-str">'Hello'</span>)).<br/> SetTimer(<span class="pas-num">1000</span>, MSG_SEND_MESSAGE).<br/> SetParameter(<span class="pas-str">'Delay'</span>, <span class="pas-num">1000</span>).<br/> SetParameter(<span class="pas-str">'Message'</span>, <span class="pas-str">'Hello'</span>).<br/> Run;<br/><span class="pas-kwd">end</span>;</pre><p>As you're exposing this worker via interface, Delphi will manage its lifetime. When the task is terminated, worker instance will be destroyed too.</p><p>Because of the same reason, there's no need to create the worker class in advance. From the demo 7_InitTest:</p><pre class="pas-source">task := OmniEventMonitor1.Monitor(CreateTask(TInitTest.Create(success), <span class="pas-str">'InitTest'</span>))<br> .SetPriority(tpIdle).Run; </pre><p>There's a catch, though. Because the CreateTask expects an interface, you must never pass to it a variable or field that is <strong>declared</strong> as a object. <u>This will not work:</u></p><pre class="pas-source"><span class="pas-kwd">procedure</span> TfrmTestTwoWayHello.actStartHelloExecute(Sender: TObject);<br/><span class="pas-kwd">var</span><br/> worker: TAsyncHello;<br/><span class="pas-kwd">begin</span><br/> worker := TAsyncHello.Create;<br/> FHelloTask :=<br/> OmniEventMonitor1.Monitor(CreateTask(worker, <span class="pas-str">'Hello'</span>)).<br/> SetTimer(<span class="pas-num">1000</span>, MSG_SEND_MESSAGE).<br/> SetParameter(<span class="pas-str">'Delay'</span>, <span class="pas-num">1000</span>).<br/> SetParameter(<span class="pas-str">'Message'</span>, <span class="pas-str">'Hello'</span>).<br/> Run;<br/><span class="pas-kwd">end</span>;</pre><p>If you need to store some information inside the worker during its execution and access it from the owner, you can use the Implementor property, which will "convert" IOmniTask interface back to the implementing object (which you must then cast to your worker class). From the demo 6_TwoWayHello_with_object_worker:</p><pre class="pas-source"><span class="pas-kwd">procedure</span> TfrmTestTwoWayHello.actStopHelloExecute(Sender: TObject);<br/><span class="pas-kwd">begin</span><br/> FHelloTask.Terminate;<br/> FHelloTask := <span class="pas-kwd">nil</span>;<br/> lbLog.ItemIndex := lbLog.Items.Add(Format(<span class="pas-str">'%d Hello World''s sent'</span>,<br/> [TAsyncHello(FWorker.Implementor).Count]));<br/><span class="pas-kwd">end</span>;</pre><p>BTW, don't just set task reference (FHelloTask in the example above) to <em>nil</em> when you want to terminate the task. You <strong>have</strong> to call Terminate or use some other way to notify the task that it must terminate.</p><p>BTW2, if you use a parameterless constructor inside the CreateTask, you have to put empty parenthesis after the .Create or Delphi would not compile your code. I'm not really sure, why. The following segment is from test 14_TerminateWhen:</p><pre class="pas-source">Log(Format(<span class="pas-str">'Task started: %d'</span>,<br/> [CreateTask(TMyWorker.Create())<br> .TerminateWhen(FTerminate).WithCounter(FCounter)<br> .MonitorWith(OmniTED).Run.UniqueID]));</pre><p>That completes today's tour.</p><p>In the introduction I mentioned the fourth CreateTask overload that will be introduced when Delphi 2009 is ready. OmniThreadLibrary will support anonymous methods so you'll be able to do something like that:</p><pre class="pas-source">CreateTask(<span class="pas-kwd">procedure</span> <span class="pas-kwd">begin</span> MessageBeep(MB_ICONEXCLAMATION); <span class="pas-kwd">end</span>);</pre><div class="blogger-post-footer"><font size=-2>---<br/>Published under the <a href="http://creativecommons.org/licenses/by/3.0/">Creative Commons Attribution 3.0</a> license</font><img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29331675-1979583711973020623?l=17slon.com%2Fblogs%2Fgabr%2Fblogger.html'/></div>gabrhttp://www.blogger.com/profile/06903558857617342477noreply@blogger.com6tag:blogger.com,1999:blog-29331675.post-29824210099727350562008-09-02T14:11:00.001+02:002008-09-02T14:11:49.319+02:00OmniThreadLibrary 1.0a released<p>This is basically a bugfix release. There was a problem with the .MsgWait decorator which I fixed in the repository three days ago. As I want to write about this function soon, I thought it would be nice to make a release first ...</p> <p>Other changes:</p> <ul> <li>TGp4AlignedInt from GpStuff.pas is supposedly D2006 compatible (says a reader) so it has been enabled on that plaform. As a result, it is now possible to compiled and use OTL in D2006 (said the same source).</li> <li>SpinLock.pas has been updated.</li> <li>Test 6 has been changed to show why one would want to use IOmniWorker.Implementor function. I'll write more about that very soon.</li> <li>I've included parts of the <a title="FastMM4" href="http://sourceforge.net/projects/fastmm/" target="_blank">FastMM4</a> package into the repository to simplify debugging. FastMM4 was created by Pierre le Riche and is not covered by the OmniThreadLibrary license. It is released under a dual licensed and you can use it either under the MPL 1.1 or LGPL 2.1. More details in the included readme file and on the FastMM4 home page.</li></ul> <div class="blogger-post-footer"><font size=-2>---<br/>Published under the <a href="http://creativecommons.org/licenses/by/3.0/">Creative Commons Attribution 3.0</a> license</font><img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29331675-2982421009972735056?l=17slon.com%2Fblogs%2Fgabr%2Fblogger.html'/></div>gabrhttp://www.blogger.com/profile/06903558857617342477noreply@blogger.com0tag:blogger.com,1999:blog-29331675.post-84070658645572527722008-09-01T08:23:00.002+02:002008-09-01T11:53:10.736+02:00Introduction to enumerators<p>The <a href="http://www.blaisepascal.eu/" target="_blank">Blaise Pascal magazine #3</a> is out. Lots of content there, including my Introduction to enumerators article (<a title="Introduction to enumerators, first page" href="http://www.blaisepascal.eu/blaisepascal_3/introduction_to_enumerators_primoz_gabrijelcic.php" target="_blank">first page</a>).</p><div class="blogger-post-footer"><font size=-2>---<br/>Published under the <a href="http://creativecommons.org/licenses/by/3.0/">Creative Commons Attribution 3.0</a> license</font><img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29331675-8407065864557252772?l=17slon.com%2Fblogs%2Fgabr%2Fblogger.html'/></div>gabrhttp://www.blogger.com/profile/06903558857617342477noreply@blogger.com0tag:blogger.com,1999:blog-29331675.post-66988315023809718472008-08-27T16:05:00.001+02:002008-08-27T16:05:16.516+02:00OmniThreadLibrary 1.0, day 1<p>First 24 hours totally exceeded my expectations.</p> <p><img style="border-right: 0px; border-top: 0px; border-left: 0px; border-bottom: 0px" height="96" alt="day 1 downloads" src="http://17slon.com/blogs/gabr/files/OmniThreadLibrary1.0day1_E22D/image.png" width="658" border="0"></p> <p>Now start using it! <img alt="Big Grin" src="http://us.i1.yimg.com/us.yimg.com/i/mesg/emoticons7/4.gif"></p> <div class="blogger-post-footer"><font size=-2>---<br/>Published under the <a href="http://creativecommons.org/licenses/by/3.0/">Creative Commons Attribution 3.0</a> license</font><img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29331675-6698831502380971847?l=17slon.com%2Fblogs%2Fgabr%2Fblogger.html'/></div>gabrhttp://www.blogger.com/profile/06903558857617342477noreply@blogger.com6