Tuesday, November 29, 2011

Per-object locking

I was always holding the opinion that locks should be as granular as possible. Putting many small locks around many unrelated pieces of code is better then using one giant lock for everything.

To make this simpler, OmniThreadLibrary includes a very useful record called TOmniCS which allows you to do locking without doing any upfront initialization. Just declare it and you’re ready to go. [You can read more about it in my previous blog post.]

Monday, November 28, 2011

Atomic interface initialization

OmniThreadLibrary includes (in the OtlSync unit) a neat record called TOmniCS which wraps IOmniCriticalSection interface (which in itself is just a wrapper around the TCriticalSection) and allows you to use it without an explicit initialization. You just declare a variable of the TOmniCS type in your code and then call Acquire and Release methods of this variable and everything is handled for you.

type
  TOmniCS = record
  strict private
    ocsSync: IOmniCriticalSection;
    function  GetSyncObj: TSynchroObject;
  public
    procedure Initialize;
    procedure Acquire; inline;
    procedure Release; inline;
    property SyncObj: TSynchroObject read GetSyncObj;
  end;
As records don’t provide automatic initialization via parameterless constructor, the code is slightly tricky. The record contains a field (ocsSync) that contains the interface reference used to do real work. This interface is initialized in the Initialize method, which is in turn called from the Acquire and Release.

procedure TOmniCS.Acquire;
begin
  Initialize;
  ocsSync.Acquire;
end;

Tuesday, November 15, 2011

OmniThreadLibrary needs more unit tests

Eh. How true. :( Care to step in?

It’s really simple. Take a sample application (for example 37_ParallelJoin), check what it’s doing and convert it into a unit test. [In this example, first button should cause a wait of about 3 sec and second button should cause a wait of about 5 sec. That’s all.] If the application you have chosen seems too complicated to be converted to a unit test, just ignore it and select another.

Why? Because I’ll be converting OmniThreadLibrary to support 64-bit and FireMonkey in the next week and I really could use a safety net which would ensure at least that I don’t break everything at once.

Why you? Because I’ll be converting OmniThreadLibrary to … but I already said that. If you can spend some time on that, then I don’t have to and everything will be finished quicker.

For really curious people – why 37_ParallelJoin? Because it is broken in the current SVN version. Parallel.Join.NumProc is not working correctly and I only noticed it by chance. [Fixing the bug right now …]

If you like OmniThreadLibrary but don’t feel competent enough to help developing it, this is your chance to step in and help the development effort!

Wednesday, November 09, 2011

TOmniValue handles arrays, hashes and records

Arrays

For quite some time, TOmniValue record has the ability to store arrays. You would use

TOmniValue.Create([1, 2, OTL, TButton.Create(nil)]) 
and some magic would store this array inside TOmniValue.
The problem, though, was that this magic was quite lame. The array was converted to a ‘variant array’ and you could only access its elements as Variant type. This, besides other things, meant that it was not simple to store pointers, interfaces and objects in TOmniValue array. Or, better said, it was simple to store them but not simple to retrieve them. You needed some ugly casting like

o := TButton(NativeUInt(ov[1]));
Since today, TOmniValue supports native arrays, that is, each item in the array is again of the TOmniValue type.

Thursday, November 03, 2011

OmniThreadLibrary in Practice [2]–Background Worker and List Partitioning

Today’s question was asked by Žarko Gajić, the excellent maintainer of About.com’s Delphi section.
Here's my (simplified) task I would like to solve using OTL (Delphi XE) - my real-world task is somehow more complicated but can be described as:
input: a string. output: a TStringList containing characters (one per entry) of the input string.
Example: input: "delphi"
A background thread ("master") grabs the input string and splits it into several pieces (let's say 2): "del" and "phi". For each of the split strings a new thread ("child") is created that fills in the TStringList (output) with characters from the section of the string it receives.
At any time the "master" thread could be signaled to terminate (from the app's main thread) all child threads (and itself).
When everything is done the app's main thread processes the string list.
Preferably, the order of the characters should (when all ends) be 'd', 'e', 'l', 'p', 'h', 'i' (note that characters are actually items in the resulting string list).