Skip to content

Commit bf9f001

Browse files
committed
Improve efficiency (reduced system resource overhead) of DNS job polling
1 parent cadcf7b commit bf9f001

1 file changed

Lines changed: 142 additions & 30 deletions

File tree

src/corelib/Providers/Rackspace/CloudDnsProvider.cs

Lines changed: 142 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1033,27 +1033,40 @@ protected Task<DnsJob> WaitForJobAsync(DnsJob job, bool showDetails, Cancellatio
10331033
if (job == null)
10341034
throw new ArgumentNullException("job");
10351035

1036-
Func<DnsJob> func =
1037-
() =>
1036+
TaskCompletionSource<DnsJob> taskCompletionSource = new TaskCompletionSource<DnsJob>();
1037+
Func<Task<DnsJob>> pollJob = () => PollJobStateAsync(job, showDetails, cancellationToken, progress);
1038+
1039+
Task<DnsJob> currentTask = pollJob();
1040+
Action<Task<DnsJob>> continuation = null;
1041+
continuation =
1042+
previousTask =>
10381043
{
1039-
while (true)
1044+
if (previousTask.Status != TaskStatus.RanToCompletion)
10401045
{
1041-
cancellationToken.ThrowIfCancellationRequested();
1042-
DnsJob updatedJob = GetJobStatusAsync(job, showDetails, cancellationToken).Result;
1043-
if (updatedJob == null || updatedJob.Id != job.Id)
1044-
throw new InvalidOperationException("Could not obtain status for job.");
1045-
1046-
if (progress != null)
1047-
progress.Report(updatedJob);
1048-
1049-
if (updatedJob.Status == DnsJobStatus.Completed || updatedJob.Status == DnsJobStatus.Error)
1050-
return updatedJob;
1046+
taskCompletionSource.SetFromTask(previousTask);
1047+
return;
1048+
}
10511049

1052-
Thread.Sleep(TimeSpan.FromSeconds(1));
1050+
DnsJob result = previousTask.Result;
1051+
if (result == null || result.Status == DnsJobStatus.Completed || result.Status == DnsJobStatus.Error)
1052+
{
1053+
// finished waiting
1054+
taskCompletionSource.SetResult(result);
1055+
return;
10531056
}
1057+
1058+
// reschedule
1059+
currentTask = Task.Factory.StartNewDelayed((int)TimeSpan.FromSeconds(1).TotalMilliseconds, cancellationToken).ContinueWith(
1060+
task =>
1061+
{
1062+
task.PropagateExceptions();
1063+
return pollJob();
1064+
}).Unwrap();
1065+
currentTask.ContinueWith(continuation);
10541066
};
1067+
currentTask.ContinueWith(continuation);
10551068

1056-
return Task.Factory.StartNew(func);
1069+
return taskCompletionSource.Task;
10571070
}
10581071

10591072
/// <summary>
@@ -1086,27 +1099,40 @@ protected Task<DnsJob<TResult>> WaitForJobAsync<TResult>(DnsJob<TResult> job, bo
10861099
if (job == null)
10871100
throw new ArgumentNullException("job");
10881101

1089-
Func<DnsJob<TResult>> func =
1090-
() =>
1102+
TaskCompletionSource<DnsJob<TResult>> taskCompletionSource = new TaskCompletionSource<DnsJob<TResult>>();
1103+
Func<Task<DnsJob<TResult>>> pollJob = () => PollJobStateAsync(job, showDetails, cancellationToken, progress);
1104+
1105+
Task<DnsJob<TResult>> currentTask = pollJob();
1106+
Action<Task<DnsJob<TResult>>> continuation = null;
1107+
continuation =
1108+
previousTask =>
10911109
{
1092-
while (true)
1110+
if (previousTask.Status != TaskStatus.RanToCompletion)
10931111
{
1094-
cancellationToken.ThrowIfCancellationRequested();
1095-
DnsJob<TResult> updatedJob = GetJobStatusAsync(job, showDetails, cancellationToken).Result;
1096-
if (updatedJob == null || updatedJob.Id != job.Id)
1097-
throw new InvalidOperationException("Could not obtain status for job.");
1098-
1099-
if (progress != null)
1100-
progress.Report(updatedJob);
1101-
1102-
if (updatedJob.Status == DnsJobStatus.Completed || updatedJob.Status == DnsJobStatus.Error)
1103-
return updatedJob;
1112+
taskCompletionSource.SetFromTask(previousTask);
1113+
return;
1114+
}
11041115

1105-
Thread.Sleep(TimeSpan.FromSeconds(1));
1116+
DnsJob<TResult> result = previousTask.Result;
1117+
if (result == null || result.Status == DnsJobStatus.Completed || result.Status == DnsJobStatus.Error)
1118+
{
1119+
// finished waiting
1120+
taskCompletionSource.SetResult(result);
1121+
return;
11061122
}
1123+
1124+
// reschedule
1125+
currentTask = Task.Factory.StartNewDelayed((int)TimeSpan.FromSeconds(1).TotalMilliseconds, cancellationToken).ContinueWith(
1126+
task =>
1127+
{
1128+
task.PropagateExceptions();
1129+
return pollJob();
1130+
}).Unwrap();
1131+
currentTask.ContinueWith(continuation);
11071132
};
1133+
currentTask.ContinueWith(continuation);
11081134

1109-
return Task.Factory.StartNew(func);
1135+
return taskCompletionSource.Task;
11101136
}
11111137

11121138
/// <inheritdoc/>
@@ -1131,5 +1157,91 @@ protected override Task<Uri> GetBaseUriAsync(CancellationToken cancellationToken
11311157
return baseUri;
11321158
});
11331159
}
1160+
1161+
/// <summary>
1162+
/// Asynchronously poll the current state of a DNS job.
1163+
/// </summary>
1164+
/// <param name="job">The job in the DNS service.</param>
1165+
/// <param name="showDetails"><c>true</c> to include detailed information about the job; otherwise, <c>false</c>.</param>
1166+
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that the task will observe.</param>
1167+
/// <param name="progress">An optional callback object to receive progress notifications. If this is <c>null</c>, no progress notifications are sent.</param>
1168+
/// <returns>
1169+
/// A <see cref="Task"/> object representing the asynchronous operation. When
1170+
/// the task completes successfully, the <see cref="Task{TResult}.Result"/>
1171+
/// property will contain a <see cref="DnsJob"/> object containing the
1172+
/// updated state information for the job in the DNS service.
1173+
/// </returns>
1174+
/// <exception cref="ArgumentNullException">If <paramref name="job"/> is <c>null</c>.</exception>
1175+
/// <exception cref="WebException">If the REST request does not return successfully.</exception>
1176+
private Task<DnsJob> PollJobStateAsync(DnsJob job, bool showDetails, CancellationToken cancellationToken, IProgress<DnsJob> progress)
1177+
{
1178+
if (job == null)
1179+
throw new ArgumentNullException("job");
1180+
1181+
Task<DnsJob> chain = GetJobStatusAsync(job, showDetails, cancellationToken);
1182+
chain = chain.ContinueWith(
1183+
task =>
1184+
{
1185+
if (task.Result == null || task.Result.Id != job.Id)
1186+
throw new InvalidOperationException("Could not obtain status for job");
1187+
1188+
return task.Result;
1189+
}, TaskContinuationOptions.ExecuteSynchronously);
1190+
1191+
if (progress != null)
1192+
{
1193+
chain = chain.ContinueWith(
1194+
task =>
1195+
{
1196+
progress.Report(task.Result);
1197+
return task.Result;
1198+
}, TaskContinuationOptions.ExecuteSynchronously);
1199+
}
1200+
1201+
return chain;
1202+
}
1203+
1204+
/// <summary>
1205+
/// Asynchronously poll the current state of a DNS job.
1206+
/// </summary>
1207+
/// <param name="job">The job in the DNS service.</param>
1208+
/// <param name="showDetails"><c>true</c> to include detailed information about the job; otherwise, <c>false</c>.</param>
1209+
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that the task will observe.</param>
1210+
/// <param name="progress">An optional callback object to receive progress notifications. If this is <c>null</c>, no progress notifications are sent.</param>
1211+
/// <returns>
1212+
/// A <see cref="Task"/> object representing the asynchronous operation. When
1213+
/// the task completes successfully, the <see cref="Task{TResult}.Result"/>
1214+
/// property will contain a <see cref="DnsJob"/> object containing the
1215+
/// updated state information for the job in the DNS service.
1216+
/// </returns>
1217+
/// <exception cref="ArgumentNullException">If <paramref name="job"/> is <c>null</c>.</exception>
1218+
/// <exception cref="WebException">If the REST request does not return successfully.</exception>
1219+
private Task<DnsJob<TResponse>> PollJobStateAsync<TResponse>(DnsJob<TResponse> job, bool showDetails, CancellationToken cancellationToken, IProgress<DnsJob<TResponse>> progress)
1220+
{
1221+
if (job == null)
1222+
throw new ArgumentNullException("job");
1223+
1224+
Task<DnsJob<TResponse>> chain = GetJobStatusAsync(job, showDetails, cancellationToken);
1225+
chain = chain.ContinueWith(
1226+
task =>
1227+
{
1228+
if (task.Result == null || task.Result.Id != job.Id)
1229+
throw new InvalidOperationException("Could not obtain status for job");
1230+
1231+
return task.Result;
1232+
}, TaskContinuationOptions.ExecuteSynchronously);
1233+
1234+
if (progress != null)
1235+
{
1236+
chain = chain.ContinueWith(
1237+
task =>
1238+
{
1239+
progress.Report(task.Result);
1240+
return task.Result;
1241+
}, TaskContinuationOptions.ExecuteSynchronously);
1242+
}
1243+
1244+
return chain;
1245+
}
11341246
}
11351247
}

0 commit comments

Comments
 (0)