@@ -92,6 +92,15 @@ struct scheduler_context
9292{
9393 epoll_scheduler const * key;
9494 scheduler_context* next;
95+ op_queue private_queue;
96+ long private_outstanding_work;
97+
98+ scheduler_context (epoll_scheduler const * k, scheduler_context* n)
99+ : key(k)
100+ , next(n)
101+ , private_outstanding_work(0 )
102+ {
103+ }
95104};
96105
97106corosio::detail::thread_local_ptr<scheduler_context> context_stack;
@@ -102,17 +111,28 @@ struct thread_context_guard
102111
103112 explicit thread_context_guard (
104113 epoll_scheduler const * ctx) noexcept
105- : frame_{ ctx, context_stack.get ()}
114+ : frame_( ctx, context_stack.get())
106115 {
107116 context_stack.set (&frame_);
108117 }
109118
110119 ~thread_context_guard () noexcept
111120 {
121+ if (!frame_.private_queue .empty ())
122+ frame_.key ->drain_thread_queue (frame_.private_queue , frame_.private_outstanding_work );
112123 context_stack.set (frame_.next );
113124 }
114125};
115126
127+ scheduler_context*
128+ find_context (epoll_scheduler const * self) noexcept
129+ {
130+ for (auto * c = context_stack.get (); c != nullptr ; c = c->next )
131+ if (c->key == self)
132+ return c;
133+ return nullptr ;
134+ }
135+
116136} // namespace
117137
118138epoll_scheduler::
@@ -259,6 +279,17 @@ post(capy::coro h) const
259279 };
260280
261281 auto ph = std::make_unique<post_handler>(h);
282+
283+ // Fast path: same thread posts to private queue without locking
284+ if (auto * ctx = find_context (this ))
285+ {
286+ outstanding_work_.fetch_add (1 , std::memory_order_relaxed);
287+ ++ctx->private_outstanding_work ;
288+ ctx->private_queue .push (ph.release ());
289+ return ;
290+ }
291+
292+ // Slow path: cross-thread post requires mutex
262293 outstanding_work_.fetch_add (1 , std::memory_order_relaxed);
263294
264295 std::unique_lock lock (mutex_);
@@ -270,6 +301,16 @@ void
270301epoll_scheduler::
271302post (scheduler_op* h) const
272303{
304+ // Fast path: same thread posts to private queue without locking
305+ if (auto * ctx = find_context (this ))
306+ {
307+ outstanding_work_.fetch_add (1 , std::memory_order_relaxed);
308+ ++ctx->private_outstanding_work ;
309+ ctx->private_queue .push (h);
310+ return ;
311+ }
312+
313+ // Slow path: cross-thread post requires mutex
273314 outstanding_work_.fetch_add (1 , std::memory_order_relaxed);
274315
275316 std::unique_lock lock (mutex_);
@@ -489,6 +530,17 @@ work_finished() const noexcept
489530 }
490531}
491532
533+ void
534+ epoll_scheduler::
535+ drain_thread_queue (op_queue& queue, long count) const
536+ {
537+ std::lock_guard lock (mutex_);
538+ // Note: outstanding_work_ was already incremented when posting
539+ completed_ops_.splice (queue);
540+ if (count > 0 )
541+ wakeup_event_.notify_all ();
542+ }
543+
492544void
493545epoll_scheduler::
494546interrupt_reactor () const
@@ -548,7 +600,6 @@ update_timerfd() const
548600 if (nearest == timer_service::time_point::max ())
549601 {
550602 // No timers - disarm by setting to 0 (relative)
551- // ts is already zeroed
552603 }
553604 else
554605 {
@@ -754,6 +805,17 @@ run_reactor(std::unique_lock<std::mutex>& lock)
754805 if (!local_ops.empty ())
755806 completed_ops_.splice (local_ops);
756807
808+ // Drain private queue (outstanding_work_ was already incremented when posting)
809+ if (auto * ctx = find_context (this ))
810+ {
811+ if (!ctx->private_queue .empty ())
812+ {
813+ completions_queued += ctx->private_outstanding_work ;
814+ ctx->private_outstanding_work = 0 ;
815+ completed_ops_.splice (ctx->private_queue );
816+ }
817+ }
818+
757819 // Only wake threads that are actually idle, and only as many as we have work
758820 if (completions_queued > 0 && idle_thread_count_ > 0 )
759821 {
@@ -778,7 +840,10 @@ do_one(long timeout_us)
778840
779841 if (op == &task_op_)
780842 {
781- bool more_handlers = !completed_ops_.empty ();
843+ // Check both global queue and private queue for pending handlers
844+ auto * ctx = find_context (this );
845+ bool more_handlers = !completed_ops_.empty () ||
846+ (ctx && !ctx->private_queue .empty ());
782847
783848 if (!more_handlers)
784849 {
@@ -821,6 +886,17 @@ do_one(long timeout_us)
821886 if (timeout_us == 0 )
822887 return 0 ;
823888
889+ // Drain private queue before blocking (outstanding_work_ was already incremented)
890+ if (auto * ctx = find_context (this ))
891+ {
892+ if (!ctx->private_queue .empty ())
893+ {
894+ ctx->private_outstanding_work = 0 ;
895+ completed_ops_.splice (ctx->private_queue );
896+ continue ;
897+ }
898+ }
899+
824900 ++idle_thread_count_;
825901 if (timeout_us < 0 )
826902 wakeup_event_.wait (lock);
0 commit comments