e213af998414d527e207fe1feda2a07b784e8ba9
[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 Data::Dumper;
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     print "fsel_open(): ", $idx, "\n";
84     my $foo = [ $idx + 0 ];
85     return (0, $foo->[0]);
86 }
87
88 sub fsel_release {
89     my ($path, $flags, $fh) = @_;
90     print 'called ', (caller(0))[3], "\n";
91
92     print "fsel_release(): \$fh is $fh\n";
93     $fsel_open_mask &= ~(1 << $fh);
94     printf("fsel_release(): \$fsel_open_mask is \%x\n", $fsel_open_mask);
95     return 0;
96 }
97
98 sub fsel_read {
99     my ($path, $size, $offset, $fh) = @_;
100     print 'called ', (caller(0))[3], "\n";
101     lock($fsel_mutex);
102
103     if ($fsel_cnt[$fh] < $size) {
104         $size = $fsel_cnt[$fh];
105     }
106     printf("READ   \%X transferred=\%u cnt=\%u\n", $fh, $size, $fsel_cnt[$fh]);
107     $fsel_cnt[$fh] -= $size;
108
109     return(chr($fh) x $size);
110 }
111
112 our $polled_zero :shared = 0;
113
114 sub fsel_poll {
115     my ($path, $ph, $revents, $fh) = @_;
116     print 'called ', (caller(0))[3], "\n";
117
118     lock($fsel_mutex);
119
120     if ($ph) {
121         my $oldph = $fsel_poll_handle[$fh];
122         if ($oldph) {
123             pollhandle_destroy($oldph);
124         }
125         $fsel_poll_notify_mask |= (1 << $fh);
126         $fsel_poll_handle[$fh] = $ph;
127     }
128
129     if ($fsel_cnt[$fh]) {
130         $revents |= POLLIN;
131         printf("POLL   \%X cnt=\%u polled_zero=\%u\n", $fh, $fsel_cnt[$fh],
132                 $polled_zero);
133         $polled_zero = 0;
134     }
135     else {
136         $polled_zero++;
137     }
138
139     return(0, $revents);
140 }
141
142 sub fsel_producer {
143     local $SIG{'KILL'} = sub { threads->exit(); };
144     print 'called ', (caller(0))[3], "\n";
145     my $tv = 0.25;
146     my $idx = 0;
147     my $nr = 1;
148
149     while (1) {
150         {
151             my ($i, $t);
152             lock($fsel_mutex);
153
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;
157
158                 $fsel_cnt[$t]++;
159                 if ($fsel_poll_notify_mask & (1 << $t)) {
160                     printf("NOTIFY \%X\n", $t);
161                     my $ph = $fsel_poll_handle[$t];
162                     notify_poll($ph);
163                     pollhandle_destroy($ph);
164                     $fsel_poll_handle[$t] = undef;
165                     $fsel_poll_notify_mask &= ~(1 << $t);
166                 }
167             }
168
169             $idx = ($idx + 1) % FSEL_FILES;
170             if ($idx == 0) {
171                 $nr = ($nr * 2) % 7;
172             }
173         }
174
175         sleep($tv);
176     }
177 }
178
179 croak("Fuse doesn't have poll") unless Fuse::fuse_version() >= 2.8;
180
181 my $thread = threads->create(\&fsel_producer);
182
183 Fuse::main(
184     'mountpoint' => $ARGV[0],
185     'getattr'   => 'main::fsel_getattr',
186     'readdir'   => 'main::fsel_readdir',
187     'open'      => 'main::fsel_open',
188     'release'   => 'main::fsel_release',
189     'read'      => 'main::fsel_read',
190     'poll'      => 'main::fsel_poll',
191     'threaded'  => 1,
192 );
193
194 $thread->kill('KILL');
195 $thread->join();