program LockWithInstance;
{$APPTYPE CONSOLE}
{$R *.res}
uses
System.SysUtils,
System.Timespan,
System.SyncObjs;
type
ELockException =
class(
Exception);
ELockNotHeldException =
class(ELockException);
TInstance = Pointer;
ILock =
interface
['
{57CCCDE4-63F8-41F6-A6F0-39B4159B06FF}']
function Lock(
const AInstance: TInstance; ATimeout: Cardinal = 0): Boolean;
/// <exception cref="ELockNotHeldException" />
procedure UnLock(
const AInstance: TInstance);
end;
ILockableResource =
interface
['
{88085418-BD27-4B5D-AD00-B456C8E017A7}']
function TryLock(
const AInstance: TInstance;
out lock: ILock;
const timeout: TTimeSpan
): Boolean;
overload;
function TryLock(
const AInstance: TInstance;
out lock: ILock): Boolean;
overload;
end;
TLock =
class(TInterfacedObject, ILock)
strict private
FInstance: TInstance;
public
constructor Create;
function Lock(
const AInstance: TInstance; ATimeout: Cardinal = 0): Boolean;
procedure UnLock(
const AInstance: TInstance);
end;
TLockableResource =
class(TInterfacedObject, ILockableResource)
strict private
FLock: ILock;
public
constructor Create();
function TryLock(
const AInstance: TInstance;
out lock: ILock;
const timeout: TTimeSpan
): Boolean;
overload;
function TryLock(
const AInstance: TInstance;
out lock: ILock): Boolean;
overload;
end;
constructor TLock.Create;
begin
inherited Create;
end;
function TLock.Lock(
const AInstance: TInstance; ATimeout: Cardinal = 0): Boolean;
begin
if FInstance =
nil then
begin
FInstance := AInstance;
Result := TMonitor.Enter(FInstance, ATimeout);
end
else
raise ELockException.Create('
This is a different instance!');
end;
procedure TLock.UnLock(
const AInstance: TInstance);
begin
if AInstance = FInstance
then
begin
TMonitor.Exit(AInstance);
end
else
raise ELockNotHeldException.Create('
This instance is not holding the lock!');
end;
{ TLockableResource }
function TLockableResource.TryLock(
const AInstance: TInstance;
out lock: ILock;
const timeout: TTimeSpan): Boolean;
begin
lock := FLock;
Result := lock.Lock(AInstance);
end;
constructor TLockableResource.Create;
begin
FLock := TLock.Create
end;
function TLockableResource.TryLock(
const AInstance: TInstance;
out lock: ILock): Boolean;
begin
lock := FLock;
Result := lock.Lock(AInstance);
end;
procedure Test1;
var
A, B: TObject;
LockableResource: ILockableResource;
Lock: ILock;
begin
A := TObject.Create;
B := TObject.Create;
LockableResource := TLockableResource.Create;
LockableResource.TryLock(A, Lock);
Lock.unlock(B);
LockableResource.TryLock(B, Lock);
B.Free;
A.Free;
end;
procedure Test2;
var
A, B: TObject;
LockableResource: ILockableResource;
Lock: ILock;
begin
A := TObject.Create;
B := TObject.Create;
LockableResource := TLockableResource.Create;
LockableResource.TryLock(A, Lock);
LockableResource.TryLock(B, Lock);
B.Free;
A.Free;
end;
begin
try
Test1;
except
on E:
Exception do
Writeln(E.ClassName, '
: ', E.
Message);
end;
try
Test2;
except
on E:
Exception do
Writeln(E.ClassName, '
: ', E.
Message);
end;
Readln;
end.