10 local $SIG{'__WARN__'} = \&Carp::cluck;
15 use IO::Poll qw(POLLIN);
16 use Time::HiRes qw(sleep);
19 # $fsel_open_mask is used to limit the number of opens to 1 per file. This
20 # uses the file index (0-F) as $fh, as poll support requires a unique handle
21 # per open file. Lifting this would require more complete open file
23 my $fsel_open_mask :shared = 0;
25 # Maximum "file" size.
26 use constant FSEL_CNT_MAX => 10;
27 use constant FSEL_FILES => 16;
29 # Used only as a lock for $fsel_poll_notify_mask and @fsel_cnt.
30 my $fsel_mutex :shared;
31 # Mask indicating what FDs have poll notifications waiting.
32 my $fsel_poll_notify_mask :shared = 0;
33 # Poll notification handles.
34 my @fsel_poll_handle :shared;
35 # Number of bytes for each "file".
37 # Initialize all byte counts.
38 map { $fsel_cnt[$_] = 0 } (0 .. (FSEL_FILES - 1));
42 print 'called ', (caller(0))[3], "\n";
44 return -1 if $path !~ m{^/([0-9A-F])$};
50 print 'called ', (caller(0))[3], "\n";
51 my @stbuf = (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
54 @stbuf[2, 3] = (S_IFDIR | 0555, 2);
58 my $idx = fsel_path_index($path);
59 return -&ENOENT if $idx < 0;
61 @stbuf[2, 3, 7] = (S_IFREG | 0444, 1, $fsel_cnt[$idx]);
66 my ($path, $offset) = @_;
67 print 'called ', (caller(0))[3], "\n";
69 return -&ENOENT if $path ne '/';
71 return('.', '..', map { sprintf('%X', $_) } (0 .. (FSEL_FILES - 1)), 0);
75 my ($path, $flags, $info) = @_;
76 print 'called ', (caller(0))[3], "\n";
78 my $idx = fsel_path_index($path);
79 return -&ENOENT if $idx < 0;
80 return -&EACCES if $flags & O_ACCMODE != O_RDONLY;
81 return -&EBUSY if $fsel_open_mask & (1 << $idx);
82 $fsel_open_mask |= (1 << $idx);
84 # fsel files are nonseekable somewhat pipe-like files which get filled
85 # up periodically by the producer thread, and consumed on read. Tell
86 # FUSE to do this right.
87 @{$info}{'direct_io', 'nonseekable'} = (1, 1);
92 my ($path, $flags, $fh) = @_;
93 print 'called ', (caller(0))[3], "\n";
95 $fsel_open_mask &= ~(1 << $fh);
100 my ($path, $size, $offset, $fh) = @_;
101 print 'called ', (caller(0))[3], "\n";
104 if ($fsel_cnt[$fh] < $size) {
105 $size = $fsel_cnt[$fh];
107 printf("READ \%X transferred=\%u cnt=\%u\n", $fh, $size, $fsel_cnt[$fh]);
108 $fsel_cnt[$fh] -= $size;
110 return(chr($fh) x $size);
113 our $polled_zero :shared = 0;
116 my ($path, $ph, $revents, $fh) = @_;
117 print 'called ', (caller(0))[3], ", path = \"$path\", fh = $fh, revents = $revents\n";
122 my $oldph = $fsel_poll_handle[$fh];
123 pollhandle_destroy($oldph) if $oldph;
124 $fsel_poll_notify_mask |= (1 << $fh);
125 $fsel_poll_handle[$fh] = $ph;
128 if ($fsel_cnt[$fh]) {
130 printf("POLL \%X cnt=\%u polled_zero=\%u\n", $fh, $fsel_cnt[$fh],
142 print 'called ', (caller(0))[3], "\n";
143 local $SIG{'KILL'} = sub { threads->exit(); };
144 my ($tv, $idx, $nr) = (0.25, 0, 1);
151 # This is the main producer loop which is executed every 250
152 # msec. On each iteration, it adds one byte to 1, 2 or 4 files
153 # and sends a poll notification if a poll handle is present.
154 for (($i, $t) = (0, $idx); $i < $nr; $i++,
155 $t = (($t + int(FSEL_FILES / $nr)) % FSEL_FILES)) {
156 next if $fsel_cnt[$t] == FSEL_CNT_MAX;
159 if ($fsel_poll_notify_mask & (1 << $t)) {
160 printf("NOTIFY \%X\n", $t);
161 my $ph = $fsel_poll_handle[$t];
163 pollhandle_destroy($ph);
164 $fsel_poll_notify_mask &= ~(1 << $t);
165 $fsel_poll_handle[$t] = undef;
169 $idx = ($idx + 1) % FSEL_FILES;
171 # Cycle through 1, 2 and 4.
180 croak("Fuse doesn't have poll") unless Fuse::fuse_version() >= 2.8;
183 'getattr' => 'main::fsel_getattr',
184 'readdir' => 'main::fsel_readdir',
185 'open' => 'main::fsel_open',
186 'release' => 'main::fsel_release',
187 'read' => 'main::fsel_read',
188 'poll' => 'main::fsel_poll',
192 'use-threads' => sub {
193 print STDERR "Warning: Fuse currently has bugs related to threading which may cause misbehavior\n";
194 $fuseargs{'threaded'} = 1;
197 $fuseargs{'debug'} = 1;
199 ) || croak("Malformed options passed");
201 $fuseargs{'mountpoint'} = $ARGV[0];
203 my $thread = threads->create(\&fsel_producer);
205 Fuse::main(%fuseargs);
207 $thread->kill('KILL');