Jump to content

Primož Gabrijelčič

Members
  • Content Count

    246
  • Joined

  • Last visited

  • Days Won

    11

Everything posted by Primož Gabrijelčič

  1. Primož Gabrijelčič

    Object destroyed too soon?

    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
  2. Primož Gabrijelčič

    Object destroyed too soon?

    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 😞
  3. Primož Gabrijelčič

    Object destroyed too soon?

    Hmmm, I'll have to think about it. That can be a solution but I'm afraid I will cause too big mess. Thanks.
  4. You have: Renderer := Parallel.ParallelTask.NumTasks(numTasks).NoWait .Execute( procedure begin workItem := RenderQueue.Next; ... end); and then: for iTask := 0 to numTasks-1 do begin TileSource := workItem.Data['TileSource' + iTask.ToString]; RenderQueue.Add(TOmniValue.Create([iTask, TileSource])); end; If you change only the parameter to `ParallelTask.NumTasks`, some tasks won't ever get data because you'll schedule only `numTasks` items to `RenderQueue`.
  5. Primož Gabrijelčič

    Object destroyed too soon?

    Do we have any neat solution for that problem? I can change `IWorkItem` to: IWorkItem = interface ['{7C583FC8-90DD-46A5-81B9-81B911AA1CBE}'] function GetResult: POmniValue; procedure SetResult(const Value: TOmniValue); property Result: POmniValue read GetResult; end; This allows me to use: workItem.Result.AsOwnedObject := TTestObj.Create; But it also prevents me from doing: workItem.Result := TTestObj.Create; Which is more common usage. Grrrrr!
  6. `workItem.Result` is a record property. Because of that, this code: workItem.Result.AsOwnedObject := TBitmap32.Create(256,256); is equivalent to running: var tmp: TOmniValue; tmp := workItem.Result; tmp.AsOwnedObject := TBitmap32.Create(256,256); And that fails. You should change the code to: var tmp: TOmniValue; tmp.AsOwnedObject := TBitmap32.Create(256,256); workItem.Result := tmp; I'll see if I can change the implementation of `IOmniWorkitem` to prevent such problems.
  7. Primož Gabrijelčič

    Object destroyed too soon?

    Doh! I knew I was just stupid. To recap (for people who don't want to dig through ton of code). procedure Test(const workItem: IWorkItem); begin workItem.Result.AsOwnedObject := TTestObj.Create; end; is equivalent to: procedure Test(const workItem: IWorkItem); var tmp: TOmniValue; begin tmp := workItem.Result; tmp.AsOwnedObject := TTestObj.Create; end; To do it correctly, one must change the code to: procedure Test(const workItem: IWorkItem); var ov: TOmniValue; begin ov.AsOwnedObject := TTestObj.Create; workItem.Result := ov; end; Thank you, guys!
  8. Minimized problem: Maybe somebody can tell me what is going on because I'm clueless.
  9. Indeed, if you set `workItem.Result` as owned object, it gets destroyed immediately after the `Asy_Execute` exits. This is the minimal code that reproduces the problem: procedure TfrmBackgroundWorkerImageFactory.Asy_Factory(const workItem: IOmniWorkItem); begin if not workItem.CancellationToken.IsSignalled then workItem.Result.AsOwnedObject := TBitmap32.Create(256,256); end; At the moment, don't use `OwnsObject` or `AsOwnedObject` at that point. I'll dig in to see what's going on.
  10. Primož Gabrijelčič

    Freeing tasks memory

    Create a reproducible test case and post i here. BTW, why are you createing 4000 tasks? Surely, there's a better way to solve your problem.
  11. I see accvio in G32.pas in procedure TCustomBitmap32.ChangeSize(var Width, Height: Integer; NewWidth, NewHeight: Integer); begin FBackend.ChangeSize(Width, Height, NewWidth, NewHeight); end; FBackend is nil Sorry, no idea why.
  12. Primož Gabrijelčič

    Caching with class variables

    Don't think so as we don't use crazily big sets. Plus this code is mostly used for compatibility reason; because some old configuration files store enum and set data as integers. But of course, all code that can fail, will fail.
  13. Primož Gabrijelčič

    Caching with class variables

    Copy&paste bug, sorry. Was OK on the blog, wrong here in the forum. Corrected. Thank you!
  14. Primož Gabrijelčič

    Caching with class variables

    It works for our code 🙂 We don't pass large sets through that function and everything is fine.
  15. Primož Gabrijelčič

    Caching with class variables

    Exactly - I somehow deleted that line from the gist, while still keeping it in my local copy of the code. 😞 😞 😞 (Actually, this Move should not be 'if'-ed. Just always execute it.) I'll fix my blog, gist example and this post when I come home later today. Thank you for finding the bug!
  16. Pipeline would work as you can run each stage in more than one parallel task (by using .NumTasks). But Parallel.BackgroundWorker is probably more appropriate.
  17. Primož Gabrijelčič

    Compiling Options?

    Was IDE Fix Pack merged into 10.3 release?
  18. Primož Gabrijelčič

    Watch one task with another - restart if stopped

    This looks like a bug in OTL. Can you please create a small, self-contained, compilable example so I can retest? As a workaround, why do you need Task1 to monitor Task2? This should work equally well: procedure RunTask; begin Task2 := CreateTask( procedure(const mTask: IOmniTask) begin ... end) .OnTerminated(procedure(const mTask: IOmniTaskControl) begin Task2 := nil; RunTask; end); Task2.Run; end;
  19. Primož Gabrijelčič

    Changes in Parallel Library

    When I need a busy loop I usually just do a := 1; while not timeout do a := Cos(a);
  20. Primož Gabrijelčič

    Changes in Parallel Library

    "Might or might not". Sleep is a good tool to demonstrate a bug. But I agree with you - if it works with Sleep, it still may not work with a high CPU load code. So proving with "Sleep" that the library works is not good enough. OTOH, proving with a high CPU load code that it works is also not good enough. "Unit tests can only be used to prove that a parallel code is NOT working." - me (BTW, I know that we agree here 🙂 I just wanted to state that testing parallel code with Sleep is a perfectly good tool. It is just not good enough.)
  21. Primož Gabrijelčič

    Changes in Parallel Library

    Because? Unless the thread pool manager is measuring CPU load, the result should be the same. And if the thread pool manager does that, it is easy to replace the code with a busy loop. As far as my bug hunting in Delphi's TThreadPool went, replacing Sleep(2000) with a 2 second busy loop never changed anything.
  22. How do you delete the folder? By deleting files one by one and the removing the parent folder? In that case, you may want to execute cmd /c rmdir /q /s target_folder from your program. Should be much faster.
  23. Primož Gabrijelčič

    Changes in Parallel Library

    The biggest problem with PPL (apart from number of terrifying bugs that were only recently fixed) is the annoying and uncontrollable behavior of its thread pool. It will start threads as it seems appropriate, sometimes seemingly without considering min/max concurrent tasks configuration (of which the 'min' part is actually not, as per source, see below). // Returns false if attempting to set a value < 0 or > MaxWorkerThreads. The actual number of pool // threads could be less than this value depending on actual demand. Setting this to too few threads could // be less than optimal resource utilization. function SetMinWorkerThreads(Value: Integer): Boolean; (BTW, the comment suggests that MinWorkerThreads can be set to the same value as MaxWorkerThreads. This is not the case. It must be less than that.) "Annoying and uncontrollable behavior": Sometimes you schedule N threads, then wait, then schedule M (M > N, but M < MaxWorkerThreads) threads, but only N would start. I can repeatedly reproduce this in Tokyo (and Berlin, and Seattle ...). The problem was mostly, but not completely, fixed in Rio. I noticed some problems there, too, but at the moment I don't yet know how to reproduce the problem from scratch.
  24. I don't understand how that should work. "Several windows are interested in receiving messages from the queue" - are they then fighting to fetch that message? It is quite possible that something goes wrong in such case. I don't believe I ever tested such setup. Can you get me a sample program so I can repeat the problem?
×