File Coverage

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;