Aias00 opened a new issue, #3522:
URL: https://github.com/apache/dubbo-go/issues/3522

   ### Problem
   
   Both `cluster/loadbalance/p2c` and `cluster/cluster/adaptivesvc` initialize 
a package-level singleton with the same buggy pattern: an `if instance == nil` 
check **outside** `sync.Once.Do`, then `return instance` also outside `Do`.
   
   ```go
   var (
       once     sync.Once
       instance loadbalance.LoadBalance
   )
   
   func newP2CLoadBalance(r randomPicker) loadbalance.LoadBalance {
       if r == nil { r = defaultRnd }
       if instance == nil {              // unsynchronized read
           once.Do(func() { instance = &p2cLoadBalance{...} })
       }
       return instance                  // unsynchronized read when the check 
was false
   }
   ```
   
   `sync.Once.Do` only establishes a happens-before for callers that **call 
`Do`**. A goroutine that observes `instance != nil` (check false) skips `Do` 
entirely and reads `instance` with no happens-before to the goroutine that 
wrote it inside `Do`. Two concurrent first-time callers therefore race on 
`instance`: `go test -race` flags it. The identical pattern exists in 
`cluster/cluster/adaptivesvc/cluster.go:newAdaptiveServiceCluster`.
   
   Because both factories are registered in `init()` via 
`extension.SetLoadbalance`/`SetCluster`, every concurrent RPC at process start 
(before first use) hits this path.
   
   ### Current behavior
   
   - `cluster/loadbalance/p2c/loadbalance.go` `newP2CLoadBalance` reads 
`instance` outside `once.Do`.
   - `cluster/cluster/adaptivesvc/cluster.go` `newAdaptiveServiceCluster` reads 
`instance` outside `once.Do`.
   
   ### Expected behavior
   
   The read of `instance` must have a happens-before relation to the write, so 
concurrent first-time callers cannot race.
   
   ### Suggested approach
   
   - Drop the `if instance == nil` fast path; always call `once.Do(...)` then 
`return instance`. `sync.Once.Do` guarantees all callers see the writes done 
inside `Do`, and there is no read of `instance` outside `Do`.
   - The `r == nil` defaulting in p2c moves inside the `once.Do` closure (only 
the first call's `r` is used, identical to before).
   
   ### Acceptance criteria
   
   - [ ] No read of `instance` outside `once.Do` in either package.
   - [ ] A concurrency test (100 goroutines racing the factory) passes under 
`-race`.
   


-- 
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