Переглянути джерело

Pool.Close()

does normal shutdown with the Quit Client method.

also adding support for go 1.1 by including the Close method ourselves.
Travis J Parker 10 роки тому
батько
коміт
dc21ed13cc
1 змінених файлів з 40 додано та 8 видалено
  1. 40 8
      pool.go

+ 40 - 8
pool.go

@@ -21,6 +21,7 @@ type Pool struct {
 	rebuild      chan struct{}
 	mut          *sync.Mutex
 	lastBuildErr *timestampedErr
+	closing      chan struct{}
 }
 
 type client struct {
@@ -35,7 +36,10 @@ type timestampedErr struct {
 
 const maxFails = 4
 
-var ErrTimeout = errors.New("timed out")
+var (
+	ErrClosed  = errors.New("pool closed")
+	ErrTimeout = errors.New("timed out")
+)
 
 func NewPool(address string, count int, auth smtp.Auth) *Pool {
 	return &Pool{
@@ -44,10 +48,16 @@ func NewPool(address string, count int, auth smtp.Auth) *Pool {
 		max:     count,
 		clients: make(chan *client, count),
 		rebuild: make(chan struct{}),
+		closing: make(chan struct{}),
 		mut:     &sync.Mutex{},
 	}
 }
 
+// go1.1 didn't have this method
+func (c *client) Close() error {
+	return c.Text.Close()
+}
+
 func (p *Pool) get(timeout time.Duration) *client {
 	select {
 	case c := <-p.clients:
@@ -74,6 +84,8 @@ func (p *Pool) get(timeout time.Duration) *client {
 			p.makeOne()
 		case <-deadline:
 			return nil
+		case <-p.closing:
+			return nil
 		}
 	}
 }
@@ -183,14 +195,12 @@ func (p *Pool) build() (*client, error) {
 	}
 
 	if _, err := startTLS(c, p.addr); err != nil {
-		c.Quit()
 		c.Close()
 		return nil, err
 	}
 
 	if p.auth != nil {
 		if _, err := addAuth(c, p.auth); err != nil {
-			c.Quit()
 			c.Close()
 			return nil, err
 		}
@@ -224,10 +234,23 @@ func (p *Pool) maybeReplace(err error, c *client) {
 
 shutdown:
 	p.dec()
-	c.Quit()
 	c.Close()
 }
 
+func (p *Pool) failedToGet(startTime time.Time) error {
+	select {
+	case <-p.closing:
+		return ErrClosed
+	default:
+	}
+
+	if p.lastBuildErr != nil && startTime.Before(p.lastBuildErr.ts) {
+		return p.lastBuildErr.err
+	}
+
+	return ErrTimeout
+}
+
 // Send sends an email via a connection pulled from the Pool. The timeout may
 // be <0 to indicate no timeout. Otherwise reaching the timeout will produce
 // and error building a connection that occurred while we were waiting, or
@@ -236,10 +259,7 @@ func (p *Pool) Send(e *Email, timeout time.Duration) (err error) {
 	start := time.Now()
 	c := p.get(timeout)
 	if c == nil {
-		if p.lastBuildErr != nil && start.Before(p.lastBuildErr.ts) {
-			return p.lastBuildErr.err
-		}
-		return ErrTimeout
+		return p.failedToGet(start)
 	}
 
 	defer func() {
@@ -310,3 +330,15 @@ func addressLists(lists ...[]string) ([]string, error) {
 
 	return combined, nil
 }
+
+// Close immediately changes the pool's state so no new connections will be
+// created, then gets and closes the existing ones as they become available.
+func (p *Pool) Close() {
+	close(p.closing)
+
+	for p.created > 0 {
+		c := <-p.clients
+		c.Quit()
+		p.dec()
+	}
+}