Remove some print statements, and handle options for debug output and threads.
[perl-fuse.git] / examples / fsel.pl
1 #!/usr/bin/env perl
2
3 use strict;
4 no strict qw(refs);
5
6 use threads;
7 use threads::shared;
8
9 use Carp;
10 local $SIG{'__WARN__'} = \&Carp::cluck;
11
12 use Fuse qw(:all);
13 use Fcntl qw(:mode);
14 use POSIX;
15 use IO::Poll qw(POLLIN);
16 use Time::HiRes qw(sleep);
17 use Getopt::Long;
18
19 use constant FSEL_CNT_MAX   => 10;
20 use constant FSEL_FILES     => 16;
21
22 my $fsel_open_mask :shared = 0;
23 my $fsel_poll_notify_mask :shared = 0;
24 my @fsel_poll_handle :shared;
25 my @fsel_cnt :shared;
26 map { $fsel_cnt[$_] = 0 } (0 .. (FSEL_FILES - 1));
27
28 my $fsel_mutex :shared;
29
30 sub fsel_path_index {
31     my ($path) = @_;
32     print 'called ', (caller(0))[3], "\n";
33
34     my $ch = substr($path, 1, 1);
35     if (length($path) != 2 || substr($path, 0, 1) ne '/' || 
36             $ch !~ /^[0-9A-F]$/) {
37         return -1;
38     }
39     return hex($ch);
40 }
41
42 sub fsel_getattr {
43     my ($path) = @_;
44     print 'called ', (caller(0))[3], "\n";
45     my @stbuf = (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
46
47     if ($path eq '/') {
48         $stbuf[2] = S_IFDIR | 0555;
49         $stbuf[3] = 2;
50         return @stbuf;
51     }
52
53     my $idx = fsel_path_index($path);
54     return -&ENOENT if $idx < 0;
55
56     $stbuf[2] = S_IFREG | 0444;
57     $stbuf[3] = 1;
58     $stbuf[7] = $fsel_cnt[$idx];
59     return @stbuf;
60 }
61
62 sub fsel_readdir {
63     my ($path, $offset) = @_;
64     print 'called ', (caller(0))[3], "\n";
65
66     return -&ENOENT if $path ne '/';
67
68     return('.', '..', map { sprintf('%X', $_) } (0 .. (FSEL_FILES - 1)), 0);
69 }
70
71 sub fsel_open {
72     my ($path, $flags, $info) = @_;
73     print 'called ', (caller(0))[3], "\n";
74
75     my $idx = fsel_path_index($path);
76     return -&ENOENT if $idx < 0;
77     return -&EACCES if $flags & 3 != O_RDONLY;
78     return -&EBUSY if $fsel_open_mask & (1 << $idx);
79     $fsel_open_mask |= (1 << $idx);
80
81     $info->{'direct_io'} = 1;
82     $info->{'nonseekable'} = 1;
83     my $foo = [ $idx + 0 ];
84     return (0, $foo->[0]);
85 }
86
87 sub fsel_release {
88     my ($path, $flags, $fh) = @_;
89     print 'called ', (caller(0))[3], "\n";
90     ## HACK
91     #$fh = fsel_path_index($path);
92
93     $fsel_open_mask &= ~(1 << $fh);
94     return 0;
95 }
96
97 sub fsel_read {
98     my ($path, $size, $offset, $fh) = @_;
99     print 'called ', (caller(0))[3], "\n";
100     ## HACK
101     #$fh = fsel_path_index($path);
102     lock($fsel_mutex);
103
104     if ($fsel_cnt[$fh] < $size) {
105         $size = $fsel_cnt[$fh];
106     }
107     printf("READ   \%X transferred=\%u cnt=\%u\n", $fh, $size, $fsel_cnt[$fh]);
108     $fsel_cnt[$fh] -= $size;
109
110     return(chr($fh) x $size);
111 }
112
113 our $polled_zero :shared = 0;
114
115 sub fsel_poll {
116     my ($path, $ph, $revents, $fh) = @_;
117     print 'called ', (caller(0))[3], ", path = \"$path\", fh = $fh, revents = $revents\n";
118     ## HACK
119     #$fh = fsel_path_index($path);
120
121     lock($fsel_mutex);
122
123     if ($ph) {
124         my $oldph = $fsel_poll_handle[$fh];
125         if ($oldph) {
126             pollhandle_destroy($oldph);
127         }
128         $fsel_poll_notify_mask |= (1 << $fh);
129         $fsel_poll_handle[$fh] = $ph;
130     }
131
132     if ($fsel_cnt[$fh]) {
133         $revents |= POLLIN;
134         printf("POLL   \%X cnt=\%u polled_zero=\%u\n", $fh, $fsel_cnt[$fh],
135                 $polled_zero);
136         $polled_zero = 0;
137     }
138     else {
139         $polled_zero++;
140     }
141
142     return(0, $revents);
143 }
144
145 sub fsel_producer {
146     print 'called ', (caller(0))[3], "\n";
147     local $SIG{'KILL'} = sub { threads->exit(); };
148     my $tv = 0.25;
149     my $idx = 0;
150     my $nr = 1;
151
152     while (1) {
153         {
154             my ($i, $t);
155             lock($fsel_mutex);
156
157             for (($i, $t) = (0, $idx); $i < $nr; $i++,
158                     $t = (($t + int(FSEL_FILES / $nr)) % FSEL_FILES)) {
159                 next if $fsel_cnt[$t] == FSEL_CNT_MAX;
160
161                 $fsel_cnt[$t]++;
162                 if ($fsel_poll_notify_mask & (1 << $t)) {
163                     printf("NOTIFY \%X\n", $t);
164                     my $ph = $fsel_poll_handle[$t];
165                     notify_poll($ph);
166                     pollhandle_destroy($ph);
167                     $fsel_poll_handle[$t] = undef;
168                     $fsel_poll_notify_mask &= ~(1 << $t);
169                 }
170             }
171
172             $idx = ($idx + 1) % FSEL_FILES;
173             if ($idx == 0) {
174                 $nr = ($nr * 2) % 7;
175             }
176         }
177
178         sleep($tv);
179     }
180 }
181
182 croak("Fuse doesn't have poll") unless Fuse::fuse_version() >= 2.8;
183
184 my %fuseargs = (
185     'mountpoint' => $ARGV[0],
186     'getattr'   => 'main::fsel_getattr',
187     'readdir'   => 'main::fsel_readdir',
188     'open'      => 'main::fsel_open',
189     'release'   => 'main::fsel_release',
190     'read'      => 'main::fsel_read',
191     'poll'      => 'main::fsel_poll',
192 );
193
194 GetOptions(
195     'use-threads'       => sub {
196         print STDERR "Warning: Fuse currently has bugs related to threading which may cause misbehavior\n";
197         $fuseargs{'threaded'} = 1;
198     },
199     'debug'             => sub {
200         $fuseargs{'debug'} = 1;
201     }
202 ) || croak("Malformed options passed");
203
204 my $thread = threads->create(\&fsel_producer);
205
206 Fuse::main(%fuseargs);
207
208 $thread->kill('KILL');
209 $thread->join();