2017-07-16 19:36:44 +02:00
|
|
|
// *************************************************************************** }
|
|
|
|
//
|
|
|
|
// Delphi MVC Framework
|
|
|
|
//
|
2020-01-06 16:49:18 +01:00
|
|
|
// Copyright (c) 2010-2020 Daniele Teti and the DMVCFramework Team
|
2017-07-16 19:36:44 +02:00
|
|
|
//
|
|
|
|
// https://github.com/danieleteti/delphimvcframework
|
|
|
|
//
|
|
|
|
// ***************************************************************************
|
|
|
|
//
|
|
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
// you may not use this file except in compliance with the License.
|
|
|
|
// You may obtain a copy of the License at
|
|
|
|
//
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
//
|
|
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
// See the License for the specific language governing permissions and
|
|
|
|
// limitations under the License.
|
|
|
|
//
|
|
|
|
// ***************************************************************************
|
|
|
|
|
|
|
|
unit MVCFramework.Cache;
|
|
|
|
|
|
|
|
interface
|
|
|
|
|
|
|
|
uses
|
|
|
|
System.Classes, System.SysUtils, System.Generics.Collections, System.Rtti,
|
|
|
|
System.SyncObjs, MVCFramework.Commons, System.DateUtils;
|
|
|
|
|
|
|
|
const
|
|
|
|
DEFAULT_CACHE_DURATION = OneMinute * 10;
|
|
|
|
|
|
|
|
type
|
|
|
|
TMVCCacheItem = class
|
|
|
|
private
|
|
|
|
FCriticalSection: TCriticalSection;
|
|
|
|
FValue: TValue;
|
2018-01-29 17:30:53 +01:00
|
|
|
FTimeStamp: TDateTime;
|
2017-07-16 19:36:44 +02:00
|
|
|
function GetValue: TValue;
|
|
|
|
procedure SetValue(const Value: TValue);
|
|
|
|
public
|
|
|
|
constructor Create;
|
|
|
|
destructor Destroy; override;
|
|
|
|
property Value: TValue read GetValue write SetValue;
|
2018-01-29 17:30:53 +01:00
|
|
|
property TimeStamp: TDateTime read FTimeStamp;
|
2017-07-16 19:36:44 +02:00
|
|
|
end;
|
|
|
|
|
|
|
|
TMVCCache = class sealed
|
|
|
|
private
|
|
|
|
FStorage: TObjectDictionary<string, TMVCCacheItem>;
|
2017-10-09 16:17:12 +02:00
|
|
|
FMREW: TMultiReadExclusiveWriteSynchronizer;
|
2017-07-16 19:36:44 +02:00
|
|
|
public
|
|
|
|
constructor Create;
|
|
|
|
destructor Destroy; override;
|
2018-01-29 17:30:53 +01:00
|
|
|
function SetValue(const AName: string; const AValue: TValue): TMVCCacheItem;
|
2017-07-16 19:36:44 +02:00
|
|
|
function Contains(const AName: string; out AValue: TValue): Boolean;
|
2018-01-29 17:30:53 +01:00
|
|
|
function ContainsItem(const AName: string; out AItem: TMVCCacheItem): Boolean;
|
2017-07-16 19:36:44 +02:00
|
|
|
function GetValue(const AName: string): TValue;
|
2017-10-09 16:17:12 +02:00
|
|
|
function ExecOnItemWithWriteLock(const AName: string; const AAction: TProc<TValue>): Boolean;
|
2017-07-16 19:36:44 +02:00
|
|
|
end;
|
|
|
|
|
|
|
|
TMVCCacheSingleton = class
|
|
|
|
private
|
|
|
|
class var Lock: TObject;
|
|
|
|
class var SInstance: TMVCCache;
|
|
|
|
protected
|
|
|
|
class function GetInstance: TMVCCache; static;
|
|
|
|
public
|
|
|
|
class property Instance: TMVCCache read GetInstance;
|
|
|
|
class constructor Create;
|
|
|
|
class destructor Destroy;
|
|
|
|
end;
|
|
|
|
|
|
|
|
implementation
|
|
|
|
|
|
|
|
uses
|
|
|
|
System.Math;
|
|
|
|
|
|
|
|
{ TCache }
|
|
|
|
|
2018-01-29 17:30:53 +01:00
|
|
|
function TMVCCache.SetValue(const AName: string; const AValue: TValue): TMVCCacheItem;
|
2017-07-16 19:36:44 +02:00
|
|
|
var
|
2018-01-29 17:30:53 +01:00
|
|
|
lCacheItem: TMVCCacheItem;
|
2019-02-19 13:04:53 +01:00
|
|
|
Value: TValue;
|
2017-07-16 19:36:44 +02:00
|
|
|
begin
|
2019-02-19 13:04:53 +01:00
|
|
|
Value := AValue;
|
|
|
|
|
2017-10-09 16:17:12 +02:00
|
|
|
FMREW.DoWithWriteLock(
|
2017-07-16 19:36:44 +02:00
|
|
|
procedure
|
|
|
|
var
|
|
|
|
lItem: TMVCCacheItem;
|
|
|
|
begin
|
|
|
|
if FStorage.TryGetValue(AName, lItem) then
|
|
|
|
begin
|
2019-02-19 13:04:53 +01:00
|
|
|
lItem.Value := Value;
|
2017-07-16 19:36:44 +02:00
|
|
|
end
|
|
|
|
else
|
|
|
|
begin
|
2018-01-29 17:30:53 +01:00
|
|
|
lCacheItem := TMVCCacheItem.Create;
|
2017-07-16 19:36:44 +02:00
|
|
|
try
|
2019-02-19 13:04:53 +01:00
|
|
|
lCacheItem.Value := Value;
|
2018-01-29 17:30:53 +01:00
|
|
|
FStorage.Add(AName, lCacheItem);
|
2017-07-16 19:36:44 +02:00
|
|
|
except
|
2018-01-29 17:30:53 +01:00
|
|
|
lCacheItem.Free;
|
2017-07-16 19:36:44 +02:00
|
|
|
raise;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
end);
|
2018-01-29 17:30:53 +01:00
|
|
|
Result := lCacheItem;
|
2017-07-16 19:36:44 +02:00
|
|
|
end;
|
|
|
|
|
2017-10-09 16:17:12 +02:00
|
|
|
function TMVCCache.ExecOnItemWithWriteLock(const AName: string; const AAction: TProc<TValue>): Boolean;
|
2017-07-16 19:36:44 +02:00
|
|
|
var
|
|
|
|
lItem: TMVCCacheItem;
|
|
|
|
begin
|
|
|
|
Result := False;
|
2017-10-09 16:17:12 +02:00
|
|
|
FMREW.BeginWrite;
|
2017-07-16 19:36:44 +02:00
|
|
|
try
|
|
|
|
if FStorage.TryGetValue(AName, lItem) then
|
|
|
|
begin
|
|
|
|
Result := True;
|
|
|
|
AAction(lItem.Value);
|
|
|
|
end;
|
|
|
|
finally
|
2017-10-09 16:17:12 +02:00
|
|
|
FMREW.EndWrite;
|
2017-07-16 19:36:44 +02:00
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
|
|
|
function TMVCCache.Contains(const AName: string; out AValue: TValue): Boolean;
|
|
|
|
var
|
2018-01-29 17:30:53 +01:00
|
|
|
lItem: TMVCCacheItem;
|
|
|
|
begin
|
|
|
|
Result := ContainsItem(AName, lItem);
|
|
|
|
if Result then
|
|
|
|
AValue := lItem.Value;
|
|
|
|
end;
|
|
|
|
|
|
|
|
function TMVCCache.ContainsItem(const AName: string;
|
|
|
|
out AItem: TMVCCacheItem): Boolean;
|
|
|
|
var
|
|
|
|
lItem: TMVCCacheItem;
|
2017-07-16 19:36:44 +02:00
|
|
|
lRes: Boolean;
|
|
|
|
begin
|
2017-10-09 16:17:12 +02:00
|
|
|
FMREW.DoWithReadLock(
|
2017-07-16 19:36:44 +02:00
|
|
|
procedure
|
|
|
|
begin
|
2018-01-29 17:30:53 +01:00
|
|
|
lRes := FStorage.TryGetValue(AName, lItem);
|
2017-07-16 19:36:44 +02:00
|
|
|
end);
|
|
|
|
Result := lRes;
|
|
|
|
if Result then
|
2018-01-29 17:30:53 +01:00
|
|
|
AItem := lItem;
|
2017-07-16 19:36:44 +02:00
|
|
|
end;
|
|
|
|
|
|
|
|
constructor TMVCCache.Create;
|
|
|
|
begin
|
|
|
|
inherited Create;
|
|
|
|
FStorage := TObjectDictionary<string, TMVCCacheItem>.Create([doOwnsValues]);
|
2017-10-09 16:17:12 +02:00
|
|
|
FMREW := TMultiReadExclusiveWriteSynchronizer.Create;
|
2017-07-16 19:36:44 +02:00
|
|
|
end;
|
|
|
|
|
|
|
|
destructor TMVCCache.Destroy;
|
|
|
|
begin
|
2017-10-09 16:17:12 +02:00
|
|
|
FMREW.Free;
|
2017-07-16 19:36:44 +02:00
|
|
|
FStorage.Free;
|
|
|
|
inherited;
|
|
|
|
end;
|
|
|
|
|
|
|
|
function TMVCCache.GetValue(const AName: string): TValue;
|
|
|
|
var
|
|
|
|
lItem: TMVCCacheItem;
|
|
|
|
lResult: TValue;
|
|
|
|
begin
|
|
|
|
Result := TValue.Empty;
|
2017-10-09 16:17:12 +02:00
|
|
|
FMREW.DoWithReadLock(
|
2017-07-16 19:36:44 +02:00
|
|
|
procedure
|
|
|
|
begin
|
|
|
|
if FStorage.TryGetValue(AName, lItem) then
|
|
|
|
begin
|
|
|
|
lResult := lItem.Value;
|
|
|
|
end;
|
|
|
|
end);
|
|
|
|
Result := lResult;
|
|
|
|
end;
|
|
|
|
|
|
|
|
{ TMVCFrameworkCacheItem }
|
|
|
|
|
|
|
|
constructor TMVCCacheItem.Create;
|
|
|
|
begin
|
|
|
|
inherited Create;
|
|
|
|
FCriticalSection := TCriticalSection.Create;
|
|
|
|
FValue := TValue.Empty;
|
2018-01-29 17:30:53 +01:00
|
|
|
FTimeStamp := 0;
|
2017-07-16 19:36:44 +02:00
|
|
|
end;
|
|
|
|
|
|
|
|
destructor TMVCCacheItem.Destroy;
|
|
|
|
begin
|
|
|
|
FCriticalSection.Free;
|
|
|
|
inherited;
|
|
|
|
end;
|
|
|
|
|
|
|
|
function TMVCCacheItem.GetValue: TValue;
|
|
|
|
begin
|
|
|
|
Result := FValue;
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure TMVCCacheItem.SetValue(const Value: TValue);
|
|
|
|
begin
|
|
|
|
FValue := Value;
|
2018-01-29 17:30:53 +01:00
|
|
|
FTimeStamp := Now;
|
2017-07-16 19:36:44 +02:00
|
|
|
end;
|
|
|
|
|
|
|
|
{ TMVCCacheSingleton }
|
|
|
|
|
|
|
|
class constructor TMVCCacheSingleton.Create;
|
|
|
|
begin
|
|
|
|
SInstance := nil;
|
|
|
|
Lock := TObject.Create;
|
|
|
|
end;
|
|
|
|
|
|
|
|
class destructor TMVCCacheSingleton.Destroy;
|
|
|
|
begin
|
|
|
|
FreeAndNil(SInstance);
|
|
|
|
FreeAndNil(Lock);
|
|
|
|
end;
|
|
|
|
|
|
|
|
class function TMVCCacheSingleton.GetInstance: TMVCCache;
|
|
|
|
begin
|
|
|
|
if not Assigned(SInstance) then
|
|
|
|
begin
|
|
|
|
TMonitor.Enter(Lock);
|
|
|
|
try
|
|
|
|
if not Assigned(SInstance) then // doublecheck here
|
|
|
|
begin
|
|
|
|
SInstance := TMVCCache.Create;
|
|
|
|
end;
|
|
|
|
finally
|
|
|
|
TMonitor.Exit(Lock);
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
Result := SInstance;
|
|
|
|
end;
|
|
|
|
|
|
|
|
end.
|