When writing a DataSnap appserver, you should probably try to make it thread-safe and register it to use the multi-threaded apartment (MTA) model (ThreadingModel = tmFree) because it gives you total control over synchronization and optimization of your code. But writing thread-safe code also requires more work. In some cases, it may be useful to register your appserver to use the single-threaded apartment (STA) model (ThreadingModel = tmApartment) and rely on COM to serialize calls from different threads into one thread “apartment” – the same thread which was used to create the instance.
Recently, after reviewing an external library I’m using in my appserver, and coming to suspect that it might not be thread-safe, I’ve chosen to use the STA model as a temporary workaround until I fix the problem.
Soon enough, I hit a problem – in the STA model, interface pointers cannot be freely passed from thread to thread – they need to be marshaled across thread boundaries, otherwise the call will fail with RPC_E_WRONG_THREAD error.
To marshal interface pointers across threads, you can use CoMarshalInterThreadInterfaceInStream . Another, very easy and convenient way is to aggregate the Free Threaded Marshaler.
Delphi makes aggregation a piece of cake! 😉 Here’s a simple class which you can derive your appserver from (instead of deriving from TRemoteDataModule directly):
unit DataBkrEx;
interface
uses
  Classes, ActiveX,
  DataBkr;
type
  TRemoteDataModuleEx = class(TRemoteDataModule, IMarshal)
  private
    FMarshal: IUnknown;
    function GetMarshal: IMarshal;
    property Marshal: IMarshal read GetMarshal implements IMarshal;
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
  end;
implementation
uses ComObj;
{ TRemoteDataModuleEx private }
function TRemoteDataModuleEx.GetMarshal: IMarshal;
begin
  if not Assigned(FMarshal) then
    OleCheck(CoCreateFreeThreadedMarshaler(Self as IUnknown, FMarshal));
  Result := FMarshal as IMarshal;
end;
{ TRemoteDataModuleEx public }
constructor TRemoteDataModuleEx.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  FMarshal := nil;
end;
destructor TRemoteDataModuleEx.Destroy;
begin
  FMarshal := nil;
  inherited Destroy;
end;
end.
Note that I’m not checking for a controller object (in case the appserver itself is aggregated) but that’s normally not used in DataSnap appservers. The correct implementation would also require a replacement for TComponentFactory in the VclCom unit.