jolestar/go-commons-pool

deadlock in BorrowObject

xhjmc opened this issue · 2 comments

xhjmc commented

Problem

When creating object fails and using n goroutines (n > MaxTotal) to call BorrowObject, it will deadlock. But when I lock BorrowObject, the deadlock will not happen. Is this a bug?

Env

go version 1.13
windows10
go-commons-pool version v2.11

Code

package pool

import (
	"context"
	"errors"
	"fmt"
	"github.com/jolestar/go-commons-pool/v2"
	"sync"
	"testing"
	"time"
)

func TestPool(t *testing.T) {
	factory := pool.NewPooledObjectFactorySimple(
		func(context.Context) (interface{}, error) {
			time.Sleep(time.Second)
			return nil, errors.New("create object fail") // simulate the failure of creating objects
		})

	ctx := context.Background()
	conf := pool.NewDefaultPoolConfig()
	conf.MaxTotal = 1
	p := pool.NewObjectPool(ctx, factory, conf)

	n := 2
	wg := sync.WaitGroup{}
	wg.Add(n)
	//lock := sync.Mutex{}
	for i := 0; i < n; i++ {
		id := i
		go func() { // when using goroutines without locking BorrowObject, it will deadlock
			defer wg.Done()
			fmt.Println(id, "before borrow")
			//lock.Lock()
			obj, err := p.BorrowObject(ctx)
			//lock.Unlock()
			fmt.Println(id, "after borrow")
			if err != nil {
				fmt.Println(id, "borrow err:", err)
			}

			err = p.ReturnObject(ctx, obj)
			if err != nil {
				fmt.Println(id, "return err:", err)
			}
		}()
	}
	wg.Wait()
}

xhjmc commented

Env fix
go version 1.13.1
go-commons-pool version v2.1.1

  1. the first goroutine is blocked by time.Sleep, and use the 1 MaxTotal limit object。
  2. the second goroutine can not create a new object, so just wait for a returned object at PollFirstWithContext, but not one to return object, so deadlocked.

Please set a timeout to ctx, or increment the MaxTotal config, this is not a bug.