Skip to content

Commit 4e8be12

Browse files
GordonSmithCopilot
andcommitted
feat: add recursion check and separate threads table
Align with upstream canonical ABI changes: - Add ComponentInstance::parent for component hierarchy tracking - Add reflexive_ancestors() and is_reflexive_ancestor_of() methods - Add call_might_be_recursive() to detect recursive cross-component calls - Separate thread entries into dedicated ComponentInstance::threads table - Wrap Store::invoke caller in host Supertask with instance=nullptr - Update all thread operations to use threads table instead of table - Add tests for ancestry methods and recursion detection - Bump canonical reference submodules Refs: component-model commits 3a00c74, 5f49720 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent d66bc32 commit 4e8be12

File tree

6 files changed

+298
-96
lines changed

6 files changed

+298
-96
lines changed

include/cmcpp/context.hpp

Lines changed: 114 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#include <thread>
1616
#include <condition_variable>
1717
#include <typeindex>
18+
#include <set>
1819
#include <unordered_map>
1920
#include <vector>
2021
#include <cstring>
@@ -757,11 +758,7 @@ namespace cmcpp
757758
pending_on_copy(
758759
[this]()
759760
{
760-
std::unique_lock<std::mutex> reclaim_lock(mu, std::try_to_lock);
761-
if (!reclaim_lock.owns_lock())
762-
{
763-
return;
764-
}
761+
std::scoped_lock<std::mutex> reclaim_lock(mu);
765762
reset_pending();
766763
});
767764
}
@@ -902,11 +899,6 @@ namespace cmcpp
902899
}
903900
}
904901

905-
if (!sync)
906-
{
907-
return BLOCKED;
908-
}
909-
910902
auto event = get_pending_event(trap);
911903
return event.payload;
912904
}
@@ -1025,11 +1017,6 @@ namespace cmcpp
10251017
}
10261018
}
10271019

1028-
if (!sync)
1029-
{
1030-
return BLOCKED;
1031-
}
1032-
10331020
auto event = get_pending_event(trap);
10341021
return event.payload;
10351022
}
@@ -1240,10 +1227,6 @@ namespace cmcpp
12401227
return BLOCKED;
12411228
}
12421229
}
1243-
if (!sync)
1244-
{
1245-
return BLOCKED;
1246-
}
12471230
auto event = get_pending_event(trap);
12481231
return event.payload;
12491232
}
@@ -1328,11 +1311,15 @@ namespace cmcpp
13281311
}
13291312
if (!has_pending_event())
13301313
{
1331-
return BLOCKED;
1332-
}
1333-
if (!sync)
1334-
{
1335-
return BLOCKED;
1314+
if (sync)
1315+
{
1316+
shared_->wait_until([this]()
1317+
{ return has_pending_event(); });
1318+
}
1319+
else
1320+
{
1321+
return BLOCKED;
1322+
}
13361323
}
13371324
auto event = get_pending_event(trap);
13381325
return event.payload;
@@ -1475,13 +1462,40 @@ namespace cmcpp
14751462
struct ComponentInstance
14761463
{
14771464
Store *store = nullptr;
1465+
ComponentInstance *parent = nullptr;
14781466
bool may_leave = true;
14791467
bool may_enter = true;
14801468
bool exclusive = false;
14811469
uint32_t backpressure = 0;
14821470
uint32_t num_waiting_to_enter = 0;
14831471
HandleTables handles;
14841472
InstanceTable table;
1473+
InstanceTable threads;
1474+
1475+
std::set<const ComponentInstance *> reflexive_ancestors() const
1476+
{
1477+
std::set<const ComponentInstance *> s;
1478+
const ComponentInstance *inst = this;
1479+
while (inst != nullptr)
1480+
{
1481+
s.insert(inst);
1482+
inst = inst->parent;
1483+
}
1484+
return s;
1485+
}
1486+
1487+
bool is_reflexive_ancestor_of(const ComponentInstance *other) const
1488+
{
1489+
while (other != nullptr)
1490+
{
1491+
if (this == other)
1492+
{
1493+
return true;
1494+
}
1495+
other = other->parent;
1496+
}
1497+
return false;
1498+
}
14851499
};
14861500

14871501
inline void ensure_may_leave(ComponentInstance &inst, const HostTrap &trap)
@@ -1509,6 +1523,40 @@ namespace cmcpp
15091523
inst.backpressure -= 1;
15101524
}
15111525

1526+
inline bool call_might_be_recursive(const SupertaskPtr &caller, const ComponentInstance *callee_inst)
1527+
{
1528+
if (!caller || !callee_inst)
1529+
{
1530+
return false;
1531+
}
1532+
if (caller->instance == nullptr)
1533+
{
1534+
auto callee_ancestors = callee_inst->reflexive_ancestors();
1535+
auto current = caller.get();
1536+
while (current != nullptr)
1537+
{
1538+
if (current->instance)
1539+
{
1540+
auto caller_ancestors = current->instance->reflexive_ancestors();
1541+
for (auto *a : caller_ancestors)
1542+
{
1543+
if (callee_ancestors.count(a))
1544+
{
1545+
return true;
1546+
}
1547+
}
1548+
}
1549+
current = current->parent.get();
1550+
}
1551+
return false;
1552+
}
1553+
else
1554+
{
1555+
return caller->instance->is_reflexive_ancestor_of(callee_inst) ||
1556+
callee_inst->is_reflexive_ancestor_of(caller->instance);
1557+
}
1558+
}
1559+
15121560
class Task : public std::enable_shared_from_this<Task>
15131561
{
15141562
public:
@@ -1656,6 +1704,36 @@ namespace cmcpp
16561704
return {EventCode::NONE, 0, 0};
16571705
}
16581706

1707+
Event wait_until(Thread::ReadyFn ready, bool cancellable, std::shared_ptr<WaitableSet> wset, const HostTrap &trap)
1708+
{
1709+
if (wset)
1710+
{
1711+
wset->begin_wait();
1712+
}
1713+
auto ready_and_has_event = [ready = std::move(ready), wset]() -> bool
1714+
{
1715+
return ready() && (!wset || wset->has_pending_event());
1716+
};
1717+
Event event;
1718+
if (!suspend_until(ready_and_has_event, cancellable))
1719+
{
1720+
event = {EventCode::TASK_CANCELLED, 0, 0};
1721+
}
1722+
else if (wset)
1723+
{
1724+
event = wset->take_pending_event(trap);
1725+
}
1726+
else
1727+
{
1728+
event = {EventCode::NONE, 0, 0};
1729+
}
1730+
if (wset)
1731+
{
1732+
wset->end_wait();
1733+
}
1734+
return event;
1735+
}
1736+
16591737
void return_result(std::vector<std::any> result, const HostTrap &trap)
16601738
{
16611739
ensure_resolvable(trap);
@@ -1796,7 +1874,7 @@ namespace cmcpp
17961874
trap_if(trap_cx, inst == nullptr, "thread.resume-later missing component instance");
17971875
ensure_may_leave(*inst, trap);
17981876

1799-
auto entry = inst->table.get<ThreadEntry>(thread_index, trap);
1877+
auto entry = inst->threads.get<ThreadEntry>(thread_index, trap);
18001878
auto other_thread = entry->thread();
18011879
trap_if(trap_cx, !other_thread, "thread.resume-later null thread");
18021880
trap_if(trap_cx, !other_thread->suspended(), "thread not suspended");
@@ -1810,7 +1888,7 @@ namespace cmcpp
18101888
trap_if(trap_cx, inst == nullptr, "thread.yield-to missing component instance");
18111889
ensure_may_leave(*inst, trap);
18121890

1813-
auto entry = inst->table.get<ThreadEntry>(thread_index, trap);
1891+
auto entry = inst->threads.get<ThreadEntry>(thread_index, trap);
18141892
auto other_thread = entry->thread();
18151893
trap_if(trap_cx, !other_thread, "thread.yield-to null thread");
18161894
trap_if(trap_cx, !other_thread->suspended(), "thread not suspended");
@@ -1836,7 +1914,7 @@ namespace cmcpp
18361914
trap_if(trap_cx, inst == nullptr, "thread.switch-to missing component instance");
18371915
ensure_may_leave(*inst, trap);
18381916

1839-
auto entry = inst->table.get<ThreadEntry>(thread_index, trap);
1917+
auto entry = inst->threads.get<ThreadEntry>(thread_index, trap);
18401918
auto other_thread = entry->thread();
18411919
trap_if(trap_cx, !other_thread, "thread.switch-to null thread");
18421920
trap_if(trap_cx, !other_thread->suspended(), "thread not suspended");
@@ -1881,7 +1959,7 @@ namespace cmcpp
18811959
{});
18821960
trap_if(trap_cx, !thread || !thread->suspended(), "thread.new-ref failed to create suspended thread");
18831961

1884-
uint32_t index = inst->table.add(std::make_shared<ThreadEntry>(thread), trap);
1962+
uint32_t index = inst->threads.add(std::make_shared<ThreadEntry>(thread), trap);
18851963
thread->set_index(index);
18861964
return index;
18871965
}
@@ -1959,7 +2037,7 @@ namespace cmcpp
19592037
return static_cast<uint32_t>(SuspendResult::NOT_CANCELLED);
19602038
}
19612039

1962-
inline uint32_t canon_task_wait(bool /*cancellable*/,
2040+
inline uint32_t canon_task_wait(bool cancellable,
19632041
GuestMemory mem,
19642042
Task &task,
19652043
uint32_t waitable_set_handle,
@@ -1970,17 +2048,15 @@ namespace cmcpp
19702048
auto trap_cx = make_trap_context(trap);
19712049
trap_if(trap_cx, inst == nullptr, "task.wait missing component instance");
19722050
ensure_may_leave(*inst, trap);
2051+
trap_if(trap_cx, !task.may_block(), "task.wait may not block");
19732052

19742053
auto wset = inst->table.get<WaitableSet>(waitable_set_handle, trap);
1975-
wset->begin_wait();
1976-
if (!wset->has_pending_event())
1977-
{
1978-
wset->end_wait();
1979-
write_event_fields(mem, event_ptr, 0, 0, trap);
1980-
return BLOCKED;
1981-
}
1982-
auto event = wset->take_pending_event(trap);
1983-
wset->end_wait();
2054+
auto event = task.wait_until(
2055+
[]()
2056+
{ return true; },
2057+
cancellable,
2058+
wset,
2059+
trap);
19842060
write_event_fields(mem, event_ptr, event.index, event.payload, trap);
19852061
return static_cast<uint32_t>(event.code);
19862062
}

include/cmcpp/runtime.hpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -443,7 +443,10 @@ namespace cmcpp
443443
{
444444
return Call();
445445
}
446-
return func(*this, std::move(caller), std::move(on_start), std::move(on_resolve));
446+
auto host_caller = std::make_shared<Supertask>();
447+
host_caller->instance = nullptr;
448+
host_caller->parent = std::move(caller);
449+
return func(*this, std::move(host_caller), std::move(on_start), std::move(on_resolve));
447450
}
448451

449452
inline void Store::tick()

ref/wasm-micro-runtime

Submodule wasm-micro-runtime updated 262 files

ref/wit-bindgen

Submodule wit-bindgen updated 427 files

0 commit comments

Comments
 (0)