Skip to content

Commit 373b6c1

Browse files
author
bt
committed
Implemented utils classes to imlpement coroutines
1 parent a6af5b9 commit 373b6c1

File tree

3 files changed

+239
-2
lines changed

3 files changed

+239
-2
lines changed

CMakeLists.txt

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,20 @@ add_custom_command(TARGET day1
5858

5959
find_package(CURL)
6060
if(CURL_FOUND)
61-
add_executable(input_downloader src/utils/input_downloader.cpp)
62-
target_link_libraries(input_downloader PRIVATE ${CURL_LIBRARIES})
61+
62+
set(UCORO_HDRS
63+
"${CMAKE_CURRENT_SOURCE_DIR}/include/ucoro/coro.hpp")
64+
add_library(ucoro INTERFACE ${UCORO_HDRS})
65+
target_include_directories(ucoro INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/include)
66+
target_compile_options(ucoro INTERFACE -fcoroutines)
67+
68+
if(UNIX)
69+
add_executable(input_downloader src/utils/input_downloader_coro.cpp)
70+
target_link_libraries(input_downloader PRIVATE ${CURL_LIBRARIES} ucoro)
71+
set_property(TARGET input_downloader PROPERTY CXX_STANDARD 20)
72+
endif()
73+
74+
# An example to use CURL in an async mode
75+
add_executable(non_blocking_curl src/utils/non_blocking_curl.cpp)
76+
target_link_libraries(non_blocking_curl PRIVATE ${CURL_LIBRARIES})
6377
endif()

include/ucoro/coro.hpp

Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
#pragma once
2+
3+
#include <coroutine>
4+
#include <concepts>
5+
#include <utility>
6+
#include <iostream>
7+
8+
namespace ucoro {
9+
10+
template<typename T>
11+
requires (!std::is_reference_v<T> && !std::is_pointer_v<T>)
12+
class [[nodiscard]] Coro final
13+
{
14+
public:
15+
class FinalAwaitable;
16+
class Awaitable;
17+
18+
class promise_type final
19+
{
20+
public:
21+
Coro get_return_object() { return Coro{std::coroutine_handle<promise_type>::from_promise(*this)}; }
22+
std::suspend_always initial_suspend() noexcept { return {}; }
23+
24+
void return_value(T&& new_value) {
25+
std::cerr << "\nPromiseType::return_value";
26+
m_value = std::move(new_value);
27+
}
28+
29+
// an exception escapes the body of the coroutine.
30+
void unhandled_exception() {
31+
std::terminate();
32+
}
33+
34+
FinalAwaitable final_suspend() noexcept { return FinalAwaitable{}; }
35+
36+
void continuation(std::coroutine_handle<> continuation) {
37+
m_continuation = continuation;
38+
}
39+
40+
std::coroutine_handle<> continuation() const {
41+
return m_continuation;
42+
}
43+
44+
T&& value() && {
45+
return std::move(m_value);
46+
}
47+
48+
private:
49+
T m_value{};
50+
51+
std::coroutine_handle<> m_continuation{nullptr};
52+
};
53+
54+
explicit Coro(std::coroutine_handle<promise_type> handle)
55+
: m_handle{handle} {}
56+
57+
~Coro() {
58+
if(m_handle)
59+
m_handle.destroy();
60+
}
61+
62+
Coro(const Coro&) = delete;
63+
Coro& operator=(const Coro&) = delete;
64+
65+
Coro(Coro&& rhs) noexcept
66+
: m_handle(std::move(rhs.m_handle)) {
67+
rhs.m_handle = nullptr;
68+
}
69+
70+
Coro& operator=(Coro&& rhs) noexcept {
71+
if(&rhs != this) {
72+
if(m_handle) {
73+
m_handle.destroy();
74+
}
75+
m_handle = std::exchange(rhs.m_handle, nullptr);
76+
}
77+
return *this;
78+
}
79+
80+
Awaitable operator co_await() && noexcept;
81+
82+
private:
83+
std::coroutine_handle<promise_type> m_handle;
84+
};
85+
86+
template<typename T>
87+
class Coro<T>::FinalAwaitable final {
88+
public:
89+
bool await_ready() const noexcept { return false; }
90+
91+
std::coroutine_handle<> await_suspend(std::coroutine_handle<typename Coro<T>::promise_type> coroutine) noexcept
92+
{
93+
std::cerr << "\nFinalAwaitable::await_suspend done? " << coroutine.done()
94+
<< " continuation nullptr? " << (coroutine.promise().continuation() == nullptr);
95+
// If there is a continuation call it, otherwise this is the end of the line.
96+
// auto& promise = coroutine.promise();
97+
// if (promise.continuation() != nullptr)
98+
// {
99+
// return promise.continuation();
100+
// }
101+
// else
102+
// {
103+
// return std::noop_coroutine();
104+
// }
105+
return coroutine.promise().continuation();
106+
}
107+
108+
void await_resume() noexcept {
109+
std::cerr << "\nFinalAwaitable::await_resume";
110+
}
111+
};
112+
113+
template<typename T>
114+
class Coro<T>::Awaitable final
115+
{
116+
using promise_type = typename Coro<T>::promise_type;
117+
public:
118+
explicit Awaitable(std::coroutine_handle<promise_type> handler) noexcept
119+
: m_handler{std::move(handler)} {}
120+
121+
bool await_ready() const {
122+
return !m_handler || m_handler.done();
123+
}
124+
125+
std::coroutine_handle<> await_suspend(std::coroutine_handle<promise_type> continuation) {
126+
std::cerr << "\nAwaitableBase::await_suspend";
127+
// Store the continuation in the task's promise so that the final_suspend()
128+
// knows to resume this coroutine when the task completes.
129+
130+
m_handler.promise().continuation(continuation);
131+
132+
// Then we resume the task's coroutine, which is currently suspended
133+
// at the initial-suspend-point (ie. at the open curly brace).
134+
return m_handler;
135+
}
136+
137+
T&& await_resume() const {
138+
std::cerr << "\nResponseAwaitable::await_resume";
139+
return std::move(m_handler.promise()).value();
140+
}
141+
142+
protected:
143+
std::coroutine_handle<promise_type> m_handler;
144+
};
145+
146+
147+
template<typename T>
148+
typename Coro<T>::Awaitable Coro<T>::operator co_await() && noexcept {
149+
return typename Coro<T>::Awaitable{m_handle};
150+
}
151+
152+
153+
} //< namespace ucoro

src/utils/non_blocking_curl.cpp

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
#include <stdio.h>
2+
#include <stdlib.h>
3+
#include <string.h>
4+
#include <curl/curl.h>
5+
6+
size_t write_data(void *ptr, size_t size, size_t nmemb, FILE *stream) {
7+
return fwrite(ptr, size, nmemb, stream);
8+
}
9+
10+
int main(void) {
11+
CURL *easy_handle;
12+
CURLM *multi_handle;
13+
CURLMcode mcode;
14+
int still_running = 1;
15+
16+
const char *url = "https://example.com/file.txt";
17+
const char *output_filename = "downloaded_file.txt";
18+
19+
FILE *fp = fopen(output_filename, "wb");
20+
if (!fp) {
21+
fprintf(stderr, "Failed to open file for writing\n");
22+
return 1;
23+
}
24+
25+
curl_global_init(CURL_GLOBAL_DEFAULT);
26+
easy_handle = curl_easy_init();
27+
28+
curl_easy_setopt(easy_handle, CURLOPT_URL, url);
29+
curl_easy_setopt(easy_handle, CURLOPT_WRITEFUNCTION, write_data);
30+
curl_easy_setopt(easy_handle, CURLOPT_WRITEDATA, fp);
31+
curl_easy_setopt(easy_handle, CURLOPT_FOLLOWLOCATION, 1L);
32+
33+
multi_handle = curl_multi_init();
34+
curl_multi_add_handle(multi_handle, easy_handle);
35+
36+
37+
while (still_running) {
38+
printf("\nmulti perform");
39+
curl_multi_perform(multi_handle, &still_running);
40+
int numfds;
41+
printf("\ncurl_multi_wait started");
42+
mcode = curl_multi_wait(multi_handle, NULL, 0, 10000, &numfds);
43+
printf("\ncurl_multi_wait finished");
44+
if (mcode != CURLM_OK) {
45+
fprintf(stderr, "curl_multi_wait() failed: %s\n", curl_multi_strerror(mcode));
46+
break;
47+
}
48+
}
49+
50+
// Check for any errors
51+
CURLMsg *msg;
52+
int msgs_left;
53+
while ((msg = curl_multi_info_read(multi_handle, &msgs_left))) {
54+
if (msg->msg == CURLMSG_DONE) {
55+
if (msg->data.result != CURLE_OK) {
56+
fprintf(stderr, "Download failed: %s\n", curl_easy_strerror(msg->data.result));
57+
} else {
58+
printf("Download completed successfully.\n");
59+
}
60+
}
61+
}
62+
63+
curl_multi_remove_handle(multi_handle, easy_handle);
64+
curl_easy_cleanup(easy_handle);
65+
curl_multi_cleanup(multi_handle);
66+
curl_global_cleanup();
67+
fclose(fp);
68+
69+
return 0;
70+
}

0 commit comments

Comments
 (0)