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.
No comments:
Post a Comment