Friday, December 18, 2009

OmniThreadLibrary 1.04b – It’s all Embarcadero’s fault

Delphi 2010 Update 2/3 broke OmniThreadLibrary, but as this update was revoked, I didn’t look into the problem at all.

Now that Update 4/5 is out and OTL is still broken I had no choice but to fix it. Luckily for me, ahwux did most of the work in detecting the problem and providing (at least partial) fix.

OTL is written without resorting to ugly hacks (at least whenever possible). So what could they do to break my code?

OTL uses RTTI information to implement ‘call by name’ mechanism. And that’s not the basic RTTI, implemented in TypInfo unit, but extended class-RTTI from ObjAuto. [In case you want to take a peek at the code – the relevant bits can be found in method TOmniTaskExecutor.GetMethodAddrAndSignature inside the OtlTaskControl unit.] The code checks the method signature (number of parameters, their types and the way they are passed to the method) to see if it matches one of three supported signatures.

For example, first parameter must be the Self object and the code checked this by testing (params^.Flags = []) and (paramType^.Kind = tkClass). This worked in Delphi 2007, 2009, and 2010 – but only in the original release and Update 1. Starting with the Update 2, params^.Flags equals [pfAddress] in this case.

Similarly, constant parameters had flags [pfVar] up to D2010 Update 1 while this changed to [pfConst, pfReference] in D2010 Update 2.

I’m not against those changes. After all, the RTTI parameter description is now much more accurate. But why do they have to make this change in an update!? [Yes, I’m screaming.]

The problem here is that I can’t detect during the compilation whether the Update 4 has been installed. I can easily check for Delphi 2010, but that’s all – there’s no way (I’m aware of) of detecting which update is installed. So now my code looks like this:

  function VerifyObjectFlags(flags, requiredFlags: TParamFlags): boolean;
Result := ((flags * requiredFlags) = requiredFlags);
if not Result then
flags := flags - requiredFlags;
{$IF CompilerVersion < 21}
Result := (flags = []);
{$ELSEIF CompilerVersion = 21}
// Delphi 2010 original and Update 1: []
// Delphi 2010 while Update 2 and 4: [pfAddress]
Result := (flags = []) or (flags = [pfAddress]);
{$ELSE} // best guess
Result := (flags = [pfAddress]);
end; { VerifyObjectFlags }

function VerifyConstFlags(flags: TParamFlags): boolean;
{$IF CompilerVersion < 21}
Result := (flags = [pfVar]);
{$ELSEIF CompilerVersion = 21}
// Delphi 2010 original and Update 1: [pfVar]
// Delphi 2010 Update 2 and 4: [pfConst, pfReference]
Result := (flags = [pfVar]) or (flags = [pfConst, pfReference]);
{$ELSE} // best guess
Result := (flags = [pfConst, pfReference]);
end; { VerifyConstFlags }


If anybody from Embarcadero is reading this: Could you please refrain from doing such changes in IDE updates? Thanks in advance.

Oh, I almost forgot – OTL 1.04b is available on the Google Code.


  1. Wild guess, but my first thought is that the change in question was done to fix a bug or two in DataSnap and/or D2010's high level RTTI interface (i.e., the TRttiXXX stuff). I agree it's a pain in the backside when something like this happens to your code, but what you are in effect asking for is that an update should never fix a bug in a (relatively) high level part of the RTL and/or VCL if this means changing the public behaviour of even the most esoteric low level part.

    Now, you may well be right here. However, there is still the other side, which might be said to run: 'Stupid Embarcadero people. Can't they fix even the most basic bugs in much-publicised new features?!'

  2. I fully agree with your last thought, too :)

    What we would really need is a way (conditional define, $IF expression) to test for exact Delphi version (including upgrades) in the code.

  3. Don't you know that each Delphi release is only finished after an update pack or two?

  4. I had to uninstall Update 4/5 due to the new RTTI Bug dealing with Invoke.