X-Git-Url: http://git.rot13.org/?a=blobdiff_plain;f=examples%2Ffsel.pl;h=f76255bb602971f3aee5495570892c1c00bc1be4;hb=98bd016b90b2ad4c5ce7715d739715fdc8a1495e;hp=e213af998414d527e207fe1feda2a07b784e8ba9;hpb=805a31912a12a7c8ddf8ff0922e5451f35eb7d93;p=perl-fuse.git diff --git a/examples/fsel.pl b/examples/fsel.pl index e213af9..f76255b 100755 --- a/examples/fsel.pl +++ b/examples/fsel.pl @@ -14,29 +14,35 @@ use Fcntl qw(:mode); use POSIX; use IO::Poll qw(POLLIN); use Time::HiRes qw(sleep); -use Data::Dumper; +use Getopt::Long; +# $fsel_open_mask is used to limit the number of opens to 1 per file. This +# uses the file index (0-F) as $fh, as poll support requires a unique handle +# per open file. Lifting this would require more complete open file +# management. +my $fsel_open_mask :shared = 0; + +# Maximum "file" size. use constant FSEL_CNT_MAX => 10; use constant FSEL_FILES => 16; -my $fsel_open_mask :shared = 0; +# Used only as a lock for $fsel_poll_notify_mask and @fsel_cnt. +my $fsel_mutex :shared; +# Mask indicating what FDs have poll notifications waiting. my $fsel_poll_notify_mask :shared = 0; +# Poll notification handles. my @fsel_poll_handle :shared; +# Number of bytes for each "file". my @fsel_cnt :shared; +# Initialize all byte counts. map { $fsel_cnt[$_] = 0 } (0 .. (FSEL_FILES - 1)); -my $fsel_mutex :shared; - sub fsel_path_index { my ($path) = @_; print 'called ', (caller(0))[3], "\n"; - my $ch = substr($path, 1, 1); - if (length($path) != 2 || substr($path, 0, 1) ne '/' || - $ch !~ /^[0-9A-F]$/) { - return -1; - } - return hex($ch); + return -1 if $path !~ m{^/([0-9A-F])$}; + return hex($1); } sub fsel_getattr { @@ -45,17 +51,14 @@ sub fsel_getattr { my @stbuf = (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); if ($path eq '/') { - $stbuf[2] = S_IFDIR | 0555; - $stbuf[3] = 2; + @stbuf[2, 3] = (S_IFDIR | 0555, 2); return @stbuf; } my $idx = fsel_path_index($path); return -&ENOENT if $idx < 0; - $stbuf[2] = S_IFREG | 0444; - $stbuf[3] = 1; - $stbuf[7] = $fsel_cnt[$idx]; + @stbuf[2, 3, 7] = (S_IFREG | 0444, 1, $fsel_cnt[$idx]); return @stbuf; } @@ -74,24 +77,22 @@ sub fsel_open { my $idx = fsel_path_index($path); return -&ENOENT if $idx < 0; - return -&EACCES if $flags & 3 != O_RDONLY; - return -&EBUSY if $fsel_open_mask & (1 << $idx); + return -&EACCES if $flags & O_ACCMODE != O_RDONLY; + return -&EBUSY if $fsel_open_mask & (1 << $idx); $fsel_open_mask |= (1 << $idx); - $info->{'direct_io'} = 1; - $info->{'nonseekable'} = 1; - print "fsel_open(): ", $idx, "\n"; - my $foo = [ $idx + 0 ]; - return (0, $foo->[0]); + # fsel files are nonseekable somewhat pipe-like files which get filled + # up periodically by the producer thread, and consumed on read. Tell + # FUSE to do this right. + @{$info}{'direct_io', 'nonseekable'} = (1, 1); + return (0, $idx); } sub fsel_release { my ($path, $flags, $fh) = @_; print 'called ', (caller(0))[3], "\n"; - print "fsel_release(): \$fh is $fh\n"; $fsel_open_mask &= ~(1 << $fh); - printf("fsel_release(): \$fsel_open_mask is \%x\n", $fsel_open_mask); return 0; } @@ -113,15 +114,13 @@ our $polled_zero :shared = 0; sub fsel_poll { my ($path, $ph, $revents, $fh) = @_; - print 'called ', (caller(0))[3], "\n"; + print 'called ', (caller(0))[3], ", path = \"$path\", fh = $fh, revents = $revents\n"; lock($fsel_mutex); if ($ph) { my $oldph = $fsel_poll_handle[$fh]; - if ($oldph) { - pollhandle_destroy($oldph); - } + pollhandle_destroy($oldph) if $oldph; $fsel_poll_notify_mask |= (1 << $fh); $fsel_poll_handle[$fh] = $ph; } @@ -140,17 +139,18 @@ sub fsel_poll { } sub fsel_producer { - local $SIG{'KILL'} = sub { threads->exit(); }; print 'called ', (caller(0))[3], "\n"; - my $tv = 0.25; - my $idx = 0; - my $nr = 1; + local $SIG{'KILL'} = sub { threads->exit(); }; + my ($tv, $idx, $nr) = (0.25, 0, 1); while (1) { { my ($i, $t); lock($fsel_mutex); + # This is the main producer loop which is executed every 250 + # msec. On each iteration, it adds one byte to 1, 2 or 4 files + # and sends a poll notification if a poll handle is present. for (($i, $t) = (0, $idx); $i < $nr; $i++, $t = (($t + int(FSEL_FILES / $nr)) % FSEL_FILES)) { next if $fsel_cnt[$t] == FSEL_CNT_MAX; @@ -161,13 +161,14 @@ sub fsel_producer { my $ph = $fsel_poll_handle[$t]; notify_poll($ph); pollhandle_destroy($ph); - $fsel_poll_handle[$t] = undef; $fsel_poll_notify_mask &= ~(1 << $t); + $fsel_poll_handle[$t] = undef; } } $idx = ($idx + 1) % FSEL_FILES; if ($idx == 0) { + # Cycle through 1, 2 and 4. $nr = ($nr * 2) % 7; } } @@ -178,18 +179,30 @@ sub fsel_producer { croak("Fuse doesn't have poll") unless Fuse::fuse_version() >= 2.8; -my $thread = threads->create(\&fsel_producer); - -Fuse::main( - 'mountpoint' => $ARGV[0], +my %fuseargs = ( 'getattr' => 'main::fsel_getattr', 'readdir' => 'main::fsel_readdir', 'open' => 'main::fsel_open', 'release' => 'main::fsel_release', 'read' => 'main::fsel_read', 'poll' => 'main::fsel_poll', - 'threaded' => 1, ); +GetOptions( + 'use-threads' => sub { + print STDERR "Warning: Fuse currently has bugs related to threading which may cause misbehavior\n"; + $fuseargs{'threaded'} = 1; + }, + 'debug' => sub { + $fuseargs{'debug'} = 1; + } +) || croak("Malformed options passed"); + +$fuseargs{'mountpoint'} = $ARGV[0]; + +my $thread = threads->create(\&fsel_producer); + +Fuse::main(%fuseargs); + $thread->kill('KILL'); $thread->join();