Skip to content

Commit fc79775

Browse files
author
1-coder
committed
try to adding timeout
1 parent c15790a commit fc79775

File tree

4 files changed

+100
-4
lines changed

4 files changed

+100
-4
lines changed

HW3.sln

+1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Shared", "Shared", "{5AB057
1515
Shared\shared_utils.h = Shared\shared_utils.h
1616
Shared\stopwatch.h = Shared\stopwatch.h
1717
Shared\stopwatch_decorator.h = Shared\stopwatch_decorator.h
18+
Shared\timeout.h = Shared\timeout.h
1819
EndProjectSection
1920
EndProject
2021
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "KargerSteinMinCut", "KargerSteinMinCut\KargerSteinMinCut.vcxproj", "{8DA0F624-4588-4226-8A15-32674C1B39FA}"

KargerMinCut/karger.h

+8-2
Original file line numberDiff line numberDiff line change
@@ -8,23 +8,27 @@
88
#include "AdjacencyMapGraph.h"
99
#include "full_contraction.h"
1010
#include "stopwatch_decorator.h"
11+
#include "timeout.h"
1112

1213
/**
1314
* Run Karger's randomized min-cut algorithm k times on the given graph.
1415
* Return the a tuple containing the min-cut found and the discovery time.
1516
* Time: O(n^4 * log(n))
1617
* Space: O(n + m)
1718
*/
18-
[[nodiscard]] auto karger(const std::shared_ptr<AdjacencyMapGraph>& graph, size_t k,
19+
[[nodiscard]] auto karger(timeout::timeout_signal& signal,
20+
const std::shared_ptr<AdjacencyMapGraph>& graph, size_t k,
1921
const stopwatch::time_point_t program_time_start) noexcept {
2022
// keep track of the min-cut discovery time
2123
stopwatch::time_point_t discovery_time_stop;
2224

2325
// keeps track of the minimum cut
2426
size_t min_cut = std::numeric_limits<size_t>::max();
2527

28+
bool keep_going = true;
29+
2630
// execute full_contraction k times to hopefully find the minimum cut.
27-
for (size_t i = 0; i < k; ++i) {
31+
for (size_t i = 0; i < k && keep_going; ++i) {
2832
// get the contracted graph and the full_contraction execution time in microseconds
2933
const auto [contracted_graph, full_contraction_duration] =
3034
stopwatch::decorator<stopwatch::us_t>(full_contraction)(graph, 2);
@@ -39,6 +43,8 @@
3943
}
4044

4145
std::cout << "full_contraction: " << full_contraction_duration << '\n';
46+
47+
keep_going = !signal.is_expired();
4248
}
4349

4450
// number of microseconds needed to find the lowest cut among all k full_contraction iterations

KargerMinCut/main.cpp

+10-2
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
#include "read_file.h"
66
#include "shared_utils.h"
77
#include "stopwatch_decorator.h"
8+
#include "timeout.h"
9+
810

911
int main(int argc, char** argv) {
1012
using namespace std::string_literals;
@@ -29,8 +31,14 @@ int main(int argc, char** argv) {
2931

3032
std::cout << "k: "s << k << '\n';
3133

32-
const auto [min_cut, discovery_time, karger_duration] =
33-
stopwatch::decorator<stopwatch::us_t>(karger)(graph, k, program_time_start);
34+
// the timeout is set to 2 minutes
35+
auto timeout_min = 2min;
36+
37+
const auto [min_cut, discovery_time, karger_duration] = timeout::with_timeout(
38+
&timeout_min, &stopwatch::decorator<stopwatch::us_t>(karger), &graph, &k, &program_time_start);
39+
40+
41+
//stopwatch::decorator<stopwatch::us_t>(karger)(graph, k, program_time_start);
3442

3543
// stop the stopwatch
3644
auto program_time_stop = stopwatch::now();

Shared/timeout.h

+81
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
#pragma once
2+
3+
#include <chrono> // std::milliseconds, std::chrono_literals
4+
#include <future> // std::future, std::promise
5+
#include <thread> // std::thread
6+
#include <type_traits> // std::invoke_result_t
7+
8+
using namespace std::chrono_literals;
9+
10+
namespace timeout {
11+
namespace detail {
12+
// returns true iff the given future was terminated by a timeout
13+
template <typename R>
14+
bool is_expired(std::future<R> const& future) noexcept {
15+
return future.wait_for(std::chrono::milliseconds(0)) == std::future_status::ready;
16+
}
17+
} // namespace detail
18+
19+
// future<void> wrapper used to signal expired timeout signals
20+
struct timeout_signal {
21+
timeout_signal() = delete;
22+
23+
explicit timeout_signal(std::future<void>&& signal_future) :
24+
signal_future(std::move(signal_future)) {
25+
}
26+
27+
// returns true iff the timeout expired
28+
[[nodiscard]] bool is_expired() const {
29+
return detail::is_expired(signal_future);
30+
}
31+
32+
// create a new timeout_signal from a promise
33+
static timeout_signal from_promise(std::promise<void>& promise) {
34+
return timeout_signal{promise.get_future()};
35+
}
36+
37+
private:
38+
std::future<void> signal_future;
39+
};
40+
41+
// execute a function f with the given arguments with a timeout guard of the specified duration.
42+
// The first argument of f must be a timeout_signal&& object.
43+
// The function is executed in a separate thread. The caller thread is blocked until either
44+
// the function returns or the timeout expires, whatever of the two conditions happens earlier.
45+
// If the function concludes before the timeout expires, the result is immediately returned.
46+
// If the timeout expires while the function is still in execution, its timeout signal object is
47+
// marked as expired. The function itself must check whether the timeout signal was triggered or
48+
// not with the timeout_signal::is_expired() mmethod.
49+
template <typename Duration, typename Function, class... Args>
50+
std::invoke_result_t<Function, timeout_signal&&, Args...> with_timeout(Duration duration,
51+
Function&& f,
52+
Args&&... args) {
53+
using R = std::invoke_result_t<Function, timeout_signal&&, Args...>;
54+
55+
// create promise and timeout for signaling the timeout expiration
56+
std::promise<void> promise_signal;
57+
timeout_signal signal{timeout_signal::from_promise(promise_signal)};
58+
59+
// we create a task to run the function f in a separate thread
60+
std::packaged_task<R(timeout_signal&&, Args...)> task(f);
61+
std::future<R> future = task.get_future();
62+
63+
// start the function in a new thread. The timeout signal object is the first argument of
64+
// the function.
65+
std::thread thread(std::move(task), std::move(signal), std::forward<Args>(args)...);
66+
67+
// if the packaged function returns before the timeout expired, return its
68+
// result.
69+
if (future.wait_for(duration) == std::future_status::timeout) {
70+
// the allotted timeout expired.
71+
// Tell the function thread to exit the function immediately
72+
promise_signal.set_value();
73+
}
74+
75+
// wait for thread to join
76+
thread.join();
77+
78+
// return the function result
79+
return future.get();
80+
}
81+
} // namespace timeout

0 commit comments

Comments
 (0)