{
Timers – запуск событий по истечении заданного времени.
Copyright © 2016, 2019, 2022–2023 Малик Разработчик
Это свободная программа: вы можете перераспространять её и/или
изменять её на условиях Меньшей Стандартной общественной лицензии GNU в том виде,
в каком она была опубликована Фондом свободного программного обеспечения;
либо версии 3 лицензии, либо (по вашему выбору) любой более поздней версии.
Эта программа распространяется в надежде, что она может быть полезна,
но БЕЗО ВСЯКИХ ГАРАНТИЙ; даже без неявной гарантии ТОВАРНОГО ВИДА
или ПРИГОДНОСТИ ДЛЯ ОПРЕДЕЛЁННЫХ ЦЕЛЕЙ. Подробнее см. в Меньшей Стандартной
общественной лицензии GNU.
Вы должны были получить копию Меньшей Стандартной общественной лицензии GNU
вместе с этой программой. Если это не так, см.
<http://www.gnu.org/licenses/>.
}
unit Timers;
{$MODE DELPHI}
{$ASMMODE INTEL}
interface
uses
Windows,
Lang;
{%region public }
const
TIMER_HANDLER_GUID = '{74C86B60-AC5B-4E8D-8ACD-29A50B5C1511}';
type
TimerHandler = interface;
Timer = class;
TimerHandler = interface(_Interface) [TIMER_HANDLER_GUID]
procedure timerHandle(timer: Timer; millisecondsElapsed: long);
end;
Timer = class(_Object, Runnable)
private
handler: TimerHandler;
handlerLock: ThreadLock;
nextHandleLock: ThreadLock;
nextHandle: long;
prevHandle: long;
terminated: boolean;
procedure setNextHandle(nextHandle: long);
function getNextHandle(): long;
public
constructor create(handler: TimerHandler);
destructor destroy; override;
procedure run();
procedure terminate();
procedure setInterval(milliseconds: long);
function getRemainingMilliseconds(): long;
end;
{%endregion}
{%region routine }
function getMilliseconds(): long;
{%endregion}
implementation
{%region routine }
function getMilliseconds(): long; assembler; nostackframe;
asm
push ebx
push esi
push edi
xor eax, eax
xor edx, edx
call Windows.getTickCount64
pop edi
pop esi
pop ebx
end;
{%endregion}
{%region Timer }
constructor Timer.create(handler: TimerHandler);
begin
inherited create();
self.handler := handler;
self.handlerLock := ThreadLock.create();
self.nextHandleLock := ThreadLock.create();
self.prevHandle := -1;
self.nextHandle := -1;
self.terminated := false;
startThread(self);
end;
destructor Timer.destroy;
begin
terminate();
while not terminated do begin
sleep(2);
end;
nextHandleLock.free();
handlerLock.free();
inherited destroy;
end;
procedure Timer.setNextHandle(nextHandle: long);
begin
nextHandleLock.enter();
try
self.nextHandle := nextHandle;
finally
nextHandleLock.leave();
end;
end;
function Timer.getNextHandle(): long;
begin
nextHandleLock.enter();
try
result := nextHandle;
finally
nextHandleLock.leave();
end;
end;
procedure Timer.run();
var
m: long;
p: long;
n: long;
h: TimerHandler;
begin
try
repeat
repeat
n := getNextHandle();
if n >= 0 then begin
break;
end;
if handler = nil then begin
exit;
end;
sleep(2);
until false;
m := getMilliseconds();
if m >= n then begin
handlerLock.enter();
try
h := handler;
if h = nil then begin
exit;
end;
p := prevHandle;
prevHandle := m;
setNextHandle(-1);
try
if p < 0 then begin
h.timerHandle(self, 0);
end else begin
h.timerHandle(self, m - p);
end;
except
end;
h := nil;
finally
handlerLock.leave();
end;
end else begin
sleep(2);
end;
until false;
finally
terminated := true;
end;
end;
procedure Timer.terminate();
begin
handlerLock.enter();
try
handler := nil;
finally
handlerLock.leave();
end;
end;
procedure Timer.setInterval(milliseconds: long);
var
next: long;
begin
handlerLock.enter();
try
next := getMilliseconds() + milliseconds;
if (milliseconds > 0) and (next >= 0) then begin
setNextHandle(next);
end else begin
setNextHandle(-1);
end;
finally
handlerLock.leave();
end;
end;
function Timer.getRemainingMilliseconds(): long;
var
milliseconds: long;
begin
milliseconds := getNextHandle();
if milliseconds < 0 then begin
result := -1;
exit;
end;
result := milliseconds - getMilliseconds();
end;
{%endregion}
end.