Jump to content

Primož Gabrijelčič

Members
  • Content Count

    246
  • Joined

  • Last visited

  • Days Won

    11

Posts posted by Primož Gabrijelčič


  1. Forgot to write that? Oh, me! 😞

     

    In this case, the code can just assume that the first value in the pipeline is a database name:

     

    function Build_DbInserterStage(const ADatabaseName: string): TPipelineStageDelegateEx;
    begin
      Result := (
        procedure(const input, output: IOmniBlockingCollection; const task: IOmniTask)
        var
          ovIN: TOmniValue;
          DB: TxxxDatabase;
        begin
          DB := TxxxDatabase.Create();
          DB.DatabaseName := ovIN.Take;
          for ovIN in input do
          begin
            // ... insert downloaded file
          end;
          DB.Commit;
        end);
    end;

     

    In more general terms you can declare your special messages which carry metadata (database name) instead of a normal data (to be operated upon). The worker thread can then check the type of the message and react accordingly. You can use TOmniValue's array support for that:

     

        pipeline.PipelineStage[3].Input.Add(TOmniValue.CreateNamed(['Type', 'Config', 'DBName', dbName]));
        pipeline.Input.Add(TOmniValue.CreateNamed(['Type', 'Data', 'Value', value]));
    
        for ov in input do
          if ov['Type'] = 'Config' then
            db.DatabaseName := ov['DBName']
          else
            Process(ov['Value']);

     

     

    • Thanks 1

  2. IOmniPipeline.PipelineStage[num] returns an interface which exposes Input and Output pipeline for that stage:

     

    type
      IOmniPipelineStage = interface ['{DFDA7A07-6B28-4AA6-9218-59D3DF9C4B8E}']
        function  GetInput: IOmniBlockingCollection;
        function  GetOutput: IOmniBlockingCollection;
      //
        property Input: IOmniBlockingCollection read GetInput;
        property Output: IOmniBlockingCollection read GetOutput;
      end;
    
      IOmniPipeline = interface
        function  GetPipelineStage(idxStage: integer): IOmniPipelineStage;
      //
        ...
        property PipelineStage[idxStage: integer]: IOmniPipelineStage read GetPipelineStage;
      end;

     

    You can use them to send data to a specific pipeline stage, for example:

     

    pipeline.PipelineStage[2].Input.Add(42);

    First pipeline stage has index 0.


  3. New OmniThreadLibrary is out! Get it while it’s hot!

    Version 3.07.7 is mostly a bugfix release. It fixes a stupid mistake introduced in version 3.07.6 plus some other minor bugs.

    You can get it now on git, download the ZIP archive, install it with Delphinus or with GetIt.

    For more information, visit OmniThreadLibrary home page or write your question on the forum.

    New features

    • [HHasenack] On XE3 and above, TOmniValue.CastTo<T> supports casting to an interface.
    • [HHasenack] Implemented Parallel.ForEach(IEnumerator<T>) and Parallel.ForEach(IEnumerable<T>).

    Bug fixes

    • If additional wait objects registered with RegisterWaitObject were constantly signalled, timers were never called.
    • OtlParallel threads were incorrectly scheduled to the main pool instead of GlobalParallelPool unless IOmniTaskConfig.ThreadPool was used. (introduced in 3.07.6)
    • Using Parallel.Join, .For, and .ForEach with .OnStopInvoke failed with access violation if Join/For/ForEach was not executed with .NoWait.
    • Thread pool creation code waits for thread pool management thread to be started and initialized. Without that, CountQueued and CountExecuting may not be initialized correctly when thread pool creation code exits. [tnx to Roland Skinner]
    • Like 4

  4. http://docwiki.embarcadero.com/RADStudio/en/Declarations_and_Statements_(Delphi)#For_Statements

     

    Quote

    For purposes of controlling the execution of the loop, the expressions initialValue and finalValue are evaluated only once, before the loop begins. Hence, the for...to statement is almost, but not quite, equivalent to this while construction:

    
     begin
       counter := initialValue;
       while counter <= finalValue do
       begin
         ... {statement};
         counter := Succ(counter);
       end;
     end.

     

     


  5. 10 hours ago, GPRSNerd said:

    Reason is, that the path to the core dll is common for all installed releases through a setting in the registry:

     

    [HKEY_CURRENT_USER\Software\Parnassus OU\Core]
    "Path"="C:\\Users\\Public\\Documents\\Embarcadero\\Studio\\20.0\\Experts"

     

    This always points to the last installation.

     

    @David Millington Can you - or Embarcadero - please publish Bookmarks and Navigator 1.6 for older platforms? Or create a version that does not depend on share "Core" DLL? I think the community would very much appreciate it. I know I would.

    • Like 4

  6. Installed Bookmarks & Navigator in Rio.

     

    Now my Berlin reports "Access violation at address 07E8D047 in module 'ParnassusCoreEditor.dll'. Read of address 00000000." when I start the IDE.  I get the same error when loading a project and then Bookmarks doesn't work.

     

    I also get a bunch of errors when I open a form unit and then I get into "Access violation at address 20837CFB in module 'coreide240.bpl'. Read of address 00000018." which repeats indefinitely.
     

    😠

     

    I have Bookmarks 1.5.1 installed in Berlin.

     

    Is there anything I can do to have working Bookmarks both in Berlin and Rio?


  7. Indeed, I'm working on it. Now that I have finished my book, OTL will again become a top priority side project.

     

    It's hard to tell how much work is yet to be done as at the moment some very basic parts are still missing and I don't know how much time I'll need for them.

     

    A crowdfund would be nice - one where you can give me additional time inside a day. 30h per day would be good to have 😉

    • Like 5
    • Thanks 1

  8. Nice additions, Andrea 🙂

     

    Whether for the good or bad, who can tell 😉 

     

    My initial approach was trying to get too much from LiveBindings anyway (intentionally). Your changes are definitely in direction of making my messy code more maintainable so they make it better. They, however, introduce more code, so from my original viewpoint they make it worse. 😉 

    • Like 1

  9. Great that you got it working! 

     

    Re 4000 tasks I was thinking more about - what if you set up a Parallel.BackgroundWorker with multiple workers and then schedule work units to it? You would have one always running execution engine (i.e. background worker) and instead of 4000 tasks you would have 4000 work units.


  10. Got it.

    type
      POmniValue = ^TOmniValue;
      TOmniValue = record
      private
        FOwnedObject: IAutoDestroyObject;
        function GetAsOwnedObject: TObject;
        procedure SetAsOwnedObject(const Value: TObject);
      public
        class operator Implicit(var ov: TOmniValue): POmniValue; static;
        property AsOwnedObject: TObject read GetAsOwnedObject write SetAsOwnedObject;
      end;
    
      IWorkItem = interface ['{7C583FC8-90DD-46A5-81B9-81B911AA1CBE}']
        function GetResult: POmniValue;
        procedure SetResult(const Value: POmniValue);
        property Result: POmniValue read GetResult write SetResult;
      end;
    
      TWorkItem = class(TInterfacedObject, IWorkItem)
      strict private
        FResult: TOmniValue;
      strict protected
        function GetResult: POmniValue;
        procedure SetResult(const Value: POmniValue);
      public
        destructor Destroy; override;
        property Result: POmniValue read GetResult write SetResult;
      end;
    
    class operator TOmniValue.Implicit(var ov: TOmniValue): POmniValue;
    begin
      Result := @ov;
    end;
    
    function TWorkItem.GetResult: POmniValue;
    begin
      Result := @FResult;
    end;
    
    procedure TWorkItem.SetResult(const Value: POmniValue);
    begin
      FResult := Value^;
    end;

     

    It's quite possible that this breaks quite easily, though, as this solution looks quite fragile to me. It is simple and efficient, though.

     

    full code here


  11. This works, but requires lots of code if I would want to implement it with the original (very rich) `TOmniValue` record:

     

      IOmniValueFwd = interface;
    
      TOmniValue = record
      private
        FOwnedObject: IAutoDestroyObject;
        function GetAsOwnedObject: TObject;
        procedure SetAsOwnedObject(const Value: TObject);
      public
        class operator Implicit(const ov: TOmniValue): IOmniValueFwd;
        property AsOwnedObject: TObject read GetAsOwnedObject write SetAsOwnedObject;
      end;
    
      IOmniValueFwd = interface ['{4CCE0702-1CBF-4467-8185-8C38C37BA624}']
        function GetAsOwnedObject: TObject;
        function GetValue: TOmniValue;
        procedure SetAsOwnedObject(const Value: TObject);
        property AsOwnedObject: TObject read GetAsOwnedObject write SetAsOwnedObject;
        property Value: TOmniValue read GetValue;
      end;
    
      TOmniValueFwd = class(TInterfacedObject, IOmniValueFwd)
      strict private
        FOmniValue: TOmniValue;
      strict protected
        function GetAsOwnedObject: TObject;
        function GetValue: TOmniValue;
        procedure SetAsOwnedObject(const Value: TObject);
      public
        constructor Create(const ov: TOmniValue);
        property AsOwnedObject: TObject read GetAsOwnedObject write SetAsOwnedObject;
        property Value: TOmniValue read GetValue;
      end;
    
      IWorkItem = interface ['{7C583FC8-90DD-46A5-81B9-81B911AA1CBE}']
        function GetResult: IOmniValueFwd;
        procedure SetResult(const Value: IOmniValueFwd);
        property Result: IOmniValueFwd read GetResult write SetResult;
      end;

    I can then use:

    procedure Test(const workItem: IWorkItem);
    begin
      workItem.Result.AsOwnedObject := TTestObj.Create;
    end;
    

    or:

    procedure Test(const workItem: IWorkItem);
    var
      ov: TOmniValue;
    begin
      ov.AsOwnedObject := TTestObj.Create;
      workItem.Result := ov;
    end;
    

    And both work fine.

     

    full code here

    • Like 1

  12. I'm thinking about changing `IWorkItem.Result` into `TOmniValueClass = class ...` where `TOmniValueClass` exports same API as `TOmniValue` and forwards all requests to a private `TOmniValue` record.

     

    LTR: Meh, doesn't work. That makes the getter work fine, but again stops the setter from working 😞

×