<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/atom10full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><feed xmlns="http://www.w3.org/2005/Atom" xmlns:openSearch="http://a9.com/-/spec/opensearchrss/1.0/" xmlns:georss="http://www.georss.org/georss" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0"><id>tag:blogger.com,1999:blog-10106032</id><updated>2009-11-11T07:56:14.700Z</updated><title type="text">Entropy Overload</title><subtitle type="html" /><link rel="http://schemas.google.com/g/2005#feed" type="application/atom+xml" href="http://blog.barrkel.com/feeds/posts/default" /><link rel="alternate" type="text/html" href="http://blog.barrkel.com/" /><link rel="hub" href="http://pubsubhubbub.appspot.com/" /><link rel="next" type="application/atom+xml" href="http://www.blogger.com/feeds/10106032/posts/default?start-index=26&amp;max-results=25" /><author><name>Barry Kelly</name><uri>http://www.blogger.com/profile/10559947643606684495</uri><email>barry.j.kelly@gmail.com</email></author><generator version="7.00" uri="http://www.blogger.com">Blogger</generator><openSearch:totalResults>81</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><link rel="self" href="http://feeds.feedburner.com/EntropyOverload" type="application/atom+xml" /><feedburner:browserFriendly>If I didn't put in a message here, you'd be getting a blank space and wondering. So there.</feedburner:browserFriendly><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com" /><entry><id>tag:blogger.com,1999:blog-10106032.post-5427911644529611211</id><published>2009-11-10T16:59:00.007Z</published><updated>2009-11-10T17:54:08.710Z</updated><title type="text">On the Difficulty of setting the Windows 7 Desktop Wallpaper</title><content type="html">&lt;p&gt;You need to be a software engineer to precisely and correctly set the Windows background in Windows 7.&lt;/p&gt;

&lt;p&gt;I have a multi-monitor setup, and I use a very particular method for laying out windows on my secondary screen: I have them cascaded, from the top-left corner of the screen towards the bottom right, such that a little of the bottom-left corner of every window is visible on-screen at all times, like this:&lt;/p&gt;

&lt;img src="http://3.bp.blogspot.com/_2GNT8vlZj48/Svme4EwbLWI/AAAAAAAAAIE/rmTTey5TQ0U/s1600/Cascade.png"&gt;

&lt;p&gt;The corners form a mechanism for switching between windows based on spatial layout. Certain windows, such as terminals, browsers, email apps, etc. have "natural" dimensions. Browsers generally like to be page-shaped; terminal windows are quite squat; documentation browsers are somewhere in between, with a treeview on the left and a content pane on the right. These natural shapes guide their spatial positioning. So, all my terminal windows end up cascaded one after the other on at the top left of the screen, followed by Firefox's downloads window, then my bug tracker client, then Thunderbird, then MSDN documentation, with Firefox at the bottom-right, filling the full height of the screen.&lt;/p&gt;

&lt;p&gt;Switching between applications based on their spatial layout turns out to usually be more efficient than almost any other scheme, particularly when one's hands aren't on the keyboard. Even alt-tab doesn't distinguish very clearly between different instances of e.g. a terminal app, when in fact each one may be logged in via ssh to different machines and thus it matters very much which one I select.&lt;/p&gt;

&lt;p&gt;This scheme works well enough, but the bottom-left corners can get a little ragged. To make aligning the corners easier, I have a special wallpaper which consists of a line carefully placed so that it shows up on the bottom-left corner of my second monitor. When the wallpaper mode is set to "tile", a wallpaper which is big enough to cover the entire desktop area will show up at the expected offset, and everything works out OK.&lt;/p&gt;

&lt;p&gt;This wallpaper, a 16-colour bitmap, is so small when compressed that I can embed it here uuencoded directly:&lt;/p&gt;

&lt;pre&gt;
begin 644 wallpaper2.bmp.bz2
M0EIH.3%!629365%3W2&amp;lt;`1PST[.0U0`!`0!`"`0!``!``0`!$``,(L`#9`0JC
M4#1HR::%&amp;amp;C(&amp;amp;C3(T"E4TAM30`&amp;amp;=?M"6=)(E^T$2WA+9$L*JDF-E$NG?'AI55
M0/&gt;6^L(CB*T(550`Q-.DJ".(B"O,*JH`8F(WU0$&amp;lt;@^P((\&gt;[801S_$;&amp;lt;:YG'
:LR**SC&amp;lt;GG'.8JJ@!M,13_%W)%.%"045/=)P`
`
end
&lt;/pre&gt;

&lt;p&gt;If you save that to a file with a '.uue' extension, many common archivers, such as WinZip or WinRar, will be able to open it up. Here's what the meat of it looks like, anyway:&lt;/p&gt;

&lt;img src="http://4.bp.blogspot.com/_2GNT8vlZj48/SvmjJbobg1I/AAAAAAAAAIM/2FL5egmOQAg/s1600/Cancade-Line.png"&gt;

&lt;p&gt;However, it turns out, if you try to set this background using the "Personalization" Control Panel applet in Windows 7, it does some helpful "conversions" on it: it appears to round-trip the damn bitmap through JPG, resulting in artefacts. I've enhanced a zoom of the line after Windows has butchered it:&lt;/p&gt;

&lt;img src="http://3.bp.blogspot.com/_2GNT8vlZj48/SvmklETDGBI/AAAAAAAAAIU/eNCv9qHnqtI/s1600/Crap-Highlight.png"&gt;

&lt;p&gt;The line looks unclean, and in particular, it ends up with wave of intensity throughout its length, which is quite distracting and not the desired effect.&lt;/p&gt;

&lt;p&gt;Things can get worse though; if you try to use Windows Photo Viewer to set the background using the original bitmap, it will actually resize the thing down to fit the dimensions of your primary monitor (!!!), all but zooming the line out of existence. This drove me up the wall for about 20 minutes, as I tried to figure out where my line had gone when I first transitioned from XP to Windows 7.&lt;/p&gt;

&lt;p&gt;The easiest way I currently know to correctly set the Windows 7 desktop wallpaper is to write a program that uses the &lt;a href="http://msdn.microsoft.com/en-us/library/ms724947%28VS.85%29.aspx"&gt;SystemParametersInfo function&lt;/a&gt; directly, to ensure the bits get through unmolested.&lt;/p&gt;

&lt;p&gt;I have a lot more Windows 7 annoyances where this came from, but this one cost me quite a bit of time, so it sticks out. My verdict on Windows 7 is pretty mixed. Many applications, particularly games, are incompatible, and overall I dislike the shell compared to XP, especially Windows Explorer and the Vista-esque Start Menu. About the best things I can say in favour of the move is having less concern about future compatibility, and having a 64-bit mode that mostly works, despite all the horrific hacks going on beneath the covers to make it happen. Oh, and I'm running with UAC disabled. I can only take so many access denied errors as I poke around the system before I crack - and command-line apps generally just give you the error, rather than popping up an elevation prompt.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10106032-5427911644529611211?l=blog.barrkel.com'/&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/EntropyOverload?a=Dy3nQ_2lULM:ok6quYugMtw:cGdyc7Q-1BI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/EntropyOverload?d=cGdyc7Q-1BI" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/EntropyOverload?a=Dy3nQ_2lULM:ok6quYugMtw:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/EntropyOverload?i=Dy3nQ_2lULM:ok6quYugMtw:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/EntropyOverload?a=Dy3nQ_2lULM:ok6quYugMtw:UT3xtbGYFzA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/EntropyOverload?d=UT3xtbGYFzA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/EntropyOverload?a=Dy3nQ_2lULM:ok6quYugMtw:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/EntropyOverload?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/EntropyOverload/~4/Dy3nQ_2lULM" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.barrkel.com/feeds/5427911644529611211/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=10106032&amp;postID=5427911644529611211" title="2 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/10106032/posts/default/5427911644529611211" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/10106032/posts/default/5427911644529611211" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/EntropyOverload/~3/Dy3nQ_2lULM/on-difficulty-of-setting-windows-7.html" title="On the Difficulty of setting the Windows 7 Desktop Wallpaper" /><author><name>Barry Kelly</name><uri>http://www.blogger.com/profile/10559947643606684495</uri><email>barry.j.kelly@gmail.com</email><gd:extendedProperty xmlns:gd="http://schemas.google.com/g/2005" name="OpenSocialUserId" value="08331762029805209075" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://3.bp.blogspot.com/_2GNT8vlZj48/Svme4EwbLWI/AAAAAAAAAIE/rmTTey5TQ0U/s72-c/Cascade.png" height="72" width="72" /><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">2</thr:total><feedburner:origLink>http://blog.barrkel.com/2009/11/on-difficulty-of-setting-windows-7.html</feedburner:origLink></entry><entry><id>tag:blogger.com,1999:blog-10106032.post-2005543594902778022</id><published>2009-10-24T23:27:00.002+01:00</published><updated>2009-10-25T00:28:05.662+01:00</updated><title type="text">Bad week</title><content type="html">&lt;p&gt;It's been a bad week. My main hard drive crashed last Monday or so, while today my scooter is gone, presumed stolen. On the bright side, the final component of my new PC arrived and I was able to get Windows 7 up and running.&lt;/p&gt;

&lt;p&gt;The new machine has an i7 920, so things like %NUMBER_OF_PROCESSORS% returns 8 - it's a quad core with 2x hyperthreading. Having so many logical cores has some downsides: maxed-out single-threaded processes only take up 12.5% CPU, so it can look like the machine is not particularly busy even though a process is working as hard as it can. Other specs include 12GB RAM, AMD 5870 graphics, 80GB Intel SSD and 1TB Samsung F3.&lt;/p&gt;

&lt;p&gt;Maximizing the value of the small boot drive has been interesting. Windows 7 all by itself takes up a good 18G of space, what with the side-by-side (SxS) feature keeping lots of versions of the same APIs, and a big hiberfil.sys in the root - I moved the page file to the big drive. Because of the acute space constraints, I've been strongly motivated to keep almost all the junk that apps usually dump into the file system off the boot drive.  By and large, I achieve that by creating directories on the 1TB drive and pointing to them with junctions on the boot drive. That way, apps think they are installing onto the C drive but they don't steal too much space. My prior drive - the one that died - was a 10K WD Raptor, so I've had some experience with this, and I wrote a script to automate the process:&lt;/p&gt;

&lt;pre&gt;
#!/bin/bash

. _die

function usage
{
    echo "usage: $(basename $0) &amp;lt;directory&gt;..."
    echo "Replace directories with junctions pointing to /bulk, after moving contents."
    exit 1
}

test -n "$1" || usage

for f in "$@"; do
    
    test -d "$f" || die "'$f' isn't a directory"
    
    dest="$(abs-path "$f")"
    dest=/bulk/mathom/"${dest//\//_}"
    
    test -d "$dest" &amp;&amp; die "'$dest' already exists"
    
    # Try to copy stuff over; if an error, delete target and give up.
    cp -r "$f" "$dest" || {
        rm -rf "$dest"
        die "couldn't copy '$f' to '$dest'"
    }
    
    # Rename source, in case something can't be deleted.
    aside="$(move-aside "$f")" || die "failed in move-aside"
    
    # Create junction.
    junction "$dest" "$f" || die "failed to create junction"
    
    # Remove the aside.
    rm -rf "$aside" || {
        echo "warning: couldn't delete '$aside'"
    }
done
&lt;/pre&gt;

&lt;p&gt;This script uses a number of little wrapper utilities I wrote - at this point I really ought to be thinking of putting them in an online repository. Here's a summary so you can follow along:

&lt;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;b&gt;abs-path:&lt;/b&gt; Canonicalizes and makes an absolute path by using cygpath twice, once to Windows format and once again back to Unix format, and prepending $PWD if necessary.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;_die:&lt;/b&gt; Defines a &lt;b&gt;die&lt;/b&gt; function which prints out appropriate error and exits.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;move-aside:&lt;/b&gt; Renames a file or directory so that a failed attempt at creating a junction can be rolled back. Also helps with locked files - generally these can be copied and renamed, but not deleted. The new name is printed out on standard output.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;junction:&lt;/b&gt; This wraps &lt;a href="http://technet.microsoft.com/en-us/sysinternals/bb896768.aspx"&gt;SysInternals junction&lt;/a&gt; to create a junction in the TARGET LINK_NAME argument order familiar from &lt;b&gt;ln&lt;/b&gt;, and permits using Cygwin paths.&lt;/li&gt;
&lt;/ul&gt;
&lt;/p&gt;

&lt;p&gt;With this script, and the big drive mounted at /bulk with a directory called &lt;b&gt;mathom&lt;/b&gt; in its root, any given directory tree can be moved across readily enough. However, things in Windows 7 x64 are slightly more complicated.&lt;/p&gt;

&lt;p&gt;Some of the directories I moved over to /bulk/mathom included Program Files, Program Files (x86), and ProgramData. Windows 7 itself has some junctions set up (the rats nest here is a proper horrorshow, I should document it at some point just for my own use, if nothing else). For example, "ProgramData\Application Data" points at ProgramData itself, and a simple cmd /c dir will fall into the infinite recursion and print out errors when the file path gets too long. So, in moving these directories across, I had to examine them for existing junctions and replace these as necessary. The Cygwin tools of Unix heritage are more robust against cycles in the file system hierarchy - for example, &lt;b&gt;find&lt;/b&gt; keeps track of inodes to avoid infinite recursion.&lt;/p&gt;

&lt;p&gt;All this checking of existing junctions wasn't sufficient to get everything working nicely, though. The permissions generally also need tweaking, particularly for folders that get modified by installers. These generally perform operations using either the NT SERVICE\TrustedInstaller or NT AUTHORITY\SYSTEM accounts - and sometimes both, in a dance of setup.exe and msiexec.exe. So, by process of checking, trial and error, using &lt;a href="http://technet.microsoft.com/en-us/sysinternals/bb896645.aspx"&gt;SysInternals Process Monitor&lt;/a&gt; to track installation failures, I put together a new script to ensure Program Files, ProgramData and folders of their ilk have the right set of permissions:&lt;/p&gt;

&lt;pre&gt;
#!/bin/bash

function usage
{
    echo "usage: $(basename $0) &amp;lt;file/directory&gt;..."
    echo "Resets files and directories to Windows 7 general install permissions."
    echo "Essentialy this is: SYSTEM/TrustedInstaller/Admin: full; AuthUsers: Modify; Users: Read"
    exit 1
}

test -e "$1" || usage

for arg; do
    
    # First, take ownership so we can definitely set all ACLs.
    # /F &amp;lt;file&gt;; /R - recurse; /D &amp;lt;Y/N&gt; - take ownership if no directory listing
    takeown /F "$(cygpath -w "$arg")" /R /D Y &gt; /dev/null
    
    # Reset ACLs recursively to inherit from parent
    icacls "$(cygpath -w "$arg")" /reset /t /c /q &gt; /dev/null
    
    # Set appropriate ACL on root, to progate recursively
    icacls "$(cygpath -w "$arg")" \
        /grant "Administrators:(OI)(CI)F" \
        /grant "SYSTEM:(OI)(CI)F" \
        /grant 'NT SERVICE\TrustedInstaller:(OI)(CI)F' \
        /grant 'Authenticated Users:(OI)(CI)M' \
        /grant 'Users:(OI)(CI)RX' \
        /inheritance:r /c /q &gt; /dev/null
    
done
&lt;/pre&gt;

&lt;p&gt;The logic is applied in three phases:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Take ownership, with takeown&lt;/li&gt;
&lt;li&gt;Reset permissions recursively so that they inherit from parent&lt;/li&gt;
&lt;li&gt;Set ACL on the root, so children will inherit from it, but make sure the root itself doesn't inherit permissions.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I tried for a while to get it all into one icacls invocation, but I couldn't get the two effects working together: enabling inheritance in the children, but disabling it in the root. This is something you can do in one "Apply" step using the Security tab in Explorer, though it's quite tedious picking out all the users and setting the appropriate checkboxes.&lt;/p&gt;

&lt;p&gt;As an aside, the Security tab is quite confused by junctions; when permissions have been inherited from a directory on a different drive, because the current file / directory is in a subtree pointed to by a junction, the Security tab will search in vain through the parents looking for what provided the given inherited permission. Sometimes it gets lucky, and finds a random parent that coincidentally has the same permission; otherwise, it bails out with "Parent Object" as the indicated "Inherited From" column. But my proper list of Windows 7 issues will have to wait until I really feel the need to rant, which will no doubt happen sooner or later.&lt;/p&gt;

&lt;p&gt;I should add that all the drastic file system surgery I write about here was performed with UAC effectively out of the picture, and all Cygwin tools running with the administrative privilege bits set. It's been my experience that command-line apps don't pop up the required UAC prompt; instead, they fail with an access denied message, which is rather vexing when you're trying to automate things.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10106032-2005543594902778022?l=blog.barrkel.com'/&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/EntropyOverload?a=xa6to5RV62s:5K8SJQ_LV3E:cGdyc7Q-1BI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/EntropyOverload?d=cGdyc7Q-1BI" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/EntropyOverload?a=xa6to5RV62s:5K8SJQ_LV3E:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/EntropyOverload?i=xa6to5RV62s:5K8SJQ_LV3E:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/EntropyOverload?a=xa6to5RV62s:5K8SJQ_LV3E:UT3xtbGYFzA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/EntropyOverload?d=UT3xtbGYFzA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/EntropyOverload?a=xa6to5RV62s:5K8SJQ_LV3E:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/EntropyOverload?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/EntropyOverload/~4/xa6to5RV62s" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.barrkel.com/feeds/2005543594902778022/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=10106032&amp;postID=2005543594902778022" title="1 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/10106032/posts/default/2005543594902778022" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/10106032/posts/default/2005543594902778022" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/EntropyOverload/~3/xa6to5RV62s/bad-week.html" title="Bad week" /><author><name>Barry Kelly</name><uri>http://www.blogger.com/profile/10559947643606684495</uri><email>barry.j.kelly@gmail.com</email><gd:extendedProperty xmlns:gd="http://schemas.google.com/g/2005" name="OpenSocialUserId" value="08331762029805209075" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">1</thr:total><feedburner:origLink>http://blog.barrkel.com/2009/10/bad-week.html</feedburner:origLink></entry><entry><id>tag:blogger.com,1999:blog-10106032.post-1355993718739294075</id><published>2009-07-15T13:15:00.002+01:00</published><updated>2009-07-15T13:21:20.305+01:00</updated><title type="text">Public interest rule of thumb</title><content type="html">&lt;p&gt;&lt;a href="http://www.techcrunch.com/2009/07/15/our-reaction-to-your-reactions-on-the-twitter-confidential-documents-post/"&gt;TechCrunch&lt;/a&gt; is having a crisis of conscience over what they'll do with internal Twitter documents they've received.&lt;/p&gt;

&lt;p&gt;My rule of thumb is pretty simple. The documents are stolen. Unless the documents reveal wrongdoing greater than stealing, such that the public interest served in publishing is greater than the private harm, then it's unethical to publish. Idle musings about "they are going to be published somewhere on the Internet" is specious; the morality of one's actions don't depend on whether someone else is "doing it", much less everyone.&lt;/p&gt;

&lt;p&gt;If TechCrunch goes ahead and publishes these documents, it'll only further cement my already pretty low opinion of Arrington. (I'm not a habitual reader of TechCrunch, or otherwise I'd waste away over all the food I'd be vomiting up.)&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10106032-1355993718739294075?l=blog.barrkel.com'/&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/EntropyOverload?a=5ao-mnMsk4I:EyBYAe5ATxs:cGdyc7Q-1BI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/EntropyOverload?d=cGdyc7Q-1BI" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/EntropyOverload?a=5ao-mnMsk4I:EyBYAe5ATxs:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/EntropyOverload?i=5ao-mnMsk4I:EyBYAe5ATxs:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/EntropyOverload?a=5ao-mnMsk4I:EyBYAe5ATxs:UT3xtbGYFzA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/EntropyOverload?d=UT3xtbGYFzA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/EntropyOverload?a=5ao-mnMsk4I:EyBYAe5ATxs:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/EntropyOverload?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/EntropyOverload/~4/5ao-mnMsk4I" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.barrkel.com/feeds/1355993718739294075/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=10106032&amp;postID=1355993718739294075" title="6 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/10106032/posts/default/1355993718739294075" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/10106032/posts/default/1355993718739294075" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/EntropyOverload/~3/5ao-mnMsk4I/public-interest-rule-of-thumb.html" title="Public interest rule of thumb" /><author><name>Barry Kelly</name><uri>http://www.blogger.com/profile/10559947643606684495</uri><email>barry.j.kelly@gmail.com</email><gd:extendedProperty xmlns:gd="http://schemas.google.com/g/2005" name="OpenSocialUserId" value="08331762029805209075" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">6</thr:total><feedburner:origLink>http://blog.barrkel.com/2009/07/public-interest-rule-of-thumb.html</feedburner:origLink></entry><entry><id>tag:blogger.com,1999:blog-10106032.post-6399463208554311751</id><published>2009-07-14T19:45:00.002+01:00</published><updated>2009-07-14T19:55:00.552+01:00</updated><title type="text">StackOverflow and religious language in programming</title><content type="html">&lt;p&gt;
I was reading &lt;a href="http://unhandled-exceptions.com/blog/index.php/2009/07/13/why-i-dont-listen-to-the-stackoverflow-podcast-any-more/"&gt;Why I Don’t Listen to the StackOverflow Podcast any More&lt;/a&gt;, and in reading the comments, I found it interesting to see the amount of religious language.
&lt;/p&gt;

&lt;blockquote&gt;
#21 (sbohlen) I’d have to agree that they do indeed appear successful, but success / failure isn’t a boolean [...] 
The world is full of people who succeed due to all kinds of factors including dumb-luck, [...]
&lt;/blockquote&gt;

&lt;p&gt;
This reminds me intensely of "the race is not always to the swift" etc. &lt;a href="http://www.biblegateway.com/passage/?search=Ecclesiastes%209:11&amp;version=9;"&gt;Eccl 9:11&lt;/a&gt;.
&lt;/p&gt;

&lt;p&gt;I changed adopters to believers to accentuate the effect:

&lt;blockquote&gt; "#16: [...] I have also experienced that the non-[believers] aren’t (gnerally) non-[believers] out of choice but are instead non-[believers] largely out of ignorance and inexperience — they just don’t know what they don’t know [...]"
&lt;/blockquote&gt;

&lt;blockquote&gt;
#16: "the [types] who aren’t interested in self-improvement; there’s not a damned thing I can suggest to help these people — I cannot MAKE someone want to better themselves, that’s gotta come from within"
&lt;/blockquote&gt;

&lt;blockquote&gt;
#16: "I [...] think that Jeff and Joel are indeed preaching [...] they are (IMO) abdicating the unofficial professional responsibility that comes with having a pulpit from which to preach."
&lt;/blockquote&gt;

&lt;blockquote&gt;
#14: "I am a strong believer"
&lt;/blockquote&gt;

&lt;blockquote&gt;
#13: "@sbohlen. I fully agree with the sentiment that “up and running” is not a valid metric"
&lt;/blockquote&gt;

&lt;p&gt;
I'm not going to go on with this, though I could - the &lt;a href="http://unhandled-exceptions.com/blog/index.php/about/"&gt;about page for the blog&lt;/a&gt; is particularly ripe for further examples of religious language - but I do want to make a further point.
&lt;/p&gt;

&lt;p&gt;
Could it be that latent religious feelings are responsible for a good portion of the minor angst on display in these comments? Could it be that people are unconsciously annoyed, not with StackOverflow's success, but rather its success even in the face of quite mild contempt at the unit-testing / SOLID religion?
&lt;/p&gt;

&lt;p&gt;
My own position: I'm a "believer" in evolution and the market. Neither evolution nor the market have any moral content (they can't say what "ought" to be), and both only measure success by effects rather than processes. In a phrase, &lt;b&gt;"up and running" is not just a valid metric - it's the only metric, until it stops running&lt;/b&gt;. If SO continues to thrive, and doesn't die under the weight of its lack of unit testing (which seems to be the logical end result of the beliefs of the believers in the comments), where then is the truth in the proclamations of doom from the prophets of the true religion?
&lt;/p&gt;

&lt;p&gt;
I don't think unit-testing is the only way to avoid long-term bit-rot. I think it's a good way, but not the only way; moreover, I don't think unit testing is especially useful in the very early stages of a project, where the design changes often and rewriting unit tests becomes a disincentive to larger refactoring and redesign. I think every one of the concepts embodied in S.O.L.I.D. is debatable in certain scenarios (except perhaps LSP).
&lt;/p&gt;

&lt;p&gt;
In my experience, premature abstraction has been responsible for a similar number of ills to premature optimization: abstractions chosen at the wrong boundaries, for the wrong reasons, because designers thought they could foretell the future and anticipate where changes would come. Thus I am wary of merchants of abstractions and patterns, principles and practices. I'll use them when appropriate, but never with religious zeal.
&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10106032-6399463208554311751?l=blog.barrkel.com'/&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/EntropyOverload?a=OFtqMbjwldU:WI27Od5wWag:cGdyc7Q-1BI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/EntropyOverload?d=cGdyc7Q-1BI" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/EntropyOverload?a=OFtqMbjwldU:WI27Od5wWag:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/EntropyOverload?i=OFtqMbjwldU:WI27Od5wWag:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/EntropyOverload?a=OFtqMbjwldU:WI27Od5wWag:UT3xtbGYFzA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/EntropyOverload?d=UT3xtbGYFzA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/EntropyOverload?a=OFtqMbjwldU:WI27Od5wWag:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/EntropyOverload?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/EntropyOverload/~4/OFtqMbjwldU" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.barrkel.com/feeds/6399463208554311751/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=10106032&amp;postID=6399463208554311751" title="2 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/10106032/posts/default/6399463208554311751" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/10106032/posts/default/6399463208554311751" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/EntropyOverload/~3/OFtqMbjwldU/stackoverflow-and-religious-language-in.html" title="StackOverflow and religious language in programming" /><author><name>Barry Kelly</name><uri>http://www.blogger.com/profile/10559947643606684495</uri><email>barry.j.kelly@gmail.com</email><gd:extendedProperty xmlns:gd="http://schemas.google.com/g/2005" name="OpenSocialUserId" value="08331762029805209075" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">2</thr:total><feedburner:origLink>http://blog.barrkel.com/2009/07/stackoverflow-and-religious-language-in.html</feedburner:origLink></entry><entry><id>tag:blogger.com,1999:blog-10106032.post-6935923233161606214</id><published>2009-05-22T13:03:00.008+01:00</published><updated>2009-05-22T13:45:36.847+01:00</updated><title type="text">WPF / VS2010 Font rendering: blurryville</title><content type="html">&lt;p&gt;I downloaded and installed &lt;a href="http://msdn.microsoft.com/en-us/netframework/dd582936.aspx"&gt;VS2010 Beta 1&lt;/a&gt; to see how it's looking. It's not pleasant though - it makes me feel like I'm looking at the screen through a layer of coke-bottle plastic:&lt;/p&gt;

&lt;img src="http://2.bp.blogspot.com/_2GNT8vlZj48/ShaVILCB1gI/AAAAAAAAAFI/qOSq8gTP_RA/s1600/new-font.png"&gt;

&lt;p&gt;Compared to the old rendering, where my preferred fixed-width code / terminal font, &lt;a href="http://www.donationcoder.com/Software/Jibz/Dina/index.html"&gt;Dina&lt;/a&gt;, is correctly chosen, as opposed to falling back to what looks like Courier New:&lt;/p&gt;

&lt;img src="http://3.bp.blogspot.com/_2GNT8vlZj48/ShaVf-PrdQI/AAAAAAAAAFQ/q1lcH2PFj_4/s1600/old-font.png"&gt;

&lt;p&gt;The new rendering hurts my eyes (they keep trying to focus more, but it doesn't help!) and makes VS2010 Beta unusable, as far as I'm concerned.&lt;/p&gt;

&lt;p&gt;The &lt;a href="http://windowsclient.net/wpf/white-papers/wpftextclarity.aspx"&gt;"recommended solution"&lt;/a&gt; for text clarity issues in WPF is "use the largest font size possible". That's a bit of a cop-out for the working programmer, where displaying as much code on-screen as is comfortably legible is usually the best option.&lt;/p&gt;

&lt;p&gt;FWIW, I registered a &lt;a href="https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=456163"&gt;feedback item&lt;/a&gt; on this.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10106032-6935923233161606214?l=blog.barrkel.com'/&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/EntropyOverload?a=W6Qc8d5VBBk:KUgxbql9Bkw:cGdyc7Q-1BI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/EntropyOverload?d=cGdyc7Q-1BI" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/EntropyOverload?a=W6Qc8d5VBBk:KUgxbql9Bkw:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/EntropyOverload?i=W6Qc8d5VBBk:KUgxbql9Bkw:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/EntropyOverload?a=W6Qc8d5VBBk:KUgxbql9Bkw:UT3xtbGYFzA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/EntropyOverload?d=UT3xtbGYFzA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/EntropyOverload?a=W6Qc8d5VBBk:KUgxbql9Bkw:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/EntropyOverload?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/EntropyOverload/~4/W6Qc8d5VBBk" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.barrkel.com/feeds/6935923233161606214/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=10106032&amp;postID=6935923233161606214" title="10 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/10106032/posts/default/6935923233161606214" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/10106032/posts/default/6935923233161606214" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/EntropyOverload/~3/W6Qc8d5VBBk/wpf-vs2010-font-rendering-blurryville.html" title="WPF / VS2010 Font rendering: blurryville" /><author><name>Barry Kelly</name><uri>http://www.blogger.com/profile/10559947643606684495</uri><email>barry.j.kelly@gmail.com</email><gd:extendedProperty xmlns:gd="http://schemas.google.com/g/2005" name="OpenSocialUserId" value="08331762029805209075" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://2.bp.blogspot.com/_2GNT8vlZj48/ShaVILCB1gI/AAAAAAAAAFI/qOSq8gTP_RA/s72-c/new-font.png" height="72" width="72" /><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">10</thr:total><feedburner:origLink>http://blog.barrkel.com/2009/05/wpf-vs2010-font-rendering-blurryville.html</feedburner:origLink></entry><entry><id>tag:blogger.com,1999:blog-10106032.post-7083222075048158169</id><published>2009-04-28T13:47:00.006+01:00</published><updated>2009-04-28T14:49:29.604+01:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="printing" /><category scheme="http://www.blogger.com/atom/ns#" term="solaris" /><title type="text">Sharing a printer, the hard way: writing your own spooler</title><content type="html">&lt;p&gt;I have an ultra-cheap HP Color Laserjet 1600 printer, the kind that costs less than a full set of replacement toner cartridges, and the ones that are shipped in the box are only 20% full. From the driver publisher info it appears the I/O protocol logic of the printer was provided by a company called &lt;a href="http://www.zeno.com"&gt;Zenographics&lt;/a&gt;, which appears to now be either a part of or owned by Marvell. It works well enough when you have it directly connected to a machine via the USB cable, but sharing it is a nightmare.&lt;/p&gt;

&lt;p&gt;In particular, the drivers are very unstable when targeting shared printers. Especially problematic is printing from Vista to XP, or XP to XP, or XP to Vista. With some combinations, the print spooler crashes; with others, it simply hangs, and the &lt;b&gt;&lt;code&gt;spooler&lt;/code&gt;&lt;/b&gt; service needs to be restarted to get the document printing at all. The &lt;a href="http://forums11.itrc.hp.com/service/forums/questionanswer.do?threadId=1256503"&gt;folks on the HP forums&lt;/a&gt; seem to imply (a) that this is by design, and (b) sharing the printer effectively is impossible. I find this hard to swallow; since the printer works when directly connected, there is no valid reason why printing across the network should not also work, since the physical printer doesn't come into the equation. Bits are bits; if the driver can't consistently send some bits across the network, either there's some atrocious QA going on inside Zenographics, or worse, malice.&lt;/p&gt;

&lt;p&gt;I had despaired of ever getting it working nicely as a small workgroup printer, but I saw a faint glimmer of hope when setting up my &lt;a href="http://barrkel.blogspot.com/2009/03/zfssolaris-as-nas.html"&gt;home NAS&lt;/a&gt;, based on Solaris. &lt;a href="http://www.cups.org/"&gt;CUPS&lt;/a&gt; is available for Solaris, and there's a &lt;a href="http://www.linuxfoundation.org/en/OpenPrinting/Database/Foomatic"&gt;Foomatic&lt;/a&gt; printer &lt;a href="http://foo2hp.rkkda.com/"&gt;driver&lt;/a&gt; available for this specific model as well.&lt;/p&gt;

&lt;p&gt;It turns out that I couldn't get CUPS configured with this printer and driver setup. CUPS uses a web page interface for configuration, but it was desperately slow, and every time I tried to print a test page, it informed me that the printer had just gone offline. I could see all the background processes it had started up to print, and the printer device at /dev/printers/0 was indeed opened for writing, but it never got anywhere.&lt;/p&gt;

&lt;p&gt;But that didn't matter. The foo2hp driver provided the only real tools I needed: a PostScript to ZjStream (the printer protocol) filter, which simply reads the raw PostScript and outputs the raw print data. Windows ships with a generic PostScript driver; I use the PScript5-based "HP Color Laserjet PS". A simple shell pipeline from source to printer device would do.&lt;/p&gt;

&lt;p&gt;To share the printer, I needed some kind of queue for buffering. I decided on a simple filesystem-based approach, which seems to me to be in the Unix tradition. I created a share on my Solaris box specifically for files to be printed. Windows clients can use the aforementioned generic printer driver with a FILE: target, and place the output in this shared directory. The spooler script I wrote polls this directory periodically, and attempts to print whatever it finds.&lt;/p&gt;

&lt;p&gt;The printing logic is pretty simple, at its core:&lt;/p&gt;

&lt;pre&gt;
foo2hp2600-wrapper -p 9 input.ps &gt; /dev/printers/0
&lt;/pre&gt;

&lt;p&gt;The actual code is a mite more sophisticated, of course; it does logging, moves documents to be printed into a directory categorized by date and time, etc. And in future it can be extended to other document types than PS, e.g. printing PDFs or photos simply by copying them to a directory.&lt;/p&gt;

&lt;p&gt;Another advantage of this approach is that I can shell out to &lt;code&gt;ps2pdf&lt;/code&gt; to create an archival version of the printed document. The PDF is usually far smaller than the PS and is viewable from Windows without installing a PostScript viewer. This archival version is useful if I ever need to print it again. Consider online flight checkins, for example - some websites (such as &lt;a href="http://easyjet.com"&gt;EasyJet&lt;/a&gt;) prevent printing the boarding pass more than three times, which is a little inconvenient if you're having printer trouble.&lt;/p&gt;

&lt;p&gt;On the downside, printer options that can't be expressed directly in the PostScript with the generic driver can't be configured. For example, the way I have things configured I'm limited to printing monochrome on A4 by default. In practice this isn't really a problem, since the vast majority of print jobs fit this template; I could create other spooler directories for different defaults.&lt;/p&gt;

&lt;p&gt;I expect that with enough research, I'll be able to get CUPS configured correctly, or to get Samba to look like a PostScript printer that dumps the raw network data into my spooler directory. However, I'm pretty happy with my hack for now...&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10106032-7083222075048158169?l=blog.barrkel.com'/&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/EntropyOverload?a=7ZB0CX4B53I:vI2nxUzJ88w:cGdyc7Q-1BI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/EntropyOverload?d=cGdyc7Q-1BI" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/EntropyOverload?a=7ZB0CX4B53I:vI2nxUzJ88w:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/EntropyOverload?i=7ZB0CX4B53I:vI2nxUzJ88w:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/EntropyOverload?a=7ZB0CX4B53I:vI2nxUzJ88w:UT3xtbGYFzA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/EntropyOverload?d=UT3xtbGYFzA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/EntropyOverload?a=7ZB0CX4B53I:vI2nxUzJ88w:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/EntropyOverload?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/EntropyOverload/~4/7ZB0CX4B53I" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.barrkel.com/feeds/7083222075048158169/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=10106032&amp;postID=7083222075048158169" title="2 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/10106032/posts/default/7083222075048158169" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/10106032/posts/default/7083222075048158169" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/EntropyOverload/~3/7ZB0CX4B53I/sharing-printer-hard-way-writing-your.html" title="Sharing a printer, the hard way: writing your own spooler" /><author><name>Barry Kelly</name><uri>http://www.blogger.com/profile/10559947643606684495</uri><email>barry.j.kelly@gmail.com</email><gd:extendedProperty xmlns:gd="http://schemas.google.com/g/2005" name="OpenSocialUserId" value="08331762029805209075" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">2</thr:total><feedburner:origLink>http://blog.barrkel.com/2009/04/sharing-printer-hard-way-writing-your.html</feedburner:origLink></entry><entry><id>tag:blogger.com,1999:blog-10106032.post-1597894966115326829</id><published>2009-03-22T03:24:00.004Z</published><updated>2009-03-22T04:53:20.734Z</updated><title type="text">ZFS/Solaris as a NAS</title><content type="html">&lt;p&gt;I've finally got a semblance of a Solaris system up and running with a nice fat ZFS storage pool. It was neither trivial nor pleasant, however.&lt;/p&gt;

&lt;p&gt;Here's the executive summary of what I learned:
&lt;ul&gt;
&lt;li&gt;Don't use OpenSolaris 2008.11; use at least NexentaCore 2 instead; others may work.
&lt;li&gt;Don't use an Intel or Realtek network adapter; if you must, use Realtek with an older version of the gani driver.
&lt;li&gt;Don't use Solaris's built-in &lt;a href="http://en.wikipedia.org/wiki/Server_Message_Block"&gt;CIFS&lt;/a&gt; implementation, smb/server, aka hereafter "cifs" (to contrast with "samba").
&lt;li&gt;Don't let ZFS to default to 128KB max block size unless all your files are big (greater than about 2MB) or tiny (less than about 64KB).
&lt;/ul&gt;
&lt;/p&gt;

&lt;p&gt;&lt;a href="http://opensolaris.org/os/downloads/"&gt;OpenSolaris 2008.11&lt;/a&gt;, the version I tried, is not production ready unless you have extremely specific hardware and quite narrow requirements. I finally ended up using &lt;a href="http://www.nexenta.org/os/Download"&gt;NexentaCore Unstable v2.0 Beta 2&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The prime requirements for a working NAS are:

&lt;/p&gt;&lt;ol&gt;
&lt;li&gt;Lots of attachment points for disks&lt;/li&gt;
&lt;li&gt;Good physical network connectivity&lt;/li&gt;
&lt;li&gt;Good network protocol implementation&lt;/li&gt;
&lt;li&gt;Good filesystem implementation&lt;/li&gt;
&lt;/ol&gt;

There is no one of these that I didn't have issues with.
&lt;p&gt;&lt;/p&gt;

&lt;p&gt;The first one, attachment points, was a nice-to-know gotcha that it took me a while to figure out. My motherboard (an old Abit AW9D-MAX) has 7 internal SATA attachment points, as well as 1 PATA channel for two extra PATA devices. This being insufficient, I procured an extra PCIe two-port SATA adapter. I was up and running fine with 5 disks attached (1 boot drive and 4 drives in a raidz ZFS pool - effectively &lt;a href="http://en.wikipedia.org/wiki/RAID_5"&gt;RAID 5&lt;/a&gt; without the need for &lt;a href="http://en.wikipedia.org/wiki/NVRAM"&gt;NVRAM&lt;/a&gt;), and in preparation for creating my second raidz vdev (virtual device in ZFS lingo), I started attaching more drives, one by one so that I could label them (and thereby know which one failed when it fails - which it will, eventually). When I had 7 drives attached, the OS occasionally failed to boot, but would reliably hang upon execution of the 'format' command (the handiest way of figuring out the drive device name under Solaris).&lt;/p&gt;

&lt;p&gt;The gotcha, however, is that the BIOS defaults to IDE emulation mode, which, for this particular BIOS, supports a maximum of 6 devices. It packs the first 2 hard drives into pretend-PATA (complete with "master" and "slave") even if they aren't actually PATA, and then labels the next 4 drives as SATA 1 through 4. I had to change the BIOS's mode to AHCI to get it to support more drives. Luckily, my boot drive is an old PATA one, so it didn't need compatibility to stay in the same place (master on the first PATA channel).&lt;/p&gt;

&lt;p&gt;Physical network connectivity was a far, far harder task, and one that still isn't quite complete. My motherboard has two physical 1Gb Ethernet adapters built in. Unfortunately, OpenSolaris defaults to a driver it calls &lt;b&gt;rge&lt;/b&gt; for the chipset (RTL8111/8168B PCIe), but this driver has big issues, that I suspect are related somehow to duplex operations. Big transfers using big buffers using cifs (the Windows-compatible protocol Solaris ships with) will work with reasonable performance (30MB/sec or so), but streaming video (small reads that expect low latency) performs abysmally. Even worse, at non-deterministically random times (as far as I could ascertain), the rge driver would fall into a mode where transfers were extremely slow, as in less than 1MB/sec slow. I took to keeping a ssh session open to the Solaris machine and simply echoing '.' back and forth (echo '.' loop piped to ssh cat -), and mysteriously enough, so long as there was small back and forth traffic, transfer performance rocketed back up again.&lt;/p&gt;

&lt;p&gt;This being unsatisfactory, I tried to use the &lt;a href="http://homepage2.nifty.com/mrym3/taiyodo/eng/"&gt;gani driver&lt;/a&gt;. I never could get gani-2.6.3 to work with my chipset, however. I didn't know if it was a problem with the driver, a conflict with my chipset, or my poor configuration skills with Solaris. All I know is that I tried every technique I could find, up to and including patching driver_aliases and running sys-unconfig to start things off from a fresh basis.&lt;/p&gt;

&lt;p&gt;So, I bought an Intel 1000/PRO Gigabit ethernet adapter. Folks online, in response to reports of rge not working correctly, seemed to say that the Intel adapter "just worked", but I should have dug deeper... The driver Solaris uses for this is called e1000g. The version of this driver shipping in OpenSolaris 2008.11 doesn't work. It drops packets. Simply pinging the Solaris machine from the outside shows packet loss exceeding 5%. With a ssh session open to Solaris, simply holding down '.' on the keyboard and watching the character getting echoed in the terminal is sufficient to demonstrate the hiccups: the pauses are visible and disturbing. It actually affects typing in ssh, almost as if one was at the end of a dodgy Internet connection, perhaps some half-baked cafe wifi link in Marrakesh. A recursive diff of about 100MB of data across about 1000 files using cifs took a whole 45 minutes. A trivial test using &lt;b&gt;dd&lt;/b&gt; to copy a 640KB blob of data using a 64KB buffer size took about 15 seconds and dd reported transfer rate of 45KB/sec.&lt;/p&gt;

&lt;p&gt;It was this network connectivity that prompted me to start trying out other distributions. NexentaCore 2 was first in line. It defaults to the &lt;b&gt;gani&lt;/b&gt; driver for the Realtek adapter, but an earlier version, 2.6.2. This works, but only at 100Mbps; try as I might, I can't convince it to go higher. (Windows running on the same hardware, same patch cable and same switch, never, ever dropped down to 100Mbps - something I can't say about the other Windows machines I have here.) Alos with significant importance, NexentaCore doesn't ship with a GUI (so no need to disable it), and does ship with apt-get and a reasonable selection of packages, including my preferred Linux/Unix editor, joe (very similar to old Borland DOS IDE text editors). I detest vi - I've never used a program that gleefully punished more heavily every transgression of the user.&lt;/p&gt;

&lt;p&gt;Anyhow, that's how I got one reasonably-working 100Mbps connection out of three 1Gbps adapters, four driver versions and two operating system installs.&lt;/p&gt;

&lt;p&gt;Next, the network protocol. Solaris defaults to using something that is colloquially called cifs, although the usual identifiers used when e.g. enabling and disabling with svcadm are smb/server and smb/client. I can't say much about smb/client - I'm sure it works well enough, but I don't have much use for it - but smb/server barely works at all. It's implemented as a kernel module, which unfortunately means it has odd and painful limitations compared to how user-level programs view the file system. In particular, only one ZFS filesystem can be navigated from a cifs share, significantly harming the usefulness of creating lots of ZFS filesystems. A ZFS file system mounted inside an existing filesystem, where the mountpoint is visible from a cifs share, will show up as an empty directory to clients, whereas it appears correctly mounted locally on the Solaris box.&lt;/p&gt;

&lt;p&gt;Another limitation is symbolic links. By apparent design, cifs prohibits what Samba calls "wide links": symbolic links that resolve to locations that are outside the share's hard-linked subtree. Such symbolic links look much like dead links from Windows, i.e. the text little files they are implemented as. Samba defaults to "wide links" on &lt;a href="http://tldp.org/LDP/solrhe/Securing-Optimizing-Linux-RH-Edition-v1.3/chap29sec287.html"&gt;for performance reasons&lt;/a&gt; if nothing else.&lt;/p&gt;

&lt;p&gt;A final limitation is hard links created from Windows using CreateHardLink. Cygwin 'ln' ultimately uses this API. For whatever reason (and I didn't dig too deeply, like investigating Wireshark traces), Cygwin determines that 'ln' isn't supported on cifs shares and falls back to copying instead. Cygwin 'ln' works correctly on Windows shares, however, and it also works correctly over Samba.
&lt;/p&gt;

&lt;p&gt;The verdict: cifs isn't worth it. Needs more time to bake. Use Samba instead.&lt;/p&gt;

&lt;p&gt;Finally, ZFS itself. ZFS is the big draw, and the reason I chose to use Solaris. Userspace implementations are available in Linux via FUSE, and also in FreeBSD, but I had bad performance experiences of NTFS-3G/FUSE in Linux, and FreeBSD's implementation sounded dangerously non-production ready. ZFS largely works as advertised. The primary limitation is the inflexibility in removing drives from pools. Raidz vdevs can't have drives removed at all. A word of warning, though: ZFS has what I can only consider &lt;a href="http://bugs.opensolaris.org/bugdatabase/view_bug.do?bug_id=5003563"&gt;a bad bug&lt;/a&gt; for files in the 128KB..2MB range. If you have a file of about 129KB, depending on how it was created, it ought to be using up about 100% more space than it should be. In some largeish directories of files I was seeing wastage of about 35% (as measured by 'du' versus 'du --apparent-size'), whereas NTFS on Windows had wastage in the region of 5%. Paring back the ZFS default block size (dynamically settable on ZFS - yay! - but only affects subsequent file operations) to 8KB or 16KB improves things immensely, but still not quite as space-efficient as NTFS.&lt;/p&gt;

&lt;p&gt;In conclusion, ZFS/Solaris as a NAS can work well with very carefully chosen hardware and select software configuration. If you get it working better than I describe herein, be very careful what you touch, and whatever you do, don't upgrade your zpools until you're sure that whatever step forward you take is better than what you had previously.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10106032-1597894966115326829?l=blog.barrkel.com'/&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/EntropyOverload?a=w0XhPWlMWKE:-dqPcmSiFDY:cGdyc7Q-1BI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/EntropyOverload?d=cGdyc7Q-1BI" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/EntropyOverload?a=w0XhPWlMWKE:-dqPcmSiFDY:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/EntropyOverload?i=w0XhPWlMWKE:-dqPcmSiFDY:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/EntropyOverload?a=w0XhPWlMWKE:-dqPcmSiFDY:UT3xtbGYFzA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/EntropyOverload?d=UT3xtbGYFzA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/EntropyOverload?a=w0XhPWlMWKE:-dqPcmSiFDY:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/EntropyOverload?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/EntropyOverload/~4/w0XhPWlMWKE" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.barrkel.com/feeds/1597894966115326829/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=10106032&amp;postID=1597894966115326829" title="5 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/10106032/posts/default/1597894966115326829" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/10106032/posts/default/1597894966115326829" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/EntropyOverload/~3/w0XhPWlMWKE/zfssolaris-as-nas.html" title="ZFS/Solaris as a NAS" /><author><name>Barry Kelly</name><uri>http://www.blogger.com/profile/10559947643606684495</uri><email>barry.j.kelly@gmail.com</email><gd:extendedProperty xmlns:gd="http://schemas.google.com/g/2005" name="OpenSocialUserId" value="08331762029805209075" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">5</thr:total><feedburner:origLink>http://blog.barrkel.com/2009/03/zfssolaris-as-nas.html</feedburner:origLink></entry><entry><id>tag:blogger.com,1999:blog-10106032.post-6162886775542740268</id><published>2009-03-06T06:14:00.002Z</published><updated>2009-03-06T06:34:13.666Z</updated><title type="text">OpenSolaris, ZFS, Dvorak and VI</title><content type="html">&lt;p&gt;Been experimenting with OpenSolaris to try out ZFS. OpenSolaris is not a pleasant experience even after using an average Linux.&lt;/p&gt;

&lt;p&gt;Its termcap and terminfo databases don't include complete information for xterm as implemented by the very gnome-terminal it defaults to, much less gnome-terminal itself; nor does it include any entries for rxvt derivatives (so I have to export TERM=xterm upon ssh in from Cygwin). Thus, there is much fun with Del, Home, End, PgUp, PgDn etc. brokenness: Ctrl+A/E instead of Home/End, Ctrl+D instead of Del, f/b in less rather than PgUp/PgDn, etc. The terminfo DB is spartan even by comparison with Cygwin, and termcap rather, eh, antique:&lt;/p&gt;

&lt;pre&gt;
solaris$ find /usr/share/lib/terminfo/ | wc
    1716    1716   58553
cygwin$ find /usr/share/terminfo/ | wc
   2544    2544   79257

solaris$ grep 1982 /usr/share/lib/termcap
# From research!ikeya!rob Tue Aug 31 23:41 EDT 1982
# From jwb Wed Mar 31 13:25:09 1982 remote from ihuxp
# Extensive changes to c108 by arpavax:eric Feb 1982
&lt;/pre&gt;

&lt;p&gt;Also, there is apparently no text-mode Dvorak layout shipped with OpenSolaris. I managed to patch something rudimentary together reverse engineering the contents of /usr/share/lib/keytables and applying with loadkeys. Within X, xmodmap can load an appropriate translation, but getting that working before gdm comes up for the login and password screen is tedious. Getting a vnc server session running gdm is easier.&lt;/p&gt;

&lt;p&gt;Finally, I have to resort to using vi to edit basic files. Vi is a line editor masquerading as a text editor. Now I feel like I'm living in the 80s. The early 80s.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10106032-6162886775542740268?l=blog.barrkel.com'/&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/EntropyOverload?a=WlwSfzeW9X0:-K8gmXFoSHc:cGdyc7Q-1BI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/EntropyOverload?d=cGdyc7Q-1BI" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/EntropyOverload?a=WlwSfzeW9X0:-K8gmXFoSHc:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/EntropyOverload?i=WlwSfzeW9X0:-K8gmXFoSHc:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/EntropyOverload?a=WlwSfzeW9X0:-K8gmXFoSHc:UT3xtbGYFzA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/EntropyOverload?d=UT3xtbGYFzA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/EntropyOverload?a=WlwSfzeW9X0:-K8gmXFoSHc:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/EntropyOverload?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/EntropyOverload/~4/WlwSfzeW9X0" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.barrkel.com/feeds/6162886775542740268/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=10106032&amp;postID=6162886775542740268" title="3 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/10106032/posts/default/6162886775542740268" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/10106032/posts/default/6162886775542740268" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/EntropyOverload/~3/WlwSfzeW9X0/opensolaris-zfs-dvorak-and-vi.html" title="OpenSolaris, ZFS, Dvorak and VI" /><author><name>Barry Kelly</name><uri>http://www.blogger.com/profile/10559947643606684495</uri><email>barry.j.kelly@gmail.com</email><gd:extendedProperty xmlns:gd="http://schemas.google.com/g/2005" name="OpenSocialUserId" value="08331762029805209075" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">3</thr:total><feedburner:origLink>http://blog.barrkel.com/2009/03/opensolaris-zfs-dvorak-and-vi.html</feedburner:origLink></entry><entry><id>tag:blogger.com,1999:blog-10106032.post-6498974578569438387</id><published>2009-02-04T18:34:00.004Z</published><updated>2009-02-05T03:04:36.603Z</updated><title type="text">SecuROM and SysInternals tools</title><content type="html">&lt;p&gt;If you're like me, you like visibility into the workings of your OS, which processes are running, what modules are loaded and from where and whether they are signed or not, what files are open, what files are being touched and what registry entries are being frobbed.&lt;/p&gt;

&lt;p&gt;I use the &lt;a href="http://technet.microsoft.com/en-us/sysinternals/default.aspx"&gt;SysInternals tools&lt;/a&gt; for getting increased visibility into these things. In particular, I use Process Explorer (procexp) alongside Task Manager (the two aren't complete overlaps for functionality), with File Monitor (filemon) and Registry Monitor (regmon) on XP and Process Monitor (procmon) on Vista. Filemon and regmon are more focused to their specific tasks and have easier to configure filters than procmon, so I usually prefer them, but they are not compatible with Vista.&lt;/p&gt;

&lt;p&gt;Any time I see an unexpected filesystem locking error, such as a failure in Windows Explorer to delete a folder, I can search for the culprit in procexp's handle view. Often the culprit is Windows Explorer itself - perhaps caused by a shell extension like Winzip or Winrar - and I can force closed the handle right there from the procexp interface itself.&lt;/p&gt;

&lt;p&gt;I have a set of "verification columns" configured procexp that makes visible the Company, Description and Verified Signer columns for processes and modules (DLLs). With Options | Verify Image Signatures turned on, this makes for easy checking for suspicious DLLs and kernel drivers (modules loaded into the System process) loaded in the system.&lt;/p&gt;

&lt;p&gt;Procmon supports a whole lot more analysis than the easier to use tools like filemon and regmon, but the most important distinct feature is probably the stack trace on every file and registry operation. Double-clicking an event in the procmon view pops up a dialog that has a Stack tab, wherein the stack trace is displayed. When Windows symbols are correctly configured, procmon will call out to windbg.dll and show symbolic information such as function name and offset for each code address in the stack trace, up to and including downloading the symbols PDB if necessary.&lt;/p&gt;

&lt;p&gt;Since I don't think I've done it already on this blog, I'll outline the sequence of steps required to get symbols correctly configured.

&lt;ol&gt;
&lt;li&gt;Download and install &lt;a href="http://www.microsoft.com/whdc/devtools/debugging/"&gt;WinDbg&lt;/a&gt;. &lt;a href="http://en.wikipedia.org/wiki/WinDbg"&gt;WinDbg&lt;/a&gt; is a useful tool in its own right for low-level Windows debugging, and particularly for .NET debugging when using the SOS command extension DLL.&lt;/li&gt;

&lt;li&gt;Choose a location - i.e. an empty directory - for the symbol cache on your system. Once chosen, create an environment variable called _NT_SYMBOL_PATH with the value "&lt;code&gt;SRV*[symbol-cache-dir]*http://msdl.microsoft.com/download/symbols&lt;/code&gt;" (without quotes), but replace [symbol-cache-dir] with the path to your selected directory.&lt;/li&gt;

&lt;li&gt;Now, the SysInternals tools can be configured with these settings. Procmon and Procexp both have Configure Symbols dialogs in their Options menus. Set the DbgHelp.dll path to the DbgHelp.dll from the installation location of WinDbg, and set the "Symbol paths" textbox to the value of the _NT_SYMBOL_PATH environment variable created earlier.&lt;/li&gt;

&lt;/ol&gt;
&lt;/p&gt;

&lt;p&gt;However, the reason I wrote this post is not merely to laud the SysInternals tools and the work of &lt;a href="http://blogs.technet.com/markrussinovich/"&gt;Mark Russinovich&lt;/a&gt;. The fact is that SecuROM, a DRM solution used in many games these days, has &lt;a href="http://www.securom.com/message.asp?m=module&amp;c=5017"&gt;panic&lt;/a&gt; &lt;a href="http://www.securom.com/message.asp?m=module&amp;c=5016"&gt;attacks&lt;/a&gt; when it sees evidence of SysInternals tools being used. I don't really understand what SecuROM's justification for its conniptions could be, since monitoring the process with the tools could only help in an initial cracking attempt, yet game cracks appear to come out no slower than they ever did. All it seems to do is annoy honest users - and in this case, developers. Not only does SecuROM want you to exit the applications in question, but it also wants you to reboot the entire system, ignoring the fact that you might have concurrent long-lived tasks that don't merit interruption by a mere game&lt;/p&gt;

&lt;p&gt;Well, I couldn't let that stand. I created a tool to enable me to simply exit the SysInternals utilities in question, rather than having to reboot the machine. The key to SecuROM's detection of the tools is in the communication mechanism between the SysInternals tools and their kernel driver counterparts, in particular the named devices in the Windows Object Manager namespace, visible using the &lt;a href="http://technet.microsoft.com/en-us/sysinternals/bb896657.aspx"&gt;WinObj tool&lt;/a&gt;. It turns out that merely by changing the ACL (access control list) on the device object to a single deny-all ACE (access control entry) is sufficient to fool SecuROM from thinking the device is no longer loaded - though SecuROM also looks for top-level windows with particular window classes, so the utilities do have to be exited as well.&lt;/p&gt;

&lt;p&gt;So, I wrote a simple Delphi console application, called cacls_driver, to modify the ACL of a driver device. A key set of libraries that made this easy was the &lt;a href="http://jedi-apilib.sourceforge.net/"&gt;JEDI Win32 API library&lt;/a&gt;, in particular the units JwaWinType, JwaWinBase, JwaWinNT, JwaNative and JwaSddl. I've appended the tool's source code to this entry.&lt;/p&gt;

&lt;p&gt;Using the tool is simple enough. The most important thing to know is the name of the driver one is changing the permissions of. For the versions of filemon and regmon I have installed, the correct driver names are &lt;b&gt;filemon701&lt;/b&gt; and &lt;b&gt;regmon701&lt;/b&gt; respectively. I figured this out by running the aforementioned WinObj and looking in the 'GLOBAL??' folder for devices with names starting with filemon and regmon. For the original Crysis, SecuROM even complained about Process Explorer, so I had to include &lt;b&gt;procexp110&lt;/b&gt; (IIRC) as well. SecuROM support, on the other hand, recommended that I upgrade Process Explorer, which of course used an incrementally different device name (procexp111) and thereby avoided the SecuROM sanction.&lt;/p&gt;

&lt;p&gt;Here's an example of the tool in use hiding the regmon and filemon driver links ($ is my command-line prompt, and # are comments):&lt;/p&gt;

&lt;pre&gt;
# This simply shows the current ACL
$ cacls_driver -s filemon701 regmon701
filemon701 :: D:(A;;CCRC;;;WD)(A;;CCSDRCWDWO;;;SY)(A;;CCSDRCWDWO;;;BA)(A;;CCRC;;;RC)
regmon701 :: D:(A;;CCRC;;;WD)(A;;CCSDRCWDWO;;;SY)(A;;CCSDRCWDWO;;;BA)(A;;CCRC;;;RC)

# This disables the ACL. There's no output from this command.
$ cacls_driver -d filemon701 regmon701

# Verifying that the ACL has been set.
$ cacls_driver -s filemon701 regmon701
filemon701 :: D:
regmon701 :: D:
&lt;/pre&gt;

&lt;p&gt;These ACLs having been applied - and ensuring the tools in question aren't still running - I'm free to run my games without the odious requirement for a reboot.&lt;/p&gt;

&lt;p&gt;Source code follows...&lt;/p&gt;

&lt;pre&gt;
program cacls_driver;

{$APPTYPE CONSOLE}

uses
  SysUtils, JwaWinType, JwaWinBase, JwaWinNT, JwaNative, JwaSddl, Generics.Collections;

type
  TOperation = (opNone, opApply, opShow);

  TOptions = class
  private
    FOperation: TOperation;
    FDrivers: TList&amp;lt;string&gt;;
    FDacl: string;
  public
    constructor Create;
    destructor Destroy; override;
    property Operation: TOperation read FOperation write FOperation;
    property Drivers: TList&amp;lt;string&gt; read FDrivers;
    property Dacl: string read FDacl write FDacl;
  end;

function ParseOptions: TOptions;
const
  // http://msdn.microsoft.com/en-us/library/cc230374.aspx
  // These were the defaults on my system (WinXP SP3)
  // SID tokens (last two letters in ACEs):
  // SY = local system; BA = builtin administrators; WD = everyone; 
  // RC = restricted code
  // Permissions:
  // CC = create child; RC = read control; SD = delete; WD = write DAC;
  // WO = write owner
  DefaultDacl = 'D:(A;;CCRC;;;WD)(A;;CCSDRCWDWO;;;SY)(A;;CCSDRCWDWO;;;BA)(A;;CCRC;;;RC)';
  NullDacl = 'D:';
var
  i: Integer;
  opt: string;

  procedure Usage;
  begin
    Writeln(Format('usage: %s &amp;lt;command&gt; &amp;lt;driver...&gt;', [ParamStr(0)]));
    Writeln('Modify discretionary ACL for the given global namespace driver symlinks.');
    Writeln('  -d       Disable access (deny-all ACL)');
    Writeln('  -e       Enable access (apply default ACL: ', DefaultDacl, ')');
    Writeln('  -s       Show ACL for the given drivers');
    Writeln('  -a &amp;lt;acl&gt; Apply an explicit ACL');
  end;
  
  procedure SetOpOnly(Op: TOperation);
  begin
    if Result.Operation &amp;lt;&gt; opNone then
      raise Exception.Create('only one of -s, -d or -e allowed');
    Result.Operation := Op;
  end;

  procedure SetOp(Op: TOperation; const ADacl: string);
  begin
    SetOpOnly(Op);
    Result.Dacl := ADacl;
  end;
  
  procedure SetOpArg(Op: TOperation);
  begin
    Inc(i);
    if i &gt; ParamCount then
      raise Exception.Create('missing argument to option ' + ParamStr(i - 1));
    SetOp(Op, ParamStr(i));
  end;
  
begin
  Result := TOptions.Create;
  try
    i := 1;
    if ParamCount = 0 then
    begin
      Usage;
      Exit;
    end;
    
    while i &amp;lt;= ParamCount do
    begin
      opt := ParamStr(i);
      case opt[1] of
        '/', '-':
        begin
          if Length(opt) &gt; 2 then
            raise Exception.Create('invalid option');
          case opt[2] of
            's': SetOpOnly(opShow);
            'd': SetOp(opApply, NullDacl);
            'e': SetOp(opApply, DefaultDacl);
            'a': SetOpArg(opApply);
            '?', 'h': Usage;
          else
            raise Exception.Create('invalid option');
          end;
        end;
      else
        Result.Drivers.Add(opt);
      end;
      Inc(i);
    end;
  except
    Result.Free;
    raise;
  end;
end;

{ TOptions }

constructor TOptions.Create;
begin
  FDrivers := TList&amp;lt;string&gt;.Create;
end;

destructor TOptions.Destroy;
begin
  FDrivers.Free;
  inherited;
end;

procedure EnablePrivilege(Process: THandle; const Name: string);
var
  privs: TTokenPrivileges;
begin
  FillChar(privs, SizeOf(privs), 0);
  privs.PrivilegeCount := 1;
  privs.Privileges[0].Attributes := SE_PRIVILEGE_ENABLED;
  Win32Check(LookupPrivilegeValue(nil, PChar(Name),
    privs.Privileges[0].Luid));
  Win32Check(
    AdjustTokenPrivileges(Process, False, @privs, SizeOf(privs), nil, nil));
end;

procedure ElevatePrivileges;
var
  process: THandle;
begin
  Win32Check(OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, process));
  try
    EnablePrivilege(process, SE_SECURITY_NAME);
    EnablePrivilege(process, SE_DEBUG_NAME);
  finally
    CloseHandle(process);
  end;
end;

function NtErrorMessage(Status: TNTStatus): string;
var
  buf: PChar;
  h: THandle;
begin
  h := LoadLibrary('ntdll.dll');
  try
    FormatMessage(
      FORMAT_MESSAGE_ALLOCATE_BUFFER or FORMAT_MESSAGE_FROM_SYSTEM or
        FORMAT_MESSAGE_FROM_HMODULE,
      Pointer(h),
      Status,
      0,
      PChar(@buf),
      0,
      nil);
    try
      Result := TrimRight(buf);
    finally
      LocalFree(Cardinal(buf));
    end;
  finally
    CloseHandle(h);
  end;
end;

procedure NtCheck(Status: TNTStatus);
begin
  if Status &amp;lt;&gt; 0 then
    raise EOSError.Create(NtErrorMessage(Status));
end;

procedure WithDriver(const Name: string; Mask: TAccessMask; const Proc: TProc&amp;lt;THandle&gt;);
var
  path: string;
  driverName: TUnicodeString;
  driverAttr: TObjectAttributes;
  handle: THandle;
begin
  path := '\GLOBAL??\' + Name;
  RtlInitUnicodeString(@driverName, PChar(path));
  try
    FillChar(driverAttr, SizeOf(driverAttr), 0);
    driverAttr.Length := SizeOf(driverAttr);
    driverAttr.ObjectName := @driverName;
    
    NtCheck(NtOpenSymbolicLinkObject(@handle, Mask, @driverAttr));
  finally
    RtlFreeUnicodeString(@driverName);
  end;
  try
    Proc(handle);
  finally
    NtClose(handle);
  end;
end;

function GetKernelObjectDacl(Handle: THandle): string;
var
  len: Cardinal;
  desc: PSecurityDescriptor;
  buf: PChar;
begin
  GetKernelObjectSecurity(handle, DACL_SECURITY_INFORMATION, nil, 0, len);
  desc := AllocMem(len);
  try
    Win32Check(
      GetKernelObjectSecurity(handle, DACL_SECURITY_INFORMATION, desc, len, len));

    Win32Check(ConvertSecurityDescriptorToStringSecurityDescriptor(desc,
      SDDL_REVISION_1, DACL_SECURITY_INFORMATION, buf, nil));
    try
      Result := buf;
    finally
      LocalFree(Cardinal(buf));
    end;
  finally
    FreeMem(desc);
  end;
end;

procedure SetKernelObjectDacl(Handle: THandle; const Dacl: string);
var
  secDesc: PSecurityDescriptor;
begin
  Win32Check(ConvertStringSecurityDescriptorToSecurityDescriptor(PChar(Dacl), 
    SDDL_REVISION_1, secDesc, nil));
  try
    Win32Check(SetKernelObjectSecurity(Handle, DACL_SECURITY_INFORMATION, secDesc));
  finally
    LocalFree(Cardinal(secDesc));
  end;
end;

procedure ApplyToDriverHandle(Drivers: TList&amp;lt;string&gt;; const Proc: TProc&amp;lt;string,THandle&gt;);
var
  driver: string;
begin
  ElevatePrivileges;
  
  for driver in Drivers do
    WithDriver(driver, MAXIMUM_ALLOWED,
      procedure(Handle: THandle)
      begin
        try
          Proc(driver, Handle);
        except
          on e: Exception do
            Writeln(Format('failed on "%s": %s', [driver, e.Message]));
        end;
      end);
end;

procedure DoShow(Drivers: TList&amp;lt;string&gt;);
begin
  ApplyToDriverHandle(Drivers,
    procedure(Driver: string; Handle: THandle)
    begin
      Writeln(Format('%s :: %s', [driver, GetKernelObjectDacl(Handle)]));
    end);
end;

procedure DoApply(Drivers: TList&amp;lt;string&gt;; const Dacl: string);
begin
  ApplyToDriverHandle(Drivers,
    procedure(Driver: string; Handle: THandle)
    begin
      SetKernelObjectDacl(Handle, Dacl);
    end);
end;

begin
  try
    with ParseOptions do
      try
        case Operation of
          opShow: DoShow(Drivers);
          opApply: DoApply(Drivers, Dacl);
        end;
      finally
        Free;
      end;
  except
    on E: EOSError do
      Writeln('error: ', e.Message);
    on E: Exception do
      Writeln(e.Message);
  end;
end.
&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10106032-6498974578569438387?l=blog.barrkel.com'/&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/EntropyOverload?a=j4-xXoBD4uw:59JIWqNCxRM:cGdyc7Q-1BI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/EntropyOverload?d=cGdyc7Q-1BI" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/EntropyOverload?a=j4-xXoBD4uw:59JIWqNCxRM:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/EntropyOverload?i=j4-xXoBD4uw:59JIWqNCxRM:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/EntropyOverload?a=j4-xXoBD4uw:59JIWqNCxRM:UT3xtbGYFzA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/EntropyOverload?d=UT3xtbGYFzA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/EntropyOverload?a=j4-xXoBD4uw:59JIWqNCxRM:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/EntropyOverload?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/EntropyOverload/~4/j4-xXoBD4uw" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.barrkel.com/feeds/6498974578569438387/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=10106032&amp;postID=6498974578569438387" title="2 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/10106032/posts/default/6498974578569438387" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/10106032/posts/default/6498974578569438387" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/EntropyOverload/~3/j4-xXoBD4uw/securom-and-sysinternals-tools.html" title="SecuROM and SysInternals tools" /><author><name>Barry Kelly</name><uri>http://www.blogger.com/profile/10559947643606684495</uri><email>barry.j.kelly@gmail.com</email><gd:extendedProperty xmlns:gd="http://schemas.google.com/g/2005" name="OpenSocialUserId" value="08331762029805209075" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">2</thr:total><feedburner:origLink>http://blog.barrkel.com/2009/02/securom-and-sysinternals-tools.html</feedburner:origLink></entry><entry><id>tag:blogger.com,1999:blog-10106032.post-4939138084283110367</id><published>2009-01-30T15:22:00.003Z</published><updated>2009-01-30T15:26:24.158Z</updated><title type="text">Irony</title><content type="html">&lt;p&gt;I just received two magazines through my letterbox. This is the first one, addressed to a previous occupant of my home, is from the UK Ministry of Defense:&lt;/p&gt;

&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_2GNT8vlZj48/SYMbkkQ_lnI/AAAAAAAAAEY/lxGBGGCeCv4/s1600-h/irony-1.jpg"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 389px; height: 400px;" src="http://3.bp.blogspot.com/_2GNT8vlZj48/SYMbkkQ_lnI/AAAAAAAAAEY/lxGBGGCeCv4/s400/irony-1.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5297107901742028402" /&gt;&lt;/a&gt;

&lt;p&gt;The second is my weekly subscription:&lt;/p&gt;

&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_2GNT8vlZj48/SYMbtqSTZzI/AAAAAAAAAEg/ZHa0Am6L1gc/s1600-h/irony-2.jpg"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 312px; height: 400px;" src="http://3.bp.blogspot.com/_2GNT8vlZj48/SYMbtqSTZzI/AAAAAAAAAEg/ZHa0Am6L1gc/s400/irony-2.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5297108057976956722" /&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10106032-4939138084283110367?l=blog.barrkel.com'/&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/EntropyOverload?a=IaUVxjO1W_s:klIEWDjDsXY:cGdyc7Q-1BI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/EntropyOverload?d=cGdyc7Q-1BI" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/EntropyOverload?a=IaUVxjO1W_s:klIEWDjDsXY:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/EntropyOverload?i=IaUVxjO1W_s:klIEWDjDsXY:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/EntropyOverload?a=IaUVxjO1W_s:klIEWDjDsXY:UT3xtbGYFzA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/EntropyOverload?d=UT3xtbGYFzA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/EntropyOverload?a=IaUVxjO1W_s:klIEWDjDsXY:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/EntropyOverload?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/EntropyOverload/~4/IaUVxjO1W_s" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.barrkel.com/feeds/4939138084283110367/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=10106032&amp;postID=4939138084283110367" title="2 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/10106032/posts/default/4939138084283110367" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/10106032/posts/default/4939138084283110367" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/EntropyOverload/~3/IaUVxjO1W_s/irony.html" title="Irony" /><author><name>Barry Kelly</name><uri>http://www.blogger.com/profile/10559947643606684495</uri><email>barry.j.kelly@gmail.com</email><gd:extendedProperty xmlns:gd="http://schemas.google.com/g/2005" name="OpenSocialUserId" value="08331762029805209075" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://3.bp.blogspot.com/_2GNT8vlZj48/SYMbkkQ_lnI/AAAAAAAAAEY/lxGBGGCeCv4/s72-c/irony-1.jpg" height="72" width="72" /><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">2</thr:total><feedburner:origLink>http://blog.barrkel.com/2009/01/irony.html</feedburner:origLink></entry><entry><id>tag:blogger.com,1999:blog-10106032.post-4884455671034622919</id><published>2009-01-22T12:15:00.002Z</published><updated>2009-01-22T12:17:35.230Z</updated><category scheme="http://www.blogger.com/atom/ns#" term="politics" /><title type="text">Awesome sentence fragment</title><content type="html">&lt;p&gt;Best phrase I've read in some time: &lt;a href="http://www.economist.com/displayStory.cfm?story_id=12931660"&gt;an army of over-promoted, ideologically vetted homunculi&lt;/a&gt;&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10106032-4884455671034622919?l=blog.barrkel.com'/&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/EntropyOverload?a=NTcKivrg7uE:9j5PrEMTeIg:cGdyc7Q-1BI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/EntropyOverload?d=cGdyc7Q-1BI" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/EntropyOverload?a=NTcKivrg7uE:9j5PrEMTeIg:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/EntropyOverload?i=NTcKivrg7uE:9j5PrEMTeIg:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/EntropyOverload?a=NTcKivrg7uE:9j5PrEMTeIg:UT3xtbGYFzA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/EntropyOverload?d=UT3xtbGYFzA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/EntropyOverload?a=NTcKivrg7uE:9j5PrEMTeIg:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/EntropyOverload?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/EntropyOverload/~4/NTcKivrg7uE" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.barrkel.com/feeds/4884455671034622919/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=10106032&amp;postID=4884455671034622919" title="1 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/10106032/posts/default/4884455671034622919" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/10106032/posts/default/4884455671034622919" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/EntropyOverload/~3/NTcKivrg7uE/awesome-sentence-fragment.html" title="Awesome sentence fragment" /><author><name>Barry Kelly</name><uri>http://www.blogger.com/profile/10559947643606684495</uri><email>barry.j.kelly@gmail.com</email><gd:extendedProperty xmlns:gd="http://schemas.google.com/g/2005" name="OpenSocialUserId" value="08331762029805209075" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">1</thr:total><feedburner:origLink>http://blog.barrkel.com/2009/01/awesome-sentence-fragment.html</feedburner:origLink></entry><entry><id>tag:blogger.com,1999:blog-10106032.post-7658812785872937954</id><published>2009-01-15T17:18:00.005Z</published><updated>2009-01-16T13:47:34.544Z</updated><title type="text">Jeff is Wrong and don't listen to him</title><content type="html">&lt;p style="text-align: center;"&gt;&lt;a href="http://xkcd.com/386/"&gt;&lt;img src="http://imgs.xkcd.com/comics/duty_calls.png" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I rarely (I hope) post specific corrections to misleading information out there on the web, not least because there's so much of it and it's usually a futile effort, but &lt;a href="http://www.codinghorror.com/blog/archives/001211.html"&gt;Jeff Atwood's latest post&lt;/a&gt; really rubbed me up the wrong way.&lt;/p&gt;

&lt;p&gt;In his post, Jeff writes some unidiomatic C and bewails its ugliness and pain etc.:&lt;/p&gt;

&lt;blockquote&gt;
&lt;pre&gt;
b1 = (double *)malloc(m*sizeof(double));
&lt;/pre&gt;
&lt;/blockquote&gt;

&lt;p&gt;By way of comparison, he then writes some C# (I expect that's what it's supposed to be) that doesn't actually use the GC, since it's not allocated on the heap:&lt;/p&gt;

&lt;blockquote&gt;
&lt;pre&gt;
Double b1;
&lt;/pre&gt;
&lt;/blockquote&gt;

&lt;p&gt;(Of course, if it's not C#, and is in fact supposed to be e.g. Java, it's still not an example since it hasn't been initialized.)&lt;/p&gt;

&lt;p&gt;Now Jeff's core message (at least at the start of the post) that GC is a Good Thing, I'm all in favour of. I strongly believe that manual memory allocation has good reasons to be used only in about 5% (or less) of programming tasks, usually restricted to things like operating systems and embedded devices. I consider well-defined, trivially provably correct zoned allocation to be a good 80% of the way to GC, so I would include good uses of that in the 95% case.&lt;/p&gt;

&lt;p&gt;Jeff's final point, however, about "disposal anxiety" is where he casually reveals that he doesn't pay much attention to a very important issue: disposal of resources. He mocks a particular piece of code that has at least the core concept right:&lt;p&gt;

&lt;blockquote&gt;
&lt;pre&gt;
sqlConnection.Close();
sqlConnection.Dispose();
sqlConnection = null;
&lt;/pre&gt;
&lt;/blockquote&gt;

&lt;p&gt;Two-thirds of this code is redundant - the last line is always so unless sqlConnection is read later in the same routine, while either of the first two would do for resource disposal. This wouldn't be so bad but for Jeff saying:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;
Personally, I view explicit disposal as more of an optimization than anything else, but it can be a pretty important optimization on a heavily loaded webserver, or a performance intensive desktop application plowing through gigabytes of data.
&lt;/p&gt;&lt;/blockquote&gt;

&lt;p&gt;Disposal of resources in a long-running application using GC is not a performance issue. It's a correctness issue.&lt;/p&gt;

&lt;p&gt;Garbage collection knows about memory. With most collectors, GC is only invoked when the GC is asked for more memory than is immediately available, taking specific tuning parameters into account. In other words, the GC is &lt;b&gt;only sensitive to memory pressure&lt;/b&gt;. It doesn't know about resources that it sees only as pointer-sized handles, it doesn't know how much they "cost", and indeed those resources might be on a different machine or even spread across many different machines.&lt;/p&gt;

&lt;p&gt;More critically, garbage collection gets its performance surplus over and above manual garbage collection by &lt;b&gt;not collecting&lt;/b&gt; until as late as reasonably possible, where "reasonable" is usually a function of how much free memory is available without swapping to disk. The long-run-average optimal collector for a program that has a machine all to itself won't collect &lt;b&gt;at all&lt;/b&gt; until &lt;b&gt;every last remaining byte of RAM&lt;/b&gt; has been allocated, which may of course take some time. (Added to clarify: this is a theoretical optimum, and not how most GCs act in practice. They collect much sooner e.g. the youngest generation may fit in L2 cache and so be very fast to collect.)&lt;/p&gt;

&lt;p&gt;Precise tracing garbage collectors work somewhat paradoxically not by collecting garbage, but by collecting live objects. The "garbage" is everything left over after all the live objects have been collected. The more garbage as a fraction of live objects there is, the cheaper, proportionally, it has been to collect. (Added to clarify: this means that the collection of garbage is amortized; any amount of garbage costs the same to collect, providing the set of live objects is held constant.) This is how GCs outperform manual memory allocation on average and with sufficient free memory. Ideally, the GC never runs at all, and program termination cleans up.&lt;/p&gt;

&lt;p&gt;With this insight under your belt, it should be clear that expecting the GC to clean up resources is to be ignoring one of the key benefits of GC. Not only that, but you shouldn't be expecting the GC to finalize your objects at all. If your resources &lt;b&gt;must&lt;/b&gt; be disposed of - and almost all resources should, e.g. TCP sockets, file handles, etc. - then you need to take care of that yourself, and deterministically. Leaving e.g. file handles to be disposed of by the GC is opening up the program (and possibly the user) to odd non-deterministic failures when they find files on disk are still locked, even though the program should have closed them.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10106032-7658812785872937954?l=blog.barrkel.com'/&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/EntropyOverload?a=FrkDhz0N7tY:MVnspc73YuE:cGdyc7Q-1BI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/EntropyOverload?d=cGdyc7Q-1BI" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/EntropyOverload?a=FrkDhz0N7tY:MVnspc73YuE:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/EntropyOverload?i=FrkDhz0N7tY:MVnspc73YuE:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/EntropyOverload?a=FrkDhz0N7tY:MVnspc73YuE:UT3xtbGYFzA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/EntropyOverload?d=UT3xtbGYFzA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/EntropyOverload?a=FrkDhz0N7tY:MVnspc73YuE:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/EntropyOverload?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/EntropyOverload/~4/FrkDhz0N7tY" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.barrkel.com/feeds/7658812785872937954/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=10106032&amp;postID=7658812785872937954" title="32 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/10106032/posts/default/7658812785872937954" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/10106032/posts/default/7658812785872937954" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/EntropyOverload/~3/FrkDhz0N7tY/jeff-is-wrong-and-dont-listen-to-him.html" title="Jeff is Wrong and don't listen to him" /><author><name>Barry Kelly</name><uri>http://www.blogger.com/profile/10559947643606684495</uri><email>barry.j.kelly@gmail.com</email><gd:extendedProperty xmlns:gd="http://schemas.google.com/g/2005" name="OpenSocialUserId" value="08331762029805209075" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">32</thr:total><feedburner:origLink>http://blog.barrkel.com/2009/01/jeff-is-wrong-and-dont-listen-to-him.html</feedburner:origLink></entry><entry><id>tag:blogger.com,1999:blog-10106032.post-1470436303549719838</id><published>2009-01-13T18:02:00.003Z</published><updated>2009-01-13T19:13:36.484Z</updated><title type="text">Implementing user-defined copy-on-write data structures in Delphi</title><content type="html">&lt;p&gt;There was &lt;a href="https://forums.codegear.com/thread.jspa?threadID=10173&amp;tstart=0"&gt;some discussion&lt;/a&gt; about user-defined class operators in the Delphi non-technical forum. Some folks seem to be asking for operators for heap-allocated classes, but in the absence of either GC or reference counting, these are problematic.&lt;/p&gt;

&lt;p&gt;I suggested that records combined with interfaces can get much of the benefit of both worlds: user-defined operators on the record, life-time management via interface reference counting, and arbitrary data sizing through the interface being a heap reference.&lt;/p&gt;

&lt;p&gt;To demonstrate the idea through example, I have written up a quick copy-on-write array type that has a user-defined '+' operator that concatenates two lists. Let me build it up from the bottom. First, I've defined a generic dynamic array type to avoid the pitfalls of nominal typing that Delphi has inherited from Pascal (a common newbie mistake, particularly with the syntactic ambiguity with open arrays in parameter lists):&lt;/p&gt;

&lt;pre&gt;
type
  TArray&amp;lt;T&gt; = array of T;
&lt;/pre&gt;

&lt;p&gt;Next up is the interface. Using an interface for communicating between the record and the heap value means that I get reference counting, and thus lifetime management, for free.&lt;/p&gt;

&lt;pre&gt;
  ICowArrayData&amp;lt;T&gt; = interface
    function GetLength: Integer;
    function MutableClone: ICowArrayData&amp;lt;T&gt;;
    function GetItem(Index: Integer): T;
    procedure SetItem(Index: Integer; const Value: T);
    function ToArray: TArray&amp;lt;T&gt;;
  end;

  TCowArrayData&amp;lt;T&gt; = class(TInterfacedObject, ICowArrayData&amp;lt;T&gt;)
  private
    FData: TArray&amp;lt;T&gt;;
  public
    constructor Create(const Data: TArray&amp;lt;T&gt;);
    function GetLength: Integer;
    function MutableClone: ICowArrayData&amp;lt;T&gt;;
    function GetItem(Index: Integer): T;
    procedure SetItem(Index: Integer; const Value: T);
    function ToArray: TArray&amp;lt;T&gt;;
  end;
&lt;/pre&gt;

&lt;p&gt;The only "interesting" method really is MutableClone, which I'll be calling to verify that I have a unique instance before I perform any mutating operations. This is necessary to get copy-on-write behaviour. The implementation class doesn't add anything beyond a constructor, as all communication will be through the interface.&lt;/p&gt;

&lt;p&gt;Here's the actual record, and the only type that the user of this bundle should concern themselves with:&lt;/p&gt;

&lt;pre&gt;
  TCowArray&amp;lt;T&gt; = record
  private
    FData: ICowArrayData&amp;lt;T&gt;;
    function GetItems(Index: Integer): T;
    procedure SetItems(Index: Integer; const Value: T);
    function GetLength: Integer;
  public
    constructor Create(const Data: TArray&amp;lt;T&gt;); overload;
    constructor Create(const Data: array of T); overload;
    property Items[Index: Integer]: T read GetItems write SetItems; default;
    property Length: Integer read GetLength;
    function ToArray: TArray&amp;lt;T&gt;;

    class operator Add(const Left, Right: TCowArray&amp;lt;T&gt;): TCowArray&amp;lt;T&gt;;
  end;
&lt;/pre&gt;

&lt;p&gt;It adds a nice default Items property so that values can be indexed like arrays, exposes a Length property much like .NET, and adds in the Add operator to prove the point.&lt;/p&gt;

&lt;p&gt;The implementation of most the methods are pretty routine, but a few are important. First up, because interfaces are managed types, they get zero-initialized by default. Thus, we can't expect the record's FData field to be initialized. We can take a leaf out of Delphi's built-in array (and string) types, and let zero-length arrays be represented by a nil FData. That affects the implementation of the record methods, e.g.:&lt;/p&gt;

&lt;pre&gt;
function TCowArray&amp;lt;T&gt;.GetLength: Integer;
begin
  if FData = nil then
    Exit(0); // nil =&gt; zero-length
  Result := FData.GetLength;
end;
&lt;/pre&gt;

&lt;p&gt;Another important method is the implementation of the copy-on-write itself. The only mutating method on this type is SetItems, and here it is:&lt;/p&gt;

&lt;pre&gt;
procedure TCowArray&amp;lt;T&gt;.SetItems(Index: Integer; const Value: T);
begin
  FData := FData.MutableClone;
  FData.SetItem(Index, Value);
end;
&lt;/pre&gt;

&lt;p&gt;This isn't particularly efficient, but it does make sure that we have a unique reference when required. The implementation of MutableClone is simple enough too:&lt;/p&gt;

&lt;pre&gt;
function TCowArrayData&amp;lt;T&gt;.MutableClone: ICowArrayData&amp;lt;T&gt;;
begin
  if RefCount = 1 then
    Exit(Self);
  Result := TCowArrayData&amp;lt;T&gt;.Create(ToArray);
end;
&lt;/pre&gt;

&lt;p&gt;Using the inherited RefCount from TInterfacedObject, we can determine safely if we are only referred to by our caller. In a race scenario, where RefCount might e.g. spuriously be 2, there won't be any correctness problem if we duplicate the array anyway.&lt;/p&gt;

&lt;p&gt;The implementation of the '+' operator is pretty trivial too:&lt;/p&gt;

&lt;pre&gt;
class operator TCowArray&amp;lt;T&gt;.Add(const Left, Right: TCowArray&amp;lt;T&gt;): TCowArray&amp;lt;T&gt;;
var
  resultArr: TArray&amp;lt;T&gt;;
  i: Integer;
begin
  SetLength(resultArr, Left.Length + Right.Length);
  for i := 0 to Left.Length - 1 do
    resultArr[i] := Left[i];
  for i := 0 to Right.Length - 1 do
    resultArr[Left.Length + i] := Right[i];
  Result := TCowArray&amp;lt;T&gt;.Create(resultArr);
end;
&lt;/pre&gt;

&lt;p&gt;Finally, let's look at the program body itself, testing these types:&lt;/p&gt;

&lt;pre&gt;
procedure WriteArray(const Msg: string; Arr: TCowArray&amp;lt;Integer&gt;);
var
  i: Integer;
begin
  Write(Msg, ':');
  for i := 0 to Arr.Length - 1 do
    Write(' ', Arr[i]);
  Writeln;
end;

var
  x, y: TCowArray&amp;lt;Integer&gt;;
begin
  try
    x := TCowArray&amp;lt;Integer&gt;.Create([1, 2, 3]);
    y := x;

    Writeln('Starting out, both x and y refer to same instance data');
    WriteArray('x', x);
    WriteArray('y', y);

    Writeln('Modifying x; note that y doesn''t change:');
    x[1] := 42;
    WriteArray('x', x);
    WriteArray('y', y);

    // Add operator as concatenation
    Writeln('Concatenation:');
    y := x + y;
    WriteArray('x', x);
    WriteArray('y', y);
    
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
end.
&lt;/pre&gt;

&lt;p&gt;The output does indeed show that the array is copied on writes, and that concatenation works as expected:&lt;/p&gt;

&lt;pre&gt;
Starting out, both x and y refer to same instance data
x: 1 2 3
y: 1 2 3
Modifying x; note that y doesn't change:
x: 1 42 3
y: 1 2 3
Concatenation:
x: 1 42 3
y: 1 42 3 1 2 3
&lt;/pre&gt;

&lt;p&gt;As an addendum, let me add the full source for reference. Note that due to some bugs in the current compiler's generics implementation for dynamic arrays, the code won't work if it's part of a unit - it needs to be in a single whole for now, unfortunately.&lt;/p&gt;

&lt;pre&gt;
{$apptype console}
uses SysUtils;

type
  TArray&amp;lt;T&gt; = array of T;
  
  ICowArrayData&amp;lt;T&gt; = interface
    function GetLength: Integer;
    function MutableClone: ICowArrayData&amp;lt;T&gt;;
    function GetItem(Index: Integer): T;
    procedure SetItem(Index: Integer; const Value: T);
    function ToArray: TArray&amp;lt;T&gt;;
  end;
  
  TCowArrayData&amp;lt;T&gt; = class(TInterfacedObject, ICowArrayData&amp;lt;T&gt;)
  private
    FData: TArray&amp;lt;T&gt;;
  public
    constructor Create(const Data: TArray&amp;lt;T&gt;);
    function GetLength: Integer;
    function MutableClone: ICowArrayData&amp;lt;T&gt;;
    function GetItem(Index: Integer): T;
    procedure SetItem(Index: Integer; const Value: T);
    function ToArray: TArray&amp;lt;T&gt;;
  end;
  
  TCowArray&amp;lt;T&gt; = record
  private
    FData: ICowArrayData&amp;lt;T&gt;;
    function GetItems(Index: Integer): T;
    procedure SetItems(Index: Integer; const Value: T);
    function GetLength: Integer;
  public
    constructor Create(const Data: TArray&amp;lt;T&gt;); overload;
    constructor Create(const Data: array of T); overload;
    property Items[Index: Integer]: T read GetItems write SetItems; default;
    property Length: Integer read GetLength;
    function ToArray: TArray&amp;lt;T&gt;;

    class operator Add(const Left, Right: TCowArray&amp;lt;T&gt;): TCowArray&amp;lt;T&gt;;
  end;

{ TCowArray&amp;lt;T&gt; }

class operator TCowArray&amp;lt;T&gt;.Add(const Left, Right: TCowArray&amp;lt;T&gt;): TCowArray&amp;lt;T&gt;;
var
  resultArr: TArray&amp;lt;T&gt;;
  i: Integer;
begin
  SetLength(resultArr, Left.Length + Right.Length);
  for i := 0 to Left.Length - 1 do
    resultArr[i] := Left[i];
  for i := 0 to Right.Length - 1 do
    resultArr[Left.Length + i] := Right[i];
  Result := TCowArray&amp;lt;T&gt;.Create(resultArr);
end;

constructor TCowArray&amp;lt;T&gt;.Create(const Data: TArray&amp;lt;T&gt;);
begin
  if Data = nil then
    FData := nil
  else
    FData := TCowArrayData&amp;lt;T&gt;.Create(Data);
end;

constructor TCowArray&amp;lt;T&gt;.Create(const Data: array of T);
var
  arr: TArray&amp;lt;T&gt;;
  i: Integer;
begin
  if System.Length(Data) = 0 then
    FData := nil
  else
  begin
    SetLength(arr, System.Length(Data));
    for i := 0 to System.Length(Data) - 1 do
      arr[i] := Data[i];
    FData := TCowArrayData&amp;lt;T&gt;.Create(arr);
  end;
end;

function TCowArray&amp;lt;T&gt;.GetItems(Index: Integer): T;
begin
  Result := FData.GetItem(Index);
end;

function TCowArray&amp;lt;T&gt;.GetLength: Integer;
begin
  if FData = nil then
    Exit(0);
  Result := FData.GetLength;
end;

procedure TCowArray&amp;lt;T&gt;.SetItems(Index: Integer; const Value: T);
begin
  FData := FData.MutableClone;
  FData.SetItem(Index, Value);
end;

function TCowArray&amp;lt;T&gt;.ToArray: TArray&amp;lt;T&gt;;
begin
  if FData = nil then
    Exit(nil);
  Result := FData.ToArray;
end;

{ TCowArrayData&amp;lt;T&gt; }

constructor TCowArrayData&amp;lt;T&gt;.Create(const Data: TArray&amp;lt;T&gt;);
begin
  FData := Data;
end;

function TCowArrayData&amp;lt;T&gt;.GetItem(Index: Integer): T;
begin
  Result := FData[Index];
end;

function TCowArrayData&amp;lt;T&gt;.GetLength: Integer;
begin
  Result := Length(FData);
end;

function TCowArrayData&amp;lt;T&gt;.MutableClone: ICowArrayData&amp;lt;T&gt;;
begin
  if RefCount = 1 then
    Exit(Self);
  Result := TCowArrayData&amp;lt;T&gt;.Create(ToArray);
end;

procedure TCowArrayData&amp;lt;T&gt;.SetItem(Index: Integer; const Value: T);
begin
  FData[Index] := Value;
end;

function TCowArrayData&amp;lt;T&gt;.ToArray: TArray&amp;lt;T&gt;;
var
  i: Integer;
begin
  SetLength(Result, Length(FData));
  for i := 0 to Length(FData) - 1 do
    Result[i] := FData[i];
end;

procedure WriteArray(const Msg: string; Arr: TCowArray&amp;lt;Integer&gt;);
var
  i: Integer;
begin
  Write(Msg, ':');
  for i := 0 to Arr.Length - 1 do
    Write(' ', Arr[i]);
  Writeln;
end;

var
  x, y: TCowArray&amp;lt;Integer&gt;;
begin
  try
    x := TCowArray&amp;lt;Integer&gt;.Create([1, 2, 3]);
    y := x;

    Writeln('Starting out, both x and y refer to same instance data');
    WriteArray('x', x);
    WriteArray('y', y);

    Writeln('Modifying x; note that y doesn''t change:');
    x[1] := 42;
    WriteArray('x', x);
    WriteArray('y', y);

    // Add operator as concatenation
    Writeln('Concatenation:');
    y := x + y;
    WriteArray('x', x);
    WriteArray('y', y);
    
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
end.
&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10106032-1470436303549719838?l=blog.barrkel.com'/&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/EntropyOverload?a=R9ITIwUzv-Q:rya6eZMgjz4:cGdyc7Q-1BI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/EntropyOverload?d=cGdyc7Q-1BI" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/EntropyOverload?a=R9ITIwUzv-Q:rya6eZMgjz4:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/EntropyOverload?i=R9ITIwUzv-Q:rya6eZMgjz4:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/EntropyOverload?a=R9ITIwUzv-Q:rya6eZMgjz4:UT3xtbGYFzA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/EntropyOverload?d=UT3xtbGYFzA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/EntropyOverload?a=R9ITIwUzv-Q:rya6eZMgjz4:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/EntropyOverload?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/EntropyOverload/~4/R9ITIwUzv-Q" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.barrkel.com/feeds/1470436303549719838/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=10106032&amp;postID=1470436303549719838" title="4 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/10106032/posts/default/1470436303549719838" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/10106032/posts/default/1470436303549719838" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/EntropyOverload/~3/R9ITIwUzv-Q/implementing-user-defined-copy-on-write.html" title="Implementing user-defined copy-on-write data structures in Delphi" /><author><name>Barry Kelly</name><uri>http://www.blogger.com/profile/10559947643606684495</uri><email>barry.j.kelly@gmail.com</email><gd:extendedProperty xmlns:gd="http://schemas.google.com/g/2005" name="OpenSocialUserId" value="08331762029805209075" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">4</thr:total><feedburner:origLink>http://blog.barrkel.com/2009/01/implementing-user-defined-copy-on-write.html</feedburner:origLink></entry><entry><id>tag:blogger.com,1999:blog-10106032.post-8970317665044757565</id><published>2009-01-12T16:05:00.003Z</published><updated>2009-01-12T16:12:32.263Z</updated><title type="text">Semantics, Formats and Markets</title><content type="html">&lt;p&gt;I was reading &lt;a href="http://www.25hoursaday.com/weblog/2009/01/12/CanRDFReallySaveUsFromDataFormatProliferation.aspx"&gt;Dare's response&lt;/a&gt; to &lt;a href="http://www.dehora.net/journal/2009/01/10/format-debt-what-you-cant-say/"&gt;Bill's post&lt;/a&gt; on format proliferation and RDF. Bill hopes that more folks will understand RDF and seems to see it somewhat as a silver bullet for the problem, while Dare is more pessimistic.&lt;/p&gt;

&lt;p&gt;The way I see it, there are distinctly two issues here. The first is the obvious one, solving the n*m problem, where n = format count and m = format producer and consumer count. The idea behind any common format or format mapping / shaping / semantic extraction is to reduce it to n*1 + 1*m, i.e. n+m.&lt;/p&gt;

&lt;p&gt;That's all well and good, but it has problems: lowest common denominator, semantic loss in conversion, inhibition of innovation (gated on lowest common denominator), barriers to entry (new producers want only to be concerned with their specifics, not a huge standard, while new consumers don't want to have to implement the world before being useful).&lt;/p&gt;

&lt;p&gt;More germane to the RDF situtaion, a requirement to understand a meta-model as well as any given model itself is so large a barrier to non-specialists just trying to get their work done that it's highly unlikely to ever receive serious attention. More on that in a bit.&lt;/p&gt;

&lt;p&gt;Official standards-body approaches are the other issue. Formats in an area of innovation act like a bubbling market; take-up of formats by consumers and producers determine the winner, until eventually network effects reduce the total number of formats to just a handful. The problem with trying to manage this market process via a committee is much like the problem of trying implement socialism: the total information embodied in various choices of one format over and above another, which a free market reveals naturally, is not available to committees. Committees tend to be dominated by large market players which have various strategic and political objectives that may be quite distinct, and indeed sometimes covertly opposed, to the average market desires for any given format.&lt;/p&gt;

&lt;p&gt;The working programmer, at the end of the day, is either putting square pegs into square holes (in which case, no problem), or trying to put square pegs into round holes, and then having to create an adapter to convert square pegs into round pegs. An arbitrary selection of square or round as a "peg standard" doesn't necessarily help him for his &lt;b&gt;specific&lt;/b&gt; needs; similarly, pointing at some generalized framework for describing the semantic meaning of square and round pegs respectively is far too abstract for him to get his job done efficiently - i.e. without investment whose &lt;b&gt;cost exceeds the value&lt;/b&gt; of getting the original job done.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10106032-8970317665044757565?l=blog.barrkel.com'/&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/EntropyOverload?a=WdwQuN0xcU0:exgxrh0EqU8:cGdyc7Q-1BI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/EntropyOverload?d=cGdyc7Q-1BI" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/EntropyOverload?a=WdwQuN0xcU0:exgxrh0EqU8:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/EntropyOverload?i=WdwQuN0xcU0:exgxrh0EqU8:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/EntropyOverload?a=WdwQuN0xcU0:exgxrh0EqU8:UT3xtbGYFzA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/EntropyOverload?d=UT3xtbGYFzA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/EntropyOverload?a=WdwQuN0xcU0:exgxrh0EqU8:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/EntropyOverload?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/EntropyOverload/~4/WdwQuN0xcU0" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.barrkel.com/feeds/8970317665044757565/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=10106032&amp;postID=8970317665044757565" title="1 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/10106032/posts/default/8970317665044757565" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/10106032/posts/default/8970317665044757565" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/EntropyOverload/~3/WdwQuN0xcU0/semantics-formats-and-markets.html" title="Semantics, Formats and Markets" /><author><name>Barry Kelly</name><uri>http://www.blogger.com/profile/10559947643606684495</uri><email>barry.j.kelly@gmail.com</email><gd:extendedProperty xmlns:gd="http://schemas.google.com/g/2005" name="OpenSocialUserId" value="08331762029805209075" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">1</thr:total><feedburner:origLink>http://blog.barrkel.com/2009/01/semantics-formats-and-markets.html</feedburner:origLink></entry><entry><id>tag:blogger.com,1999:blog-10106032.post-2076229414169671468</id><published>2009-01-03T00:42:00.003Z</published><updated>2009-01-03T01:56:34.059Z</updated><category scheme="http://www.blogger.com/atom/ns#" term="bash" /><category scheme="http://www.blogger.com/atom/ns#" term="ipod" /><category scheme="http://www.blogger.com/atom/ns#" term="scripting" /><category scheme="http://www.blogger.com/atom/ns#" term="itunes" /><category scheme="http://www.blogger.com/atom/ns#" term="apple" /><title type="text">iPod Touch, iTunes, and unwanted processes</title><content type="html">&lt;p&gt;I recently got a second-generation &lt;a href="http://www.apple.com/ipodtouch/"&gt;iPod Touch&lt;/a&gt;. I don't make enough phone calls to make a phone contract worthwhile, much less an iPhone contract with O2 in the UK; and O2's PAYG (pay as you go) appears to charge GBP 7.50 per roaming MB, while I pay EUR 1 for first 50 MB with my Vodafone Ireland PAYG, whether I'm in the UK, anywhere else in Europe, or in the US. So, iPod Touch it is.&lt;p&gt;

&lt;p&gt;It's a nice device, both style-wise and as a hand-held web-browsing experience. The browser is good enough to create a paradox; limitations that only surface because of the increased expectations start to get a little annoying. For example, up to eight separate pages can be browsed simultaneously, but as soon as one of the pages gets big enough, information about the other open pages is forgotten beyond the URL / request parameters. This means that e.g. you don't want to leave a purchase page open while browsing in a parallel window, or you'll break navigation flow / possibly pay twice.&lt;/p&gt;

&lt;p&gt;The browser also crashes a lot. I've had the device for about two days, and suffered many (8+) unprompted "back to main menu" transitions, with nary a hint from the device that the browser had crashed. It does make me wonder; how much of the reputation Apple has for good firmware is due to pretending that errors don't happen? The subsequent OS-level cleanup / resource management doesn't seem too solid either, since searches on the topic &lt;a href="http://forums.macrumors.com/showthread.php?t=403400"&gt;suggest that a clean reboot&lt;/a&gt; is what's necessary to restore this BSD-based Unix kernel to good running. This doesn't inspire confidence.&lt;/p&gt;

&lt;p&gt;As a device for playing music, it's too large and heavy for my taste; I'm still using my second-generation Nano, even though I also have a third-generation Nano - the wheel is too small on it.&lt;/p&gt;

&lt;p&gt;The draconian limitations that are de rigeur with Apple firmware chafe quite a lot. Even my humble K800 phone can create folders in the file system, browse it, start applications from arbitrary locations, open videos, music and pictures from arbitrary locations, etc., all using the same tree that you see when browsing the CF card from a PC. The iPod Touch doesn't have any of this: it's Apple's way or screw you, to be blunt. iTunes synchronization is a useless to me; I'm a file system guy - give me scripting, cron and hard &amp;amp; symbolic links and I'll create the structure I prefer myself. I suspect I won't be happy until the device is jailbroken.&lt;/p&gt;

&lt;p&gt;Anyhow, the other reason I wanted to write this post, other than to praise and complain about the device, is the little setup I created to cope with iTunes 7, which I was reluctantly forced to upgrade to. The iPod Touch doesn't work without iTunes 7, and it also doesn't work without a bunch of other background services running, most importantly, the 'Apple Mobile Device' service.&lt;/p&gt;

&lt;p&gt;Rather than have half a dozen Apple-related processes running, even though I'm not running iTunes and don't have a device connected to my machine, I wrote a little script to start up the necessary services upon iTunes startup, and kill off the unwanted processes after iTunes shutdown. They rely on Cygwin and some little utilities I wrote myself.&lt;/p&gt;

&lt;p&gt;First up is 'hide.exe'. This simple executable, written in Delphi, runs a given process with a list of command-line arguments, but in a hidden window, by passing SW_HIDE as the nShow parameter. This basically lets a console hang around running my script while iTunes is running, waiting for it to exit, so that the script can clean things up later. The hide.exe executable itself is a GUI subsystem app, though it doesn't have a message pump or anything.&lt;/p&gt;

&lt;p&gt;The second is a very simple killall script for Cygwin:&lt;/p&gt;

&lt;pre&gt;
#!/bin/bash

if test -z "$1"; then
    echo "usage: $(basename $0) &lt;grep-spec&gt;..."
    echo "Kills all specified processes."
    exit
fi

while [ -n "$1" ]; do
    ps -W | grep -i "$1" | cut -b -10 | print0 | xargs -0 kill -f
    shift
done
&lt;/pre&gt;

&lt;p&gt;Obviously, when using this script you don't want to be too ambiguous about your process search string. The 'print0' in the pipe is another little utility I wrote to bridge the gap between line-oriented programs, word-oriented programs and programs that can accept null-terminated strings. It simply reads each line one at a time, and prints out the same line with a null terminator instead of a newline. Without it, any programs with spaces in their names would be parsed by xargs as multiple separate arguments, since xargs, by default, breaks arguments on any whitespace, not just newlines.&lt;/p&gt;

&lt;p&gt;With that aside, my iTunes wrapper script (I call it start-itunes) is fairly simple:&lt;/p&gt;

&lt;pre&gt;
#!/bin/bash

itunes="${itunes:-/c/other/itunes/itunes.exe}"

net start 'Apple Mobile Device' &gt; /dev/null
"$itunes" || messagebox "Failed to start \"$itunes\""
net stop 'Apple Mobile Device' &gt; /dev/null || messagebox 'Failed to stop "Apple Mobile Device"'
net stop 'iPod Service' &gt; /dev/null
killall SyncServer.exe distnoted.exe
&lt;/pre&gt;

&lt;p&gt;It simply starts the required service, lets iTunes run to completion, and then stops the redundant services and blows away the crap left behind by iTunes. I don't use iTunes to "sync" anything, so I'm assuming that blowing them away doesn't hurt. I haven't had any problems, anyhow.&lt;/p&gt;

&lt;p&gt;This script uses 'messagebox', yet another little utility I wrote, to display errors using the Win32 MessageBox function. This is necessary, otherwise the errors wouldn't be visible - the script is run from a hidden window.&lt;/p&gt;

&lt;p&gt;The final step is the shortcut itself. Cygwin has a utility, mkshortcut, to create shortcuts, though I don't like its command-line syntax and wrote a wrapper script to make it look more like ln and friends. However, a Cygwin mkshortcut command-line for creating an appropriate shortcut for my script above might look a bit like this (watch the backslash, added for nicer PRE formatting):&lt;/p&gt;

&lt;pre&gt;
mkshortcut -a "$(cygpath -w $(which bash.exe)) $(which start-itunes)" \
    -n start-itunes.lnk -i /c/other/itunes/iTunes.exe $(which hide.exe)
&lt;/pre&gt;

&lt;p&gt;Since start-itunes and hide.exe are useful in themselves, they're on my path, so 'which' is able to find them.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10106032-2076229414169671468?l=blog.barrkel.com'/&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/EntropyOverload?a=9mSs0l4GQDo:2JI0Y0d9hEs:cGdyc7Q-1BI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/EntropyOverload?d=cGdyc7Q-1BI" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/EntropyOverload?a=9mSs0l4GQDo:2JI0Y0d9hEs:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/EntropyOverload?i=9mSs0l4GQDo:2JI0Y0d9hEs:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/EntropyOverload?a=9mSs0l4GQDo:2JI0Y0d9hEs:UT3xtbGYFzA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/EntropyOverload?d=UT3xtbGYFzA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/EntropyOverload?a=9mSs0l4GQDo:2JI0Y0d9hEs:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/EntropyOverload?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/EntropyOverload/~4/9mSs0l4GQDo" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.barrkel.com/feeds/2076229414169671468/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=10106032&amp;postID=2076229414169671468" title="1 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/10106032/posts/default/2076229414169671468" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/10106032/posts/default/2076229414169671468" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/EntropyOverload/~3/9mSs0l4GQDo/ipod-touch-itunes-and-unwanted.html" title="iPod Touch, iTunes, and unwanted processes" /><author><name>Barry Kelly</name><uri>http://www.blogger.com/profile/10559947643606684495</uri><email>barry.j.kelly@gmail.com</email><gd:extendedProperty xmlns:gd="http://schemas.google.com/g/2005" name="OpenSocialUserId" value="08331762029805209075" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">1</thr:total><feedburner:origLink>http://blog.barrkel.com/2009/01/ipod-touch-itunes-and-unwanted.html</feedburner:origLink></entry><entry><id>tag:blogger.com,1999:blog-10106032.post-7405996332900408272</id><published>2008-11-02T23:44:00.003Z</published><updated>2008-11-02T23:47:40.769Z</updated><title type="text">Somewhat more efficient smart pointers</title><content type="html">&lt;p&gt;There was a little to and fro in the comments on &lt;a href="http://barrkel.blogspot.com/2008/11/reference-counted-pointers-revisited.html"&gt;yesterday's post&lt;/a&gt; on more fluent smart pointers.&lt;/p&gt;

&lt;p&gt;It wasn't my intention to create the ultimate in performance for the smart pointer, so I didn't pay much attention to it; I focused mainly on getting an effect from composing a number of simple reusable primitives and ideas.&lt;/p&gt;

&lt;p&gt;However, I'd like to point out that since method references are just interfaces, a more efficient implementation can simply implement the interface directly. A yet more efficient implementation might choose to construct a vtable directly, and use a simple 64-bit value on the heap (32-bits for the reference count, 32-bits for the instance pointer), but I'll leave that as an exercise for the reader.&lt;/p&gt;

&lt;p&gt;Anyhow, here it is: construction is now sufficient to assign to a location of type TFunc&amp;lt;T&gt;, rather than needing an extra Wrap method:&lt;/p&gt;


&lt;pre&gt;
unit ObjHandle2;

interface

uses SysUtils;

type
  TObjectHandle&amp;lt;T: class&gt; = class(TInterfacedObject, TFunc&amp;lt;T&gt;)
  private
    FValue: T;
  public
    constructor Create(AValue: T);
    destructor Destroy; override;
    function Invoke: T;
  end;
  
implementation

constructor TObjectHandle&amp;lt;T&gt;.Create(AValue: T);
begin
  FValue := AValue;
end;

destructor TObjectHandle&amp;lt;T&gt;.Destroy;
begin
  FValue.Free;
end;

function TObjectHandle&amp;lt;T&gt;.Invoke: T;
begin
  Result := FValue;
end;

end.
&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10106032-7405996332900408272?l=blog.barrkel.com'/&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/EntropyOverload?a=jUh2N91rsZA:Ljw5AV-3At0:cGdyc7Q-1BI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/EntropyOverload?d=cGdyc7Q-1BI" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/EntropyOverload?a=jUh2N91rsZA:Ljw5AV-3At0:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/EntropyOverload?i=jUh2N91rsZA:Ljw5AV-3At0:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/EntropyOverload?a=jUh2N91rsZA:Ljw5AV-3At0:UT3xtbGYFzA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/EntropyOverload?d=UT3xtbGYFzA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/EntropyOverload?a=jUh2N91rsZA:Ljw5AV-3At0:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/EntropyOverload?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/EntropyOverload/~4/jUh2N91rsZA" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.barrkel.com/feeds/7405996332900408272/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=10106032&amp;postID=7405996332900408272" title="16 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/10106032/posts/default/7405996332900408272" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/10106032/posts/default/7405996332900408272" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/EntropyOverload/~3/jUh2N91rsZA/somewhat-more-efficient-smart-pointers.html" title="Somewhat more efficient smart pointers" /><author><name>Barry Kelly</name><uri>http://www.blogger.com/profile/10559947643606684495</uri><email>barry.j.kelly@gmail.com</email><gd:extendedProperty xmlns:gd="http://schemas.google.com/g/2005" name="OpenSocialUserId" value="08331762029805209075" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">16</thr:total><feedburner:origLink>http://blog.barrkel.com/2008/11/somewhat-more-efficient-smart-pointers.html</feedburner:origLink></entry><entry><id>tag:blogger.com,1999:blog-10106032.post-7019989795952640207</id><published>2008-11-01T19:05:00.002Z</published><updated>2008-11-01T19:19:39.830Z</updated><title type="text">Reference-counted pointers, revisited</title><content type="html">&lt;p&gt;&lt;a href="http://barrkel.blogspot.com/2008/09/smart-pointers-in-delphi.html"&gt;Some time ago, I blogged about writing smart pointers (i.e. reference-counted auto-destruction) in Delphi&lt;/a&gt;. While having dinner with some of the speakers at the &lt;a href="http://it-republik.de/entwicklerkonferenz/"&gt;EKON 12 conference&lt;/a&gt; I attended last week, a more fluent interface for using smart pointers in Delphi occurred to me.&lt;/p&gt;

&lt;p&gt;I'm using the same TSmartPointer&amp;lt;T&gt; class that I started out with in the previous article, though I've renamed it TObjectHandle&amp;lt;T&gt;. The main tricks I'm pointing out here are (1) to use method references to avoid having to use the Value property all the time, and (2) to use aliases at the point of class definition to make construction slightly more palatable.&lt;/p&gt;

&lt;p&gt;So, here's my new TObjectHandle&amp;lt;T&gt; class; the main change, apart from the name, is a new method called Wrap:&lt;/p&gt;

&lt;pre&gt;
type
  TObjectHandle&amp;lt;T: class&gt; = record
  private
    FValue: T;
    FLifetimeWatcher: IInterface;
  public
    constructor Create(const AValue: T);
    property Value: T read FValue;
    class operator Implicit(const AValue: T): TObjectHandle&amp;lt;T&gt;;
    class function Wrap(const AValue: T): TFunc&amp;lt;T&gt;; static;
  end;
&lt;/pre&gt;

&lt;p&gt;The implementation of the new method is pretty simple too:&lt;/p&gt;

&lt;pre&gt;
class function TObjectHandle&amp;lt;T&gt;.Wrap(const AValue: T): TFunc&amp;lt;T&gt;;
var
  h: TObjectHandle&amp;lt;T&gt;;
begin
  h := AValue;
  Result := function: T
  begin
    Result := h.Value;
  end;
end;
&lt;/pre&gt;

&lt;p&gt;The capture of the h local variable will mean that the handle will be kept alive as long as the method reference constructed from the anonymous method is kept alive.&lt;/p&gt;

&lt;p&gt;Here it is in use, as two versions, so that the usage difference can be seen. This is also where the additional lubrication of declaring aliases comes in. I start out with a little TCanary class which can keep track of destruction, and has a Name property to demo the fluency of the technique:&lt;/p&gt;

&lt;pre&gt;
type
  TCanary = class
  private
    FName: string;
  public
    destructor Destroy; override;
    property Name: string read FName write FName;
  end;
  
  OHCanary = TObjectHandle&amp;lt;TCanary&gt;;
  HCanary = TFunc&amp;lt;TCanary&gt;;
&lt;/pre&gt;

&lt;p&gt;The destructor prints out the name of the canary when it is destroyed. The two aliases represent an Object Handle for TCanary and a Handle for TCanary respectively. The fluent technique relies on both; the second is used for smart pointer locations and the first for smart pointer construction. There is a tradeoff involved in the technique, between construction fluency and usage fluency:&lt;/p&gt;

&lt;pre&gt;
procedure Test1;
var
  canary: OHCanary;
begin
  // easy construction (implicit operator)
  canary := TCanary.Create;
  // but cumbersome access - always need Value accessor
  canary.Value.Name := 'Test1 canary';
end;
&lt;/pre&gt;

&lt;p&gt;The new style has slightly worse construction, but better actual use:&lt;/p&gt;

&lt;pre&gt;
procedure Test2;
var
  canary: HCanary;
begin
  // cumbersome constructor
  canary := OHCanary.Wrap(TCanary.Create);
  // but much nicer access
  canary.Name := 'Test2 canary';
end;
&lt;/pre&gt;

&lt;p&gt;Without having to access everything by prefixing every access with .Value, a lot of fluency is gained, IMHO.&lt;/p&gt;

&lt;p&gt;To summarize, here's the entire ObjHandle.pas unit:&lt;/p&gt;

&lt;pre&gt;
unit ObjHandle;

interface

uses SysUtils;

type
  TObjectHandle&amp;lt;T: class&gt; = record
  private
    FValue: T;
    FLifetimeWatcher: IInterface;
  public
    constructor Create(const AValue: T);
    property Value: T read FValue;
    class operator Implicit(const AValue: T): TObjectHandle&amp;lt;T&gt;;
    class function Wrap(const AValue: T): TFunc&amp;lt;T&gt;; static;
  end;
  
  TObjectHandleArray&amp;lt;T: class&gt; = array of TObjectHandle&amp;lt;T&gt;;

procedure MakeDestroyer(Obj: TObject; out Result: IInterface);

implementation

{ TLifetimeWatcher }

type
  TLifetimeWatcher = class(TInterfacedObject)
  private
    FProc: TProc;
  public
    constructor Create(const AProc: TProc);
    destructor Destroy; override;
  end;

constructor TLifetimeWatcher.Create(const AProc: TProc);
begin
  FProc := AProc;
end;

destructor TLifetimeWatcher.Destroy;
begin
  if Assigned(FProc) then
    FProc;
  inherited;
end;

procedure MakeLifetimeWatcher(out Result: IInterface; const AProc: TProc);
begin
  Result := TLifetimeWatcher.Create(AProc);
end;
  
procedure MakeDestroyer(Obj: TObject; out Result: IInterface);
begin
  Result := TLifetimeWatcher.Create(procedure
    begin
      Obj.Free;
    end);
end;

{ TObjectHandle&amp;lt;T&gt; }

constructor TObjectHandle&amp;lt;T&gt;.Create(const AValue: T);
begin
  FValue := AValue;
  MakeDestroyer(FValue, FLifetimeWatcher);
end;

class operator TObjectHandle&amp;lt;T&gt;.Implicit(const AValue: T): TObjectHandle&amp;lt;T&gt;;
begin
  Result := TObjectHandle&amp;lt;T&gt;.Create(AValue);
end;

class function TObjectHandle&amp;lt;T&gt;.Wrap(const AValue: T): TFunc&amp;lt;T&gt;;
var
  h: TObjectHandle&amp;lt;T&gt;;
begin
  h := AValue;
  Result := function: T
  begin
    Result := h.Value;
  end;
end;

end.
&lt;/pre&gt;

&lt;p&gt;And here's the entire demo program:&lt;/p&gt;

&lt;pre&gt;
{$apptype console}

uses SysUtils, ObjHandle;

type
  TCanary = class
  private
    FName: string;
  public
    destructor Destroy; override;
    property Name: string read FName write FName;
  end;
  
  OHCanary = TObjectHandle&amp;lt;TCanary&gt;;
  HCanary = TFunc&amp;lt;TCanary&gt;;

destructor TCanary.Destroy;
begin
  Writeln(FName, ' died.');
end;

procedure Test1;
var
  canary: OHCanary;
begin
  // easy construction (implicit operator)
  canary := TCanary.Create;
  // but cumbersome access - always need Value accessor
  canary.Value.Name := 'Test1 canary';
end;

procedure Test2;
var
  canary: HCanary;
begin
  // cumbersome constructor
  canary := OHCanary.Wrap(TCanary.Create);
  // but much nicer access
  canary.Name := 'Test2 canary';
end;

begin
  Test1;
  Test2;
end.
&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10106032-7019989795952640207?l=blog.barrkel.com'/&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/EntropyOverload?a=RVhj0HzF-Tc:BU4NAXl5AFo:cGdyc7Q-1BI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/EntropyOverload?d=cGdyc7Q-1BI" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/EntropyOverload?a=RVhj0HzF-Tc:BU4NAXl5AFo:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/EntropyOverload?i=RVhj0HzF-Tc:BU4NAXl5AFo:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/EntropyOverload?a=RVhj0HzF-Tc:BU4NAXl5AFo:UT3xtbGYFzA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/EntropyOverload?d=UT3xtbGYFzA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/EntropyOverload?a=RVhj0HzF-Tc:BU4NAXl5AFo:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/EntropyOverload?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/EntropyOverload/~4/RVhj0HzF-Tc" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.barrkel.com/feeds/7019989795952640207/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=10106032&amp;postID=7019989795952640207" title="15 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/10106032/posts/default/7019989795952640207" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/10106032/posts/default/7019989795952640207" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/EntropyOverload/~3/RVhj0HzF-Tc/reference-counted-pointers-revisited.html" title="Reference-counted pointers, revisited" /><author><name>Barry Kelly</name><uri>http://www.blogger.com/profile/10559947643606684495</uri><email>barry.j.kelly@gmail.com</email><gd:extendedProperty xmlns:gd="http://schemas.google.com/g/2005" name="OpenSocialUserId" value="08331762029805209075" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">15</thr:total><feedburner:origLink>http://blog.barrkel.com/2008/11/reference-counted-pointers-revisited.html</feedburner:origLink></entry><entry><id>tag:blogger.com,1999:blog-10106032.post-9008694833807083192</id><published>2008-10-20T11:28:00.003+01:00</published><updated>2008-10-20T13:16:44.809+01:00</updated><title type="text">Types: Values versus Locations</title><content type="html">&lt;p&gt;Minor thought I had this morning: I was doing some prep work for &lt;a href="http://it-republik.de/entwicklerkonferenz/"&gt;the conference&lt;/a&gt; I'm speaking at next week, and I noticed I was being perhaps overly pedantic about the terminology of types in a way that only matters for imperative languages.&lt;/p&gt;

&lt;p&gt;I habitually make explicit the distinction between values and locations of a particular type. I might say, the storing of values of multiple types in a location of a single type is an instance of polymorphism. In describing a class, I might say, this is "an iterator over a stream of values of type T", instead of just saying "an iterator over a stream of T" (no, not a teapot!).&lt;/p&gt;

&lt;p&gt;The distinction is important in languages that have mutable state. Locations can have their address taken, they are subject to polymorphism, and the location itself has an identity independent of its value. Values, on the other hand, cannot have their address taken - at most, the value &lt;b&gt;is or contains&lt;/b&gt; an address. Values always have a fixed type, but locations may contain values of different types if the type of the location is polymorphic. Locations may be lvalues or rvalues, but values are always rvalues (unless you dereference, index or field-access them). Especially important is the fact that implementations of closures in imperative languages like C#, Delphi, Ruby, etc. have almost always opted to capture locations, not values.&lt;/p&gt;

&lt;p&gt;However, consider a pure language, like Haskell. If the language doesn't have mutable state, there's no such thing as a location (at the conceptual level). If you have an iterator in such a language (perhaps modeled using a tail-call continuation design), it's redundant to say "iterator over a stream of values of T" - it's always OK to say "iterator over a stream of T" instead. And the closures might capture values or locations, as performance demands, since the semantics don't change.&lt;/p&gt;

&lt;p&gt;Painfully Amateur Philosophy addendum: locations are a pretty physical concept - you know the data is in there in memory somewhere - but values are  more of a platonic concept, existing in some pure universe, and we can only refer to them by metaphor and convention by using specific bit patterns interpreted in precise ways. I specifically use the word &lt;b&gt;&lt;a href="http://en.wikipedia.org/wiki/Conceptual_metaphor"&gt;metaphor&lt;/a&gt;&lt;/b&gt; in the conceptual metaphor sense: in no way are the electron levels inside the machine representing the ASCII characters of "cat" anything like the furry mammal. Rather, the bit pattern is like a pointer only the human mind can dereference, once it has been transformed into what humans have agreed to be a semantically equivalent representation on screen (which is just a different part of memory) or on paper (which is just bits streamed out over the wire).&lt;/p&gt;

&lt;p&gt;Perhaps most people find this obvious and boring, but what interests me is the way the representational power of the bits is entirely unmagical, yet it permits meaning to be stored inside the machine. I say &lt;b&gt;meaning&lt;/b&gt;, by which I mean that we humans, the only judges of what is meaningful, &lt;b&gt;find&lt;/b&gt; to be meaningful, but no more: I do not think meaning is something inherent in objects, just in judges. What if brains had no more magic in their neurons than the circuits in the machine? (It seems entirely plausible to me, and to many programmers I imagine. And this would constrain those judges to be mere boolean functions over matter patterns.)&lt;/p&gt;

&lt;p&gt;In such a scenario, &lt;a href="http://en.wikipedia.org/wiki/Qualia"&gt;qualia&lt;/a&gt; would be beyond our power to deconstruct using physical means, since the brain/machine could be evaluated "on paper", and such an evaluated brain would report the same qualia as you or I, and we would have no way to argue otherwise in a one on one dialogue (this is how I think of the Turing test - as a philosophical concept, not an actual benchmark, which I think is silly and pointless). Under this assumption, there isn't any argument that could prove that the machine isn't conscious which doesn't itself rely on arbitrarily chosen boolean functions which explicitly return false for non-mysterious matter patterns (e.g. &lt;a href="http://en.wikipedia.org/wiki/Chinese_Room"&gt;capable of "understanding"&lt;/a&gt;). Closely related is the &lt;a href="http://en.wikipedia.org/wiki/Problem_of_other_minds"&gt;problem of other minds&lt;/a&gt;, about which I'm on Turing's side - if we can't tell the difference, then there isn't any.&lt;/p&gt;

&lt;p&gt;Perhaps consciousness and the experience of qualia is just what matter feels like when it's part of a causal chain? (The technical term, I understand from my Googling, is "&lt;a href="http://en.wikipedia.org/wiki/Type_physicalism"&gt;type physicalism&lt;/a&gt;", though epiphenominalism is related.)&lt;/p&gt;

&lt;p&gt;Philosophy over. I need to get back to work :)&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10106032-9008694833807083192?l=blog.barrkel.com'/&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/EntropyOverload?a=CmY_0isX3UM:dHcvGrM4SPA:cGdyc7Q-1BI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/EntropyOverload?d=cGdyc7Q-1BI" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/EntropyOverload?a=CmY_0isX3UM:dHcvGrM4SPA:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/EntropyOverload?i=CmY_0isX3UM:dHcvGrM4SPA:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/EntropyOverload?a=CmY_0isX3UM:dHcvGrM4SPA:UT3xtbGYFzA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/EntropyOverload?d=UT3xtbGYFzA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/EntropyOverload?a=CmY_0isX3UM:dHcvGrM4SPA:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/EntropyOverload?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/EntropyOverload/~4/CmY_0isX3UM" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.barrkel.com/feeds/9008694833807083192/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=10106032&amp;postID=9008694833807083192" title="1 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/10106032/posts/default/9008694833807083192" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/10106032/posts/default/9008694833807083192" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/EntropyOverload/~3/CmY_0isX3UM/types-values-versus-locations.html" title="Types: Values versus Locations" /><author><name>Barry Kelly</name><uri>http://www.blogger.com/profile/10559947643606684495</uri><email>barry.j.kelly@gmail.com</email><gd:extendedProperty xmlns:gd="http://schemas.google.com/g/2005" name="OpenSocialUserId" value="08331762029805209075" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">1</thr:total><feedburner:origLink>http://blog.barrkel.com/2008/10/types-values-versus-locations.html</feedburner:origLink></entry><entry><id>tag:blogger.com,1999:blog-10106032.post-9170893597039537707</id><published>2008-10-18T23:26:00.006+01:00</published><updated>2008-10-19T23:55:05.278+01:00</updated><title type="text">WASD's little known alternative, QWAS</title><content type="html">&lt;p&gt;I was &lt;a href="http://www.tweaktown.com/reviews/1627/microsoft_sidewinder_x6_gaming_keyboard/index.html"&gt;reading a review&lt;/a&gt; of a new Microsoft gaming keyboard today and I noticed that it has extra highlighting for the traditional &lt;a href="http://en.wikipedia.org/wiki/WASD"&gt;WASD keyboard layout&lt;/a&gt; most commonly used by &lt;a href="http://en.wikipedia.org/wiki/First-person_shooter"&gt;first-person shooters (FPSs)&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This interests me because I don't use WASD; I use a variant that I have almost never seen anyone else describe, namely, QWAS, with W for forward, Q/S for strafe left/right, and A for reverse.&lt;/p&gt;

&lt;p&gt;It was a long time in gestation. I started out playing Doom with just the keyboard, like most other people at the time, but then I read - I forget where and can't find the reference - about using the mouse for looking and the keyboard, specifically the Z and X keys, for strafing, with Space, Ctrl and mouse buttons used in some combination for Use, Fire, Forward, Run, etc. I didn't find this combination especially easy to use, though, so it wasn't long before I stumbled onto the QWAS hold I use today. I do, however, find it puzzling that so few other people use it.&lt;/p&gt;

&lt;p&gt;The reason I don't use WASD is because I think the layout is awkward for the wrist when the keyboard is centrally positioned, ready for touch-typing:
&lt;br&gt;
&lt;img src="http://1.bp.blogspot.com/_2GNT8vlZj48/SPpizjQGoTI/AAAAAAAAADA/Ovaq2i2Ui0k/s1600/wasd.jpg"&gt;
&lt;br&gt;
Moreover, since, like most men, &lt;a href="http://www.google.com/search?q=ring+index+finger+length"&gt;my ring finger is much longer than my index finger&lt;/a&gt;, WASD means my ring finger on the A key is in an uncomfortable cramp-prone position:
&lt;br&gt;
&lt;img src="http://3.bp.blogspot.com/_2GNT8vlZj48/SPu5c_Ha8oI/AAAAAAAAADI/949dPCNbyog/s1600/wasd-finger.jpg"&gt;
&lt;/p&gt;

&lt;p&gt;When this is compared with the QWAS layout, the advantage in comfort and ergonomics - for my hand shape and keyboard positioning, at least - is clear:&lt;br&gt;

&lt;img src="http://1.bp.blogspot.com/_2GNT8vlZj48/SPu50iMpwFI/AAAAAAAAADQ/83CqjBegfTo/s1600/qwas.jpg"&gt;
&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10106032-9170893597039537707?l=blog.barrkel.com'/&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/EntropyOverload?a=1SaiYpnV4_k:tw4gaIzLybs:cGdyc7Q-1BI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/EntropyOverload?d=cGdyc7Q-1BI" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/EntropyOverload?a=1SaiYpnV4_k:tw4gaIzLybs:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/EntropyOverload?i=1SaiYpnV4_k:tw4gaIzLybs:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/EntropyOverload?a=1SaiYpnV4_k:tw4gaIzLybs:UT3xtbGYFzA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/EntropyOverload?d=UT3xtbGYFzA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/EntropyOverload?a=1SaiYpnV4_k:tw4gaIzLybs:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/EntropyOverload?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/EntropyOverload/~4/1SaiYpnV4_k" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.barrkel.com/feeds/9170893597039537707/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=10106032&amp;postID=9170893597039537707" title="5 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/10106032/posts/default/9170893597039537707" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/10106032/posts/default/9170893597039537707" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/EntropyOverload/~3/1SaiYpnV4_k/wasds-little-known-alternative-qwas.html" title="WASD's little known alternative, QWAS" /><author><name>Barry Kelly</name><uri>http://www.blogger.com/profile/10559947643606684495</uri><email>barry.j.kelly@gmail.com</email><gd:extendedProperty xmlns:gd="http://schemas.google.com/g/2005" name="OpenSocialUserId" value="08331762029805209075" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://1.bp.blogspot.com/_2GNT8vlZj48/SPpizjQGoTI/AAAAAAAAADA/Ovaq2i2Ui0k/s72-c/wasd.jpg" height="72" width="72" /><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">5</thr:total><feedburner:origLink>http://blog.barrkel.com/2008/10/wasds-little-known-alternative-qwas.html</feedburner:origLink></entry><entry><id>tag:blogger.com,1999:blog-10106032.post-504857840115934334</id><published>2008-10-18T19:22:00.003+01:00</published><updated>2008-10-18T19:52:55.531+01:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="open source" /><category scheme="http://www.blogger.com/atom/ns#" term="competition" /><category scheme="http://www.blogger.com/atom/ns#" term="buy" /><category scheme="http://www.blogger.com/atom/ns#" term="build" /><category scheme="http://www.blogger.com/atom/ns#" term="business" /><title type="text">The Search for Differentiation and Build vs Buy/Adopt/Coopt</title><content type="html">&lt;p&gt;There's &lt;a href="http://duartes.org/gustavo/blog/post/most-programming-is-not-core"&gt;been&lt;/a&gt; some &lt;a href="http://www.codinghorror.com/blog/archives/001172.html"&gt;chatter&lt;/a&gt; in the development blogosphere recently about that old chestnut, the build vs buy decision; but in these days of open source development, there are more alternatives to build: adoption and cooption (private forking / customized modules etc.).&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.codinghorror.com/blog/archives/001172.html"&gt;Jeff Atwood wrote an article&lt;/a&gt; about not being ashamed to write your own software (i.e. build) when the functionality in question is core to your business. Much of the reaction to Jeff's fairly strongly stated article has been pulling in the opposite direction. I'd like to argue back in Jeff's direction, though.&lt;/p&gt;

&lt;p&gt;The basic trouble I have is that the vast majority of open source is crap. Once you tread outside the critical path of high-user-base server-side software, like (to take three examples completely at random) Linux, Hibernate and MySQL, you very quickly get stuck in the mire; even then, much of the "good" open-source software is only good in parts.&lt;/p&gt;

&lt;p&gt;For example, if you're looking for a mature, well-designed and &lt;b&gt;stable&lt;/b&gt; interface for writing drivers for Linux - exactly what is needed to make supplying binary drivers viable for hardware vendors - tough luck, buddy. Economically speaking, &lt;b&gt;incentives in open source are almost always not aligned with consumers' interests&lt;/b&gt;, but rather with the companies which hire the developers: and those guys are usually consultant / service shops, i.e. aligned with &lt;b&gt;corporate&lt;/b&gt; customers, not consumers. Firefox is an exception because of Google advertising revenue, which is &lt;b&gt;correlated with consumer adoption&lt;/b&gt;, thus their incentives are aligned.&lt;/p&gt;

&lt;p&gt;Rails has a solid concept and has a reasonably good API, but its execution is, IMHO, not great. I have little patience for server-side technologies that aren't thread-safe, as request/response cycles are trivially parallel; though, from an historical viewpoint I can understand it, as relatively few scripting languages implement threading. All the same, though, the kind of coding that would make a server-side library thread-unsafe, I wouldn't tolerate even if the platform was single-threaded: it implies poorly-factored mutable global state. I wouldn't touch PHP with a barge pole - I have zero technical respect for the language.&lt;/p&gt;

&lt;p&gt;To compete, you need differentiation: you need to do something, for some subset of the market, in a way that is head and shoulders better than every other choice. Open source (or, indeed, buying) can only give you what everyone else has; you need to add in the magic yourself that makes you stand out. Private forks of open-source software are one way of doing that, but is vulnerable to all the usual problems that forking has (staying current, merging bugfixes, divergent evolution, etc.). Creating your differentiating software yourself means your codebase will be smaller and more perfectly adapted to your problem domain, and thus easier to maintain and keep up to date than a private fork of an ongoing public open-source project.&lt;/p&gt;

&lt;p&gt;The only real question is which software in your organization is, in the language of strategic management, going to help give you a sustainable competitive advantage. Open source software and third-party libraries can never give you that (unless you own the founding team &amp; brand), because they are equally available to your competitors. Choosing the balance of build vs buy means trading off risk and cost versus lack of differentiation. You have to measure and compare the resources available to you and the risk of non-completion of differentiated software, taking into account opportunity cost, with the lack of differentiation that would come from the buy / ad/coopt decision.&lt;/p&gt;

&lt;p&gt;Where the balance lies thus depends on both sides: how much [human+monetary] capital you have, versus the competitive landscape on the other. Whether a third-party library exists or not, in and of itself, is not a sufficient reason to always use it. Making users happy isn't sufficient; you must make them &lt;b&gt;happier&lt;/b&gt; than the competition, and that's not going to happen if you don't do something the competition isn't already doing. The only thing that holds you back is risk &amp;amp; capital versus the reward in differentiation.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10106032-504857840115934334?l=blog.barrkel.com'/&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/EntropyOverload?a=DNMmFVe7FNs:mwJ3zfrCr4A:cGdyc7Q-1BI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/EntropyOverload?d=cGdyc7Q-1BI" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/EntropyOverload?a=DNMmFVe7FNs:mwJ3zfrCr4A:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/EntropyOverload?i=DNMmFVe7FNs:mwJ3zfrCr4A:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/EntropyOverload?a=DNMmFVe7FNs:mwJ3zfrCr4A:UT3xtbGYFzA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/EntropyOverload?d=UT3xtbGYFzA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/EntropyOverload?a=DNMmFVe7FNs:mwJ3zfrCr4A:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/EntropyOverload?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/EntropyOverload/~4/DNMmFVe7FNs" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.barrkel.com/feeds/504857840115934334/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=10106032&amp;postID=504857840115934334" title="2 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/10106032/posts/default/504857840115934334" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/10106032/posts/default/504857840115934334" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/EntropyOverload/~3/DNMmFVe7FNs/search-for-differentiation-and-build-vs.html" title="The Search for Differentiation and Build vs Buy/Adopt/Coopt" /><author><name>Barry Kelly</name><uri>http://www.blogger.com/profile/10559947643606684495</uri><email>barry.j.kelly@gmail.com</email><gd:extendedProperty xmlns:gd="http://schemas.google.com/g/2005" name="OpenSocialUserId" value="08331762029805209075" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">2</thr:total><feedburner:origLink>http://blog.barrkel.com/2008/10/search-for-differentiation-and-build-vs.html</feedburner:origLink></entry><entry><id>tag:blogger.com,1999:blog-10106032.post-4290207528369414439</id><published>2008-09-25T19:05:00.003+01:00</published><updated>2008-09-25T19:40:50.292+01:00</updated><title type="text">Anonymous methods as an alternative to 'out' parameters</title><content type="html">&lt;p&gt;Out parameters are a useful language feature for multiple results, particularly when the language in question doesn't have tuples as a first-class feature.  Even though Java omitted pass-by-reference (meaning both &lt;b&gt;&lt;code&gt;var&lt;/code&gt;&lt;/b&gt; and &lt;b&gt;&lt;code&gt;out&lt;/code&gt;&lt;/b&gt; in Delphi parlance), C# did not follow its lead and includes both parameter-passing semantics. And so it follows that the .NET method System.Int32.TryParse(String,Int32&amp;) is possible in C#, but not in Java.&lt;/p&gt;

&lt;p&gt;The API works well enough when you want to put the extra return value(s) into local variables or visible fields. What about times when you want to put the return value into a property, though? Since you can't (generally) take a reference to a property, you have to manually create a local variable, pass the local variable by reference, and then, in a separate statement, assign the variable to the property.&lt;/p&gt;

&lt;p&gt;Anonymous methods can provide an "out" here, if you'll pardon the pun. By passing a &lt;b&gt;setter&lt;/b&gt; instead of a mutable reference, you can escape from this constraint. Here's are two overloaded &lt;code&gt;ReadLine&lt;/code&gt; functions, one using the &lt;b&gt;&lt;code&gt;out&lt;/code&gt;&lt;/b&gt; mechanism, the other using the setter pattern:&lt;/p&gt;

&lt;pre&gt;
function ReadLine(const Prompt: string; out Dest: string): Boolean; overload;
var
  line: string;
begin
  Write(Prompt, '&gt; ');
  Flush(Output);
  Readln(line);
  Result := line &amp;lt;&gt; '';
  if Result then
    Dest := line;
end;

function ReadLine(const Prompt: string; const Setter: TProc&amp;lt;string&gt;): Boolean; overload;
var
  line: string;
begin
  Write(Prompt, '&gt; ');
  Flush(Output);
  Readln(line);
  Result := line &amp;lt;&gt; '';
  if Result then
    Setter(line);
end;
&lt;/pre&gt;

&lt;p&gt;As can be seen, the code that uses setters versus out parameters is little different. (It would be more different in a language that requires definite assignment, like C#, as otherwise we would have to set Dest to some value in the case of no entry.)&lt;/p&gt;

&lt;p&gt;The real difference is in the usage point of this idiom. (And don't forget, this is an idiom; it might look odd the first time you see it, but when you understand anonymous methods, or even just the idiom, it's a trivial pattern and easy to understand.) Here's the definition of a class with a couple of properties, and a couple of functions. The first uses the &lt;b&gt;&lt;code&gt;out&lt;/code&gt;&lt;/b&gt; idiom, and the second uses the &lt;b&gt;setter&lt;/b&gt; idiom:&lt;/p&gt;

&lt;pre&gt;
type
  TFrob = class
  private
    FFoo: string;
    FBar: string;
  public
    property Foo: string read FFoo write FFoo;
    property Bar: string read FBar write FBar;
  end;

// Using 'out' idiom - requires separate assignment statement
function WorkWithFrob: Boolean;
var
  frob: TFrob;
  temp: string;
begin
  frob := TFrob.Create;
  try
    if not ReadLine('Give me Foo', temp) then
      Exit(False);
    frob.Foo := temp;
    if not ReadLine('Give me Bar', temp) then
      Exit(False);
    frob.Bar := temp;
    Writeln(Format('Your foo is %s and bar is %s', [frob.Foo, frob.Bar]));
    Result := True;
  finally
    frob.Free;
  end;
end;

// Using 'setter' idiom - can be done inline
function WorkWithFrob2: Boolean;
var
  frob: TFrob;
begin
  frob := TFrob.Create;
  try
    if not ReadLine('Give me Foo', procedure(x: string) begin frob.Foo := x; end)
        or not ReadLine('Give me Bar', procedure(x: string) begin frob.Bar := x; end) then
      Exit(False);
    Writeln(Format('Your foo is %s and bar is %s', [frob.Foo, frob.Bar]));
    Result := True;
  finally
    frob.Free;
  end;
end;
&lt;/pre&gt;

&lt;p&gt;This idiom isn't useful very often, but when the right situation comes up, it's really quite nice to have, especially when you need &lt;b&gt;persistent&lt;/b&gt; references to properties - e.g. when you need to store the reference in some structure somewhere, and update it with different values over time.&lt;/p&gt;

&lt;p&gt;Viewed more generally, anonymous methods / method references can act as kinds of pipes that join up producers and consumers of data, with getters (TFunc&amp;lt;TResult&gt;) for consumer-pull and setters (TProc&amp;lt;TArg&gt;) for producer-push.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10106032-4290207528369414439?l=blog.barrkel.com'/&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/EntropyOverload?a=fX4rcAxyylE:bwzSOnpKoXo:cGdyc7Q-1BI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/EntropyOverload?d=cGdyc7Q-1BI" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/EntropyOverload?a=fX4rcAxyylE:bwzSOnpKoXo:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/EntropyOverload?i=fX4rcAxyylE:bwzSOnpKoXo:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/EntropyOverload?a=fX4rcAxyylE:bwzSOnpKoXo:UT3xtbGYFzA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/EntropyOverload?d=UT3xtbGYFzA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/EntropyOverload?a=fX4rcAxyylE:bwzSOnpKoXo:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/EntropyOverload?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/EntropyOverload/~4/fX4rcAxyylE" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.barrkel.com/feeds/4290207528369414439/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=10106032&amp;postID=4290207528369414439" title="9 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/10106032/posts/default/4290207528369414439" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/10106032/posts/default/4290207528369414439" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/EntropyOverload/~3/fX4rcAxyylE/anonymous-methods-as-alternative-to-out.html" title="Anonymous methods as an alternative to 'out' parameters" /><author><name>Barry Kelly</name><uri>http://www.blogger.com/profile/10559947643606684495</uri><email>barry.j.kelly@gmail.com</email><gd:extendedProperty xmlns:gd="http://schemas.google.com/g/2005" name="OpenSocialUserId" value="08331762029805209075" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">9</thr:total><feedburner:origLink>http://blog.barrkel.com/2008/09/anonymous-methods-as-alternative-to-out.html</feedburner:origLink></entry><entry><id>tag:blogger.com,1999:blog-10106032.post-26488531543377002</id><published>2008-09-17T17:40:00.003+01:00</published><updated>2008-09-17T19:08:10.266+01:00</updated><title type="text">Google Developer Day 2008, London</title><content type="html">&lt;p&gt;I was at &lt;a href="http://code.google.com/intl/en_uk/events/developerday/2008/agenda.html"&gt;Google Developer Day&lt;/a&gt; here in London yesterday. Most of it was pretty light on technical details, but there was some good info on &lt;a href="http://code.google.com/apis/v8/design.html"&gt;V8&lt;/a&gt;, Google's new JavaScript engine which is used in Google Chrome.
&lt;/p&gt;

&lt;p&gt;The specific sessions I went to were Intro to Android, Intro to Android SDK, Google Data API mashups, and V8 - "the Chrome Engine" (sic). The heavy tilt towards Android was chiefly driven by curiosity after Mike Jennings took out a pre-production &lt;a href="http://www.openhandsetalliance.com/"&gt;OHA&lt;/a&gt; phone and demoed it in the keynote. It looks reasonably neat, borrowing some nifty effects from Apple's iPhone; no pinch-zooming though.&lt;/p&gt;

&lt;p&gt;Intro to Android was a mix of business / very lightweight technical details about the phone. Apparently, Google has twisted some arms in back-rooms to get buy-in from operators &amp; OEMs, so anything with the OHA / Android branding etc. should have some minimum level of openness etc. Should be a welcome relief from the vice-grips of Apple. There wasn't much description of the Dalvik VM running behind the scenes, other than that you take your .class files from javac and run them through a processor to get running on the device, and apparently the guy Mike talks to about the VM is "very bright". That was mentioned a number of times, so it must be important.&lt;/p&gt;

&lt;p&gt;The Android SDK intro was given by a guy (Carl-Gusaf Harroch) from a &lt;a href="http://novoda.com"&gt;local startup&lt;/a&gt;, not an actual Google guy. He very roughly described &lt;a href="http://code.google.com/android/devel/data/contentproviders.html"&gt;content providers&lt;/a&gt;, and also briefly outlined some entries in the application manifests, which are the mechanism by which the Android OS figures out what events your application is interested in (phone call arrived, moved certain distance according to GPS, that kind of thing). There was a laboured comparison of content providers with REST, in that there are methods that correspond to CRUD operations, but apparently there are other concerns such as observability etc. which make them not as simple as REST (and thus an invalid comparison, in my view). Apparently content stored on the phone and exposed to other applications is heavily skewed towards assuming that the content is living in an SQLite DB.&lt;/p&gt;

&lt;p&gt;The GData mashup session wasn't interesting to anyone who has interacted with Google REST / AtomPub APIs even trivially. &lt;a href="http://barrkel.blogspot.com/2006/08/blogger-api.html"&gt;Once upon a time&lt;/a&gt; I wrote a blogger post app, so I didn't learn much.&lt;/p&gt;

&lt;p&gt;Oh, and if you &lt;b&gt;are&lt;/b&gt; writing a client to GData etc., I recommend that you don't start by trying to grok any of the Google API libraries unless you need deep integration. I didn't like the look of them last time (Java-itis, factories etc. everywhere), and I'm fairly sure they haven't improved.&lt;/p&gt;

&lt;p&gt;Finally, I went to the V8 talk by &lt;a href="http://www.brics.dk/~kmilli/"&gt;Kevin Millikin&lt;/a&gt;. This was the best and most technical by a long, long way; to be honest, if it hadn't been for this talk, the day would have been a waste, on net. He described some &lt;a href="http://code.google.com/apis/v8/design.html"&gt;V8 implementation details&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Roughly speaking, the main approach V8 takes is to try and shoe-horn classes into the JavaScript dynamic type system so that other, more classical dynamic object-oriented optimizations can be applied in the future. The way it works is with hidden classes. Every freshly new'd up object obj gets the same class, call it class_0. The first time you add a property, call it 'x', to obj, a new class class_1 is created which has a single property called 'x'. Additionally, a class transition edge is added to class_0 pointing at class_1, and this edge is labeled 'x'. This means that all objects which are freshly new'd up and have a single property called 'x' added to them will &lt;b&gt;all share the same class&lt;/b&gt;. So, in theory, every object in the system which has the same properties &lt;b&gt;in the same order&lt;/b&gt; will share the same class. The class transitions described above form a tree (I asked), and can't close over to make a dag, because if that happened, enumeration order would change. (I believe this is an implementation detail of ECMA, so people shouldn't be relying on it; however, it's something the V8 folks discovered that they could not change from extant practice.) This in turn means that if you do have conditional branches in your JavaScript constructors, you should assign to all properties in the same order, if that is feasible.&lt;/p&gt;

&lt;p&gt;Anyhow, the use of classes as indicated above means that object use sites can now be optimized based on the runtime class of the object. Here's a specific example: whenever you access a property in JS running on V8, the access site will be a little stub function using one of 4 templates: uninitialized, pre-monomorphic, fast-monomorphic, and megamorphic. The first time the access site is invoked, the runtime class is inspected and noted, and the stub moves to the pre-monomorphic state. The second time it's accessed, the runtime class is checked against the previous class and if it's the same, the stub moves to the fast-monomorphic state, and is written so that it is very simple: compare object type, if it's as expected then dereference to object storage, then load property at the specific offset (stored in the property in the class but inlined as machine code in the access site). If the class wasn't as expected, then the stub is changed to the megamorphic state. Finally, the megamorphic state is the slow path that falls back to hash-based lookup, just like most other JS implementations.&lt;/p&gt;

&lt;p&gt;Since arrays in JS are just tightly-packed hash buckets with particular key patterns, the same approach could be taken but it wouldn't necessarily be fast. Apparently the V8 folks discovered that a lot of artificial JS benchmarks were based around array manipulations, so they put a little work in this, but they're not finished in this area (as far as I could make out). In any case, array access strategy is governed by a heuristic; for small packed arrays accessed with integers, a direct lookup can be made. For larger, sparse arrays, the property access mechanism is used.&lt;/p&gt;

&lt;p&gt;Variable capture in closures (a topic dear to my heart at this point) also works along similar lines as property creation, from what Kevin described. There is a caveat: if you are using the "with" statement in JavaScript, or there is &lt;b&gt;eval(&lt;/b&gt; between the variable definition and the access site in your closure, then the access can't be optimized; these constructs fall back to the hash-based lookup, because they effectively need to use dynamic scope. So, don't create closures in that way unless you're prepared for the speed bump.&lt;/p&gt;

&lt;p&gt;The majority of the JS standard library, things like array.join etc., are implemented in JavaScript itself in V8. They're using a special %AddProperty function to make them non-enumerable. V8 also uses a freeze-dried heap mechanism to reduce the cost of initializing the standard library. It can essentially save and restore the heap, with appropriate relocations etc. as required, so that it doesn't have to reparse everything on startup.&lt;/p&gt;

&lt;p&gt;The garbage collector for V8 looks less interesting as a source of performance. It's almost certainly an improvement on what other JS implementations are using for GC, but I think it's some way from the last word on the topic. It has only 2 generations, so intermediate live objects that get promoted when collecting new-space will eventually force a costly old-space collection. Kevin didn't say that they were using write barriers to reduce need to scan old-space looking for pointers to new-space, but grepping the V8 sources turns up some write barrier hits, so maybe they are. V8's GC is definitely better than reference counting as implemented in IE of older days, of course.&lt;/p&gt;

&lt;p&gt;Since the main approach taken thus far was just to give values a class, and use that to optimize property access, there is still a lot of scope for optimization in V8. I wouldn't be surprised to see significant (2-5x) performance improvements in the not too distant future in V8, as more techniques are integrated. They're currently going straight from the JS AST to machine code, no inlining of aforementioned property access sites (AFAICT - there was a 'ret' at the end of the demo access site). The main thing is (a) they have objects pinned down to types now, and (b) hopefully as JS developers learn how to make code run fast under this paradigm, objects will look even more type-ful and thus increase scope for other optimizations.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10106032-26488531543377002?l=blog.barrkel.com'/&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/EntropyOverload?a=kBCNfK93XAE:oA6wIAUrdU0:cGdyc7Q-1BI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/EntropyOverload?d=cGdyc7Q-1BI" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/EntropyOverload?a=kBCNfK93XAE:oA6wIAUrdU0:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/EntropyOverload?i=kBCNfK93XAE:oA6wIAUrdU0:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/EntropyOverload?a=kBCNfK93XAE:oA6wIAUrdU0:UT3xtbGYFzA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/EntropyOverload?d=UT3xtbGYFzA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/EntropyOverload?a=kBCNfK93XAE:oA6wIAUrdU0:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/EntropyOverload?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/EntropyOverload/~4/kBCNfK93XAE" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.barrkel.com/feeds/26488531543377002/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=10106032&amp;postID=26488531543377002" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/10106032/posts/default/26488531543377002" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/10106032/posts/default/26488531543377002" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/EntropyOverload/~3/kBCNfK93XAE/google-developer-day-2008-london.html" title="Google Developer Day 2008, London" /><author><name>Barry Kelly</name><uri>http://www.blogger.com/profile/10559947643606684495</uri><email>barry.j.kelly@gmail.com</email><gd:extendedProperty xmlns:gd="http://schemas.google.com/g/2005" name="OpenSocialUserId" value="08331762029805209075" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total><feedburner:origLink>http://blog.barrkel.com/2008/09/google-developer-day-2008-london.html</feedburner:origLink></entry><entry><id>tag:blogger.com,1999:blog-10106032.post-275827614727842642</id><published>2008-09-11T04:01:00.003+01:00</published><updated>2008-09-11T04:16:38.554+01:00</updated><title type="text">Exogenous Exceptions (oh and another Vista rant :)</title><content type="html">&lt;p&gt;&lt;a href="http://blogs.msdn.com/ericlippert/archive/2008/09/10/vexing-exceptions.aspx"&gt;Eric Lippert has just posted&lt;/a&gt; an entry about "vexing exceptions", talking about the various buckets he classifies exceptions into and different approaches for handling them:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;
&lt;b&gt;Vexing exceptions&lt;/b&gt; are the result of unfortunate design decisions. Vexing exceptions are thrown in a completely non-exceptional circumstance, and therefore must be caught and handled all the time.
&lt;/p&gt;
&lt;p&gt;[...]&lt;/p&gt;
&lt;p&gt;
The classic example of a vexing exception is Int32.Parse, which throws if you give it a string that cannot be parsed as an integer. But the 99% use case for this method is transforming strings input by the user, which could be any old thing, and therefore it is in no way exceptional for the parse to fail.
&lt;/p&gt;
&lt;p&gt;[...]&lt;/p&gt;
&lt;p&gt;
And finally, &lt;b&gt;exogenous exceptions&lt;/b&gt; appear to be somewhat like vexing exceptions except that they are not the result of unfortunate design choices. Rather, they are the result of untidy external realities impinging upon your beautiful, crisp program logic. [Example of file-not-found follows, and points out that File.Exists check would only be a race.]
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;However, I strongly disagree on his suggestion that you try to catch exogenous exceptions, and somewhat disagree on the "vexing" exceptions (here, it's probably just the specific example he chose that I don't really agree with).&lt;/p&gt;

&lt;p&gt;In my opinion, exceptions should, in general, be propagated all the way to a user-visible dialog box / notification message, if the exception represents an error by the user. So, for example, if a user enters a floating-point value or a string value in a box that should semantically be an integer, it is fine for an exception in Int32.Parse to bubble its way back into the user's view - so long as the message in the exception is meaningful to the user, and is written in the correct language / jargon etc. If not, then certainly the exception should be wrapped in another exception and re-propagated, but the exception itself shouldn't be just caught.
&lt;/p&gt;

&lt;p&gt;
Of course, if there is valid logic for a failure case that doesn't simply mean telling the user about a problem in their input / what they're trying to do, then that's a situation where TryParse etc. makes sense.
&lt;/p&gt;

&lt;p&gt;
On the exogenous question, catching these is &lt;b&gt;extremely&lt;/b&gt; problematic, because when the user eventually sees the message, they'll need to figure out what went wrong. So, if you have a file access problem, the very first exception raised - the exogenous one when the CreateFile call at the heart of things failed - should have a message associated with it indicating that such-and-such file couldn't be accessed. This is the same message that should eventually be propagated to the user, either in an event log, a dialog box, or other notification mechanism, particularly if the high-level attempted action was user-initiated.
&lt;/p&gt;

&lt;p&gt;
To do otherwise leaves the user stranded with a generic, non-specific (and thus meaningless) "I couldn't do something" message. I've seen far too much MS software that gives non-specific and non-actionable error messages in failure situations that this kind of advice &lt;b&gt;really&lt;/b&gt; annoys me unless it is very carefully qualified and described.
&lt;/p&gt;

&lt;p&gt;
If anything, exceptions should be wrapped rather than caught, with higher-level semantic information wrapping the underlying reason. So, if you're trying to e.g. modify a record in a data entry application, the chain of failure might be "couldn't modify record because" -&gt; "couldn't contact database for locking because" -&gt; "couldn't connect to database server because" -&gt; "remote server name FooBar could not be found". This kind of message has information about every level of the stack, and should a user have to e.g. contact IT, the full message (the technical details can be hidden in a pop-out dialog etc.) is 100% actionable, and even regular users, let alone power users, may find it actionable.
&lt;/p&gt;

&lt;p&gt;
Software does not have AI-level capabilities, and is very far from it. Describing &lt;b&gt;what&lt;/b&gt; went wrong is 100x more useful than presenting something vaguely actionable. Unless the error case is &lt;b&gt;very&lt;/b&gt; common, and thus you are &lt;b&gt;very&lt;/b&gt; certain what the fix is, you should &lt;b&gt;not&lt;/b&gt; try to present "actionable" advice over describing what went wrong, simply because to give good actionable advice in general, you need to embed an expert system; without populating the expert system with data, it needs to include IT-support-level AI, which like I said, isn't happening any time soon.
&lt;/p&gt;

&lt;p&gt;
Here's a specific example that really burned me just the other day. Vista Ultimate has a full PC backup capability. I found out that my main HD is failing (SMART alert). I wanted to restore that backup onto a different disk of the same make and size (actually, right down to consecutive serial numbers). However, my machine is rather complex - there are 7 SATA disk devices in the machine. The Vista OS DVD failed to restore the backup to my perfectly-matched disk, but I have no idea why. All I &lt;b&gt;do&lt;/b&gt; know is that the error message was "vaguely actionable":

&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;
Error details: There are too few disks on this computer or one or more of the disks is too small. Add or change disks so they match the disks in the backup and try the restore again. (0x80042401)
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;
This message is &lt;b&gt;completely and utterly useless&lt;/b&gt;, because it does not describe &lt;b&gt;what&lt;/b&gt; went wrong, only "how" to fix it - but because the software isn't AI-level and doesn't include an expert system, it itself can't produce a specific set of instructions.
&lt;/p&gt;

&lt;p&gt;
In this machine, I had 4x1TB disks, 1x400GB disk, and 1x200GB disk; the backup was on the 400GB disk and the target of the restore was the 200GB disk. 2 of the 1TB disks were &lt;b&gt;blank and formatted&lt;/b&gt;. Thus, there was no lack of disks or space. Similarly, the target of the restore was at Disk 0, achieved through careful selection of the SATA connection on the motherboard. Still didn't work though, and I can't fix it because the error message is following the &lt;b&gt;wrong philosophy&lt;/b&gt; for our current knowledge of AI.
&lt;/p&gt;

&lt;p&gt;
FWIW, &lt;a href="http://professionalinsight.net/VistaBackup.aspx"&gt;here's another user's experience with this problem&lt;/a&gt;. Notice the procedure to actually show the user the actual error, rather than the useless message:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;
&lt;ul&gt;
&lt;li&gt; Boot from the Vista DVD
&lt;/li&gt;&lt;li&gt; Go to Repair Computer -&gt; Command Prompt
&lt;/li&gt;&lt;li&gt; Go into Regedit
&lt;/li&gt;&lt;li&gt; Under HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\
&lt;/li&gt;&lt;li&gt; Create the key: Asr
&lt;/li&gt;&lt;li&gt; Under Asr create the key LogFileSetting
&lt;/li&gt;&lt;li&gt; Under LogFileSetting create the dword EnableLogging with the value 1
&lt;/li&gt;&lt;li&gt; Under LogFileSetting create the string LogPathName (string) with a value such as D:\Asr.log
&lt;/li&gt;&lt;li&gt; - you should specify a physical drive (e.g. I used the drive you are going to restore from) not the ramdrive (X:) so that the log is saved after reboot.
&lt;/li&gt;&lt;li&gt; Exit Regedit
&lt;/li&gt;&lt;li&gt; From the Repair menu launch Complete PC Restore
&lt;/li&gt;&lt;li&gt; Attempt the Complete PC Restore
&lt;/li&gt;&lt;li&gt; When you get the error, check the logging path to be sure the
Asr.log file exists. I did that by going back to the Command
Prompt and getting a directly listing before rebooting.
&lt;/li&gt;&lt;/ul&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;
This is disgraceful, and frankly, unforgivable. Don't do this with your exogenous exceptions.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10106032-275827614727842642?l=blog.barrkel.com'/&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/EntropyOverload?a=_8kHcMApvwM:KtN4z7HuNtE:cGdyc7Q-1BI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/EntropyOverload?d=cGdyc7Q-1BI" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/EntropyOverload?a=_8kHcMApvwM:KtN4z7HuNtE:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/EntropyOverload?i=_8kHcMApvwM:KtN4z7HuNtE:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/EntropyOverload?a=_8kHcMApvwM:KtN4z7HuNtE:UT3xtbGYFzA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/EntropyOverload?d=UT3xtbGYFzA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/EntropyOverload?a=_8kHcMApvwM:KtN4z7HuNtE:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/EntropyOverload?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/EntropyOverload/~4/_8kHcMApvwM" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.barrkel.com/feeds/275827614727842642/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=10106032&amp;postID=275827614727842642" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/10106032/posts/default/275827614727842642" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/10106032/posts/default/275827614727842642" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/EntropyOverload/~3/_8kHcMApvwM/exogenous-exceptions-oh-and-another.html" title="Exogenous Exceptions (oh and another Vista rant :)" /><author><name>Barry Kelly</name><uri>http://www.blogger.com/profile/10559947643606684495</uri><email>barry.j.kelly@gmail.com</email><gd:extendedProperty xmlns:gd="http://schemas.google.com/g/2005" name="OpenSocialUserId" value="08331762029805209075" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total><feedburner:origLink>http://blog.barrkel.com/2008/09/exogenous-exceptions-oh-and-another.html</feedburner:origLink></entry><entry><id>tag:blogger.com,1999:blog-10106032.post-1178634688407683327</id><published>2008-09-09T11:05:00.004+01:00</published><updated>2008-09-11T04:15:04.730+01:00</updated><title type="text">Vista LanmanServer falling apart on you?</title><content type="html">&lt;p&gt;I've been having a lot of trouble over the past day or so with my Vista machine. Since I can't tolerate it long as a main desktop, it's primarily acting as a file and print server.&lt;/p&gt;

&lt;p&gt;Over the past day, however, the Vista network sharing service, LanmanServer, has been falling over on me. All attempts to connect to Vista shares fail with error 1130, aka ERROR_NOT_ENOUGH_SERVER_MEMORY, and the event log on Vista is filled with 2017 (primarily) and 2021 Event IDs:&lt;/p&gt;

&lt;pre&gt;
2017: The server was unable to allocate from the system nonpaged pool because the server reached the configured limit for nonpaged pool allocations.

2021: The server was unable to allocate a work item %d times in the last 60 seconds.
&lt;/pre&gt;

&lt;p&gt;Fixing it required stopping Computer Browser (net stop browser) and Server (net stop server) and restarting them (net start etc.) on the Vista machine.&lt;/p&gt;

&lt;p&gt;Other people have been having this problem too; &lt;a href="http://forums.microsoft.com/TechNet/ShowPost.aspx?PostID=2949315&amp;SiteID=17"&gt;here's a relevant thread&lt;/a&gt; where you can read up about these frustrations:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;
[...] Vista Ultimate file/print server that is accessed by various Vista and XP client workstations. Recently - and increasingly - the XP machines have been spontaneously losing their connections to the Vista file server. This started out as a minor annoyance about a month ago, but has escalated to the point where the XP clients cannot stay connected for more than 5-30 minutes[...]
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;
Anyhow, I've found &lt;a href="http://www.hardforum.com/showthread.php?p=1032714322"&gt;a solution&lt;/a&gt; that appears to work, linked to at the end of the previous thread (which makes me suspect it fixed the problem for those folks too).
&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10106032-1178634688407683327?l=blog.barrkel.com'/&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/EntropyOverload?a=dSdsTjuDrr0:s889bW0ZcAU:cGdyc7Q-1BI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/EntropyOverload?d=cGdyc7Q-1BI" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/EntropyOverload?a=dSdsTjuDrr0:s889bW0ZcAU:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/EntropyOverload?i=dSdsTjuDrr0:s889bW0ZcAU:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/EntropyOverload?a=dSdsTjuDrr0:s889bW0ZcAU:UT3xtbGYFzA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/EntropyOverload?d=UT3xtbGYFzA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/EntropyOverload?a=dSdsTjuDrr0:s889bW0ZcAU:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/EntropyOverload?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/EntropyOverload/~4/dSdsTjuDrr0" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.barrkel.com/feeds/1178634688407683327/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=10106032&amp;postID=1178634688407683327" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/10106032/posts/default/1178634688407683327" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/10106032/posts/default/1178634688407683327" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/EntropyOverload/~3/dSdsTjuDrr0/vista-lanmanserver-falling-apart-on-you.html" title="Vista LanmanServer falling apart on you?" /><author><name>Barry Kelly</name><uri>http://www.blogger.com/profile/10559947643606684495</uri><email>barry.j.kelly@gmail.com</email><gd:extendedProperty xmlns:gd="http://schemas.google.com/g/2005" name="OpenSocialUserId" value="08331762029805209075" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total><feedburner:origLink>http://blog.barrkel.com/2008/09/vista-lanmanserver-falling-apart-on-you.html</feedburner:origLink></entry><entry><id>tag:blogger.com,1999:blog-10106032.post-3413989004971353317</id><published>2008-09-04T22:47:00.003+01:00</published><updated>2008-09-05T15:52:41.202+01:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Delphi" /><category scheme="http://www.blogger.com/atom/ns#" term="GC" /><category scheme="http://www.blogger.com/atom/ns#" term="Tiburon" /><category scheme="http://www.blogger.com/atom/ns#" term="generics" /><category scheme="http://www.blogger.com/atom/ns#" term="anonymous methods" /><title type="text">Smart pointers in Delphi</title><content type="html">&lt;p&gt;Strongly-typed smart pointers are now possible in Delphi, leveraging the work on generics. Here's a simple smart pointer type:&lt;/p&gt;

&lt;pre&gt;
  TSmartPointer&amp;lt;T: class&gt; = record
  strict private
    FValue: T;
    FLifetime: IInterface;
  public
    constructor Create(const AValue: T); overload;
    class operator Implicit(const AValue: T): TSmartPointer&amp;lt;T&gt;;
    property Value: T read FValue;
  end;
&lt;/pre&gt;

&lt;p&gt;Here it is in action, where TLifetimeWatcher is a little class that executes some code when it dies:&lt;/p&gt;

&lt;pre&gt;
procedure UseIt;
var
  x: TSmartPointer&amp;lt;TLifetimeWatcher&gt;;
begin
  x := TLifetimeWatcher.Create(procedure
  begin
    Writeln('I died.');
  end);
end;
&lt;/pre&gt;

&lt;p&gt;Here's the full project code that defines TSmartPointer&amp;lt;&gt;, TLifetimeWatcher, and the above test routine:&lt;/p&gt;

&lt;pre&gt;
{$APPTYPE CONSOLE}

uses
  SysUtils;

type
  TLifetimeWatcher = class(TInterfacedObject)
  private
    FWhenDone: TProc;
  public
    constructor Create(const AWhenDone: TProc);
    destructor Destroy; override;
  end;

{ TLifetimeWatcher }

constructor TLifetimeWatcher.Create(const AWhenDone: TProc);
begin
  FWhenDone := AWhenDone;
end;

destructor TLifetimeWatcher.Destroy;
begin
  if Assigned(FWhenDone) then
    FWhenDone;
  inherited;
end;

type
  TSmartPointer&amp;lt;T: class&gt; = record
  strict private
    FValue: T;
    FLifetime: IInterface;
  public
    constructor Create(const AValue: T); overload;
    class operator Implicit(const AValue: T): TSmartPointer&amp;lt;T&gt;;
    property Value: T read FValue;
  end;

{ TSmartPointer&amp;lt;T&gt; }

constructor TSmartPointer&amp;lt;T&gt;.Create(const AValue: T);
begin
  FValue := AValue;
  FLifetime := TLifetimeWatcher.Create(procedure
  begin
    AValue.Free;
  end);
end;

class operator TSmartPointer&amp;lt;T&gt;.Implicit(const AValue: T): TSmartPointer&amp;lt;T&gt;;
begin
  Result := TSmartPointer&amp;lt;T&gt;.Create(AValue);
end;

procedure UseIt;
var
  x: TSmartPointer&amp;lt;TLifetimeWatcher&gt;;
begin
  x := TLifetimeWatcher.Create(procedure
  begin
    Writeln('I died.');
  end);
end;

begin
  try
    UseIt;
    Readln;
  except
    on E:Exception do
      Writeln(E.Classname, ': ', E.Message);
  end;
end.
&lt;/pre&gt;

&lt;p&gt;&lt;b&gt;Update:&lt;/b&gt; Fixed capture - was capturing a location in the structure, which of course will be freely copied etc.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10106032-3413989004971353317?l=blog.barrkel.com'/&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/EntropyOverload?a=KRp4Ik3jFto:8YCO1ppVrdM:cGdyc7Q-1BI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/EntropyOverload?d=cGdyc7Q-1BI" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/EntropyOverload?a=KRp4Ik3jFto:8YCO1ppVrdM:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/EntropyOverload?i=KRp4Ik3jFto:8YCO1ppVrdM:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/EntropyOverload?a=KRp4Ik3jFto:8YCO1ppVrdM:UT3xtbGYFzA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/EntropyOverload?d=UT3xtbGYFzA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/EntropyOverload?a=KRp4Ik3jFto:8YCO1ppVrdM:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/EntropyOverload?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/EntropyOverload/~4/KRp4Ik3jFto" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.barrkel.com/feeds/3413989004971353317/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=10106032&amp;postID=3413989004971353317" title="10 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/10106032/posts/default/3413989004971353317" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/10106032/posts/default/3413989004971353317" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/EntropyOverload/~3/KRp4Ik3jFto/smart-pointers-in-delphi.html" title="Smart pointers in Delphi" /><author><name>Barry Kelly</name><uri>http://www.blogger.com/profile/10559947643606684495</uri><email>barry.j.kelly@gmail.com</email><gd:extendedProperty xmlns:gd="http://schemas.google.com/g/2005" name="OpenSocialUserId" value="08331762029805209075" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">10</thr:total><feedburner:origLink>http://blog.barrkel.com/2008/09/smart-pointers-in-delphi.html</feedburner:origLink></entry></feed>
