I'm trying to understand a pool library codes, and when instancing a pool struct, call a function named startCleanerLocked(t Duration)
, in this function, there's one empty select...case...default...
code block, I cann't understand what is the effect for this code block.
Pool Interface
is:
// Pool interface.
type Pool interface {
Get(ctx context.Context) (io.Closer, error)
Put(ctx context.Context, c io.Closer, forceClose bool) error
Close() error
}
List Struct
implement Pool Interface
,
type List struct {
// New is an application supplied function for creating and configuring a
// item.
//
// The item returned from new must not be in a special state
// (subscribed to pubsub channel, transaction started, ...).
New func(ctx context.Context) (io.Closer, error)
// mu protects fields defined below.
mu sync.Mutex
cond chan struct{}
closed bool
active int
// clean stale items
cleanerCh chan struct{}
// Stack of item with most recently used at the front.
idles list.List
// Config pool configuration
conf *Config
}
when Create a new pool, startCleanerLocked(t Duration)
function be called:
// NewList creates a new pool.
func NewList(c *Config) *List {
// check Config
if c == nil || c.Active < c.Idle {
panic("config nil or Idle Must <= Active")
}
// new pool
p := &List{conf: c}
p.cond = make(chan struct{})
p.startCleanerLocked(time.Duration(c.IdleTimeout))
return p
}
and in startCleanerLocked(t Duration)
, there is a select...case...default
:
// startCleanerLocked
func (p *List) startCleanerLocked(d time.Duration) {
if d <= 0 {
// if set 0, staleCleaner() will return directly
return
}
if d < time.Duration(p.conf.IdleTimeout) && p.cleanerCh != nil {
select {
case p.cleanerCh <- struct{}{}:
default:
}
}
// run only one, clean stale items.
if p.cleanerCh == nil {
p.cleanerCh = make(chan struct{}, 1)
go p.staleCleaner()
}
}
what's the effect for this code block:
select {
case p.cleanerCh <- struct{}{}:
default:
}
Seems it's nothing to do...
and in staleCleaner()
, there is a same empty select..case...case
, also cannot undestand its effect:
// staleCleaner clean stale items proc.
func (p *List) staleCleaner() {
ticker := time.NewTicker(100 * time.Millisecond)
for {
select {
case <-ticker.C:
case <-p.cleanerCh: // maxLifetime was changed or db was closed.
}
p.mu.Lock()
if p.closed || p.conf.IdleTimeout <= 0 {
p.mu.Unlock()
return
}
for i, n := 0, p.idles.Len(); i < n; i {
e := p.idles.Back()
if e == nil {
// no possible
break
}
ic := e.Value.(item)
if !ic.expired(time.Duration(p.conf.IdleTimeout)) {
// not need continue.
break
}
p.idles.Remove(e)
p.release()
p.mu.Unlock()
ic.c.Close()
p.mu.Lock()
}
p.mu.Unlock()
}
}
CodePudding user response:
select {
case p.cleanerCh <- struct{}{}:
default:
}
This is a non-blocking select
statement. (because there is a default:
case)
If there is a receiver goroutine at the other end of the p.cleanerCh
channel, i.e. there is a goroutine that's currently "waiting" at a receive operation i.e. <-p.cleanerCh
, then the case p.cleanerCh <- struct{}{}
is executed immediately which effectively unblocks the receive operation <-p.cleanerCh
and then the goroutine can proceed to execute whatever statements follow.
If there is no receiver goroutine then the default:
case is immediately executed and the surrounding startCleanerLocked
function can proceed to execute whatever statement follow the select
statement.
select {
case <-ticker.C:
case <-p.cleanerCh: // maxLifetime was changed or db was closed.
}
This is a blocking select
statement. (because there is no default:
case)
This select
statement blocks the for
loop until one of the two communication cases is ready to receive.