-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathJobTracker.cs
151 lines (133 loc) · 4.72 KB
/
JobTracker.cs
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
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
using System.Threading;
namespace DotStd
{
/// <summary>
/// //TODO Does this conflict with TaskDesc ???????????
/// Track some long process/job/task that is running async on the server.
/// Web browser can periodically request updates to its progress.
/// HangFire might do this better? use SignalR for status ?
/// </summary>
public class JobTracker
{
// TODO Push updates to UI ? So we dont have the UI polling for this?
private int UserId { get; set; } // id for job = userid. Const.UserId.System = 1 = shared with other users ? 0 = not allowed.
public bool IsComplete { get; private set; } // code exited. fail or success.
public string? FailureMsg { get; private set; } // null = ok, else i failed and returned prematurely.
private Progress2 Progress = new();
private CancellationTokenSource? Cancellation { get; set; } // we can try to cancel this?
public CancellationToken CancellationToken
{
get
{
return Cancellation?.Token ?? CancellationToken.None;
}
}
public bool IsCancelled
{
// should be Called by worker periodically to see if it should stop.
get
{
if (this.FailureMsg != null)
return true;
if (Cancellation == null) // no external cancel is possible.
return false;
return Cancellation.IsCancellationRequested;
}
}
public void Cancel()
{
// Called by watcher. Cancel the JobWorker.
this.FailureMsg = "Canceled";
Cancellation?.Cancel();
}
public void SetFailureMsg(string? failureMsg)
{
// Cancel the job/worker because it failed.
if (IsCancelled)
return;
if (string.IsNullOrWhiteSpace(failureMsg))
failureMsg = null;
this.FailureMsg = failureMsg;
if (failureMsg != null)
{
Cancellation?.Cancel();
}
}
public static string GetProgressPercent(CacheIntT<JobTracker> cache, int userId, bool cancel)
{
// Find some async job in the namespace and get its status.
// Called by watcher.
JobTracker? job = cache.Get(userId); // current job for the user.
if (job == null)
return ""; // never started.
if (job.IsCancelled)
{
return job.FailureMsg ?? "Cancelled";
}
if (job.IsComplete)
{
return "Complete";
}
if (cancel)
{
job.Cancel();
}
return job.Progress.GetPercent().ToString() + "% Complete";
}
public void SetStartSize(long size)
{
// Estimated size of the job to be done.
Progress.SetSize(size);
}
public void AddStartSize(long size)
{
// We discovered the job is bigger.
this.Progress.AddSize(size);
}
public void AddProgress(int length)
{
// a chunk has been completed.
Progress.Add(length);
}
public void SetComplete(CacheIntT<JobTracker> cache)
{
// assume we call this even if canceled.
IsComplete = true;
if (!IsCancelled)
{
this.Progress.SetEnd(); // true end.
}
cache.Set(UserId, this, 5 * 60); // no need to hang around too long
}
public static JobTracker? CreateJobTracker(CacheIntT<JobTracker> cache, int userId, long size, bool cancelable = false)
{
// We are starting some async job that we want to track the progress of.
if (size <= 0)
{
return null; // not allowed.
}
var job = cache.Get(userId);
if (job == null)
{
job = new JobTracker { UserId = userId, Progress = new Progress2(size) };
}
else if (job.IsComplete)
{
// just re-use done job
job.FailureMsg = null;
job.IsComplete = false;
job.SetStartSize(size);
}
else
{
return null; // cant dupe the active job.
}
if (cancelable)
{
job.Cancellation = new CancellationTokenSource();
}
cache.Set(userId, job, 24 * 60 * 60); // update time
return job;
}
}
}