Why do I need Sharemem in my Delphi dll which only exposes a function with WideString parameters?
Asked Answered
O

1

10

I have a dll and a test application written in Delphi. The test application uses multiple threads to call the function exported by the dll. The exported function has a trivial thread safe implementation. When running the test application various errors (access violation, invalid pointer operation, stack overflow etc) happens or the application freezes. In some cases the application finishes without errors.

Note that these errors only happen (surface) when using multiple threads. When calling the function from the main thread only then everything works fine.

I have found that adding ShareMem to both the dll and the application stops all these kind of errors. But I don't understand why. To my knowledge ShareMem is only needed when passing long strings between the dll and the application. As far as I know WideString is not a long string.

Also according to this post ShareMem should not be required: Why can Delphi DLLs use WideString without using ShareMem?

Here is the source of the dll:

library External;

uses
  Winapi.Windows;

type
  TMyType = class
  private
    FText: string;
  end;

function DoSomething(input: WideString; out output: WideString): Bool; stdcall;
var
  x: TObject;
begin
  x := TMyType.Create;
  try
    output := x.ClassName;
  finally
    x.Free;
  end;
  Result := True;
end;

exports
  DoSomething;
begin
end.

Here is the test application:

program ConsoleTest;

{$APPTYPE CONSOLE}

uses
  System.SysUtils,
  Winapi.Windows,
  OtlParallel;

function DoSomething(input: WideString; out output: WideString): Bool; stdcall; external 'External.dll' name 'DoSomething';

var
  sResult: WideString;

begin
  try
    Parallel.&For(0, 500).Execute(procedure(value: Integer)
    var
      sResult: WideString;
    begin
      DoSomething('hhh', sResult);
    end);
    WriteLn('Done');
    ReadLn;
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
end.

Why ShareMem makes the bugs go away and is there another way to fix these bugs?

I am using Delphi XE2 and OmniThread 3.07.5.

Update - Same issue when run from a VCL application's button's on click event handler - If DoSomething uses a critical section inside then runs fine - If FText field is removed from TMyClass then no errors are reported but the application randomly freezes

Ormolu answered 19/3, 2018 at 8:47 Comment(9)
This smells like an issue with OTLTrilinear
Weird stuff. Converting test app to VCL "fixes" the problem.Planarian
@DavidHeffernan Exactly the same problem happens with System.Threading and TParallel.&For.Planarian
When I get a useful stack at crash, it is always inside UTF8ToString being called as part of TObject.ClassName and it crashes in SysGetMem (in FastMM) indicating corrupted memory.Planarian
Note that same problem happens when run from a VCL application; If the code in DoSomething is protected by a critical section no bug happensOrmolu
Does anything set IsMultiThread to true? Nothing here does, so perhaps OTL? It is required for FastMM to support multiple threads.Polytechnic
Indeed, setting isMultiThread to true seems to fix the problem.Polysyllabic
OTL uses TThread which calls BeginThread which sets IsMultiThread to True. The problem is on the DLL side. As ShareMem is not used, DLL is using its own copy of memory manager and nobody sets IsMultiThread on that side.Planarian
@SertacAkyuz yes, this solves the problem. Make an answer so I can accept it.Ormolu
P
11

For the standard memory manager (FastMM) to support multi threading, you need to set the IsMultiThread flag.

When you use RTL for threading, this flag is automatically set. As revealed in the comments to the question, OTL also use RTL to start its threads. So the memory manager in your executable is aware of threading, but the distinct memory manager in the dll causes errors. When you use "sharemem", there is only one memory manager which is aware of threading because of OTL, so you encounter no errors.

An alternative solution, apart from using a shared memory manager, would be to set the flag also for the memory manager in the dll.

Polytechnic answered 19/3, 2018 at 20:36 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.