@@ -503,74 +503,66 @@ func (c *Conn) handleData(arg string) {
503
503
}
504
504
505
505
type statusCollector struct {
506
- mapLock sync.Mutex
507
-
508
506
// Contains map from recipient to list of channels that are used for that
509
507
// recipient.
510
- //
511
- // First SetStatus call uses first channel, second - second, etc. Channels
512
- // that are already used are set to nil (otherwise we can accidentally
513
- // reuse channel that was consumed by handleDataLMTP already).
514
- statusMap map [string ][]chan error
508
+ statusMap map [string ]chan error
515
509
516
510
// Contains channels from statusMap, in the same
517
511
// order as Conn.recipients.
518
512
status []chan error
519
513
}
520
514
521
515
// fillRemaining sets status for all recipients SetStatus was not called for before.
522
- func (s statusCollector ) fillRemaining (err error ) {
523
- s .mapLock .Lock ()
524
- defer s .mapLock .Unlock ()
525
-
526
- // Since used channels in statusMap are set to nil, we can simply send
527
- // on all non-nil channels to fill statuses not set by LMTPData.
528
- for _ , chList := range s .statusMap {
529
- for _ , ch := range chList {
530
- if ch == nil {
531
- continue
516
+ func (s * statusCollector ) fillRemaining (err error ) {
517
+ // Amount of times certain recipient was specified is indicated by the channel
518
+ // buffer size, so once we fill it, we can be confident that we sent
519
+ // at least as much statuses as needed. Extra statuses will be ignored anyway.
520
+ chLoop:
521
+ for _ , ch := range s .statusMap {
522
+ for {
523
+ select {
524
+ case ch <- err :
525
+ default :
526
+ continue chLoop
532
527
}
533
- ch <- err
534
528
}
535
529
}
536
530
}
537
531
538
- func (s statusCollector ) SetStatus (rcptTo string , err error ) {
539
- s .mapLock .Lock ()
540
- defer s .mapLock .Unlock ()
541
-
542
- chList := s .statusMap [rcptTo ]
543
- if chList == nil {
532
+ func (s * statusCollector ) SetStatus (rcptTo string , err error ) {
533
+ ch := s .statusMap [rcptTo ]
534
+ if ch == nil {
544
535
panic ("SetStatus is called for recipient that was not specified before" )
545
536
}
546
537
547
- // Pick the first non-nil channel from list.
548
- var usedCh chan error
549
- for i , ch := range chList {
550
- if ch != nil {
551
- usedCh = ch
552
- chList [i ] = nil
553
- break
554
- }
555
- }
556
- if usedCh == nil {
538
+ select {
539
+ case ch <- err :
540
+ default :
541
+ // There enough buffer space to fit all statuses at once, if this is
542
+ // not the case - backend is doing something wrong.
557
543
panic ("SetStatus is called more times than particular recipient was specified" )
558
544
}
559
-
560
- usedCh <- err
561
545
}
562
546
563
547
func (c * Conn ) handleDataLMTP () {
564
548
r := newDataReader (c )
565
549
566
- status := statusCollector {
567
- statusMap : make (map [string ][]chan error , len (c .recipients )),
550
+ rcptCounts := make (map [string ]int , len (c .recipients ))
551
+
552
+ status := & statusCollector {
553
+ statusMap : make (map [string ]chan error , len (c .recipients )),
568
554
status : make ([]chan error , 0 , len (c .recipients )),
569
555
}
570
556
for _ , rcpt := range c .recipients {
571
- ch := make (chan error , 1 )
572
- status .status = append (status .status , ch )
573
- status .statusMap [rcpt ] = append (status .statusMap [rcpt ], ch )
557
+ rcptCounts [rcpt ]++
558
+ }
559
+ // Create channels with buffer sizes necessary to fit all
560
+ // statuses for a single recipient to avoid deadlocks.
561
+ for rcpt , count := range rcptCounts {
562
+ status .statusMap [rcpt ] = make (chan error , count )
563
+ }
564
+ for _ , rcpt := range c .recipients {
565
+ status .status = append (status .status , status .statusMap [rcpt ])
574
566
}
575
567
576
568
lmtpSession , ok := c .Session ().(LMTPSession )
0 commit comments