| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | package CHI::Driver::SharedMem; | 
| 2 |  |  |  |  |  |  |  | 
| 3 |  |  |  |  |  |  | # There is an argument for mapping namespaces into keys and then putting | 
| 4 |  |  |  |  |  |  | # different namespaces into different shared memory areas.  I will think about | 
| 5 |  |  |  |  |  |  | # that. | 
| 6 |  |  |  |  |  |  |  | 
| 7 | 5 |  |  | 5 |  | 676191 | use warnings; | 
|  | 5 |  |  |  |  | 24 |  | 
|  | 5 |  |  |  |  | 189 |  | 
| 8 | 5 |  |  | 5 |  | 32 | use strict; | 
|  | 5 |  |  |  |  | 11 |  | 
|  | 5 |  |  |  |  | 122 |  | 
| 9 | 5 |  |  | 5 |  | 1940 | use CHI::Constants qw(CHI_Meta_Namespace); | 
|  | 5 |  |  |  |  | 1970 |  | 
|  | 5 |  |  |  |  | 275 |  | 
| 10 | 5 |  |  | 5 |  | 2882 | use Moose; | 
|  | 5 |  |  |  |  | 2440232 |  | 
|  | 5 |  |  |  |  | 34 |  | 
| 11 | 5 |  |  | 5 |  | 41234 | use IPC::SysV qw(S_IRUSR S_IWUSR IPC_CREAT); | 
|  | 5 |  |  |  |  | 2526 |  | 
|  | 5 |  |  |  |  | 504 |  | 
| 12 | 5 |  |  | 5 |  | 1026 | use IPC::SharedMem; | 
|  | 5 |  |  |  |  | 10492 |  | 
|  | 5 |  |  |  |  | 184 |  | 
| 13 | 5 |  |  | 5 |  | 545 | use JSON::MaybeXS; | 
|  | 5 |  |  |  |  | 5851 |  | 
|  | 5 |  |  |  |  | 350 |  | 
| 14 | 5 |  |  | 5 |  | 45 | use Carp; | 
|  | 5 |  |  |  |  | 22 |  | 
|  | 5 |  |  |  |  | 290 |  | 
| 15 | 5 |  |  | 5 |  | 40 | use Config; | 
|  | 5 |  |  |  |  | 10 |  | 
|  | 5 |  |  |  |  | 266 |  | 
| 16 | 5 |  |  | 5 |  | 73 | use Fcntl; | 
|  | 5 |  |  |  |  | 12 |  | 
|  | 5 |  |  |  |  | 10637 |  | 
| 17 |  |  |  |  |  |  |  | 
| 18 |  |  |  |  |  |  | extends 'CHI::Driver'; | 
| 19 |  |  |  |  |  |  |  | 
| 20 |  |  |  |  |  |  | has 'shm_key' => (is => 'ro', isa => 'Int'); | 
| 21 |  |  |  |  |  |  | has 'shm' => (is => 'ro', builder => '_build_shm', lazy => 1); | 
| 22 |  |  |  |  |  |  | has 'shm_size' => (is => 'rw', isa => 'Int', default => 8 * 1024); | 
| 23 |  |  |  |  |  |  | has 'lock' => ( | 
| 24 |  |  |  |  |  |  | is => 'ro', | 
| 25 |  |  |  |  |  |  | builder => '_build_lock', | 
| 26 |  |  |  |  |  |  | ); | 
| 27 |  |  |  |  |  |  | has 'lock_file' => (is => 'rw', isa => 'Str|Undef'); | 
| 28 |  |  |  |  |  |  | has '_data_size' => ( | 
| 29 |  |  |  |  |  |  | is => 'rw', | 
| 30 |  |  |  |  |  |  | isa => 'Int', | 
| 31 |  |  |  |  |  |  | reader => '_get_data_size', | 
| 32 |  |  |  |  |  |  | writer => '_set_data_size' | 
| 33 |  |  |  |  |  |  | ); | 
| 34 |  |  |  |  |  |  | has '_data' => ( | 
| 35 |  |  |  |  |  |  | is => 'rw', | 
| 36 |  |  |  |  |  |  | # isa => 'ArrayRef[ArrayRef]',	# For Storable, now using JSON | 
| 37 |  |  |  |  |  |  | isa => 'Str', | 
| 38 |  |  |  |  |  |  | reader => '_get_data', | 
| 39 |  |  |  |  |  |  | writer => '_set_data' | 
| 40 |  |  |  |  |  |  | ); | 
| 41 |  |  |  |  |  |  |  | 
| 42 |  |  |  |  |  |  | __PACKAGE__->meta->make_immutable(); | 
| 43 |  |  |  |  |  |  |  | 
| 44 |  |  |  |  |  |  | =head1 NAME | 
| 45 |  |  |  |  |  |  |  | 
| 46 |  |  |  |  |  |  | CHI::Driver::SharedMem - Cache data in shared memory | 
| 47 |  |  |  |  |  |  |  | 
| 48 |  |  |  |  |  |  | =head1 VERSION | 
| 49 |  |  |  |  |  |  |  | 
| 50 |  |  |  |  |  |  | Version 0.18 | 
| 51 |  |  |  |  |  |  |  | 
| 52 |  |  |  |  |  |  | =cut | 
| 53 |  |  |  |  |  |  |  | 
| 54 |  |  |  |  |  |  | our $VERSION = '0.18'; | 
| 55 |  |  |  |  |  |  |  | 
| 56 |  |  |  |  |  |  | # FIXME - get the pod documentation right so that the layout of the memory | 
| 57 |  |  |  |  |  |  | # area looks correct in the man page | 
| 58 |  |  |  |  |  |  |  | 
| 59 |  |  |  |  |  |  | =head1 SYNOPSIS | 
| 60 |  |  |  |  |  |  |  | 
| 61 |  |  |  |  |  |  | L<CHI> driver which stores data in shared memory objects for persistence | 
| 62 |  |  |  |  |  |  | over processes. | 
| 63 |  |  |  |  |  |  | Size is an optional parameter containing the size of the shared memory area, | 
| 64 |  |  |  |  |  |  | in bytes. | 
| 65 |  |  |  |  |  |  | Shmkey is a mandatory parameter containing the IPC key for the shared memory | 
| 66 |  |  |  |  |  |  | area. | 
| 67 |  |  |  |  |  |  | See L<IPC::SharedMem> for more information. | 
| 68 |  |  |  |  |  |  |  | 
| 69 |  |  |  |  |  |  | use CHI; | 
| 70 |  |  |  |  |  |  | my $cache = CHI->new( | 
| 71 |  |  |  |  |  |  | driver => 'SharedMem', | 
| 72 |  |  |  |  |  |  | max_size => 2 * 1024,	# Size of the cache | 
| 73 |  |  |  |  |  |  | shm_size => 32 * 1024,	# Size of the shared memory area | 
| 74 |  |  |  |  |  |  | shm_key => 12344321,	# Choose something unique, but the same across | 
| 75 |  |  |  |  |  |  | # all caches so that namespaces will be shared, | 
| 76 |  |  |  |  |  |  | # but we won't step on any other shm areas | 
| 77 |  |  |  |  |  |  | ); | 
| 78 |  |  |  |  |  |  | # ... | 
| 79 |  |  |  |  |  |  |  | 
| 80 |  |  |  |  |  |  | The shared memory area is stored thus: | 
| 81 |  |  |  |  |  |  |  | 
| 82 |  |  |  |  |  |  | # Number of bytes in the cache [ int ] | 
| 83 |  |  |  |  |  |  | 'cache' => { | 
| 84 |  |  |  |  |  |  | 'namespace1' => { | 
| 85 |  |  |  |  |  |  | 'key1' => 'value1', | 
| 86 |  |  |  |  |  |  | 'key2' => 'value2', | 
| 87 |  |  |  |  |  |  | # ... | 
| 88 |  |  |  |  |  |  | }, | 
| 89 |  |  |  |  |  |  | 'namespace2' => { | 
| 90 |  |  |  |  |  |  | 'key1' => 'value3', | 
| 91 |  |  |  |  |  |  | 'key3' => 'value2', | 
| 92 |  |  |  |  |  |  | # ... | 
| 93 |  |  |  |  |  |  | } | 
| 94 |  |  |  |  |  |  | # ... | 
| 95 |  |  |  |  |  |  | } | 
| 96 |  |  |  |  |  |  |  | 
| 97 |  |  |  |  |  |  | =head1 SUBROUTINES/METHODS | 
| 98 |  |  |  |  |  |  |  | 
| 99 |  |  |  |  |  |  | =head2 store | 
| 100 |  |  |  |  |  |  |  | 
| 101 |  |  |  |  |  |  | Stores an object in the cache. | 
| 102 |  |  |  |  |  |  | The data are serialized into JSON. | 
| 103 |  |  |  |  |  |  |  | 
| 104 |  |  |  |  |  |  | =cut | 
| 105 |  |  |  |  |  |  |  | 
| 106 |  |  |  |  |  |  | sub store { | 
| 107 | 548 |  |  | 548 | 1 | 329057 | my($self, $key, $value) = @_; | 
| 108 |  |  |  |  |  |  |  | 
| 109 | 548 |  |  |  |  | 1655 | $self->_lock(type => 'write'); | 
| 110 | 548 |  |  |  |  | 1136 | my $h = $self->_data(); | 
| 111 | 548 |  |  |  |  | 2378 | $h->{$self->namespace()}->{$key} = $value; | 
| 112 |  |  |  |  |  |  | # if($self->{'is_size_aware'}) { | 
| 113 |  |  |  |  |  |  | # $h->{CHI_Meta_Namespace()}->{'last_used_time'}->{$key} = time; | 
| 114 |  |  |  |  |  |  | # } | 
| 115 | 548 |  |  |  |  | 1471 | $self->_data($h); | 
| 116 | 547 |  |  |  |  | 1308 | $self->_unlock(); | 
| 117 |  |  |  |  |  |  | } | 
| 118 |  |  |  |  |  |  |  | 
| 119 |  |  |  |  |  |  | =head2 fetch | 
| 120 |  |  |  |  |  |  |  | 
| 121 |  |  |  |  |  |  | Retrieves an object from the cache | 
| 122 |  |  |  |  |  |  |  | 
| 123 |  |  |  |  |  |  | =cut | 
| 124 |  |  |  |  |  |  |  | 
| 125 |  |  |  |  |  |  | sub fetch { | 
| 126 | 1785 |  |  | 1785 | 1 | 792994 | my($self, $key) = @_; | 
| 127 |  |  |  |  |  |  |  | 
| 128 |  |  |  |  |  |  | # open(my $tulip, '>>', '/tmp/tulip'); | 
| 129 |  |  |  |  |  |  | # print $tulip __LINE__, "\n"; | 
| 130 | 1785 |  |  |  |  | 4955 | $self->_lock(type => 'read'); | 
| 131 |  |  |  |  |  |  | # print $tulip __LINE__, "\n"; | 
| 132 | 1785 |  |  |  |  | 3627 | my $rc = $self->_data()->{$self->namespace()}->{$key}; | 
| 133 |  |  |  |  |  |  | # print $tulip __LINE__, "\n"; | 
| 134 | 1785 | 100 |  |  |  | 9898 | if($self->{is_size_aware}) { | 
| 135 | 367 |  |  |  |  | 1055 | $self->_lock(type => 'write'); | 
| 136 | 367 |  |  |  |  | 755 | my $h = $self->_data(); | 
| 137 | 367 |  |  |  |  | 975 | $h->{CHI_Meta_Namespace()}->{last_used_time}->{$key} = time; | 
| 138 | 367 |  |  |  |  | 762 | $self->_data($h); | 
| 139 |  |  |  |  |  |  | } | 
| 140 | 1785 |  |  |  |  | 4730 | $self->_unlock(); | 
| 141 |  |  |  |  |  |  | # print $tulip __LINE__, "\n"; | 
| 142 |  |  |  |  |  |  | # close $tulip; | 
| 143 | 1785 |  |  |  |  | 4694 | return $rc; | 
| 144 |  |  |  |  |  |  | } | 
| 145 |  |  |  |  |  |  |  | 
| 146 |  |  |  |  |  |  | =head2 remove | 
| 147 |  |  |  |  |  |  |  | 
| 148 |  |  |  |  |  |  | Remove an object from the cache | 
| 149 |  |  |  |  |  |  |  | 
| 150 |  |  |  |  |  |  | =cut | 
| 151 |  |  |  |  |  |  |  | 
| 152 |  |  |  |  |  |  | sub remove { | 
| 153 | 132 |  |  | 132 | 1 | 154745 | my($self, $key) = @_; | 
| 154 |  |  |  |  |  |  |  | 
| 155 | 132 |  |  |  |  | 454 | $self->_lock(type => 'write'); | 
| 156 | 132 |  |  |  |  | 271 | my $h = $self->_data(); | 
| 157 | 132 |  |  |  |  | 551 | delete $h->{$self->namespace()}->{$key}; | 
| 158 | 132 |  |  |  |  | 291 | delete $h->{CHI_Meta_Namespace()}->{last_used_time}->{$key}; | 
| 159 | 132 |  |  |  |  | 393 | $self->_data($h); | 
| 160 | 132 |  |  |  |  | 323 | $self->_unlock(); | 
| 161 |  |  |  |  |  |  |  | 
| 162 |  |  |  |  |  |  | # open(my $tulip, '>>', '/tmp/tulip'); | 
| 163 |  |  |  |  |  |  | # print $tulip "remove: $key\n"; | 
| 164 |  |  |  |  |  |  | } | 
| 165 |  |  |  |  |  |  |  | 
| 166 |  |  |  |  |  |  | =head2 clear | 
| 167 |  |  |  |  |  |  |  | 
| 168 |  |  |  |  |  |  | Removes all data from the current namespace | 
| 169 |  |  |  |  |  |  |  | 
| 170 |  |  |  |  |  |  | =cut | 
| 171 |  |  |  |  |  |  |  | 
| 172 |  |  |  |  |  |  | sub clear { | 
| 173 | 109 |  |  | 109 | 1 | 64039 | my $self = shift; | 
| 174 |  |  |  |  |  |  |  | 
| 175 | 109 |  |  |  |  | 366 | $self->_lock(type => 'write'); | 
| 176 | 109 |  |  |  |  | 251 | my $h = $self->_data(); | 
| 177 | 109 |  |  |  |  | 623 | delete $h->{$self->namespace()}; | 
| 178 | 109 |  |  |  |  | 302 | $self->_data($h); | 
| 179 | 109 |  |  |  |  | 288 | $self->_unlock(); | 
| 180 |  |  |  |  |  |  |  | 
| 181 |  |  |  |  |  |  | # open(my $tulip, '>>', '/tmp/tulip'); | 
| 182 |  |  |  |  |  |  | # print $tulip "clear ", $self->namespace(), "\n"; | 
| 183 |  |  |  |  |  |  | } | 
| 184 |  |  |  |  |  |  |  | 
| 185 |  |  |  |  |  |  | =head2 get_keys | 
| 186 |  |  |  |  |  |  |  | 
| 187 |  |  |  |  |  |  | Gets a list of the keys in the current namespace | 
| 188 |  |  |  |  |  |  |  | 
| 189 |  |  |  |  |  |  | =cut | 
| 190 |  |  |  |  |  |  |  | 
| 191 |  |  |  |  |  |  | sub get_keys { | 
| 192 | 126 |  |  | 126 | 1 | 59764 | my $self = shift; | 
| 193 |  |  |  |  |  |  |  | 
| 194 | 126 |  |  |  |  | 372 | $self->_lock(type => 'read'); | 
| 195 | 126 |  |  |  |  | 267 | my $h = $self->_data(); | 
| 196 | 126 |  |  |  |  | 417 | $self->_unlock(); | 
| 197 | 126 |  |  |  |  | 175 | return(keys(%{$h->{$self->namespace()}})); | 
|  | 126 |  |  |  |  | 1634 |  | 
| 198 |  |  |  |  |  |  | } | 
| 199 |  |  |  |  |  |  |  | 
| 200 |  |  |  |  |  |  | =head2 get_namespaces | 
| 201 |  |  |  |  |  |  |  | 
| 202 |  |  |  |  |  |  | Gets a list of the namespaces in the cache | 
| 203 |  |  |  |  |  |  |  | 
| 204 |  |  |  |  |  |  | =cut | 
| 205 |  |  |  |  |  |  |  | 
| 206 |  |  |  |  |  |  | sub get_namespaces { | 
| 207 | 12 |  |  | 12 | 1 | 10643 | my $self = shift; | 
| 208 |  |  |  |  |  |  |  | 
| 209 | 12 |  |  |  |  | 49 | $self->_lock(type => 'read'); | 
| 210 | 12 |  |  |  |  | 31 | my $rc = $self->_data(); | 
| 211 | 12 |  |  |  |  | 41 | $self->_unlock(); | 
| 212 |  |  |  |  |  |  | # Needs to be sorted for RT89892 | 
| 213 | 12 |  |  |  |  | 15 | my @rc = sort keys(%{$rc}); | 
|  | 12 |  |  |  |  | 67 |  | 
| 214 | 12 |  |  |  |  | 103 | return @rc; | 
| 215 |  |  |  |  |  |  | } | 
| 216 |  |  |  |  |  |  |  | 
| 217 |  |  |  |  |  |  | =head2 default_discard_policy | 
| 218 |  |  |  |  |  |  |  | 
| 219 |  |  |  |  |  |  | Use an LRU algorithm to discard items when the cache can't add anything | 
| 220 |  |  |  |  |  |  |  | 
| 221 |  |  |  |  |  |  | =cut | 
| 222 |  |  |  |  |  |  |  | 
| 223 | 4 |  |  | 4 | 1 | 325 | sub default_discard_policy { 'lru' } | 
| 224 |  |  |  |  |  |  |  | 
| 225 |  |  |  |  |  |  | =head2 discard_policy_lru | 
| 226 |  |  |  |  |  |  |  | 
| 227 |  |  |  |  |  |  | When the Shared memory area is getting close to full, discard the least recently used objects | 
| 228 |  |  |  |  |  |  |  | 
| 229 |  |  |  |  |  |  | =cut | 
| 230 |  |  |  |  |  |  |  | 
| 231 |  |  |  |  |  |  | sub discard_policy_lru { | 
| 232 |  |  |  |  |  |  | # return;	# debugging why I get uninitialized values in the sort | 
| 233 | 14 |  |  | 14 | 1 | 912 | my $self = shift; | 
| 234 |  |  |  |  |  |  |  | 
| 235 | 14 |  |  |  |  | 49 | $self->_lock(type => 'read'); | 
| 236 | 14 |  |  |  |  | 33 | my $last_used_time = $self->_data()->{CHI_Meta_Namespace()}->{last_used_time}; | 
| 237 | 14 |  |  |  |  | 103 | $self->_unlock(); | 
| 238 |  |  |  |  |  |  | my @keys_in_lru_order = | 
| 239 | 14 |  |  |  |  | 51 | sort { $last_used_time->{$a} <=> $last_used_time->{$b} } $self->get_keys(); | 
|  | 192 |  |  |  |  | 333 |  | 
| 240 |  |  |  |  |  |  | return sub { | 
| 241 | 36 |  |  | 36 |  | 4260 | shift(@keys_in_lru_order); | 
| 242 | 14 |  |  |  |  | 118 | }; | 
| 243 |  |  |  |  |  |  | } | 
| 244 |  |  |  |  |  |  |  | 
| 245 |  |  |  |  |  |  | # Internal routines | 
| 246 |  |  |  |  |  |  |  | 
| 247 |  |  |  |  |  |  | # The area must be locked by the caller | 
| 248 |  |  |  |  |  |  | sub _build_shm { | 
| 249 | 97 |  |  | 97 |  | 224 | my $self = shift; | 
| 250 | 97 |  |  |  |  | 2755 | my $shm_size = $self->shm_size(); | 
| 251 |  |  |  |  |  |  |  | 
| 252 | 97 | 50 | 33 |  |  | 440 | if((!defined($shm_size)) || ($shm_size == 0)) { | 
| 253 |  |  |  |  |  |  | # Probably some strange condition in cleanup | 
| 254 |  |  |  |  |  |  | # croak 'Size == 0'; | 
| 255 | 0 |  |  |  |  | 0 | return; | 
| 256 |  |  |  |  |  |  | } | 
| 257 | 97 |  |  |  |  | 2709 | my $shm = IPC::SharedMem->new($self->shm_key(), $shm_size, S_IRUSR|S_IWUSR); | 
| 258 | 97 | 100 |  |  |  | 3008 | unless($shm) { | 
| 259 | 4 |  |  |  |  | 137 | $shm = IPC::SharedMem->new($self->shm_key(), $shm_size, S_IRUSR|S_IWUSR|IPC_CREAT); | 
| 260 | 4 | 50 |  |  |  | 525 | unless($shm) { | 
| 261 | 0 |  |  |  |  | 0 | croak "Couldn't create a shared memory area of $shm_size bytes with key ", | 
| 262 |  |  |  |  |  |  | $self->shm_key(), ": $!"; | 
| 263 | 0 |  |  |  |  | 0 | return; | 
| 264 |  |  |  |  |  |  | } | 
| 265 | 4 |  |  |  |  | 58 | $shm->write(pack('I', 0), 0, $Config{intsize}); | 
| 266 |  |  |  |  |  |  | } | 
| 267 | 97 |  |  |  |  | 834 | $shm->attach(); | 
| 268 | 97 |  |  |  |  | 6783 | return $shm; | 
| 269 |  |  |  |  |  |  | } | 
| 270 |  |  |  |  |  |  |  | 
| 271 |  |  |  |  |  |  | sub _build_lock { | 
| 272 | 99 |  |  | 99 |  | 520319 | return; | 
| 273 |  |  |  |  |  |  |  | 
| 274 | 0 |  |  |  |  | 0 | my $self = shift; | 
| 275 |  |  |  |  |  |  |  | 
| 276 |  |  |  |  |  |  | # open(my $fd, '<', $0) || croak("$0: $!"); | 
| 277 |  |  |  |  |  |  | # FIXME: make it unique for each object, not a singleton | 
| 278 | 0 |  |  |  |  | 0 | $self->lock_file('/tmp/' . __PACKAGE__); | 
| 279 |  |  |  |  |  |  | # open(my $tulip, '>>', '/tmp/tulip'); | 
| 280 |  |  |  |  |  |  | # print $tulip "build_lock\n", $self->lock_file(), "\n"; | 
| 281 | 0 | 0 |  |  |  | 0 | open(my $fd, '>', $self->lock_file()) || croak($self->lock_file(), ": $!"); | 
| 282 | 0 |  |  |  |  | 0 | return $fd; | 
| 283 |  |  |  |  |  |  | } | 
| 284 |  |  |  |  |  |  |  | 
| 285 |  |  |  |  |  |  | sub _lock { | 
| 286 | 3190 |  |  | 3190 |  | 4643 | return; | 
| 287 |  |  |  |  |  |  |  | 
| 288 | 0 |  |  |  |  | 0 | my ($self, %params) = @_; | 
| 289 |  |  |  |  |  |  |  | 
| 290 |  |  |  |  |  |  | # open(my $tulip, '>>', '/tmp/tulip'); | 
| 291 |  |  |  |  |  |  | # print $tulip $params{'type'}, ' lock ', $self->lock_file(), "\n"; | 
| 292 |  |  |  |  |  |  | # my $i = 0; | 
| 293 |  |  |  |  |  |  | # while((my @call_details = (caller($i++)))) { | 
| 294 |  |  |  |  |  |  | # print $tulip "\t", $call_details[1], ':', $call_details[2], ' in function ', $call_details[3], "\n"; | 
| 295 |  |  |  |  |  |  | # } | 
| 296 | 0 | 0 |  |  |  | 0 | return unless $self->lock_file(); | 
| 297 |  |  |  |  |  |  |  | 
| 298 | 0 | 0 |  |  |  | 0 | if(my $lock = $self->lock()) { | 
| 299 | 0 | 0 |  |  |  | 0 | flock($lock, ($params{type} eq 'read') ? Fcntl::LOCK_SH : Fcntl::LOCK_EX); | 
| 300 |  |  |  |  |  |  | } else { | 
| 301 |  |  |  |  |  |  | # print $tulip 'lost lock ', $self->lock_file(), "\n"; | 
| 302 | 0 |  |  |  |  | 0 | croak('Lost lock: ', $self->lock_file()); | 
| 303 |  |  |  |  |  |  | } | 
| 304 |  |  |  |  |  |  | } | 
| 305 |  |  |  |  |  |  |  | 
| 306 |  |  |  |  |  |  | sub _unlock { | 
| 307 | 2822 |  |  | 2822 |  | 10224 | return; | 
| 308 |  |  |  |  |  |  |  | 
| 309 | 0 |  |  |  |  | 0 | my $self = shift; | 
| 310 |  |  |  |  |  |  |  | 
| 311 |  |  |  |  |  |  | # open(my $tulip, '>>', '/tmp/tulip'); | 
| 312 |  |  |  |  |  |  | # print $tulip 'unlock ', $self->lock_file(), "\n"; | 
| 313 | 0 | 0 |  |  |  | 0 | if(my $lock = $self->lock()) { | 
| 314 | 0 |  |  |  |  | 0 | flock($lock, Fcntl::LOCK_UN); | 
| 315 |  |  |  |  |  |  | } else { | 
| 316 |  |  |  |  |  |  | # print $tulip 'lost lock for unlock ', $self->lock_file(), "\n"; | 
| 317 | 0 |  |  |  |  | 0 | croak('Lost lock for unlock: ', $self->lock_file()); | 
| 318 |  |  |  |  |  |  | } | 
| 319 |  |  |  |  |  |  | } | 
| 320 |  |  |  |  |  |  |  | 
| 321 |  |  |  |  |  |  | # The area must be locked by the caller | 
| 322 |  |  |  |  |  |  | sub _data_size { | 
| 323 | 4345 |  |  | 4345 |  | 7036 | my($self, $value) = @_; | 
| 324 |  |  |  |  |  |  |  | 
| 325 | 4345 | 100 |  |  |  | 8501 | if(defined($value)) { | 
| 326 | 1155 |  |  |  |  | 32355 | $self->shm()->write(pack('I', $value), 0, $Config{intsize}); | 
| 327 | 1155 |  |  |  |  | 19043 | return $value; | 
| 328 |  |  |  |  |  |  | } | 
| 329 | 3190 | 50 |  |  |  | 98468 | unless($self->shm()) { | 
| 330 | 0 |  |  |  |  | 0 | return 0; | 
| 331 |  |  |  |  |  |  | } | 
| 332 | 3190 |  |  |  |  | 83140 | my $size = $self->shm()->read(0, $Config{intsize}); | 
| 333 | 3190 | 50 |  |  |  | 71671 | unless(defined($size)) { | 
| 334 | 0 |  |  |  |  | 0 | return 0; | 
| 335 |  |  |  |  |  |  | } | 
| 336 | 3190 |  |  |  |  | 9114 | return unpack('I', $size); | 
| 337 |  |  |  |  |  |  | } | 
| 338 |  |  |  |  |  |  |  | 
| 339 |  |  |  |  |  |  | # The area must be locked by the caller | 
| 340 |  |  |  |  |  |  | sub _data { | 
| 341 | 4249 |  |  | 4249 |  | 8062 | my($self, $h) = @_; | 
| 342 |  |  |  |  |  |  |  | 
| 343 |  |  |  |  |  |  | # open(my $tulip, '>>', '/tmp/tulip'); | 
| 344 |  |  |  |  |  |  | # print $tulip __LINE__, "\n"; | 
| 345 | 4249 | 100 |  |  |  | 9234 | if(defined($h)) { | 
| 346 | 1156 |  |  |  |  | 3307 | my $f = JSON::MaybeXS->new()->ascii()->encode($h); | 
| 347 | 1156 |  |  |  |  | 55466 | my $cur_size = length($f); | 
| 348 |  |  |  |  |  |  | # print $tulip __LINE__, "cmp $cur_size > ", $self->size(), "\n"; | 
| 349 | 1156 | 100 |  |  |  | 38434 | if($cur_size > ($self->shm_size() - $Config{intsize})) { | 
| 350 | 1 |  |  |  |  | 33 | croak("sharedmem set failed - value too large? ($cur_size bytes) > ", $self->shm_size()); | 
| 351 |  |  |  |  |  |  | } | 
| 352 | 1155 |  |  |  |  | 31934 | $self->shm()->write($f, $Config{intsize}, $cur_size); | 
| 353 | 1155 |  |  |  |  | 23686 | $self->_data_size($cur_size); | 
| 354 |  |  |  |  |  |  | # print $tulip "set: $cur_size bytes\n"; | 
| 355 |  |  |  |  |  |  | # close $tulip; | 
| 356 | 1155 |  |  |  |  | 4257 | return $h; | 
| 357 |  |  |  |  |  |  | } | 
| 358 | 3093 |  |  |  |  | 6343 | my $cur_size = $self->_data_size(); | 
| 359 |  |  |  |  |  |  | # print $tulip "get: $cur_size bytes\n"; | 
| 360 |  |  |  |  |  |  | # close $tulip; | 
| 361 | 3093 | 100 |  |  |  | 6599 | if($cur_size) { | 
| 362 | 3089 |  |  |  |  | 4742 | my $rc; | 
| 363 | 3089 |  |  |  |  | 4696 | eval { | 
| 364 | 3089 |  |  |  |  | 10586 | $rc = JSON::MaybeXS->new()->ascii()->decode($self->shm()->read($Config{intsize}, $cur_size)); | 
| 365 |  |  |  |  |  |  | }; | 
| 366 | 3089 | 50 |  |  |  | 203990 | if($@) { | 
| 367 | 0 |  |  |  |  | 0 | $self->_data_size(0); | 
| 368 | 0 |  |  |  |  | 0 | croak($@); | 
| 369 |  |  |  |  |  |  | } | 
| 370 | 3089 |  |  |  |  | 11075 | return $rc; | 
| 371 |  |  |  |  |  |  | # return JSON::MaybeXS->new()->ascii()->decode($self->shm()->read($Config{intsize}, $cur_size)); | 
| 372 |  |  |  |  |  |  | } | 
| 373 | 4 |  |  |  |  | 23 | return {}; | 
| 374 |  |  |  |  |  |  | } | 
| 375 |  |  |  |  |  |  |  | 
| 376 |  |  |  |  |  |  | =head2 BUILD | 
| 377 |  |  |  |  |  |  |  | 
| 378 |  |  |  |  |  |  | Constructor - validate arguments | 
| 379 |  |  |  |  |  |  |  | 
| 380 |  |  |  |  |  |  | =cut | 
| 381 |  |  |  |  |  |  |  | 
| 382 |  |  |  |  |  |  | sub BUILD { | 
| 383 | 99 |  |  | 99 | 1 | 523547 | my $self = shift; | 
| 384 |  |  |  |  |  |  |  | 
| 385 | 99 | 100 |  |  |  | 3179 | unless($self->shm_key()) { | 
| 386 | 2 |  |  |  |  | 39 | croak 'CHI::Driver::SharedMem - no key given'; | 
| 387 |  |  |  |  |  |  | } | 
| 388 | 97 |  |  |  |  | 673 | $| = 1; | 
| 389 |  |  |  |  |  |  | } | 
| 390 |  |  |  |  |  |  |  | 
| 391 |  |  |  |  |  |  | =head2 DEMOLISH | 
| 392 |  |  |  |  |  |  |  | 
| 393 |  |  |  |  |  |  | If there is no data in the shared memory area, and no-one else is using it, | 
| 394 |  |  |  |  |  |  | it's safe to remove it and reclaim the memory. | 
| 395 |  |  |  |  |  |  |  | 
| 396 |  |  |  |  |  |  | =cut | 
| 397 |  |  |  |  |  |  |  | 
| 398 |  |  |  |  |  |  | sub DEMOLISH { | 
| 399 |  |  |  |  |  |  | # if(defined($^V) && ($^V ge 'v5.14.0')) { | 
| 400 |  |  |  |  |  |  | # return if ${^GLOBAL_PHASE} eq 'DESTRUCT';	# >= 5.14.0 only | 
| 401 |  |  |  |  |  |  | # } | 
| 402 | 99 |  |  | 99 | 1 | 105301 | my $self = shift; | 
| 403 |  |  |  |  |  |  |  | 
| 404 |  |  |  |  |  |  | # open(my $tulip, '>>', '/tmp/tulip'); | 
| 405 |  |  |  |  |  |  | # print $tulip "DEMOLISH\n"; | 
| 406 | 99 | 100 | 66 |  |  | 2908 | if($self->shm_key() && $self->shm()) { | 
| 407 | 97 |  |  |  |  | 163 | my $cur_size; | 
| 408 | 97 |  |  |  |  | 281 | $self->_lock(type => 'write'); | 
| 409 | 97 |  |  |  |  | 256 | $cur_size = $self->_data_size(); | 
| 410 |  |  |  |  |  |  | # print $tulip "DEMOLISH: $cur_size bytes\n"; | 
| 411 | 97 |  |  |  |  | 194 | my $can_remove = 0; | 
| 412 | 97 |  |  |  |  | 2786 | my $stat = $self->shm()->stat(); | 
| 413 | 97 | 100 |  |  |  | 13777 | if($cur_size == 0) { | 
| 414 | 2 | 50 | 33 |  |  | 42 | if(defined($stat) && ($stat->nattch() == 1)) { | 
| 415 | 2 |  |  |  |  | 115 | $self->shm()->detach(); | 
| 416 | 2 |  |  |  |  | 140 | $self->shm()->remove(); | 
| 417 | 2 |  |  |  |  | 172 | $can_remove = 1; | 
| 418 |  |  |  |  |  |  | } | 
| 419 |  |  |  |  |  |  | # } elsif(defined($stat) && ($stat->nattch() == 1)) { | 
| 420 |  |  |  |  |  |  | # # Scan the cache and see if all has expired. | 
| 421 |  |  |  |  |  |  | # # If it has, then the cache can be removed if nattch = 1 | 
| 422 |  |  |  |  |  |  | # $can_remove = 1; | 
| 423 |  |  |  |  |  |  | # foreach my $namespace($self->get_namespaces()) { | 
| 424 |  |  |  |  |  |  | # print $tulip "DEMOLISH: namespace = $namespace\n"; | 
| 425 |  |  |  |  |  |  | # foreach my $key($self->get_keys($namespace)) { | 
| 426 |  |  |  |  |  |  | # # May give substr error in CHI | 
| 427 |  |  |  |  |  |  | # print $tulip "DEMOLISH: key = $key\n"; | 
| 428 |  |  |  |  |  |  | # if($self->is_valid($key)) { | 
| 429 |  |  |  |  |  |  | # print $tulip "DEMOLISH: is_valid\n"; | 
| 430 |  |  |  |  |  |  | # $can_remove = 0; | 
| 431 |  |  |  |  |  |  | # last; | 
| 432 |  |  |  |  |  |  | # } | 
| 433 |  |  |  |  |  |  | # } | 
| 434 |  |  |  |  |  |  | # } | 
| 435 |  |  |  |  |  |  | # $self->shm()->detach(); | 
| 436 |  |  |  |  |  |  | # if($can_remove) { | 
| 437 |  |  |  |  |  |  | # $self->shm()->remove(); | 
| 438 |  |  |  |  |  |  | # } | 
| 439 |  |  |  |  |  |  | } else { | 
| 440 | 95 |  |  |  |  | 2982 | $self->shm()->detach(); | 
| 441 |  |  |  |  |  |  | } | 
| 442 | 97 |  |  |  |  | 3939 | $self->_unlock(); | 
| 443 | 97 | 50 | 66 |  |  | 2473 | if($can_remove && (my $lock_file = $self->lock_file())) { | 
| 444 | 0 |  |  |  |  |  | $self->lock_file(undef); | 
| 445 | 0 |  |  |  |  |  | close $self->lock(); | 
| 446 | 0 |  |  |  |  |  | unlink $lock_file; | 
| 447 |  |  |  |  |  |  | # print $tulip "unlink $lock_file\n"; | 
| 448 |  |  |  |  |  |  | # close $tulip; | 
| 449 |  |  |  |  |  |  | } | 
| 450 |  |  |  |  |  |  | } | 
| 451 |  |  |  |  |  |  | } | 
| 452 |  |  |  |  |  |  |  | 
| 453 |  |  |  |  |  |  | =head1 AUTHOR | 
| 454 |  |  |  |  |  |  |  | 
| 455 |  |  |  |  |  |  | Nigel Horne, C<< <njh at bandsman.co.uk> >> | 
| 456 |  |  |  |  |  |  |  | 
| 457 |  |  |  |  |  |  | =head1 BUGS | 
| 458 |  |  |  |  |  |  |  | 
| 459 |  |  |  |  |  |  | Please report any bugs or feature requests to C<bug-chi-driver-sharedmem at rt.cpan.org>, or through | 
| 460 |  |  |  |  |  |  | the web interface at L<http://rt.cpan.org/NoAuth/ReportBug.html?Queue=CHI-Driver-SharedMem>.  I will be notified, and then you'll | 
| 461 |  |  |  |  |  |  | automatically be notified of progress on your bug as I make changes. | 
| 462 |  |  |  |  |  |  |  | 
| 463 |  |  |  |  |  |  | Max_size is handled, but if you're not consistent across the calls to each cache, | 
| 464 |  |  |  |  |  |  | the results are unpredictable because it's used to create the size of the shared memory | 
| 465 |  |  |  |  |  |  | area. | 
| 466 |  |  |  |  |  |  |  | 
| 467 |  |  |  |  |  |  | The shm_size argument should be deprecated and only the max_size argument used. | 
| 468 |  |  |  |  |  |  |  | 
| 469 |  |  |  |  |  |  | =head1 SEE ALSO | 
| 470 |  |  |  |  |  |  |  | 
| 471 |  |  |  |  |  |  | L<CHI>, L<IPC::SharedMem> | 
| 472 |  |  |  |  |  |  |  | 
| 473 |  |  |  |  |  |  | =cut | 
| 474 |  |  |  |  |  |  |  | 
| 475 |  |  |  |  |  |  | =head1 SUPPORT | 
| 476 |  |  |  |  |  |  |  | 
| 477 |  |  |  |  |  |  | You can find documentation for this module with the perldoc command. | 
| 478 |  |  |  |  |  |  |  | 
| 479 |  |  |  |  |  |  | perldoc CHI::Driver::SharedMemory | 
| 480 |  |  |  |  |  |  |  | 
| 481 |  |  |  |  |  |  | You can also look for information at: | 
| 482 |  |  |  |  |  |  |  | 
| 483 |  |  |  |  |  |  | =over 4 | 
| 484 |  |  |  |  |  |  |  | 
| 485 |  |  |  |  |  |  | =item * MetaCPAN | 
| 486 |  |  |  |  |  |  |  | 
| 487 |  |  |  |  |  |  | L<https://metacpan.org/dist/CHI-Driver-SharedMem> | 
| 488 |  |  |  |  |  |  |  | 
| 489 |  |  |  |  |  |  | =item * RT: CPAN's request tracker | 
| 490 |  |  |  |  |  |  |  | 
| 491 |  |  |  |  |  |  | L<https://rt.cpan.org/NoAuth/Bugs.html?Dist=CHI-Driver-SharedMemory> | 
| 492 |  |  |  |  |  |  |  | 
| 493 |  |  |  |  |  |  | =item * CPAN Testers' Matrix | 
| 494 |  |  |  |  |  |  |  | 
| 495 |  |  |  |  |  |  | L<http://matrix.cpantesters.org/?dist=CHI-Driver-SharedMemory> | 
| 496 |  |  |  |  |  |  |  | 
| 497 |  |  |  |  |  |  | =item * CPAN Testers Dependencies | 
| 498 |  |  |  |  |  |  |  | 
| 499 |  |  |  |  |  |  | L<http://deps.cpantesters.org/?module=CHI::Driver::SharedMemory> | 
| 500 |  |  |  |  |  |  |  | 
| 501 |  |  |  |  |  |  | =back | 
| 502 |  |  |  |  |  |  |  | 
| 503 |  |  |  |  |  |  | =head1 LICENSE AND COPYRIGHT | 
| 504 |  |  |  |  |  |  |  | 
| 505 |  |  |  |  |  |  | Copyright 2010-2023 Nigel Horne. | 
| 506 |  |  |  |  |  |  |  | 
| 507 |  |  |  |  |  |  | This program is released under the following licence: GPL2 | 
| 508 |  |  |  |  |  |  |  | 
| 509 |  |  |  |  |  |  | =cut | 
| 510 |  |  |  |  |  |  |  | 
| 511 |  |  |  |  |  |  | 1; |