Skip to content

Commit 7660f9c

Browse files
committed
Everything working again, added tick rate options and fixed some bugs
1 parent f52720d commit 7660f9c

File tree

9 files changed

+345
-199
lines changed

9 files changed

+345
-199
lines changed

ecsact/runtime/async.h

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,11 @@ typedef enum {
5353
*/
5454
ECSACT_ASYNC_ERR_PERMISSION_DENIED,
5555

56+
/**
57+
* Client sent invalid connection options
58+
*/
59+
ECSACT_ASYNC_INVALID_CONNECTION_STRING,
60+
5661
/**
5762
* Connection to the client is closed
5863
*/
@@ -70,7 +75,8 @@ typedef enum {
7075
* @param async_err when there is no async error this will be @ref
7176
* ECSACT_ASYNC_OK otherwise @see ecsact_async_error
7277
73-
* @param request_id the request ID returned by an async request function that
78+
* @param request_ids A list of request IDs returned by an async request
79+
function that
7480
* was responsible for this error
7581
* @param callback_user_data the @ref
7682
* ecsact_async_events_collector::error_callback_user_data
@@ -120,7 +126,7 @@ typedef struct ecsact_async_events_collector {
120126
ecsact_async_error_callback async_error_callback;
121127

122128
/**
123-
* `callback_user_data` passed to `error_callback`
129+
* `callback_user_data` passed to `async_error_callback`
124130
*/
125131
void* async_error_callback_user_data;
126132

@@ -133,9 +139,9 @@ typedef struct ecsact_async_events_collector {
133139
ecsact_async_create_entity_callback async_entity_callback;
134140

135141
/**
136-
* `callback_user_data` passed to `error_callback`
142+
* `callback_user_data` passed to `async_entity_callback`
137143
*/
138-
void* async_entity_error_callback_user_data;
144+
void* async_entity_callback_user_data;
139145

140146
/**
141147
* invoked when a system execution error occurred.

reference/async_reference/async_reference.cc

Lines changed: 126 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -3,27 +3,94 @@
33
#include <ranges>
44
#include <algorithm>
55
#include <iterator>
6+
#include <chrono>
67

78
#include "async_reference.hh"
89

10+
struct parsed_connection_string {
11+
std::string host;
12+
std::map<std::string, std::string> options;
13+
};
14+
15+
static auto str_subrange(std::string_view str, auto start, auto end) {
16+
return str.substr(start, end - start);
17+
}
18+
19+
static auto parse_connection_string(std::string_view str)
20+
-> parsed_connection_string {
21+
auto result = parsed_connection_string{};
22+
23+
auto options_start = str.find("?");
24+
25+
if(options_start == std::string::npos) {
26+
// No options
27+
result.host = str;
28+
return result;
29+
}
30+
31+
result.host = str.substr(0, options_start);
32+
33+
auto amp_idx = options_start;
34+
35+
while(amp_idx != std::string::npos) {
36+
auto next_amp_idx = str.find("&", amp_idx + 1);
37+
38+
auto name_str = std::string{};
39+
auto value_str = std::string{};
40+
auto eq_idx = str.find("=", amp_idx);
41+
42+
if(eq_idx >= next_amp_idx) {
43+
name_str = str_subrange(str, amp_idx + 1, next_amp_idx);
44+
} else {
45+
name_str = str_subrange(str, amp_idx + 1, eq_idx);
46+
value_str = str_subrange(str, eq_idx + 1, next_amp_idx);
47+
}
48+
49+
result.options[name_str] = value_str;
50+
51+
amp_idx = next_amp_idx;
52+
}
53+
54+
return result;
55+
}
56+
957
ecsact_async_request_id async_reference::connect(const char* connection_string
1058
) {
59+
auto req_id = next_request_id();
60+
1161
std::string connect_str(connection_string);
1262

13-
registry_id = ecsact_create_registry("async_reference_impl_reg");
63+
auto result = parse_connection_string(connect_str);
1464

15-
auto req_id = next_request_id();
16-
// The good and bad strings simulate the outcome of connections
17-
if(connect_str == "good") {
18-
is_connected = true;
65+
if(result.options.contains("tick_rate")) {
66+
auto tick_str = result.options.at("tick_rate");
1967

20-
execute_systems();
21-
} else {
22-
// Same thing that happens in enqueue? Callback next flush?
68+
int tick_int = std::stoi(tick_str);
69+
70+
tick_rate = std::chrono::milliseconds(tick_int);
71+
}
72+
73+
// The good and bad strings simulate the outcome of connections
74+
if(result.host != "good") {
2375
is_connected = false;
2476
is_connected_notified = false;
77+
return req_id;
2578
}
2679

80+
if(tick_rate.count() == 0) {
81+
types::async_error async_err{
82+
.error = ECSACT_ASYNC_INVALID_CONNECTION_STRING,
83+
.request_ids = {req_id},
84+
};
85+
86+
async_callbacks.add(async_err);
87+
return req_id;
88+
}
89+
90+
registry_id = ecsact_create_registry("async_reference_impl_reg");
91+
is_connected = true;
92+
execute_systems();
93+
2794
return req_id;
2895
}
2996

@@ -39,7 +106,6 @@ ecsact_async_request_id async_reference::enqueue_execution_options(
39106
};
40107

41108
is_connected_notified = true;
42-
// Could block here
43109
async_callbacks.add(async_err);
44110
return req_id;
45111
}
@@ -51,24 +117,46 @@ ecsact_async_request_id async_reference::enqueue_execution_options(
51117
.options = cpp_options,
52118
};
53119

54-
// Could block here
55120
tick_manager.add_pending_options(pending_options);
56121
return req_id;
57122
}
58123

59124
void async_reference::execute_systems() {
60125
execution_thread = std::thread([this] {
126+
using namespace std::chrono_literals;
127+
using clock = std::chrono::high_resolution_clock;
128+
using milliseconds = std::chrono::milliseconds;
129+
using nanoseconds = std::chrono::nanoseconds;
130+
using std::chrono::duration_cast;
131+
132+
nanoseconds execution_duration = {};
133+
nanoseconds sleep_drift = {};
134+
61135
while(is_connected == true) {
62-
// Could block here
63136
auto async_err = tick_manager.validate_pending_options();
64137

138+
const auto sleep_duration = tick_rate - execution_duration;
139+
140+
auto wait_start = clock::now();
141+
std::this_thread::sleep_for(sleep_duration - sleep_drift);
142+
auto wait_end = clock::now();
143+
144+
sleep_drift =
145+
sleep_duration - duration_cast<nanoseconds>(wait_start - wait_end);
146+
65147
if(async_err.error != ECSACT_ASYNC_OK) {
66148
async_callbacks.add(async_err);
67149

68-
disconnect();
150+
is_connected = false;
151+
break;
69152
}
70153

71-
auto cpp_options = tick_manager.get_options_now();
154+
auto start = clock::now();
155+
156+
auto cpp_options = tick_manager.move_and_increment_tick();
157+
158+
// TODO(Kelwan): Add done callbacks so we can resolve all requests
159+
// https://github.com/ecsact-dev/ecsact_runtime/issues/102
72160

73161
ecsact_execution_events_collector collector;
74162
collector.init_callback = &execution_callbacks::init_callback;
@@ -87,31 +175,17 @@ void async_reference::execute_systems() {
87175
);
88176
}
89177

90-
std::vector<ecsact_async_request_id> pending_entities;
91-
92-
// Could block here
93-
std::unique_lock lk(pending_m);
94-
pending_entities = std::move(pending_entity_requests);
95-
pending_entity_requests.clear();
96-
lk.unlock();
97-
98-
for(auto& entity_request_id : pending_entities) {
99-
auto entity = ecsact_create_entity(*registry_id);
100-
101-
types::entity created_entity{
102-
.entity_id = entity,
103-
.request_id = entity_request_id,
104-
};
105-
// Could block here
106-
async_callbacks.add(created_entity);
107-
}
178+
process_entities();
108179

109180
auto systems_error =
110181
ecsact_execute_systems(*registry_id, 1, options.get(), &collector);
111182

183+
auto end = clock::now();
184+
execution_duration = duration_cast<nanoseconds>(end - start);
185+
112186
if(systems_error != ECSACT_EXEC_SYS_OK) {
113187
async_callbacks.add(systems_error);
114-
disconnect();
188+
is_connected = false;
115189
return;
116190
}
117191
}
@@ -129,8 +203,6 @@ void async_reference::flush_events(
129203
}
130204

131205
ecsact_async_request_id async_reference::create_entity_request() {
132-
// NOTE: Add entity to both registries
133-
// Consider ensure entity
134206
auto req_id = next_request_id();
135207
if(is_connected == false && is_connected_notified == false) {
136208
types::async_error async_err{
@@ -165,3 +237,23 @@ ecsact_async_request_id async_reference::next_request_id() {
165237
ecsact_async_request_id async_reference::convert_request_id(int32_t id) {
166238
return static_cast<ecsact_async_request_id>(id);
167239
}
240+
241+
void async_reference::process_entities() {
242+
std::vector<ecsact_async_request_id> pending_entities;
243+
244+
std::unique_lock lk(pending_m);
245+
pending_entities = std::move(pending_entity_requests);
246+
pending_entity_requests.clear();
247+
lk.unlock();
248+
249+
for(auto& entity_request_id : pending_entities) {
250+
auto entity = ecsact_create_entity(*registry_id);
251+
252+
types::entity created_entity{
253+
.entity_id = entity,
254+
.request_id = entity_request_id,
255+
};
256+
257+
async_callbacks.add(created_entity);
258+
}
259+
}

reference/async_reference/async_reference.hh

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ private:
4242

4343
std::optional<ecsact_registry_id> registry_id;
4444

45+
void process_entities();
46+
4547
tick_manager tick_manager;
4648
execution_callbacks exec_callbacks;
4749
async_callbacks async_callbacks;
@@ -53,6 +55,8 @@ private:
5355
std::atomic_bool is_connected = false;
5456
std::atomic_bool is_connected_notified = false;
5557

58+
std::chrono::milliseconds tick_rate = {};
59+
5660
ecsact_async_request_id next_request_id();
5761
ecsact_async_request_id convert_request_id(int32_t id);
5862
};

reference/async_reference/callbacks/async_callbacks.cc

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
void async_callbacks::add(const types::async_requests type) {
44
std::unique_lock lk(async_m);
5-
requests.insert(requests.end(), type);
5+
requests.push_back(type);
66
}
77

88
void async_callbacks::invoke(const ecsact_async_events_collector* async_events
@@ -22,25 +22,34 @@ void async_callbacks::invoke(const ecsact_async_events_collector* async_events
2222

2323
for(auto& request : pending_requests) {
2424
std::visit(
25-
[request, &async_events](auto&& error) {
25+
[&async_events](auto&& error) {
2626
using T = std::decay_t<decltype(error)>;
2727
if constexpr(std::is_same_v<T, types::async_error>) {
28+
if(async_events->async_error_callback == nullptr) {
29+
return;
30+
}
2831
async_events->async_error_callback(
2932
error.error,
3033
error.request_ids.size(),
3134
error.request_ids.data(),
3235
async_events->async_error_callback_user_data
3336
);
3437
} else if constexpr(std::is_same_v<T, ecsact_execute_systems_error>) {
38+
if(async_events->system_error_callback == nullptr) {
39+
return;
40+
}
3541
async_events->system_error_callback(
3642
error,
3743
async_events->system_error_callback_user_data
3844
);
3945
} else if constexpr(std::is_same_v<T, types::entity>) {
46+
if(async_events->async_entity_callback == nullptr) {
47+
return;
48+
}
4049
async_events->async_entity_callback(
4150
*error.entity_id,
4251
error.request_id,
43-
async_events->async_entity_error_callback_user_data
52+
async_events->async_entity_callback_user_data
4453
);
4554
}
4655
},

0 commit comments

Comments
 (0)