storj.io/minio@v0.0.0-20230509071714-0cbc90f649b1/pkg/pubsub/pubsub.go (about)

     1  /*
     2   * MinIO Cloud Storage, (C) 2019 MinIO, Inc.
     3   *
     4   * Licensed under the Apache License, Version 2.0 (the "License");
     5   * you may not use this file except in compliance with the License.
     6   * You may obtain a copy of the License at
     7   *
     8   *     http://www.apache.org/licenses/LICENSE-2.0
     9   *
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS,
    12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   * See the License for the specific language governing permissions and
    14   * limitations under the License.
    15   */
    16  
    17  package pubsub
    18  
    19  import (
    20  	"sync"
    21  	"sync/atomic"
    22  )
    23  
    24  // Sub - subscriber entity.
    25  type Sub struct {
    26  	ch     chan interface{}
    27  	filter func(entry interface{}) bool
    28  }
    29  
    30  // PubSub holds publishers and subscribers
    31  type PubSub struct {
    32  	subs           []*Sub
    33  	numSubscribers int32
    34  	sync.RWMutex
    35  }
    36  
    37  // Publish message to the subscribers.
    38  // Note that publish is always nob-blocking send so that we don't block on slow receivers.
    39  // Hence receivers should use buffered channel so as not to miss the published events.
    40  func (ps *PubSub) Publish(item interface{}) {
    41  	ps.RLock()
    42  	defer ps.RUnlock()
    43  
    44  	for _, sub := range ps.subs {
    45  		if sub.filter == nil || sub.filter(item) {
    46  			select {
    47  			case sub.ch <- item:
    48  			default:
    49  			}
    50  		}
    51  	}
    52  }
    53  
    54  // Subscribe - Adds a subscriber to pubsub system
    55  func (ps *PubSub) Subscribe(subCh chan interface{}, doneCh <-chan struct{}, filter func(entry interface{}) bool) {
    56  	ps.Lock()
    57  	defer ps.Unlock()
    58  
    59  	sub := &Sub{subCh, filter}
    60  	ps.subs = append(ps.subs, sub)
    61  	atomic.AddInt32(&ps.numSubscribers, 1)
    62  
    63  	go func() {
    64  		<-doneCh
    65  
    66  		ps.Lock()
    67  		defer ps.Unlock()
    68  
    69  		for i, s := range ps.subs {
    70  			if s == sub {
    71  				ps.subs = append(ps.subs[:i], ps.subs[i+1:]...)
    72  			}
    73  		}
    74  		atomic.AddInt32(&ps.numSubscribers, -1)
    75  	}()
    76  }
    77  
    78  // NumSubscribers returns the number of current subscribers
    79  func (ps *PubSub) NumSubscribers() int32 {
    80  	return atomic.LoadInt32(&ps.numSubscribers)
    81  }
    82  
    83  // New inits a PubSub system
    84  func New() *PubSub {
    85  	return &PubSub{}
    86  }