github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/kbfs/libkbfs/block_retrieval_worker.go (about)

     1  // Copyright 2016 Keybase Inc. All rights reserved.
     2  // Use of this source code is governed by a BSD
     3  // license that can be found in the LICENSE file.
     4  
     5  package libkbfs
     6  
     7  import (
     8  	"io"
     9  
    10  	"github.com/eapache/channels"
    11  	"github.com/keybase/client/go/kbfs/data"
    12  )
    13  
    14  // blockRetrievalWorker processes blockRetrievalQueue requests
    15  type blockRetrievalWorker struct {
    16  	blockGetter
    17  	stopCh chan struct{}
    18  	doneCh chan struct{}
    19  	queue  *blockRetrievalQueue
    20  	workCh channels.Channel
    21  }
    22  
    23  // run runs the worker loop until Shutdown is called
    24  func (brw *blockRetrievalWorker) run() {
    25  	defer close(brw.doneCh)
    26  	for {
    27  		err := brw.HandleRequest()
    28  		// Only io.EOF is relevant to the loop; other errors are handled in
    29  		// FinalizeRequest
    30  		if err == io.EOF {
    31  			return
    32  		}
    33  	}
    34  }
    35  
    36  // newBlockRetrievalWorker returns a blockRetrievalWorker for a given
    37  // blockRetrievalQueue, using the passed in blockGetter to obtain blocks for
    38  // requests.
    39  func newBlockRetrievalWorker(bg blockGetter, q *blockRetrievalQueue,
    40  	workCh channels.Channel) *blockRetrievalWorker {
    41  	brw := &blockRetrievalWorker{
    42  		blockGetter: bg,
    43  		stopCh:      make(chan struct{}),
    44  		doneCh:      make(chan struct{}),
    45  		queue:       q,
    46  		workCh:      workCh,
    47  	}
    48  	go brw.run()
    49  	return brw
    50  }
    51  
    52  // HandleRequest is the main work method for the worker. It obtains a
    53  // blockRetrieval from the queue, retrieves the block using
    54  // blockGetter.getBlock, and responds to the subscribed requestors with the
    55  // results.
    56  func (brw *blockRetrievalWorker) HandleRequest() (err error) {
    57  	var retrieval *blockRetrieval
    58  	select {
    59  	case <-brw.workCh.Out():
    60  		retrieval = brw.queue.popIfNotEmpty()
    61  		if retrieval == nil {
    62  			return nil
    63  		}
    64  	case <-brw.stopCh:
    65  		return io.EOF
    66  	}
    67  
    68  	var block data.Block
    69  	var cacheType DiskBlockCacheType
    70  	defer func() {
    71  		brw.queue.FinalizeRequest(retrieval, block, cacheType, err)
    72  	}()
    73  
    74  	// Handle canceled contexts.
    75  	select {
    76  	case <-retrieval.ctx.Done():
    77  		return retrieval.ctx.Err()
    78  	default:
    79  	}
    80  
    81  	var action BlockRequestAction
    82  	func() {
    83  		retrieval.reqMtx.RLock()
    84  		defer retrieval.reqMtx.RUnlock()
    85  		block = retrieval.requests[0].block.NewEmpty()
    86  		action = retrieval.action
    87  	}()
    88  
    89  	// If we running with a "stop-if-full" action, before we fetch the
    90  	// block, make sure the disk cache has room for it.
    91  	if action.StopIfFull() {
    92  		dbc := brw.queue.config.DiskBlockCache()
    93  		if dbc != nil {
    94  			hasRoom, _, err := dbc.DoesCacheHaveSpace(
    95  				retrieval.ctx, action.CacheType())
    96  			if err != nil {
    97  				return err
    98  			}
    99  			if !hasRoom {
   100  				return DiskCacheTooFullForBlockError{retrieval.blockPtr, action}
   101  			}
   102  		}
   103  	}
   104  
   105  	cacheType = action.CacheType()
   106  	if action.DelayCacheCheck() {
   107  		_, err := brw.queue.checkCaches(
   108  			retrieval.ctx, retrieval.kmd, retrieval.blockPtr, block,
   109  			action.WithoutDelayedCacheCheckAction())
   110  		if err == nil {
   111  			return nil
   112  		}
   113  	}
   114  
   115  	return brw.getBlock(
   116  		retrieval.ctx, retrieval.kmd, retrieval.blockPtr, block, cacheType)
   117  }
   118  
   119  // Shutdown shuts down the blockRetrievalWorker once its current work is done.
   120  func (brw *blockRetrievalWorker) Shutdown() <-chan struct{} {
   121  	select {
   122  	case <-brw.stopCh:
   123  	default:
   124  		close(brw.stopCh)
   125  	}
   126  	return brw.doneCh
   127  }