What is the effects for this empty select-case-default code block?

Issue

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()
    }
}

Solution

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.

Answered By – mkopriva

Answer Checked By – Dawn Plyler (GoLangFix Volunteer)

Leave a Reply

Your email address will not be published.