forked from VSoftTechnologies/DUnitX
-
Notifications
You must be signed in to change notification settings - Fork 0
/
DUnitX.Timeout.pas
127 lines (101 loc) · 2.63 KB
/
DUnitX.Timeout.pas
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
unit DUnitX.Timeout;
interface
uses
Classes;
type
ITimeout = interface(IUnknown)
['{0A380F7B-9CEE-4FD7-9D86-60CE05B97C1A}']
procedure Stop;
end;
function InitialiseTimeout(const ATime: cardinal): ITimeout;
implementation
uses
DUnitX.TestFramework,
DUnitX.Utils,
Windows;
// The following TimeOut code is based on the code found at
// https://code.google.com/p/delphitimeouts/
// DelphiTimeouts version 1.1
// Copyright (c) 2007-2008 Szymon Jachim
type
TTimeoutThread = class(TThread)
private
procedure TimeoutThread;
public
ThreadHandle: Cardinal;
Timeout: Cardinal;
procedure Execute; override;
end;
TTimeout = class(TInterfacedObject, ITimeout)
private
FTimeoutThread: TTimeoutThread;
public
constructor Create(const ATimeout: Cardinal; AThreadHandle: THandle);
destructor Destroy; override;
procedure Stop;
end;
function InitialiseTimeout(const ATime: cardinal): ITimeout;
var
ThisThreadHandle: THandle;
begin
DuplicateHandle(GetCurrentProcess, GetCurrentThread, GetCurrentProcess, @ThisThreadHandle, 0, True, DUPLICATE_SAME_ACCESS);
Result := TTimeout.Create(ATime, ThisThreadHandle);
end;
procedure RaiseTimeOutException;
begin
raise ETimedOut.Create('Operation Timed Out');
end;
procedure TTimeoutThread.TimeoutThread;
var
Ctx: _CONTEXT;
begin
SuspendThread(ThreadHandle);
Ctx.ContextFlags := CONTEXT_FULL;
GetThreadContext(ThreadHandle, Ctx);
Ctx.Eip := Cardinal(@RaiseTimeOutException);
SetThreadContext(ThreadHandle, Ctx);
ResumeThread(ThreadHandle);
end;
{ TTimeout }
procedure TTimeout.Stop;
begin
FTimeoutThread.Terminate;
end;
constructor TTimeout.Create(const ATimeout: Cardinal; AThreadHandle: THandle);
begin
FTimeoutThread := TTimeoutThread.Create(true);
FTimeoutThread.FreeOnTerminate := false;
FTimeoutThread.ThreadHandle := AThreadHandle;
FTimeoutThread.Timeout := ATimeout;
FTimeoutThread.Resume;
end;
destructor TTimeout.Destroy;
begin
//Unwinding and we need to stop the thread, as it may still raise an exception
Stop;
FTimeoutThread.WaitFor;
FTimeoutThread.Free;
inherited;
end;
{ TTimeoutThread }
procedure TTimeoutThread.Execute;
var
I: Integer;
startTime : Cardinal;
elaspedTime : Cardinal;
begin
inherited;
//Get the tickcount so that we leave timing up to the system.
startTime := GetTickCount;
repeat
//Give some time back to the system to process the test.
Sleep(1);
if Terminated then
Break;
elaspedTime := GetElapsedTime(startTime);
until (elaspedTime >= Timeout);
//If we haven't been terminated then we have timed out.
if not Terminated then
TimeoutThread;
end;
end.