-
Notifications
You must be signed in to change notification settings - Fork 246
Expand file tree
/
Copy pathlet_value.cpp
More file actions
180 lines (160 loc) · 5.59 KB
/
let_value.cpp
File metadata and controls
180 lines (160 loc) · 5.59 KB
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
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
/*
* Copyright (c) 2022 Lucian Radu Teodorescu
*
* Licensed under the Apache License Version 2.0 with LLVM Exceptions
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* https://llvm.org/LICENSE.txt
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* General context:
* - server application that processes images
* - execution contexts:
* - 1 dedicated thread for network I/O
* - N worker threads used for CPU-intensive work
* - M threads for auxiliary I/O
* - optional GPU context that may be used on some types of servers
*
* Specific problem description:
*
* - we are looking at the flow of processing an HTTP request and sending back the
* response
* - show how one can break the (slightly complex) flow into steps with let_* functions
* - different phases of processing HTTP requests are broken down into separate concerns
* - each part of the processing might use different execution contexts (details not shown
* in this example)
* - error handling is generic, regardless which component fails; we always send the right
* response to the clients
*
* Example goals:
* - show how one can break more complex flows into steps with let_* functions
* - exemplify the use of let_value, let_error, let_stopped, continues_on and just
* algorithms
*/
#include <iostream>
#include <stdexcept>
#include <string>
#include <utility>
#include <vector>
// Pull in the reference implementation of P2300:
#include <stdexec/execution.hpp>
// Use a thread pool
#include "exec/start_detached.hpp"
#include "exec/static_thread_pool.hpp"
#if !STDEXEC_NO_STDCPP_EXCEPTIONS()
namespace ex = stdexec;
struct http_request
{
std::string url_;
std::vector<std::pair<std::string, std::string>> headers_;
std::string body_;
};
struct http_response
{
int status_code_;
std::string body_;
};
// Returns a sender that yields an http_request object for an incoming request
template <ex::scheduler S>
auto schedule_request_start(S sched, int idx) -> ex::sender auto
{
// app-specific-details: building of the http_request object
auto url = std::string("/query?image_idx=") + std::to_string(idx);
if (idx == 7)
url.clear(); // fake invalid request
http_request req{.url_ = std::move(url), .headers_ = {}, .body_ = {}};
std::cout << "HTTP request " << idx << " arrived\n";
// Return a sender for the incoming http_request
return ex::just(std::move(req)) | ex::continues_on(std::forward<S>(sched));
}
// Sends a response back to the client; yields a void signal on success
auto send_response(http_response const & resp) -> ex::sender auto
{
std::cout << "Sending back response: " << resp.status_code_ << "\n";
// Signal that we are done successfully
return ex::just();
}
// Validate that the HTTP request is well-formed
auto validate_request(http_request const & req) -> ex::sender auto
{
std::cout << "validating request " << req.url_ << "\n";
if (req.url_.empty())
throw std::invalid_argument("No URL");
return ex::just(req);
}
// Handle the request; main application logic
auto handle_request(http_request const & req) -> ex::sender auto
{
std::cout << "handling request " << req.url_ << "\n";
//...
return ex::just(http_response{.status_code_ = 200, .body_ = "image details"});
}
// Transforms server errors into responses to be sent to the client
auto error_to_response(std::exception_ptr err) -> ex::sender auto
{
try
{
std::rethrow_exception(err);
}
catch (std::invalid_argument const & e)
{
return ex::just(http_response{.status_code_ = 404, .body_ = e.what()});
}
catch (std::exception const & e)
{
return ex::just(http_response{.status_code_ = 500, .body_ = e.what()});
}
catch (...)
{
return ex::just(http_response{.status_code_ = 500, .body_ = "Unknown server error"});
}
}
// Transforms cancellation of the server into responses to be sent to the client
auto stopped_to_response() -> ex::sender auto
{
return ex::just(http_response{.status_code_ = 503, .body_ = "Service temporarily unavailable"});
}
auto main() -> int
{
// Create a thread pool and get a scheduler from it
exec::static_thread_pool pool{8};
ex::scheduler auto sched = pool.get_scheduler();
// Fake a couple of requests
for (int i = 0; i < 10; i++)
{
// The whole flow for transforming incoming requests into responses
ex::sender auto snd =
// get a sender when a new request comes
schedule_request_start(sched, i)
// make sure the request is valid; throw if not
| ex::let_value(validate_request)
// process the request in a function that may be using a different execution context
| ex::let_value(handle_request)
// If there are errors transform them into proper responses
| ex::let_error(error_to_response)
// If the flow is cancelled, send back a proper response
| ex::let_stopped(stopped_to_response)
// write the result back to the client
| ex::let_value(send_response)
// done
;
// execute the whole flow asynchronously
exec::start_detached(std::move(snd));
}
pool.request_stop();
return 0;
}
#else
int main()
{
std::cout << "This example requires C++ exceptions to be enabled.\n";
return 0;
}
#endif // !STDEXEC_NO_STDCPP_EXCEPTIONS()