Wednesday, December 30, 2009

A gift to all multithreaded Delphi programmers

A (very) prerelease version 1.05, available via SVN or as a ZIP archive.

I’ve managed to produce two interesting data structures:

  • TOmniQueue (existing class TOmniQueue was renamed to TOmniBoundedQueue) is a dynamically allocated, O(1) enqueue and dequeue, threadsafe,  microlocking queue. The emphasys is on dynamically allocated. In other words – it grows and shrinks!
  • TOmniBlockingCollection is a partial clone (with some enhancements) of .NET’s BlockingCollection.

Have fun and happy new year to all Delphi programmers!

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.

Sunday, December 13, 2009

DsiWin31 1.53a

This release fixes nasty bug (introduced in release 1.51) which caused various TDSiRegistry function (and other DSi code using those functions) to fail on Delphi 2009/2010.

Other changes:

  • Implemented DSiDeleteRegistryValue.
  • Added parameter 'access' to the DSiKillRegistry.
  • [Mitja] Fixed allocation in DSiGetUserName.
  • [Mitja] Also catch 'error' output in DSiExecuteAndCapture.
  • DSiAddApplicationToFirewallExceptionList renamed to DSiAddApplicationToFirewallExceptionListXP.
  • Added DSiAddApplicationToFirewallExceptionListAdvanced which uses Advanced Firewall interface, available on Vista+.
  • DSiAddApplicationToFirewallExceptionList now calls either DSiAddApplicationToFirewallExceptionListXP or DSiAddApplicationToFirewallExceptionListAdvanced, depending on OS version.
  • Implemented functions to remove application from the firewall exception list: DSiRemoveApplicationFromFirewallExceptionList, DSiRemoveApplicationFromFirewallExceptionListAdvanced, DSiRemoveApplicationFromFirewallExceptionListXP.

OmniThreadLibrary 1.04a

This minor release was released mostly because of exception handling problems when thread pool was used in version 1.04. If you’re using thread pool feature and have OTL 1.04 installed, I’d strongly urge you to upgrade.

Besides code fix I sneaked in a small API upgrade. IOmniTask interface now defines methods RegisterWaitObject/UnregisterWaitObject which the task can use to wait on any waitable object when using TOmniWorker approach (no main thread loop). There’s also a new demo application 31_WaitableObjects which demonstrates the use of this feature.