Skip to content

Commit 37d9790

Browse files
committed
Revert "Add O(1) indexed service slots to execution_context"
This reverts commit d6a8e0c.
1 parent 5903c8a commit 37d9790

File tree

5 files changed

+4
-207
lines changed

5 files changed

+4
-207
lines changed

doc/unlisted/execution-contexts.adoc

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -271,13 +271,6 @@ Service management functions (`use_service`, `make_service`, `find_service`)
271271
are thread-safe. The `shutdown()` and `destroy()` functions are NOT thread-safe
272272
and must only be called during destruction.
273273

274-
=== Performance
275-
276-
The first 32 distinct service types registered across the program benefit from
277-
an O(1) lock-free fast path for `find_service` and `use_service`. Beyond 32
278-
types, lookups fall back to a mutex-protected linear scan. In practice, 32
279-
slots is sufficient for any realistic program.
280-
281274
== When NOT to Use execution_context Directly
282275

283276
Use `execution_context` directly when:

include/boost/capy/detail/service_slot.hpp

Lines changed: 0 additions & 44 deletions
This file was deleted.

include/boost/capy/ex/execution_context.hpp

Lines changed: 4 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,8 @@
1212

1313
#include <boost/capy/detail/config.hpp>
1414
#include <boost/capy/detail/frame_memory_resource.hpp>
15-
#include <boost/capy/detail/service_slot.hpp>
1615
#include <boost/capy/detail/type_id.hpp>
1716
#include <boost/capy/concept/executor.hpp>
18-
#include <atomic>
1917
#include <concepts>
2018
#include <memory>
2119
#include <memory_resource>
@@ -225,14 +223,6 @@ class BOOST_CAPY_DECL
225223
template<class T>
226224
T* find_service() const noexcept
227225
{
228-
auto id = detail::service_slot<T>();
229-
if(id < max_service_slots)
230-
{
231-
auto* p = slots_[id].load(
232-
std::memory_order_acquire);
233-
if(p)
234-
return static_cast<T*>(p);
235-
}
236226
std::lock_guard<std::mutex> lock(mutex_);
237227
return static_cast<T*>(find_impl(detail::type_id<T>()));
238228
}
@@ -265,24 +255,6 @@ class BOOST_CAPY_DECL
265255
"T must derive from service");
266256
static_assert(std::is_constructible<T, execution_context&>::value,
267257
"T must be constructible from execution_context&");
268-
if constexpr(get_key<T>::value)
269-
{
270-
static_assert(
271-
std::is_convertible<T&, typename get_key<T>::type&>::value,
272-
"T& must be convertible to key_type&");
273-
}
274-
275-
// Fast path: O(1) slot lookup
276-
{
277-
auto id = detail::service_slot<T>();
278-
if(id < max_service_slots)
279-
{
280-
auto* p = slots_[id].load(
281-
std::memory_order_acquire);
282-
if(p)
283-
return static_cast<T&>(*p);
284-
}
285-
}
286258

287259
struct impl : factory
288260
{
@@ -291,11 +263,7 @@ class BOOST_CAPY_DECL
291263
detail::type_id<T>(),
292264
get_key<T>::value
293265
? detail::type_id<typename get_key<T>::type>()
294-
: detail::type_id<T>(),
295-
detail::service_slot<T>(),
296-
get_key<T>::value
297-
? detail::service_slot<typename get_key<T>::type>()
298-
: detail::service_slot<T>())
266+
: detail::type_id<T>())
299267
{
300268
}
301269

@@ -357,11 +325,7 @@ class BOOST_CAPY_DECL
357325
detail::type_id<T>(),
358326
get_key<T>::value
359327
? detail::type_id<typename get_key<T>::type>()
360-
: detail::type_id<T>(),
361-
detail::service_slot<T>(),
362-
get_key<T>::value
363-
? detail::service_slot<typename get_key<T>::type>()
364-
: detail::service_slot<T>())
328+
: detail::type_id<T>())
365329
, args_(std::forward<Args>(a)...)
366330
{
367331
}
@@ -541,16 +505,11 @@ class BOOST_CAPY_DECL
541505
detail::type_index t0;
542506
detail::type_index t1;
543507
BOOST_CAPY_MSVC_WARNING_POP
544-
std::size_t slot0;
545-
std::size_t slot1;
546508

547509
factory(
548510
detail::type_info const& t0_,
549-
detail::type_info const& t1_,
550-
std::size_t s0,
551-
std::size_t s1)
511+
detail::type_info const& t1_)
552512
: t0(t0_), t1(t1_)
553-
, slot0(s0), slot1(s1)
554513
{
555514
}
556515

@@ -564,7 +523,7 @@ class BOOST_CAPY_DECL
564523
service& use_service_impl(factory& f);
565524
service& make_service_impl(factory& f);
566525

567-
// warning C4251: std::mutex, std::shared_ptr, std::atomic need dll-interface
526+
// warning C4251: std::mutex, std::shared_ptr need dll-interface
568527
BOOST_CAPY_MSVC_WARNING_PUSH
569528
BOOST_CAPY_MSVC_WARNING_DISABLE(4251)
570529
mutable std::mutex mutex_;
@@ -573,12 +532,6 @@ class BOOST_CAPY_DECL
573532
std::pmr::memory_resource* frame_alloc_ = nullptr;
574533
service* head_ = nullptr;
575534
bool shutdown_ = false;
576-
577-
static constexpr std::size_t max_service_slots = 32;
578-
BOOST_CAPY_MSVC_WARNING_PUSH
579-
BOOST_CAPY_MSVC_WARNING_DISABLE(4251)
580-
std::atomic<service*> slots_[max_service_slots] = {};
581-
BOOST_CAPY_MSVC_WARNING_POP
582535
};
583536

584537
template< typename Derived >

src/ex/execution_context.cpp

Lines changed: 0 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,6 @@ destroy() noexcept
5555
delete p;
5656
p = next;
5757
}
58-
for(auto& s : slots_)
59-
s.store(nullptr, std::memory_order_relaxed);
6058
}
6159

6260
execution_context::service*
@@ -80,13 +78,7 @@ use_service_impl(factory& f)
8078
std::unique_lock<std::mutex> lock(mutex_);
8179

8280
if(auto* p = find_impl(f.t0))
83-
{
84-
if(f.slot0 < max_service_slots)
85-
slots_[f.slot0].store(p, std::memory_order_release);
86-
if(f.slot0 != f.slot1 && f.slot1 < max_service_slots)
87-
slots_[f.slot1].store(p, std::memory_order_release);
8881
return *p;
89-
}
9082

9183
lock.unlock();
9284

@@ -99,22 +91,13 @@ use_service_impl(factory& f)
9991

10092
if(auto* p = find_impl(f.t0))
10193
{
102-
if(f.slot0 < max_service_slots)
103-
slots_[f.slot0].store(p, std::memory_order_release);
104-
if(f.slot0 != f.slot1 && f.slot1 < max_service_slots)
105-
slots_[f.slot1].store(p, std::memory_order_release);
10694
delete sp;
10795
return *p;
10896
}
10997

11098
sp->next_ = head_;
11199
head_ = sp;
112100

113-
if(f.slot0 < max_service_slots)
114-
slots_[f.slot0].store(sp, std::memory_order_release);
115-
if(f.slot0 != f.slot1 && f.slot1 < max_service_slots)
116-
slots_[f.slot1].store(sp, std::memory_order_release);
117-
118101
return *sp;
119102
}
120103

@@ -158,11 +141,6 @@ make_service_impl(factory& f)
158141
p->next_ = head_;
159142
head_ = p;
160143

161-
if(f.slot0 < max_service_slots)
162-
slots_[f.slot0].store(p, std::memory_order_release);
163-
if(f.slot0 != f.slot1 && f.slot1 < max_service_slots)
164-
slots_[f.slot1].store(p, std::memory_order_release);
165-
166144
return *p;
167145
}
168146

test/unit/ex/execution_context.cpp

Lines changed: 0 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -387,85 +387,6 @@ struct execution_context_test
387387
BOOST_TEST_NE(new_mr, default_mr);
388388
}
389389

390-
void
391-
testSlotLookupConsistency()
392-
{
393-
// Verify that find_service returns the same pointer
394-
// whether from the slot fast path or linked list fallback.
395-
test_io_context ctx;
396-
397-
auto& svc = ctx.make_service<simple_service>(77);
398-
auto* p1 = ctx.find_service<simple_service>();
399-
auto* p2 = ctx.find_service<simple_service>();
400-
401-
BOOST_TEST_NE(p1, nullptr);
402-
BOOST_TEST_EQ(p1, p2);
403-
BOOST_TEST_EQ(p1, &svc);
404-
}
405-
406-
void
407-
testSlotKeyTypeLookup()
408-
{
409-
// Verify slot lookup works for both concrete and key_type.
410-
test_io_context ctx;
411-
412-
ctx.make_service<derived_service>(55);
413-
414-
auto* p1 = ctx.find_service<derived_service>();
415-
BOOST_TEST_NE(p1, nullptr);
416-
BOOST_TEST_EQ(p1->value, 55);
417-
418-
auto* p2 = ctx.find_service<base_service>();
419-
BOOST_TEST_NE(p2, nullptr);
420-
BOOST_TEST_EQ(p2->get_value(), 55);
421-
422-
// Both should point to the same object
423-
BOOST_TEST_EQ(
424-
static_cast<base_service*>(p1), p2);
425-
}
426-
427-
void
428-
testUseServiceSlotFastPath()
429-
{
430-
// Verify use_service fast path returns same instance.
431-
test_io_context ctx;
432-
433-
auto& svc1 = ctx.use_service<simple_service>();
434-
auto& svc2 = ctx.use_service<simple_service>();
435-
436-
BOOST_TEST_EQ(&svc1, &svc2);
437-
}
438-
439-
void
440-
testConcurrentUseServiceSlots()
441-
{
442-
// Stress test: many threads calling use_service simultaneously.
443-
// All must get the same service instance.
444-
test_io_context ctx;
445-
constexpr int num_threads = 16;
446-
std::atomic<simple_service*> results[num_threads] = {};
447-
448-
std::vector<std::thread> threads;
449-
threads.reserve(num_threads);
450-
451-
for(int i = 0; i < num_threads; ++i)
452-
{
453-
threads.emplace_back([&ctx, &results, i]{
454-
auto& svc = ctx.use_service<simple_service>();
455-
results[i].store(&svc,
456-
std::memory_order_relaxed);
457-
});
458-
}
459-
460-
for(auto& t : threads)
461-
t.join();
462-
463-
auto* expected = results[0].load();
464-
BOOST_TEST_NE(expected, nullptr);
465-
for(int i = 1; i < num_threads; ++i)
466-
BOOST_TEST_EQ(results[i].load(), expected);
467-
}
468-
469390
void
470391
run()
471392
{
@@ -485,10 +406,6 @@ struct execution_context_test
485406
testGetFrameAllocator();
486407
testSetFrameAllocatorRawPointer();
487408
testSetFrameAllocatorTemplate();
488-
testSlotLookupConsistency();
489-
testSlotKeyTypeLookup();
490-
testUseServiceSlotFastPath();
491-
testConcurrentUseServiceSlots();
492409
}
493410
};
494411

0 commit comments

Comments
 (0)