Copilot commented on code in PR #3418:
URL: https://github.com/apache/brpc/pull/3418#discussion_r3671468983


##########
src/bthread/task_control.cpp:
##########
@@ -218,6 +223,7 @@ TaskControl::TaskControl()
         FLAGS_task_group_ntags * FLAGS_event_dispatcher_num)
     , _pl_num_of_each_tag(FLAGS_bthread_parking_lot_of_each_tag)
     , _tagged_pl(FLAGS_task_group_ntags)
+    , _tagged_waiter_num(FLAGS_task_group_ntags)
     , _tag_cpus(FLAGS_task_group_ntags)
     , _tag_next_worker_id(FLAGS_task_group_ntags)
 {}

Review Comment:
   `_tagged_waiter_num` elements are default-constructed via 
`butil::atomic<size_t>::atomic() {}` which does not value-initialize the 
underlying atomic. This can leave the per-tag waiter counts indeterminate, 
breaking `has_waiting_workers()` (e.g., always signaling or underflowing/never 
returning to zero). Explicitly initialize all counters to 0.



##########
src/bthread/task_control.h:
##########
@@ -73,9 +73,26 @@ friend bthread_t init_for_pthread_stack_trace();
     int concurrency() const 
     { return _concurrency.load(butil::memory_order_acquire); }
 
-    int concurrency(bthread_tag_t tag) const 
+    int concurrency(bthread_tag_t tag) const
     { return _tagged_ngroup[tag].load(butil::memory_order_acquire); }
 
+    // Check if there are workers parked in wait() for the given tag.
+    // Per-tag accounting avoids a tag-A ready_to_run() needlessly issuing a
+    // signal_task() just because workers of a *different* tag B are parked.
+    bool has_waiting_workers(bthread_tag_t tag) const {
+        return tag < (bthread_tag_t)_tagged_waiter_num.size()
+               && _tagged_waiter_num[tag].load(butil::memory_order_relaxed) > 
0;
+    }

Review Comment:
   `has_waiting_workers()` does not guard against `BTHREAD_TAG_INVALID` (=-1). 
Since `bthread_tag_t` is `int`, a negative tag satisfies `tag < size` and will 
index `_tagged_waiter_num[tag]` with a negative index (UB). Add `tag >= 0` to 
the bounds check.



##########
src/bthread/task_control.cpp:
##########
@@ -302,6 +308,15 @@ int TaskControl::init(int concurrency) {
     _signal_per_second.expose("bthread_signal_second");
     _status.expose("bthread_group_status");
 
+    // Wire each ParkingLot back to this TaskControl so that ParkingLot::wait()
+    // can maintain the per-tag _tagged_waiter_num counter consulted by
+    // has_waiting_workers(tag). The outer index `i' is the owning tag.
+    for (size_t i = 0; i < _tagged_pl.size(); ++i) {
+        for (size_t j = 0; j < _pl_num_of_each_tag; ++j) {

Review Comment:
   `set_task_control()` wiring is done *after* worker threads are created. 
Workers can enter `ParkingLot::wait()` before `_tc/_tag` is set, so they won't 
be counted in `_tagged_waiter_num`. After this PR, `ready_to_run*()` may then 
skip `signal_task()` (because `has_waiting_workers()` reads 0) and leave 
already-parked workers sleeping indefinitely. Wire parking lots before starting 
worker threads (or ensure the selected ParkingLot is wired before any wait).



##########
src/bthread/task_control.h:
##########
@@ -73,9 +73,26 @@ friend bthread_t init_for_pthread_stack_trace();
     int concurrency() const 
     { return _concurrency.load(butil::memory_order_acquire); }
 
-    int concurrency(bthread_tag_t tag) const 
+    int concurrency(bthread_tag_t tag) const
     { return _tagged_ngroup[tag].load(butil::memory_order_acquire); }
 
+    // Check if there are workers parked in wait() for the given tag.
+    // Per-tag accounting avoids a tag-A ready_to_run() needlessly issuing a
+    // signal_task() just because workers of a *different* tag B are parked.
+    bool has_waiting_workers(bthread_tag_t tag) const {
+        return tag < (bthread_tag_t)_tagged_waiter_num.size()
+               && _tagged_waiter_num[tag].load(butil::memory_order_relaxed) > 
0;
+    }
+
+    // Called by ParkingLot (via parking_lot_waiter_add/sub) to track waiters
+    // entering/leaving wait() for a given tag.
+    void waiter_add(bthread_tag_t tag) {
+        _tagged_waiter_num[tag].fetch_add(1, butil::memory_order_relaxed);
+    }
+    void waiter_sub(bthread_tag_t tag) {
+        _tagged_waiter_num[tag].fetch_sub(1, butil::memory_order_relaxed);
+    }

Review Comment:
   `waiter_add()` / `waiter_sub()` index `_tagged_waiter_num[tag]` without 
validating `tag`. Even if tags are expected to be valid, these functions are 
reachable via `ParkingLot` and can be called with `BTHREAD_TAG_INVALID` if a 
caller ever misconfigures a tag, causing UB. Add a non-negative + upper-bound 
guard (and optionally DCHECK).



##########
src/bthread/parking_lot.h:
##########
@@ -30,6 +30,16 @@ namespace bthread {
 
 DECLARE_bool(parking_lot_no_signal_when_no_waiter);
 
+// Forward declaration
+class TaskControl;
+
+// Out-of-line helpers that forward to TaskControl's per-tag waiter accounting.
+// Defined in task_control.cpp (where TaskControl is complete), so that
+// parking_lot.h can keep TaskControl as an incomplete type and avoid a
+// circular include with task_control.h.
+void parking_lot_waiter_add(TaskControl* tc, bthread_tag_t tag);

Review Comment:
   `parking_lot.h` now uses `bthread_tag_t` but does not include the header 
that defines it (`bthread/types.h`), relying on indirect includes. This makes 
the header non-self-contained and can break if include order changes. Include 
`bthread/types.h` directly.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to