Jump to content

pyscripter

Members
  • Content Count

    785
  • Joined

  • Last visited

  • Days Won

    42

Everything posted by pyscripter

  1. pyscripter

    Embarcadero entries in the path

    Thank you all for your answers. What is wrong with removing all these entries. Does this cause Rad Studio to malfunction?
  2. That is so nice... Thanks for spotting the typo anyway. With the typo fixed the following test code: procedure Test(); begin try var Bob := TSmartPointer.Wrap(TTalking.Create('Bob')); Bob.Talk; var John := TSmartPointer.Wrap(TTalking.Create('John')); John.Talk; John := Bob; John.Talk; finally WriteLn('Do more stuff'); end; end; produces Bob is talking John is talking Release 0 John is gone Bob is talking Release 1 Release 0 Bob is gone Do more stuff with no AV or memory leak.
  3. True. Spring4D is the best! And for what it is (not) worth, I have voted for RSP-27375. Here is another one (more complex but still compact) which I think is comparable and similar in approach to Spring4D, based on a Stackoverflow question. I am adding it here for the completeness of the discussion. type TInjectType<T> = record public VMT: pointer; unknown: IInterface; RefCount: integer; AValue: T; end; TInject<T> = class public type TInjectType = TInjectType<T>; PInjectType = ^TInjectType; end; PInjectObjectType = TInject<TObject>.PInjectType; TSmartPointer = class class function Wrap<T: class>(const AValue: T): TFunc<T>; static; end; function Trick_Release(const obj: PInjectObjectType): Integer; stdcall; forward; function Trick_AddRef(const obj: PInjectObjectType): Integer; stdcall; forward; function Invoke(const obj: PInjectObjectType): TObject; forward; const PSEUDO_VMT: array [0 .. 3] of pointer = (nil, @Trick_AddRef, @Trick_Release, @Invoke); function Trick_AddRef(const obj: PInjectObjectType): Integer; stdcall; begin Result:= AtomicIncrement(Obj^.RefCount); end; function Trick_Release(const obj: PInjectObjectType): Integer; stdcall; begin Result:= AtomicDecrement(Obj^.RefCount); WriteLn('Release '+IntToStr(Obj.RefCount)); if Result = 0 then begin obj^.AValue.Free; FreeMem(obj); end; end; function Invoke(const obj: PInjectObjectType): TObject; begin Result:= obj^.AValue; end; class function TSmartPointer.Wrap<T>(const AValue: T): TFunc<T>; var p: PInjectObjectType; begin P:= GetMemory(SizeOf(TInjectType<T>)); p.RefCount:= 1; pointer(p.unknown):= p; p.VMT:= @PSEUDO_VMT; p.AValue:= AValue; pointer(Result):= pointer(TFunc<T>(p)); end; Note: typo corrected (see below).
  4. Minimalist implementation of SmartPointers based on a old post by Barry Kelly comparable to the Spring4D one in performance. type TObjectHandle<T: class> = class(TInterfacedObject, TFunc<T>) private FValue: T; public constructor Create(AValue: T); destructor Destroy; override; function Invoke: T; end; TSmartPointer = record class function Make<T: class>(AValue: T): TFunc<T>; static; end; constructor TObjectHandle<T>.Create(AValue: T); begin FValue := AValue; end; destructor TObjectHandle<T>.Destroy; begin FValue.Free; end; function TObjectHandle<T>.Invoke: T; begin Result := FValue; end; { TSmartPointer } class function TSmartPointer.Make<T>(AValue: T): TFunc<T>; begin Result := TObjectHandle<T>.Create(AValue); end; Used as in: var Bob := TSmartPointer.Make(TTalking.Create('Bob'))(); or var Bob := TSmartPointer.Make(TTalking.Create('Bob'));
  5. pyscripter

    You RAD Studio 10.4 Sydney appreciated features and bug fixes

    @Anders Melander The general rule that has always applied is that managed types (strings, interfaces, records with managed fields, dynamic arrays, etc,) are finalized at the end of the scope in which they are introduced. This included temp variables. The newly introduced managed records breaks this rule. Whether you like it better or not, it is inconsistent. And a local block does not solve the problem. The temp managed record will still self-destruct at the end of the statement it is used.
  6. pyscripter

    You RAD Studio 10.4 Sydney appreciated features and bug fixes

    Your example is not correct: Try: program Scope; {$APPTYPE CONSOLE} uses System.SysUtils, System.Classes; type TTestScope = class(TInterfacedObject) destructor Destroy; override; end; { TTestScope } destructor TTestScope.Destroy; begin WriteLn('Gone'); inherited; end; procedure Test; var Foo: IUnknown; begin Foo := TTestScope.Create as IUnknown; WriteLn('Do stuff'); Foo := nil; // This does destroy the object WriteLn('Do more stuff'); end; begin Test(); ReadLn; end. Output: Do stuff Gone Do more stuff
  7. pyscripter

    You RAD Studio 10.4 Sydney appreciated features and bug fixes

    I think there is a serious issue with the timing of finalization of temp managed records which now is ASAP unlike regular records. Please see Timing of finalization of temporary managed records I would love to hear what the experts think about this. @Stefan Glienke @David Heffernan @Marco Cantu etc.
  8. pyscripter

    You RAD Studio 10.4 Sydney appreciated features and bug fixes

    Also the improvements in Vcl.Styles and DPI awareness are welcome. There are still a few outstanding issues: InputQuery scaling and styling issues Styled menus DPI scaling TCustomizeDlg not fully VCL styled
  9. pyscripter

    You RAD Studio 10.4 Sydney appreciated features and bug fixes

    Right now the quality portal is updated at a frenetic rate. There was a very large number of fixes, I think more than in any other previous version, and I was quite pleased that quite a few of my reports have been resolved, some of the unexpectedly: System.AnsiStrings AdjustLineBreaks not exported Memory leak in TInvokeableVariantType.DispInvoke Optimize TInvokeableVariantType.DispInvoke Wrongly premultiplied TWICImage when assigning a Bitmap with aDefined TButtonedEdit is not styled properly System.Threading got some attention with the famous Cancel and Wait issue resolved. Issues I would like to see fixed now and consider critical are: TThreadedQueue and TMonitor issue, possible solution InterlockedCompareExchange128 doesn't restore RBX Threading - Incorrect calculation of IdleWorkerThreadCount
  10. pyscripter

    MMX supports Delphi 10.4 Sydney

    That was quick!
  11. pyscripter

    Revisiting TThreadedQueue and TMonitor

    @Anders MelanderThanks for the explanation. I have updated my comment in the RSP.
  12. pyscripter

    Revisiting TThreadedQueue and TMonitor

    Are the MOV instructions after LOCK CMPXCHG16B [R10] needed? The following passes the stress test: {$IFDEF Win64} function InterlockedCompareExchange128(Destination: Pointer; ExchangeHigh, ExchangeLow: Int64; ComparandResult: Pointer): boolean; // The parameters are in the RCX, RDX, R8 and R9 registers per the MS x64 calling convention: // RCX Destination // RDX ExchangeHigh // R8 ExchangeLow // R9 ComparandResult // // CMPXCHG16B requires the following register setup: // RDX:RAX ComparandResult.High:ComparandResult.Low // RCX:RBX ExchangeHigh:ExchangeLow // See: https://www.felixcloutier.com/x86/cmpxchg8b:cmpxchg16b asm .PUSHNV RBX MOV R10,Destination // RCX MOV RBX,ExchangeLow // R8 MOV RCX,ExchangeHigh // RDX MOV RDX,[ComparandResult+8] // R9 MOV RAX,[ComparandResult] // R9 LOCK CMPXCHG16B [R10] // MOV [ComparandResult+8],RDX // R9 // MOV [ComparandResult],RAX // R9 SETZ AL end; {$ENDIF Win64}
  13. pyscripter

    Revisiting TThreadedQueue and TMonitor

    Correct, but in the fix it is declered as boolean. So I guess MOVZX EAX,AL is not needed. I am reattaching the amended file. MonitorWaitStackFix.pas
  14. pyscripter

    Revisiting TThreadedQueue and TMonitor

    Correct in general but in this particular test and given the timeout the greater the number of threads (up to a point) the greater the number of calls to NewWaitObj (which calls the stack push and pop). You can confirm by running the stress test.
  15. pyscripter

    Revisiting TThreadedQueue and TMonitor

    I am attaching an updated fix using the corrected InterlockedCompareExchange128 by @Anders Melander. A note about @Darian Miller stress test from Github. If you run the stress test with the fix you will see failures reported. Fist a couple of important pointers: WaitForSingleObject and the rest of Windows wait functions by default use the system clock resolution and not the high-resolution timer like QueryPerformanceCounter that the StopWatch is using. The resolution of clock timer is between 7-15ms at least in Windows 7. The StopWatch ElapsedMilliseconds is a rounded figure. So when it is zero it does not mean that the elapsed time is zero. I have confirmed that in the cases where the stress test reports a failure, this is not Delphi's fault but what the WaitForSingleObject returns. To run the stress test in a meaningful way either change the POP-TIMEOUT to a value greater than 15 or change the TTestThread.Execute() so that it just reports cases in which the vStopWatch.ElapsedMilliseconds is zero but it does not stop the test: e.g. procedure TTestThread.Execute(); var Item: TItem; vWaitResult:TWaitResult; vStopwatch:TStopwatch; begin pubSyncrhonizedStart.WaitFor(); while not Terminated do begin if fQueue.ShutDown then Break; if pubTestCompletionCheck.WaitFor(0) = wrSignaled then Break; vStopwatch := TStopwatch.StartNew; vWaitResult := FQueue.PopItem( Item ); vStopWatch.Stop(); //Note: Reason for ACCEPTABLE_VARIANCE_MS is that on some low timeout values (like 200ms) it occasionally times out a little early (like 180ms) if (vWaitResult = wrTimeout) and (vStopWatch.ElapsedMilliseconds > 0) and (vStopWatch.ElapsedMilliseconds >= POP_TIMEOUT-ACCEPTABLE_TIMEOUTVARIANCE_MS) then begin //successful PopItem operation as we aren't adding anything into our queue Continue; end else begin LogIt('TTestThread ERROR: TThreadedQueue.PopItem returned [' + TRttiEnumerationType.GetName(vWaitResult) + '] unexpectedly after [' + IntToStr(vStopWatch.ElapsedMilliseconds) + 'ms]'); //pubTestCompletionCheck.SetEvent(); //Break; end; end; end; I have tested with a couple of thousand threads in both win32 and win64 and it behaves as expected. MonitorWaitStackFix.pas
  16. pyscripter

    Revisiting TThreadedQueue and TMonitor

    I have packaged the fix as a stand-alone unit (see attached). You just add this unit to your project. Uses the CAS function from OmniThreadLibrary. It would still be nice to fix InterlockedCompareExchange128 . MonitorWaitStackFix.pas
  17. pyscripter

    Revisiting TThreadedQueue and TMonitor

    If you remove the packed from from the TEventStack definition there will be no need to allocate on the heap. Please read https://stackoverflow.com/questions/8460862/what-does-packed-now-forces-byte-alignment-of-records-mean.
  18. pyscripter

    Revisiting TThreadedQueue and TMonitor

    @Darian MillerI must add that I am testing with the beta in case it makes a difference.
  19. pyscripter

    Revisiting TThreadedQueue and TMonitor

    @Darian Miller Please try with this version. It uses the OnmiThreadLibrary CAS function in both 32-bit and 64-bit. Works solidly here. iaStressTest.TThreadedQueue.PopItem.pas
  20. pyscripter

    Revisiting TThreadedQueue and TMonitor

    @Anders MelanderCurrently the only way to pass the tests in 64bits is to use the modified version of the CAS function which now looks like this: function CAS(const oldData: pointer; oldReference: NativeInt; newData: pointer; newReference: NativeInt; var destination): boolean; asm {$IFNDEF CPUX64} ... {$ELSE CPUX64} .pushnv rbx mov rax, oldData mov rbx, newData mov rcx, newReference mov r8, [destination] lock cmpxchg16b [r8] {$ENDIF CPUX64} setz al end; { CAS } Your implementation of InterlockedCompareExchange128 does not pass the tests, same as Delphi's own implementation. It would be nice to come up with a working InterlockedCompareExchange128 and submit that as a separate bug report with a working solution.
  21. pyscripter

    Revisiting TThreadedQueue and TMonitor

    SetMinimumBlockAlignment does nothing in 64bits (see the source code). Alignment is fixed at mba16Byte always.
  22. pyscripter

    Revisiting TThreadedQueue and TMonitor

    If I replace the push and pop rbx with .pushnv rbx and remove the .noframe the code crashes. How is one supposed to use .pushnv? function CAS(const oldData: pointer; oldReference: NativeInt; newData: pointer; newReference: NativeInt; var destination): boolean; asm {$IFNDEF CPUX64} {$ELSE CPUX64} .pushnv rbx //rsp := rsp - 8 ! mov rax, oldData mov rbx, newData mov rcx, newReference mov r8, [destination + 8] //+8 with respect to .noframe lock cmpxchg16b [r8] {$ENDIF CPUX64} setz al end; { CAS }
  23. pyscripter

    Revisiting TThreadedQueue and TMonitor

    @David HeffernanThanks. Could you please post here what the function should be?
  24. pyscripter

    Revisiting TThreadedQueue and TMonitor

    Well there is the Delphi implementation which does not work: function InterlockedCompareExchange128(Destination: Pointer; ExchangeHigh, ExchangeLow: Int64; ComparandResult: Pointer): Bool; stdcall; asm MOV R10,RCX MOV RBX,R8 MOV RCX,RDX MOV RDX,[R9+8] MOV RAX,[R9] LOCK CMPXCHG16B [R10] SETZ AL end; and the CAS function from OmniThreadLibrary which works well. function CAS(const oldData: pointer; oldReference: NativeInt; newData: pointer; newReference: NativeInt; var destination): boolean; asm {$IFNDEF CPUX64} push edi push ebx mov ebx, newData mov ecx, newReference mov edi, destination lock cmpxchg8b qword ptr [edi] pop ebx pop edi {$ELSE CPUX64} .noframe push rbx //rsp := rsp - 8 ! mov rax, oldData mov rbx, newData mov rcx, newReference mov r8, [destination + 8] //+8 with respect to .noframe lock cmpxchg16b [r8] pop rbx {$ENDIF CPUX64} setz al end; { CAS } @David Heffernanhas said that both are wrong (see earlier comments). By the way Allen Bauer explains .PUSHNV here.
  25. pyscripter

    Revisiting TThreadedQueue and TMonitor

    @David HeffernanI know very little about assembler and I will leave this to experts to resolve. However I do see push rbx and pop rbx in the code. Does this not save and restore rbx? This code has been in the master branch of OmniThreadLibrary for ages and should have been tested extensively. It does appear to work well we the TMonitor fix.
×