Aaron's death has had a larger effect on me than most things I read about on the web. This death is a little closer than most, and very sad.
I had a number of email conversations with him about his blog posts. His articles often annoyed me in their naivete, and what I perceived as their ignorance as to the way the world worked (especially from an economic perspective) and I have to say I flamed him a bit in that correspondence. We had a few to and fros, in the spirit of young guys who hadn't mastered tact online. My lack more than his.
But it was this naivete, this big disconnect between how he seemed to perceive the world and how it was (as I saw it), that drove him to do what he did. He was foolhardy but pure of heart, earnest, childlike. The little knocks that force most of us into alignment and compliance and conformity from an early age seemed not to have affected him, until the knocks got a lot harder, too hard, and yet he never bent.
The apparent tactics of US prosecution - to overcharge and then plea-bargain to extract guilty verdicts even from innocent parties, owing to the legal risk of one of the charges sticking - are sickening. I conjecture they're due to electoral incentives and the need to be seen to be tough on crime. Theatre to placate the mob, like Nero throwing Christians to the lions.
There's a lot of reasons. I was in the same position for more than six years, but I didn't necessarily want to be pigeonholed as a compiler guy. I was working remotely from London, with all the downsides that remote working entails: relative lack of camaraderie, out of sight / out of mind, disconnection from the actually quite vibrant local tech scene (I live about 3 miles from Old Street roundabout). But more than anything else, I'd fallen out of love with Delphi, and could no longer motivate myself to try and make it better - the gap I'd try to bridge would be a gap too far for the market to bear.
I'm currently taking stock, thinking about what I want to do next. No specific plans, but it's going to be fairly different than what went before!
I've just come back from an excellent holiday: a motorbike trip from London to southern Spain, via LlĂvia and Andorra. The highlight of the trip was the roads in the Spanish Pyrenees. Many almost abandoned (frequently went for 10 to 30 minutes without seeing another vehicle), mostly good surface, with bends that go on for miles and miles. Not to forget the fantastic views. Largely good weather - we (myself and my girlfriend, on separate bikes) stuck mostly to the east coast of Spain, and only really got wet on a couple of days, when travelling through France. Not too hot, either, one of the primary reasons we went at the tail end of April.
I want to note down some of the things I've learned over the past few touring trips I've taken, and of course distilled from year-round riding through English weather. Perhaps it will help someone else out there, save them from getting wet too many times, or buying more gear than necessary.
For context: I'm a sport-touring rider, with a bias of perhaps 3:1 sport to touring; that is, I prefer a light bike, no pillion - luggage strapped to the pillion seat - and am not too concerned about being super-comfortable on the motorway at motorway speeds; but I like a fairly upright seating position and some wind protection. I'll tolerate motorways if they're necessary to get to the really fun roads sooner, where I can throw the bike around more. I ride a Kawasaki ER6f; as a medium-capacity twin, it's got lots of torque at lower speeds without needing for a screaming engine, but is still fairly light and sporty for having fun, particularly when you keep the revs up around 6k.
In and around London though, I usually ride my scooter, an SH300. A bit of a sleeper, it often surprises guys on big bikes. It looks like a 125 - especially since maxi-scooters are fairly rare in the UK and are somewhat looked down upon - but has significant acceleration to about 40mph. It still rides like a scooter and is able to get in and around traffic with ease that's a lot harder and riskier on a big bike, primarily because of the low centre of gravity.
Motorcycle clothing for touring: my key tip is layers. Specifically, for a jacket, look for something relatively light with loads of ventilation options (i.e. zip vents, zip-off panels; this generally means textiles), but is still somewhat windproof when fully zipped up, and has a zip-in waterproof liner. Generally, people go touring in or towards good weather, so ventilation is very important, but good roads are usually in hilly areas, often shaded with trees, and times with least traffic often don't coincide with peak heat, so you need the windproofing and waterproofing to block out cold air. For extra warmth, pack a fleece, wool top, etc. No need to rely on a third liner for the jacket.
In steady rain, however, a zip-in waterproof liner isn't good enough. It'll probably keep you mostly dry (though in my experience the zip area always leaks from pools of water forming at folds and creases, combined with wind pressure), but there is another problem: the jacket outer will get soaked through, and it will carry the water down your arms and into your gloves. The waterproof liner generally only fits inside rather than outside your gloves, so it will guide the water into your gloves. So my solution is a motorbike rain jacket - i.e. one which is over-sized for its proportions so it fits on top of existing jackets. I have a high-vis fluorescent yellow Bering jacket, for extra safety in torrential downpours, which limit visibility much like fog, especially on the motorway with spray thrown up by tyres. It's very important that this jacket has cuffs with velcro or some similar positively adjustable system (not just elastic) that fits over your gloves (so water can't run into the gloves), but tightly, so that rain and wet air can't get up your sleeve.
For rain in London, I generally wear my winter jacket, which has a Gore Lock Out zip. This is a double-interlocking rubber zip, and is completely waterproof and windproof; it also needs lubrication with silicone oil once a month to keep it smooth.
For trousers, I get by with permanently ventilated textiles with zips for further ventilation. To stay warmer, I can wear jeans underneath; but the fairing on my bike keeps off most of the wind, unless it's very cold, and legs don't feel the cold nearly as much as the torso. Similarly to up top, I have waterproof over-trousers for rain, and in case it gets very cold. The weak area of waterproof trousers is the crotch. Water generally collects on your torso and runs down until it pools there. If the crotch isn't totally waterproof, it will leak. Most waterproof over-trousers don't have reinforced crotch areas, and as such they're good for perhaps 10 hours of light wear before they'll start leaking. If you can find them, get ones with a reinforced crotch and stay away from ones with simple seams right where your legs split; so far, I've had to get by with purchasing new over-trousers when the forecasts say I'll be riding through heavy rain.
Have at least two pairs of waterproof gloves for riding when expecting rain (and even sunny summer trips usually have a thunderstorm at some point). Even waterproof gloves will get really soggy leather on the outside, and damp and clammy inside. After the rain has let up, it's quite pleasant to pull over and put on some dry gloves. Even better if you have heated grips; dry and toasty fingers make all the difference, and help keep you sharp on the controls, even if you're a little wet elsewhere. I have found waterproof over-gloves quite dangerous; they're generally very slippy and hard to get on, much more trouble than they're worth. Same thing with waterproof over-boots, they're usually a poor fit and interfere or get caught on the gear lever; can be dangerous. Get quality waterproof boots; bring two pairs of boots if you don't think you can stand wearing waterproof non-ventilated boots in the expected weather.
Luggage: I get by with an oversized (Givi E55) top-box containing light things for quick access (visor sponge and wipes, spare gloves, spare earplugs, spare light bulbs, spare straps, extra rain gear), and for keeping my helmet and gloves in when parked up (this is why it's oversized) or shopping when staying at a self-catering place (exceeding the load limit for short distances at low speed); and a holdall / gym bag with rectangular base, strapped tightly to my pillion seat using two 220 daN (decanewton, ~220kg) luggage straps, so that it's completely rigid and won't move at all as the bike moves, even in vigorous cornering. I don't bother with a waterproof bag; I put my clothes and other things I want to keep dry in heavy-duty rubble sacks. At the bottom of the bag, I keep my laptop; currently a MacBook Air, on top of a long wooden cutting board approximately the shape of the bag, wrapped up together in a rubble sack. The idea here is to prevent too much bending force being applied to the laptop by the pillion seat. On top, there's clothes intermediating the force from the straps. So far my laptops have survived (in the past, I was using a Toshiba Portege) fine, except for a few minor marks on the screen from being pressed into the keyboard.
Touring by motorbike has been some of the most fun I've ever had in my life. Before I learned to ride, I didn't think much about bikes; at best, I dismissed riders as either adrenaline-junky boy-racers, or hairy old fogeys. The practicality of a scooter as a means of getting around London was my gateway drug. One thing lead to another, and now I'm hooked, for life, if I don't injure myself too badly along the way!
Delphi XE2 introduced namespaces across the runtime library. This stressed unit name lookup inside the compiler, and led to some severe performance regressions in certain cases. So during the runup to the XE2 release, I fired up a profiler and started investigating. It turns out there were numerous situations where lookups were being performed repeatedly with the same arguments, and logically the results should have been consistent across these repeated calls. A relatively cheap and easy fix seemed to be memoization. So I added a caching infrastructure to the compiler and used the profiler to guide me where to inject it.
For the most part - particularly for full builds - the cache works really well. But I've had some reports of bugs that I suspected were caused by the cache hanging on to function results slightly too long, and upon investigation, this turned out to be true. The problem with caches is usually in invalidation; if you don't invalidate the cache soon enough and in all required situations, you end up serving stale results. So there are a few bugs lurking in the Delphi compiler here, which I'm seeking out and squashing.
Some good news, however; I had anticipated that this might be the case, so I added a super secret switch that enables diagnosing a probable cache failure: caches can be selectively disabled, and if a problem goes away with the cache disabled, it's probably because of stale results.
Caches can be disabled by setting an environment variable:
The above environment variable setting disables all the compiler's caches. By including fewer than the four separate cache names, the problem can iteratively be narrowed down to a specific cache.
I've just been fixing one bug caused by the cache that brought home how needed it is. The project is large; almost 2 million lines. An initial build with the cache enabled takes about a minute on my machine; the bug exhibits itself in later incremental compiles when modifying the source code and pressing F9, producing spurious errors. However, once I disabled the cache (or rather, I recompiled the compiler with cache sanity checking enabled, which still filled out the cache, but also invoked the underlying logic, and simply compared the results to verify the cache), the build time took nearly 3 hours!
Note: invalidation is most likely to be a problem on incremental compiles, rather than full rebuilds, especially from within the IDE. The reason is that the compiler may have lots of stale data for one half of an incremental compile that it later decides is out of date (e.g. a dependency changed); this can leave a bunch of stale entries in the cache for all the memoized function calls that occurred in the first half of the compile. The cache is strictly per-compile; it keeps no data across multiple compilations, even partial compilations.
I was answering a question on Stack Overflow, but the user didn't have the latest version of Delphi. My answer included converting an interface to an object instance, which is made possible with the as cast on interfaces in recent Delphi versions. But there is another way of doing it, exploiting the regularity Delphi interface vtable implementations:
{$apptype console}
function Intf2Obj(x: IInterface): TObject;
type
TStub = array[0..3] of Byte;
const
// ADD [ESP+$04], imm8; [ESP+$04] in stdcall is Self argument, after return address
add_esp_04_imm8: TStub = ($83, $44, $24, $04);
// ADD [ESP+$04], imm32
add_esp_04_imm32: TStub = ($81, $44, $24, $04);
function Match(L, R: PByte): Boolean;
var
i: Integer;
begin
for i := 0 to SizeOf(TStub) - 1 do
if L[i] <> R[i] then
Exit(False);
Result := True;
end;
var
p: PByte;
begin
p := PPointer(x)^; // get to vtable
p := PPointer(p)^; // load stub address from vtable
if Match(p, @add_esp_04_imm8) then
begin
Inc(p, SizeOf(TStub));
Result := TObject(PByte(Pointer(x)) + PShortint(p)^);
end
else if Match(p, @add_esp_04_imm32) then
begin
Inc(p, SizeOf(TStub));
Result := TObject(PByte(Pointer(x)) + PLongint(p)^);
end
else
raise Exception.Create('Not a Delphi interface implementation?');
end;
type
ITest = interface
procedure P;
end;
TTest = class(TInterfacedObject, ITest)
F: array[0..200] of Byte;
procedure P;
end;
procedure TTest.P;
begin
Writeln('Hello');
end;
procedure Go;
var
orig: TTest;
i: ITest;
o: TObject;
begin
orig := TTest.Create;
i := orig;
i.P;
o := Intf2Obj(i);
Writeln(o = orig);
end;
begin
Go;
end.
This approach is predicated on the idea that the stub code that Delphi produces for turning the implicit interface argument into an instance argument is predictable. It generally only has two forms, depending on how much of an adjustment it needs to make (which itself depends on how much instance data there is). It ought to work for almost all 32-bit Delphi interfaces that have been implemented by instances, where the vtable was created by the compiler. If not, other stub variations can be analyzed (in the IDE CPU view) and handled too. It ought to be pretty safe, as only this specific code is permitted. It could be made even safer by ensuring that the stub ends with a JMP and that the instance returned has a ClassType descending from TClass.
Update: After a Google search I note that Hallvard also wrote about this some time ago. His code is a little tighter than mine (using Integer constants rather than byte-by-byte comparison); in my defense, I only spent a few minutes on this...