[
https://issues.apache.org/jira/browse/THRIFT-4552?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=16437123#comment-16437123
]
gansteed commented on THRIFT-4552:
----------------------------------
That's what I mean in last comment, just like you demo. in
https://issues.apache.org/jira/browse/THRIFT-4243 , it does things below:
- go serv.Serve()
- serv.Stop()
wg.Add and wg.Done will only occurred in `innerAccept` which is called by
`AcceptLoop`
wg.Wait will only occurred in `Stop`
so, if you call `go serv.Serve()`, it will spawn a goroutine, and the goroutine
will block on `client, err := p.serverTransport.Accept()` until there comes a
new request. and the main goroutine which execute `serv.Stop` will do:
1. load p.closed and check if it is already closed, if not, set it as closed
2. call `p.serverTransport.Interrupt()`
3. call `p.wg.Wait` to wait all spawned goroutines(which handle RPC requests)
the only possible race panic will occur in such a way:
1. main goroutine call `serv.Stop`, which then call `if
atomic.LoadInt32(&p.closed) != 0`, in the mean time, there comes a request, and
the goroutine which handle it call `closed := atomic.LoadInt32(&p.closed)`
2. the main goroutine call `atomic.StoreInt32(&p.closed, 1)`,
`p.serverTransport.Interrupt()`, then `p.wg.Wait()`
3. and the goroutine which handle the request call `p.wg.Add(1)`
then it panic!
so, the solution is use https://golang.org/pkg/sync/atomic/#CompareAndSwapInt32
instead of two steps(first load, then store).
may I sent a PR in Github to solve this problem?
> why acquire a lock in TSimpleServer implementation for Go?
> ----------------------------------------------------------
>
> Key: THRIFT-4552
> URL: https://issues.apache.org/jira/browse/THRIFT-4552
> Project: Thrift
> Issue Type: Improvement
> Affects Versions: 0.11.0
> Reporter: gansteed
> Priority: Major
>
> I've sent a email to groups, but I think maybe here will be better?
>
> I'm using Thrift and I'm reading thrift implementation for Go, I found code
> in `TSimpleServer.AcceptLoop` like this:
>
> {code:go}
> func (p *TSimpleServer) AcceptLoop() error {
> for {
> client, err := p.serverTransport.Accept()
> p.mu.Lock()
> if atomic.LoadInt32(&p.closed) != 0 {
> return nil
> }
> if err != nil {
> return err
> }
> if client != nil {
> p.wg.Add(1)
> go func() {
> defer p.wg.Done()
> if err := p.processRequests(client); err !=
> nil {
> log.Println("error processing
> request:", err)
> }
> }()
> }
> p.mu.Unlock()
> }
> }
> {code}
>
> every time it accept a request,it:
>
> 1. read if protocol had been closed, this step is atomic, it does not need a
> lock.
> 2. p.wg.Add(1) to accumulate a goroutine? this step is atomic, too, it does
> not need a lock
> 3. after processor processed the request, it do p.wg.Done(), it's atomic,
> too, and it does not need a lock.
>
> by the way, it seems that `p.wg.Done()` do not need to put in defer? just put
> it after p.processRequests(client)?
>
> so is there any particular to do it in this way?if not, I would like to
> submit a PR to reduce unneccessary performance overhead in TSimpleServer
> implementation.
>
>
--
This message was sent by Atlassian JIRA
(v7.6.3#76005)