Skip to content

Commit e2eeba9

Browse files
committed
fix: Support context locals and error-context APIs
- **Labels:** `enhancement`, `abi` Signed-off-by: Gordon Smith <GordonJSmith@gmail.com>
1 parent cf2d0bb commit e2eeba9

4 files changed

Lines changed: 206 additions & 23 deletions

File tree

include/cmcpp.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#include <cmcpp/integer.hpp>
66
#include <cmcpp/float.hpp>
77
#include <cmcpp/string.hpp>
8+
#include <cmcpp/error_context.hpp>
89
#include <cmcpp/flags.hpp>
910
#include <cmcpp/list.hpp>
1011
#include <cmcpp/tuple.hpp>

include/cmcpp/context.hpp

Lines changed: 55 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#include "runtime.hpp"
66

77
#include <algorithm>
8+
#include <array>
89
#include <deque>
910
#include <future>
1011
#include <memory>
@@ -969,10 +970,6 @@ namespace cmcpp
969970
std::unordered_map<const ResourceType *, HandleTable> tables_;
970971
};
971972

972-
struct ErrorContext
973-
{
974-
};
975-
976973
struct ComponentInstance
977974
{
978975
Store *store = nullptr;
@@ -1010,6 +1007,27 @@ namespace cmcpp
10101007
inst.backpressure -= 1;
10111008
}
10121009

1010+
class ContextLocalStorage
1011+
{
1012+
public:
1013+
static constexpr uint32_t LENGTH = 1;
1014+
1015+
ContextLocalStorage() = default;
1016+
1017+
void set(uint32_t index, int32_t value)
1018+
{
1019+
storage_[index] = value;
1020+
}
1021+
1022+
int32_t get(uint32_t index) const
1023+
{
1024+
return storage_[index];
1025+
}
1026+
1027+
private:
1028+
std::array<int32_t, LENGTH> storage_{};
1029+
};
1030+
10131031
class Task : public std::enable_shared_from_this<Task>
10141032
{
10151033
public:
@@ -1184,6 +1202,16 @@ namespace cmcpp
11841202
return state_;
11851203
}
11861204

1205+
ContextLocalStorage &context()
1206+
{
1207+
return context_;
1208+
}
1209+
1210+
const ContextLocalStorage &context() const
1211+
{
1212+
return context_;
1213+
}
1214+
11871215
ComponentInstance *component_instance() const
11881216
{
11891217
return inst_;
@@ -1236,6 +1264,7 @@ namespace cmcpp
12361264
uint32_t num_borrows_ = 0;
12371265
std::shared_ptr<Thread> thread_;
12381266
State state_ = State::Initial;
1267+
ContextLocalStorage context_{};
12391268
};
12401269

12411270
inline void canon_task_return(Task &task, std::vector<std::any> result, const HostTrap &trap)
@@ -1276,25 +1305,6 @@ namespace cmcpp
12761305
return event.code == EventCode::TASK_CANCELLED ? 1u : 0u;
12771306
}
12781307

1279-
class ContextLocalStorage
1280-
{
1281-
public:
1282-
static constexpr int LENGTH = 2;
1283-
int array[LENGTH] = {0, 0};
1284-
1285-
ContextLocalStorage() = default;
1286-
1287-
void set(int i, int v)
1288-
{
1289-
array[i] = v;
1290-
}
1291-
1292-
int get(int i)
1293-
{
1294-
return array[i];
1295-
}
1296-
};
1297-
12981308
struct Subtask : Waitable
12991309
{
13001310
};
@@ -1359,6 +1369,28 @@ namespace cmcpp
13591369
return element.rep;
13601370
}
13611371

1372+
inline int32_t canon_context_get(Task &task, uint32_t index, const HostTrap &trap)
1373+
{
1374+
if (auto *inst = task.component_instance())
1375+
{
1376+
ensure_may_leave(*inst, trap);
1377+
}
1378+
auto trap_cx = make_trap_context(trap);
1379+
trap_if(trap_cx, index >= ContextLocalStorage::LENGTH, "context index out of bounds");
1380+
return task.context().get(index);
1381+
}
1382+
1383+
inline void canon_context_set(Task &task, uint32_t index, int32_t value, const HostTrap &trap)
1384+
{
1385+
if (auto *inst = task.component_instance())
1386+
{
1387+
ensure_may_leave(*inst, trap);
1388+
}
1389+
auto trap_cx = make_trap_context(trap);
1390+
trap_if(trap_cx, index >= ContextLocalStorage::LENGTH, "context index out of bounds");
1391+
task.context().set(index, value);
1392+
}
1393+
13621394
inline uint32_t canon_waitable_set_new(ComponentInstance &inst, const HostTrap &trap)
13631395
{
13641396
ensure_may_leave(inst, trap);

include/cmcpp/error_context.hpp

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
#ifndef CMCPP_ERROR_CONTEXT_HPP
2+
#define CMCPP_ERROR_CONTEXT_HPP
3+
4+
#include "context.hpp"
5+
#include "string.hpp"
6+
#include <utility>
7+
8+
namespace cmcpp
9+
{
10+
class ErrorContext : public TableEntry
11+
{
12+
public:
13+
explicit ErrorContext(string_t message)
14+
: debug_message_(std::move(message))
15+
{
16+
}
17+
18+
const string_t &debug_message() const
19+
{
20+
return debug_message_;
21+
}
22+
23+
private:
24+
string_t debug_message_{};
25+
};
26+
27+
inline uint32_t canon_error_context_new(Task &task,
28+
const LiftLowerContext *cx,
29+
uint32_t ptr,
30+
uint32_t tagged_code_units,
31+
const HostTrap &trap)
32+
{
33+
auto *inst = task.component_instance();
34+
auto trap_cx = make_trap_context(trap);
35+
trap_if(trap_cx, inst == nullptr, "task missing component instance");
36+
ensure_may_leave(*inst, trap);
37+
38+
string_t message;
39+
if (cx != nullptr)
40+
{
41+
message = string::load_from_range<string_t>(*cx, ptr, tagged_code_units);
42+
}
43+
44+
auto entry = std::make_shared<ErrorContext>(std::move(message));
45+
return inst->table.add(entry, trap);
46+
}
47+
48+
inline void canon_error_context_debug_message(Task &task,
49+
LiftLowerContext &cx,
50+
uint32_t index,
51+
uint32_t ptr,
52+
const HostTrap &trap)
53+
{
54+
auto *inst = task.component_instance();
55+
auto trap_cx = make_trap_context(trap);
56+
trap_if(trap_cx, inst == nullptr, "task missing component instance");
57+
ensure_may_leave(*inst, trap);
58+
59+
auto errctx = inst->table.get<ErrorContext>(index, trap);
60+
string::store(cx, errctx->debug_message(), ptr);
61+
}
62+
63+
inline void canon_error_context_drop(Task &task, uint32_t index, const HostTrap &trap)
64+
{
65+
auto *inst = task.component_instance();
66+
auto trap_cx = make_trap_context(trap);
67+
trap_if(trap_cx, inst == nullptr, "task missing component instance");
68+
ensure_may_leave(*inst, trap);
69+
70+
inst->table.remove<ErrorContext>(index, trap);
71+
}
72+
}
73+
74+
#endif // CMCPP_ERROR_CONTEXT_HPP

test/main.cpp

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
using namespace cmcpp;
66

77
#include <iostream>
8+
#include <algorithm>
89
#include <string>
910
#include <vector>
1011
#include <utility>
@@ -232,6 +233,81 @@ TEST_CASE("Backpressure counters and may_leave guards")
232233
CHECK_NOTHROW(canon_waitable_set_new(inst, trap));
233234
}
234235

236+
TEST_CASE("Context locals provide per-task storage")
237+
{
238+
ComponentInstance inst;
239+
HostTrap trap = [](const char *msg)
240+
{
241+
throw std::runtime_error(msg ? msg : "trap");
242+
};
243+
244+
Task task(inst);
245+
246+
CHECK(ContextLocalStorage::LENGTH == 1);
247+
CHECK(canon_context_get(task, 0, trap) == 0);
248+
249+
canon_context_set(task, 0, 42, trap);
250+
CHECK(canon_context_get(task, 0, trap) == 42);
251+
252+
CHECK_THROWS(canon_context_get(task, ContextLocalStorage::LENGTH, trap));
253+
CHECK_THROWS(canon_context_set(task, ContextLocalStorage::LENGTH, 99, trap));
254+
255+
inst.may_leave = false;
256+
CHECK_THROWS(canon_context_get(task, 0, trap));
257+
inst.may_leave = true;
258+
}
259+
260+
TEST_CASE("Error context APIs manage debug messages")
261+
{
262+
Store store;
263+
ComponentInstance inst;
264+
inst.store = &store;
265+
266+
HostTrap trap = [](const char *msg)
267+
{
268+
throw std::runtime_error(msg ? msg : "trap");
269+
};
270+
271+
Task task(inst);
272+
273+
Heap heap(1024);
274+
auto cx = createLiftLowerContext(&heap, Encoding::Utf8);
275+
cx->inst = &inst;
276+
277+
const std::string guest_message = "component failure";
278+
const uint32_t guest_ptr = 32;
279+
std::copy(guest_message.begin(), guest_message.end(), heap.memory.begin() + guest_ptr);
280+
uint32_t tagged_code_units = static_cast<uint32_t>(guest_message.size());
281+
282+
uint32_t err_index = canon_error_context_new(task, cx.get(), guest_ptr, tagged_code_units, trap);
283+
CHECK(err_index != 0);
284+
285+
const uint32_t record_ptr = 128;
286+
canon_error_context_debug_message(task, *cx, err_index, record_ptr, trap);
287+
288+
uint32_t stored_ptr = integer::load<uint32_t>(*cx, record_ptr);
289+
uint32_t stored_len = integer::load<uint32_t>(*cx, record_ptr + 4);
290+
auto roundtrip = string::load_from_range<string_t>(*cx, stored_ptr, stored_len);
291+
CHECK(roundtrip == guest_message);
292+
293+
inst.may_leave = false;
294+
CHECK_THROWS(canon_error_context_debug_message(task, *cx, err_index, record_ptr + 16, trap));
295+
inst.may_leave = true;
296+
297+
canon_error_context_drop(task, err_index, trap);
298+
CHECK_THROWS(canon_error_context_drop(task, err_index, trap));
299+
300+
uint32_t empty_index = canon_error_context_new(task, nullptr, 0, 0, trap);
301+
CHECK(empty_index != 0);
302+
303+
const uint32_t empty_record = 192;
304+
canon_error_context_debug_message(task, *cx, empty_index, empty_record, trap);
305+
uint32_t empty_len = integer::load<uint32_t>(*cx, empty_record + 4);
306+
CHECK(empty_len == 0);
307+
308+
canon_error_context_drop(task, empty_index, trap);
309+
}
310+
235311
TEST_CASE("Task yield, cancel, and return")
236312
{
237313
Store store;

0 commit comments

Comments
 (0)