Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

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

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,

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

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.

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading