blib/lib/IPC/Mmap/POSIX.pm | |||
---|---|---|---|
Criterion | Covered | Total | % |
statement | 65 | 92 | 70.6 |
branch | 19 | 56 | 33.9 |
condition | 3 | 9 | 33.3 |
subroutine | 16 | 18 | 88.8 |
pod | 0 | 5 | 0.0 |
total | 103 | 180 | 57.2 |
line | stmt | bran | cond | sub | pod | time | code |
---|---|---|---|---|---|---|---|
1 | #/** | ||||||
2 | # Concrete implementation of the IPC::Mmap class for OS's supporting | ||||||
3 | # a POSIX mmap(). | ||||||
4 | #
|
||||||
5 | # Permission is granted to use this software under the same terms as Perl itself. | ||||||
6 | # Refer to the Perl Artistic License | ||||||
7 | # for details. | ||||||
8 | # | ||||||
9 | # @author D. Arnold | ||||||
10 | # @since 2006-05-01 | ||||||
11 | # @self $self | ||||||
12 | # | ||||||
13 | # maintenance and modifications - Athanasios Douitsis aduitsis@cpan.org | ||||||
14 | #*/ | ||||||
15 | package IPC::Mmap::POSIX; | ||||||
16 | # | ||||||
17 | # just bootstrap in the XS code | ||||||
18 | # | ||||||
19 | 4 | 4 | 20 | use Carp; | |||
4 | 7 | ||||||
4 | 239 | ||||||
20 | 4 | 4 | 19 | use Fcntl qw(:flock :mode); | |||
4 | 5 | ||||||
4 | 1330 | ||||||
21 | 4 | 4 | 3972 | use FileHandle; | |||
4 | 86598 | ||||||
4 | 25 | ||||||
22 | 4 | 4 | 5609 | use IPC::SysV qw(IPC_PRIVATE IPC_CREAT); | |||
4 | 8178 | ||||||
4 | 751 | ||||||
23 | 4 | 4 | 3957 | use IPC::Semaphore; | |||
4 | 32625 | ||||||
4 | 152 | ||||||
24 | 4 | 4 | 36 | use IPC::Mmap; | |||
4 | 8 | ||||||
4 | 413 | ||||||
25 | 4 | 4 | 3989 | use FindBin qw($Bin $Script); | |||
4 | 5193 | ||||||
4 | 686 | ||||||
26 | 4 | 284 | use IPC::Mmap qw(MAP_ANON MAP_ANONYMOUS MAP_FILE MAP_PRIVATE MAP_SHARED | ||||
27 | 4 | 4 | 27 | PROT_READ PROT_WRITE); | |||
4 | 8 | ||||||
28 | 4 | 4 | 19 | use base qw(IPC::Mmap); | |||
4 | 5 | ||||||
4 | 550 | ||||||
29 | |||||||
30 | 4 | 4 | 21 | use strict; | |||
4 | 9 | ||||||
4 | 140 | ||||||
31 | 4 | 4 | 21 | use warnings; | |||
4 | 8 | ||||||
4 | 116 | ||||||
32 | 4 | 4 | 4882 | use Data::Dumper; | |||
4 | 45148 | ||||||
4 | 6715 | ||||||
33 | |||||||
34 | #use constant MAP_ANON => constant('MAP_ANON', 0); | ||||||
35 | #use constant MAP_ANONYMOUS => constant('MAP_ANONYMOUS', 0); | ||||||
36 | #use constant MAP_FILE => constant('MAP_FILE', 0); | ||||||
37 | #use constant MAP_PRIVATE => constant('MAP_PRIVATE', 0); | ||||||
38 | #use constant MAP_SHARED => constant('MAP_SHARED', 0); | ||||||
39 | #use constant PROT_READ => constant('PROT_READ', 0); | ||||||
40 | #use constant PROT_WRITE => constant('PROT_WRITE', 0); | ||||||
41 | |||||||
42 | our $VERSION = '0.21'; | ||||||
43 | #/** | ||||||
44 | # Constructor. mmap()'s using POSIX mmap(). | ||||||
45 | # | ||||||
46 | # @param $filename | ||||||
47 | # @param $length optional | ||||||
48 | # @param $protflags optional | ||||||
49 | # @param $mmapflags optional | ||||||
50 | # | ||||||
51 | # @return the IPC::Mmap::POSIX object on success; undef on failure | ||||||
52 | #*/ | ||||||
53 | sub new { | ||||||
54 | 8 | 8 | 0 | 52 | my ($class, $file, $length, $prot, $mmap) = @_; | ||
55 | |||||||
56 | 8 | 51 | my $fh; | ||||
57 | |||||||
58 | #the MAN_ANON case will be handled independentlY | ||||||
59 | 8 | 50 | 25 | if($mmap & MAP_ANON) { | |||
60 | #make sure we weren't given something that is not a pathname | ||||||
61 | 0 | 0 | 0 | croak 'When using anonymous mmap, only a pathname is allowed as the first argument' unless (ref($file) eq ''); | |||
62 | |||||||
63 | #if the file doesn't exist, just touch it | ||||||
64 | 0 | 0 | 0 | if(! -e $file) { | |||
65 | 0 | 0 | 0 | open(my $fd,'>',$file) or croak $!; | |||
66 | 0 | 0 | close $fd; | ||||
67 | } | ||||||
68 | 0 | 0 | 0 | if(! -r $file) { | |||
69 | 0 | 0 | croak "For anonymous mmap, you must provide an readable filename in order for the ftok(3) to return a valid unique id. Unfortunately $file doesn't seem to be readable. "; | ||||
70 | } | ||||||
71 | |||||||
72 | 0 | 0 | my $unique_id = IPC::SysV::ftok($file,1); | ||||
73 | |||||||
74 | #create a brand new semaphore | ||||||
75 | 0 | 0 | 0 | my $sem = new IPC::Semaphore($unique_id, 1, 0666|IPC_CREAT) or croak "Cannot create semaphore:$!"; | |||
76 | ####print STDERR "semaphore is ".Dumper($sem)." \n"; | ||||||
77 | |||||||
78 | #make sure its released | ||||||
79 | 0 | 0 | 0 | $sem->op(0,1,0) or croak "Cannot op(0,1,0) on sem"; | |||
80 | #@@#warn "semaphore value is ",$sem->getval(0),"\n"; | ||||||
81 | |||||||
82 | 0 | 0 | my ($mapaddr, $maxlen, $slop) = _mmap_anon($length, $prot, $mmap); | ||||
83 | 0 | 0 | 0 | croak "mmap() failed" unless defined($mapaddr); | |||
84 | 0 | 0 | my $self = { | ||||
85 | _fh => $fh, | ||||||
86 | _file => $file, | ||||||
87 | _mmap => $mmap, | ||||||
88 | _access => $prot, | ||||||
89 | _addr => $mapaddr, | ||||||
90 | _maxlen => $maxlen, | ||||||
91 | _slop => $slop, | ||||||
92 | semaphore => $sem, | ||||||
93 | }; | ||||||
94 | |||||||
95 | 0 | 0 | return bless $self, $class; | ||||
96 | } | ||||||
97 | |||||||
98 | |||||||
99 | 8 | 50 | 33 | 49 | croak 'No filename or filehandle provided.' | ||
100 | unless defined($file) || ($mmap & MAP_ANON); | ||||||
101 | |||||||
102 | 8 | 50 | 33 | 186 | croak 'No filename or filehandle provided.' | ||
33 | |||||||
103 | if defined($file) && (ref $file) && (ref $file ne 'GLOB'); | ||||||
104 | |||||||
105 | 8 | 50 | 33 | if (ref $file) { | |||
50 | |||||||
106 | 0 | 0 | $fh = $file; | ||||
107 | } | ||||||
108 | elsif (! ($mmap & MAP_ANON)) { | ||||||
109 | # | ||||||
110 | # specified a filename, we need to open (and maybe create) it | ||||||
111 | # NOTE: POSIX doesn't seem to like mmap'ing write-only files, | ||||||
112 | # so we'll cheat | ||||||
113 | # | ||||||
114 | 8 | 100 | 22 | my $flags = ($prot == PROT_READ) ? O_RDONLY : O_RDWR; | |||
115 | 8 | 100 | 222 | $flags |= O_CREAT | |||
116 | unless -e $file; | ||||||
117 | 8 | 50 | 976 | croak "Can't open $file: $!" | |||
118 | unless sysopen($fh, $file, $flags); | ||||||
119 | } | ||||||
120 | |||||||
121 | 8 | 121 | my @filestats = stat $fh; | ||||
122 | 8 | 100 | 33 | if ($filestats[7] < $length) { | |||
123 | # | ||||||
124 | # if file not big enough, expand if its writable | ||||||
125 | # else throw error | ||||||
126 | # | ||||||
127 | 2 | 50 | 7 | croak "IPC::Mmap::new(): specified file too small" | |||
128 | unless ($prot & PROT_WRITE); | ||||||
129 | # | ||||||
130 | # seek to end, then write NULs | ||||||
131 | # NOTE: we need to chunk this out!!! | ||||||
132 | # | ||||||
133 | 2 | 5 | my $tlen = $length - $filestats[7]; | ||||
134 | 2 | 12 | seek($fh, 0, 2); | ||||
135 | 2 | 170 | syswrite($fh, "\0" x $tlen); | ||||
136 | } | ||||||
137 | 8 | 1346 | my ($mapaddr, $maxlen, $slop) = _mmap($length, $prot, $mmap, $fh); | ||||
138 | 8 | 50 | 28 | croak "mmap() failed" | |||
139 | unless defined($mapaddr); | ||||||
140 | 8 | 120 | my $self = { | ||||
141 | _fh => $fh, | ||||||
142 | _file => $file, | ||||||
143 | _mmap => $mmap, | ||||||
144 | _access => $prot, | ||||||
145 | _addr => $mapaddr, | ||||||
146 | _maxlen => $maxlen, | ||||||
147 | _slop => $slop, | ||||||
148 | }; | ||||||
149 | |||||||
150 | 8 | 64 | return bless $self, $class; | ||||
151 | } | ||||||
152 | |||||||
153 | sub DESTROY { | ||||||
154 | 8 | 50 | 8 | 6353625 | if(defined($_[0]->{semaphore})) { | ||
155 | 0 | 0 | print STDERR "destroying semaphore ".Dumper($_[0]->{semaphore})."\n"; | ||||
156 | 0 | 0 | $_[0]->{semaphore}->remove; | ||||
157 | } | ||||||
158 | } | ||||||
159 | |||||||
160 | #/** | ||||||
161 | # Locks the mmap'ed region. Implemented using flock() | ||||||
162 | # on the mmap()'ed file. | ||||||
163 | #
|
||||||
164 | # NOTE: This lock is not sufficient | ||||||
165 | # for multithreaded access control, but may be sufficient for | ||||||
166 | # multiprocess access control. | ||||||
167 | #
|
||||||
168 | # Also note that, due to flock() restrictions on some | ||||||
169 | # platforms, the type of lock is determined by the protection flags | ||||||
170 | # with which the mmap'ed region was created: if only PROT_READ, | ||||||
171 | # then shared access is used; otherwise, an exclusive lock is used. | ||||||
172 | #*/ | ||||||
173 | sub lock { | ||||||
174 | 9 | 9 | 0 | 5002385 | my ($self, $offset, $len) = @_; | ||
175 | |||||||
176 | 9 | 50 | 73 | if(defined($self->{semaphore})) { | |||
177 | #acquire | ||||||
178 | 0 | 0 | 0 | $self->{semaphore}->op(0,-1,0) or croak("Cannot op(0,-1,0) on sem"); | |||
179 | #@@#warn "semaphore acquired"; | ||||||
180 | 0 | 0 | return 1; | ||||
181 | } | ||||||
182 | |||||||
183 | 9 | 24 | my $fh = $self->{_fh}; | ||||
184 | 9 | 100 | 41 | my $mmode = ($self->{_access} == PROT_READ) ? LOCK_SH : LOCK_EX; | |||
185 | 9 | 133 | return flock($fh, $mmode); | ||||
186 | } | ||||||
187 | |||||||
188 | #/** | ||||||
189 | # Unlocks the mmap'ed region. Implemented using flock() | ||||||
190 | # on the mmap()'ed file. | ||||||
191 | #*/ | ||||||
192 | sub unlock { | ||||||
193 | 7 | 7 | 0 | 25421 | my ($self, $offset, $len) = @_; | ||
194 | |||||||
195 | 7 | 50 | 105 | if(defined($self->{semaphore})) { | |||
196 | #release | ||||||
197 | 0 | 0 | 0 | $self->{semaphore}->op(0,1,0) or croak("Cannot op(0,1,0) on sem"); | |||
198 | #@@#warn "semaphore released"; | ||||||
199 | 0 | 0 | return 1; | ||||
200 | } | ||||||
201 | |||||||
202 | 7 | 39 | my $fh = $self->{_fh}; | ||||
203 | 7 | 101 | return flock($fh, LOCK_UN); | ||||
204 | } | ||||||
205 | #/** | ||||||
206 | # Unmap the mmap()ed region. | ||||||
207 | #
|
||||||
208 | # CAUTION!!! Use of this method is discouraged and | ||||||
209 | # deprecated. Unmapping from the file in one thread | ||||||
210 | # can cause segmentation in faults in other threads, | ||||||
211 | # so best practice is to just leave the mmap() in place | ||||||
212 | # and let process rundown clean things up. | ||||||
213 | # | ||||||
214 | # @deprecated | ||||||
215 | #*/ | ||||||
216 | sub close { | ||||||
217 | 0 | 0 | 0 | my $self = shift; | |||
218 | 0 | 0 | _munmap($self->{_addr}, $self->{_maxlen}) | ||||
219 | if $self->{_addr}; | ||||||
220 | 0 | 0 | CORE::close $self->{_fh} if $self->{_fh}; | ||||
221 | } | ||||||
222 | # | ||||||
223 | # unmap and close the file | ||||||
224 | # NOTE: do we need a ref count for multithreaded environments ? | ||||||
225 | # | ||||||
226 | sub oldDESTROY { | ||||||
227 | 0 | 0 | 0 | my $self = shift; | |||
228 | 0 | print STDERR "IPC::Mmap::DESTROY: addr is $self->{_addr} len $self->{_maxlen}\n"; | |||||
229 | 0 | 0 | _munmap($self->{_addr}, $self->{_maxlen}) | ||||
230 | if $self->{_addr}; | ||||||
231 | 0 | 0 | CORE::close $self->{_fh} if $self->{_fh}; | ||||
232 | } | ||||||
233 | |||||||
234 | 1; |