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 }