github.com/scaleoutsean/fusego@v0.0.0-20220224074057-4a6429e46bb8/samples/interruptfs/interrupt_fs.go (about) 1 // Copyright 2015 Google Inc. All Rights Reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package interruptfs 16 17 import ( 18 "context" 19 "fmt" 20 "os" 21 "sync" 22 23 "github.com/scaleoutsean/fusego" 24 "github.com/scaleoutsean/fusego/fuseops" 25 "github.com/scaleoutsean/fusego/fuseutil" 26 ) 27 28 var rootAttrs = fuseops.InodeAttributes{ 29 Nlink: 1, 30 Mode: os.ModeDir | 0777, 31 } 32 33 const fooID = fuseops.RootInodeID + 1 34 35 var fooAttrs = fuseops.InodeAttributes{ 36 Nlink: 1, 37 Mode: 0777, 38 Size: 1234, 39 } 40 41 // A file system containing exactly one file, named "foo". ReadFile and 42 // FlushFile ops can be made to hang until interrupted. Exposes a method for 43 // synchronizing with the arrival of a read or a flush. 44 // 45 // Must be created with New. 46 type InterruptFS struct { 47 fuseutil.NotImplementedFileSystem 48 49 mu sync.Mutex 50 51 blockForReads bool // GUARDED_BY(mu) 52 blockForFlushes bool // GUARDED_BY(mu) 53 54 // Must hold the mutex when closing these. 55 readReceived chan struct{} 56 flushReceived chan struct{} 57 } 58 59 func New() *InterruptFS { 60 return &InterruptFS{ 61 readReceived: make(chan struct{}), 62 flushReceived: make(chan struct{}), 63 } 64 } 65 66 //////////////////////////////////////////////////////////////////////// 67 // Public interface 68 //////////////////////////////////////////////////////////////////////// 69 70 // Block until the first read is received. 71 func (fs *InterruptFS) WaitForFirstRead() { 72 <-fs.readReceived 73 } 74 75 // Block until the first flush is received. 76 func (fs *InterruptFS) WaitForFirstFlush() { 77 <-fs.flushReceived 78 } 79 80 // Enable blocking until interrupted for the next (and subsequent) read ops. 81 func (fs *InterruptFS) EnableReadBlocking() { 82 fs.mu.Lock() 83 defer fs.mu.Unlock() 84 85 fs.blockForReads = true 86 } 87 88 // Enable blocking until interrupted for the next (and subsequent) flush ops. 89 func (fs *InterruptFS) EnableFlushBlocking() { 90 fs.mu.Lock() 91 defer fs.mu.Unlock() 92 93 fs.blockForFlushes = true 94 } 95 96 //////////////////////////////////////////////////////////////////////// 97 // FileSystem methods 98 //////////////////////////////////////////////////////////////////////// 99 100 func (fs *InterruptFS) StatFS( 101 ctx context.Context, 102 op *fuseops.StatFSOp) error { 103 return nil 104 } 105 106 func (fs *InterruptFS) LookUpInode( 107 ctx context.Context, 108 op *fuseops.LookUpInodeOp) error { 109 // We support only one parent. 110 if op.Parent != fuseops.RootInodeID { 111 return fmt.Errorf("Unexpected parent: %v", op.Parent) 112 } 113 114 // We support only one name. 115 if op.Name != "foo" { 116 return fuse.ENOENT 117 } 118 119 // Fill in the response. 120 op.Entry.Child = fooID 121 op.Entry.Attributes = fooAttrs 122 123 return nil 124 } 125 126 func (fs *InterruptFS) GetInodeAttributes( 127 ctx context.Context, 128 op *fuseops.GetInodeAttributesOp) error { 129 switch op.Inode { 130 case fuseops.RootInodeID: 131 op.Attributes = rootAttrs 132 133 case fooID: 134 op.Attributes = fooAttrs 135 136 default: 137 return fmt.Errorf("Unexpected inode ID: %v", op.Inode) 138 } 139 140 return nil 141 } 142 143 func (fs *InterruptFS) OpenFile( 144 ctx context.Context, 145 op *fuseops.OpenFileOp) error { 146 return nil 147 } 148 149 func (fs *InterruptFS) ReadFile( 150 ctx context.Context, 151 op *fuseops.ReadFileOp) error { 152 fs.mu.Lock() 153 shouldBlock := fs.blockForReads 154 155 // Signal that a read has been received, if this is the first. 156 select { 157 case <-fs.readReceived: 158 default: 159 close(fs.readReceived) 160 } 161 fs.mu.Unlock() 162 163 // Wait for cancellation if enabled. 164 if shouldBlock { 165 done := ctx.Done() 166 if done == nil { 167 panic("Expected non-nil channel.") 168 } 169 170 <-done 171 return ctx.Err() 172 } 173 174 return nil 175 } 176 177 func (fs *InterruptFS) FlushFile( 178 ctx context.Context, 179 op *fuseops.FlushFileOp) error { 180 fs.mu.Lock() 181 shouldBlock := fs.blockForFlushes 182 183 // Signal that a flush has been received, if this is the first. 184 select { 185 case <-fs.flushReceived: 186 default: 187 close(fs.flushReceived) 188 } 189 fs.mu.Unlock() 190 191 // Wait for cancellation if enabled. 192 if shouldBlock { 193 done := ctx.Done() 194 if done == nil { 195 panic("Expected non-nil channel.") 196 } 197 198 <-done 199 return ctx.Err() 200 } 201 202 return nil 203 }