-
Content Count
246 -
Joined
-
Last visited
-
Days Won
11
Posts posted by Primož Gabrijelčič
-
-
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']);
- 1
-
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.
-
Sure, when FPC supports all necessary language constructs.
-
OmniThreadLibrary has many similar constructs. The closes of them is possibly a Pipeline, or a BackgroundWorker, or an Async, depending of what exactly do you want to do with the code.
There's a separate subforum for OmniThreadLibrary here: https://en.delphipraxis.net/forum/32-omnithreadlibrary/
-
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]
- 4
-
Class constructors are called when units are initialized, strictly in a single-threaded context.
-
It uses "banker's rounding", after all. 😉
- 2
-
http://docwiki.embarcadero.com/RADStudio/en/Declarations_and_Statements_(Delphi)#For_Statements
QuoteFor 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.
-
That behavior is correct. `for` loop will execute once with `i` being set to 0.
-
Uwe's post clearly states: HKEY_CURRENT_USER\Software\Parnassus OU\Core
- 1
-
Works like a charm! Thank you!
-
1 hour ago, Uwe Raabe said:Would it help to change the registry setting to "$(BDS)\\Experts" and make sure that the correct DLLs reside in the proper places?
Great idea, but unfortunately it doesnt work.
Setting it to "($BDS)\Experts" fails in the same way.
-
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.
- 4
-
I have C:\Users\gabr\Documents\Embarcadero\Studio\20.0\CatalogRepository\ParnassusCoreEditor-1.0\ParnassusCoreEditor.dll (modified 15.2.2019)
and
C:\Users\gabr\AppData\Roaming\Parnassus OU\Common\ParnassusCoreEditor_XBerlin.dll (modified 20.5.2016)
So I don't see why the 1.6 install in Rio would break Berlin at all 😞
-
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?
-
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 😉
- 5
- 1
-
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. 😉
- 1
-
On 2/8/2019 at 7:46 AM, Shrinavat said:@Primož Gabrijelčič Do you have any progress for this issue fix? There are no new commits on github. Current workaround with using extra variable is not very elegant, although it works.
No, and don't expect one soon. There are more urgent issues to be fixed before.
- 1
-
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.
-
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.
-
Yes, it is definitely not a fast solution. It is elegant for the user, though.
-
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.
- 1
-
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 😞
-
Hmmm, I'll have to think about it. That can be a solution but I'm afraid I will cause too big mess. Thanks.
Dragging a control with $F012
in VCL
Posted · Edited by Primož Gabrijelčič
Some additional info can be found here: https://stackoverflow.com/questions/3976610/moving-a-caption-less-window-by-using-a-drag-area.