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  }