| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
package IPC::Shareable; |
|
2
|
|
|
|
|
|
|
|
|
3
|
78
|
|
|
78
|
|
1621793
|
use warnings; |
|
|
78
|
|
|
|
|
156
|
|
|
|
78
|
|
|
|
|
4130
|
|
|
4
|
78
|
|
|
78
|
|
716
|
use strict; |
|
|
78
|
|
|
|
|
129
|
|
|
|
78
|
|
|
|
|
2714
|
|
|
5
|
|
|
|
|
|
|
|
|
6
|
|
|
|
|
|
|
require 5.00503; |
|
7
|
|
|
|
|
|
|
|
|
8
|
78
|
|
|
78
|
|
350
|
use Carp qw(croak confess carp); |
|
|
78
|
|
|
|
|
126
|
|
|
|
78
|
|
|
|
|
5328
|
|
|
9
|
78
|
|
|
78
|
|
427
|
use Config; |
|
|
78
|
|
|
|
|
155
|
|
|
|
78
|
|
|
|
|
4151
|
|
|
10
|
78
|
|
|
78
|
|
33585
|
use Data::Dumper; |
|
|
78
|
|
|
|
|
485967
|
|
|
|
78
|
|
|
|
|
5601
|
|
|
11
|
78
|
|
|
78
|
|
562
|
use Digest::MD5 qw(md5_hex); |
|
|
78
|
|
|
|
|
160
|
|
|
|
78
|
|
|
|
|
4659
|
|
|
12
|
78
|
|
|
78
|
|
34864
|
use IPC::Semaphore; |
|
|
78
|
|
|
|
|
492624
|
|
|
|
78
|
|
|
|
|
3002
|
|
|
13
|
78
|
|
|
78
|
|
40449
|
use IPC::Shareable::SharedMem; |
|
|
78
|
|
|
|
|
324
|
|
|
|
78
|
|
|
|
|
3566
|
|
|
14
|
78
|
|
|
|
|
6146
|
use IPC::SysV qw( |
|
15
|
|
|
|
|
|
|
IPC_PRIVATE |
|
16
|
|
|
|
|
|
|
IPC_CREAT |
|
17
|
|
|
|
|
|
|
IPC_EXCL |
|
18
|
|
|
|
|
|
|
IPC_NOWAIT |
|
19
|
|
|
|
|
|
|
IPC_RMID |
|
20
|
|
|
|
|
|
|
IPC_STAT |
|
21
|
|
|
|
|
|
|
SEM_UNDO |
|
22
|
78
|
|
|
78
|
|
562
|
); |
|
|
78
|
|
|
|
|
169
|
|
|
23
|
78
|
|
|
78
|
|
48280
|
use JSON qw(-convert_blessed_universally); |
|
|
78
|
|
|
|
|
991168
|
|
|
|
78
|
|
|
|
|
458
|
|
|
24
|
78
|
|
|
78
|
|
31464
|
use Scalar::Util; |
|
|
78
|
|
|
|
|
161
|
|
|
|
78
|
|
|
|
|
4523
|
|
|
25
|
78
|
|
|
78
|
|
29823
|
use String::CRC32; |
|
|
78
|
|
|
|
|
38062
|
|
|
|
78
|
|
|
|
|
5852
|
|
|
26
|
78
|
|
|
78
|
|
40690
|
use Storable 0.6 qw(freeze thaw); |
|
|
78
|
|
|
|
|
286042
|
|
|
|
78
|
|
|
|
|
9957
|
|
|
27
|
|
|
|
|
|
|
|
|
28
|
|
|
|
|
|
|
our $VERSION = '1.14_07'; |
|
29
|
|
|
|
|
|
|
|
|
30
|
|
|
|
|
|
|
use constant { |
|
31
|
|
|
|
|
|
|
# Locking |
|
32
|
|
|
|
|
|
|
|
|
33
|
78
|
|
|
|
|
987686
|
LOCK_SH => 1, |
|
34
|
|
|
|
|
|
|
LOCK_EX => 2, |
|
35
|
|
|
|
|
|
|
LOCK_NB => 4, |
|
36
|
|
|
|
|
|
|
LOCK_UN => 8, |
|
37
|
|
|
|
|
|
|
|
|
38
|
|
|
|
|
|
|
# SHM parameters |
|
39
|
|
|
|
|
|
|
|
|
40
|
|
|
|
|
|
|
SHM_BUFSIZ => 65536, |
|
41
|
|
|
|
|
|
|
SHMMAX_BYTES => 1073741824, # ~1 GB |
|
42
|
|
|
|
|
|
|
SHM_EXISTS => 1, |
|
43
|
|
|
|
|
|
|
|
|
44
|
|
|
|
|
|
|
# Semaphore slots |
|
45
|
|
|
|
|
|
|
|
|
46
|
|
|
|
|
|
|
SEM_MARKER => 0, |
|
47
|
|
|
|
|
|
|
SEM_READERS => 1, |
|
48
|
|
|
|
|
|
|
SEM_WRITERS => 2, |
|
49
|
|
|
|
|
|
|
SEM_PROTECTED => 3, |
|
50
|
|
|
|
|
|
|
|
|
51
|
|
|
|
|
|
|
# Perl sends in a double as opposed to an integer to shmat(), and on some |
|
52
|
|
|
|
|
|
|
# systems, this causes the IPC system to round down to the maximum integer |
|
53
|
|
|
|
|
|
|
# size of 0x80000000. We correct that when generating keys with CRC32. |
|
54
|
|
|
|
|
|
|
|
|
55
|
|
|
|
|
|
|
MAX_KEY_INT_SIZE => 0x80000000, |
|
56
|
|
|
|
|
|
|
|
|
57
|
|
|
|
|
|
|
# Number of times we'll check for existing segs |
|
58
|
|
|
|
|
|
|
|
|
59
|
|
|
|
|
|
|
EXCLUSIVE_CHECK_LIMIT => 10, |
|
60
|
|
|
|
|
|
|
|
|
61
|
|
|
|
|
|
|
# Struct types |
|
62
|
|
|
|
|
|
|
|
|
63
|
|
|
|
|
|
|
TYPE_HASH => 0, |
|
64
|
|
|
|
|
|
|
TYPE_ARRAY => 1, |
|
65
|
|
|
|
|
|
|
TYPE_SCALAR => 2, |
|
66
|
78
|
|
|
78
|
|
630
|
}; |
|
|
78
|
|
|
|
|
166
|
|
|
67
|
|
|
|
|
|
|
|
|
68
|
|
|
|
|
|
|
require Exporter; |
|
69
|
|
|
|
|
|
|
our @ISA = 'Exporter'; |
|
70
|
|
|
|
|
|
|
our @EXPORT_OK = qw( |
|
71
|
|
|
|
|
|
|
LOCK_EX |
|
72
|
|
|
|
|
|
|
LOCK_SH |
|
73
|
|
|
|
|
|
|
LOCK_NB |
|
74
|
|
|
|
|
|
|
LOCK_UN |
|
75
|
|
|
|
|
|
|
SEM_MARKER |
|
76
|
|
|
|
|
|
|
SEM_READERS |
|
77
|
|
|
|
|
|
|
SEM_WRITERS |
|
78
|
|
|
|
|
|
|
SEM_PROTECTED |
|
79
|
|
|
|
|
|
|
); |
|
80
|
|
|
|
|
|
|
our %EXPORT_TAGS = ( |
|
81
|
|
|
|
|
|
|
all => [qw( LOCK_EX LOCK_SH LOCK_NB LOCK_UN )], |
|
82
|
|
|
|
|
|
|
lock => [qw( LOCK_EX LOCK_SH LOCK_NB LOCK_UN )], |
|
83
|
|
|
|
|
|
|
flock => [qw( LOCK_EX LOCK_SH LOCK_NB LOCK_UN )], |
|
84
|
|
|
|
|
|
|
); |
|
85
|
|
|
|
|
|
|
Exporter::export_ok_tags('all', 'lock', 'flock'); |
|
86
|
|
|
|
|
|
|
|
|
87
|
|
|
|
|
|
|
# Locking scheme copied from IPC::ShareLite (with minor modifications) |
|
88
|
|
|
|
|
|
|
|
|
89
|
|
|
|
|
|
|
my %semop_args = ( |
|
90
|
|
|
|
|
|
|
(LOCK_EX), |
|
91
|
|
|
|
|
|
|
[ |
|
92
|
|
|
|
|
|
|
SEM_READERS, 0, 0, # Wait for readers to finish |
|
93
|
|
|
|
|
|
|
SEM_WRITERS, 0, 0, # Wait for writers to finish |
|
94
|
|
|
|
|
|
|
SEM_WRITERS, 1, SEM_UNDO, # Assert write lock |
|
95
|
|
|
|
|
|
|
], |
|
96
|
|
|
|
|
|
|
(LOCK_EX|LOCK_NB), |
|
97
|
|
|
|
|
|
|
[ |
|
98
|
|
|
|
|
|
|
SEM_READERS, 0, IPC_NOWAIT, # Wait for readers to finish |
|
99
|
|
|
|
|
|
|
SEM_WRITERS, 0, IPC_NOWAIT, # Wait for writers to finish |
|
100
|
|
|
|
|
|
|
SEM_WRITERS, 1, (SEM_UNDO | IPC_NOWAIT), # Assert write lock |
|
101
|
|
|
|
|
|
|
], |
|
102
|
|
|
|
|
|
|
(LOCK_EX|LOCK_UN), |
|
103
|
|
|
|
|
|
|
[ |
|
104
|
|
|
|
|
|
|
SEM_WRITERS, -1, (SEM_UNDO | IPC_NOWAIT), |
|
105
|
|
|
|
|
|
|
], |
|
106
|
|
|
|
|
|
|
(LOCK_SH), |
|
107
|
|
|
|
|
|
|
[ |
|
108
|
|
|
|
|
|
|
SEM_WRITERS, 0, 0, # Wait for writers to finish |
|
109
|
|
|
|
|
|
|
SEM_READERS, 1, SEM_UNDO, # Assert shared read lock |
|
110
|
|
|
|
|
|
|
], |
|
111
|
|
|
|
|
|
|
(LOCK_SH|LOCK_NB), |
|
112
|
|
|
|
|
|
|
[ |
|
113
|
|
|
|
|
|
|
SEM_WRITERS, 0, IPC_NOWAIT, # Wait for writers to finish |
|
114
|
|
|
|
|
|
|
SEM_READERS, 1, (SEM_UNDO | IPC_NOWAIT), # Assert shared read lock |
|
115
|
|
|
|
|
|
|
], |
|
116
|
|
|
|
|
|
|
(LOCK_SH|LOCK_UN), |
|
117
|
|
|
|
|
|
|
[ |
|
118
|
|
|
|
|
|
|
SEM_READERS, -1, (SEM_UNDO | IPC_NOWAIT), # Remove shared read lock |
|
119
|
|
|
|
|
|
|
], |
|
120
|
|
|
|
|
|
|
); |
|
121
|
|
|
|
|
|
|
|
|
122
|
|
|
|
|
|
|
my %default_options = ( |
|
123
|
|
|
|
|
|
|
key => IPC_PRIVATE, |
|
124
|
|
|
|
|
|
|
create => 0, |
|
125
|
|
|
|
|
|
|
exclusive => 0, |
|
126
|
|
|
|
|
|
|
destroy => 0, |
|
127
|
|
|
|
|
|
|
mode => 0666, |
|
128
|
|
|
|
|
|
|
size => SHM_BUFSIZ, |
|
129
|
|
|
|
|
|
|
protected => 0, |
|
130
|
|
|
|
|
|
|
limit => 1, |
|
131
|
|
|
|
|
|
|
graceful => 0, |
|
132
|
|
|
|
|
|
|
warn => 0, |
|
133
|
|
|
|
|
|
|
tidy => 1, |
|
134
|
|
|
|
|
|
|
serializer => 'json', |
|
135
|
|
|
|
|
|
|
enforced_write_locking => 1, |
|
136
|
|
|
|
|
|
|
enforced_read_locking => 1, |
|
137
|
|
|
|
|
|
|
violated_write_lock_warn => 1, |
|
138
|
|
|
|
|
|
|
violated_read_lock_warn => 1, |
|
139
|
|
|
|
|
|
|
); |
|
140
|
|
|
|
|
|
|
|
|
141
|
|
|
|
|
|
|
# Seed the random number generator |
|
142
|
|
|
|
|
|
|
|
|
143
|
|
|
|
|
|
|
srand(); |
|
144
|
|
|
|
|
|
|
|
|
145
|
|
|
|
|
|
|
# Class-level variables |
|
146
|
|
|
|
|
|
|
|
|
147
|
|
|
|
|
|
|
my %global_register; |
|
148
|
|
|
|
|
|
|
my %process_register; |
|
149
|
|
|
|
|
|
|
my %used_ids; |
|
150
|
|
|
|
|
|
|
|
|
151
|
|
|
|
|
|
|
# "Magic" methods |
|
152
|
|
|
|
|
|
|
|
|
153
|
|
|
|
|
|
|
sub TIESCALAR { |
|
154
|
134
|
|
|
134
|
|
30264432
|
return _tie('SCALAR', @_); |
|
155
|
|
|
|
|
|
|
} |
|
156
|
|
|
|
|
|
|
sub TIEARRAY { |
|
157
|
98
|
|
|
98
|
|
22086
|
return _tie('ARRAY', @_); |
|
158
|
|
|
|
|
|
|
} |
|
159
|
|
|
|
|
|
|
sub TIEHASH { |
|
160
|
267
|
|
|
267
|
|
403084
|
return _tie('HASH', @_); |
|
161
|
|
|
|
|
|
|
} |
|
162
|
|
|
|
|
|
|
sub STORE { |
|
163
|
723
|
|
|
723
|
|
256679
|
my $knot = shift; |
|
164
|
|
|
|
|
|
|
|
|
165
|
723
|
100
|
|
|
|
10690
|
return if ! _write_permitted($knot); |
|
166
|
|
|
|
|
|
|
|
|
167
|
716
|
100
|
|
|
|
2545
|
$knot->{_data} = $knot->_decode($knot->seg) unless ($knot->{_lock}); |
|
168
|
|
|
|
|
|
|
|
|
169
|
716
|
100
|
|
|
|
2300
|
if ($knot->{_type_int} == TYPE_HASH) { |
|
|
|
100
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
170
|
454
|
|
|
|
|
1405
|
my ($key, $val) = @_; |
|
171
|
|
|
|
|
|
|
# If $val is a reference, we need to create a new segment |
|
172
|
454
|
100
|
66
|
|
|
2274
|
_magic_tie($knot, $val, $key) if ref($val) && $knot->_need_tie($val, $key); |
|
173
|
453
|
|
|
|
|
2130
|
$knot->{_data}{$key} = $val; |
|
174
|
|
|
|
|
|
|
} |
|
175
|
|
|
|
|
|
|
elsif ($knot->{_type_int} == TYPE_ARRAY) { |
|
176
|
174
|
|
|
|
|
391
|
my ($i, $val) = @_; |
|
177
|
174
|
100
|
66
|
|
|
653
|
_magic_tie($knot, $val, $i) if ref($val) && $knot->_need_tie($val, $i); |
|
178
|
174
|
|
|
|
|
458
|
$knot->{_data}[$i] = $val; |
|
179
|
|
|
|
|
|
|
} |
|
180
|
|
|
|
|
|
|
elsif ($knot->{_type_int} == TYPE_SCALAR) { |
|
181
|
88
|
|
|
|
|
283
|
my ($val) = @_; |
|
182
|
88
|
100
|
100
|
|
|
346
|
_magic_tie($knot, $val) if ref($val) && $knot->_need_tie($val); |
|
183
|
88
|
|
|
|
|
304
|
$knot->{_data} = \$val; |
|
184
|
|
|
|
|
|
|
} |
|
185
|
|
|
|
|
|
|
|
|
186
|
715
|
100
|
|
|
|
2308
|
if ($knot->{_lock} & LOCK_EX) { |
|
187
|
32
|
|
|
|
|
155
|
$knot->{_was_changed} = 1; |
|
188
|
|
|
|
|
|
|
} |
|
189
|
|
|
|
|
|
|
else { |
|
190
|
683
|
100
|
|
|
|
1824
|
if (! defined $knot->_encode($knot->seg, $knot->{_data})){ |
|
191
|
1
|
|
|
|
|
448
|
croak "Could not write to shared memory: $!\n"; |
|
192
|
|
|
|
|
|
|
} |
|
193
|
|
|
|
|
|
|
} |
|
194
|
|
|
|
|
|
|
|
|
195
|
712
|
|
|
|
|
4719
|
return 1; |
|
196
|
|
|
|
|
|
|
} |
|
197
|
|
|
|
|
|
|
sub FETCH { |
|
198
|
975
|
|
|
975
|
|
15457947
|
my $knot = shift; |
|
199
|
|
|
|
|
|
|
|
|
200
|
975
|
|
|
|
|
1591
|
my $data; |
|
201
|
975
|
100
|
|
|
|
2684
|
if ($knot->{_lock}) { |
|
202
|
28
|
|
|
|
|
87
|
$data = $knot->{_data}; |
|
203
|
|
|
|
|
|
|
} |
|
204
|
|
|
|
|
|
|
else { |
|
205
|
947
|
|
|
|
|
2833
|
_read_check($knot); |
|
206
|
947
|
|
|
|
|
2526
|
$data = $knot->_decode($knot->seg); |
|
207
|
945
|
|
|
|
|
5514
|
$knot->{_data} = $data; |
|
208
|
|
|
|
|
|
|
} |
|
209
|
|
|
|
|
|
|
|
|
210
|
973
|
|
|
|
|
1896
|
my $val; |
|
211
|
|
|
|
|
|
|
|
|
212
|
973
|
100
|
|
|
|
2641
|
if ($knot->{_type_int} == TYPE_HASH) { |
|
|
|
100
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
213
|
630
|
|
|
|
|
1283
|
my $key = shift; |
|
214
|
630
|
|
|
|
|
1481
|
$val = $data->{$key}; |
|
215
|
|
|
|
|
|
|
} |
|
216
|
|
|
|
|
|
|
elsif ($knot->{_type_int} == TYPE_ARRAY) { |
|
217
|
182
|
|
|
|
|
351
|
my $i = shift; |
|
218
|
182
|
|
|
|
|
372
|
$val = $data->[$i]; |
|
219
|
|
|
|
|
|
|
} |
|
220
|
|
|
|
|
|
|
elsif ($knot->{_type_int} == TYPE_SCALAR) { |
|
221
|
161
|
100
|
|
|
|
325
|
if (defined $data) { |
|
222
|
152
|
|
|
|
|
434
|
$val = $$data; |
|
223
|
|
|
|
|
|
|
} |
|
224
|
|
|
|
|
|
|
else { |
|
225
|
9
|
|
|
|
|
70
|
return; |
|
226
|
|
|
|
|
|
|
} |
|
227
|
|
|
|
|
|
|
} |
|
228
|
|
|
|
|
|
|
|
|
229
|
964
|
100
|
66
|
|
|
3125
|
if (ref($val) && (my $inner = _is_child($val))) { |
|
230
|
|
|
|
|
|
|
# Register the inner knot so clean_up_all() can find it even when it |
|
231
|
|
|
|
|
|
|
# was created in a forked child process |
|
232
|
|
|
|
|
|
|
|
|
233
|
503
|
100
|
|
|
|
1332
|
if (! exists $global_register{$inner->seg->id}) { |
|
234
|
13
|
|
|
|
|
34
|
$global_register{$inner->seg->id} = $inner; |
|
235
|
|
|
|
|
|
|
} |
|
236
|
|
|
|
|
|
|
|
|
237
|
503
|
|
|
|
|
1062
|
my $s = $inner->seg; |
|
238
|
503
|
|
|
|
|
1141
|
$inner->{_data} = $knot->_decode($s); |
|
239
|
|
|
|
|
|
|
} |
|
240
|
964
|
|
|
|
|
8290
|
return $val; |
|
241
|
|
|
|
|
|
|
|
|
242
|
|
|
|
|
|
|
} |
|
243
|
|
|
|
|
|
|
sub CLEAR { |
|
244
|
168
|
|
|
168
|
|
8157
|
my $knot = shift; |
|
245
|
|
|
|
|
|
|
|
|
246
|
168
|
100
|
|
|
|
607
|
return if ! _write_permitted($knot); |
|
247
|
|
|
|
|
|
|
|
|
248
|
167
|
100
|
|
|
|
728
|
$knot->{_data} = $knot->_decode($knot->seg) unless $knot->{_lock}; |
|
249
|
|
|
|
|
|
|
|
|
250
|
167
|
100
|
|
|
|
636
|
if ($knot->{_type_int} == TYPE_HASH) { |
|
|
|
50
|
|
|
|
|
|
|
251
|
|
|
|
|
|
|
# Remove any child segments before discarding the data |
|
252
|
109
|
|
|
|
|
222
|
for my $val (values %{ $knot->{_data} }) { |
|
|
109
|
|
|
|
|
995
|
|
|
253
|
27
|
100
|
66
|
|
|
115
|
if (ref($val) && (my $child = _is_child($val))) { |
|
254
|
2
|
|
|
|
|
22
|
$child->remove; |
|
255
|
|
|
|
|
|
|
} |
|
256
|
|
|
|
|
|
|
} |
|
257
|
109
|
|
|
|
|
574
|
$knot->{_data} = { }; |
|
258
|
|
|
|
|
|
|
} |
|
259
|
|
|
|
|
|
|
elsif ($knot->{_type_int} == TYPE_ARRAY) { |
|
260
|
|
|
|
|
|
|
# Remove any child segments before discarding the data |
|
261
|
58
|
|
|
|
|
100
|
for my $val (@{ $knot->{_data} }) { |
|
|
58
|
|
|
|
|
303
|
|
|
262
|
20
|
50
|
33
|
|
|
56
|
if (ref($val) && (my $child = _is_child($val))) { |
|
263
|
0
|
|
|
|
|
0
|
$child->remove; |
|
264
|
|
|
|
|
|
|
} |
|
265
|
|
|
|
|
|
|
} |
|
266
|
58
|
|
|
|
|
152
|
$knot->{_data} = [ ]; |
|
267
|
|
|
|
|
|
|
} |
|
268
|
|
|
|
|
|
|
|
|
269
|
167
|
100
|
|
|
|
599
|
if ($knot->{_lock} & LOCK_EX) { |
|
270
|
1
|
|
|
|
|
3
|
$knot->{_was_changed} = 1; |
|
271
|
|
|
|
|
|
|
} |
|
272
|
|
|
|
|
|
|
else { |
|
273
|
166
|
100
|
|
|
|
553
|
if (! defined $knot->_encode($knot->seg, $knot->{_data})){ |
|
274
|
1
|
|
|
|
|
210
|
croak "Could not write to shared memory: $!"; |
|
275
|
|
|
|
|
|
|
} |
|
276
|
|
|
|
|
|
|
} |
|
277
|
|
|
|
|
|
|
} |
|
278
|
|
|
|
|
|
|
sub DELETE { |
|
279
|
7
|
|
|
7
|
|
1482
|
my $knot = shift; |
|
280
|
7
|
|
|
|
|
26
|
my $key = shift; |
|
281
|
|
|
|
|
|
|
|
|
282
|
7
|
100
|
|
|
|
41
|
return if ! _write_permitted($knot); |
|
283
|
|
|
|
|
|
|
|
|
284
|
6
|
100
|
|
|
|
29
|
$knot->{_data} = $knot->_decode($knot->seg) unless $knot->{_lock}; |
|
285
|
6
|
|
|
|
|
20
|
my $val = delete $knot->{_data}->{$key}; |
|
286
|
|
|
|
|
|
|
|
|
287
|
|
|
|
|
|
|
# Remove the child segment if the deleted value was a nested tied ref |
|
288
|
|
|
|
|
|
|
|
|
289
|
6
|
100
|
66
|
|
|
41
|
if (ref($val) && (my $child = _is_child($val))) { |
|
290
|
2
|
|
|
|
|
24
|
$child->remove; |
|
291
|
|
|
|
|
|
|
} |
|
292
|
|
|
|
|
|
|
|
|
293
|
6
|
100
|
|
|
|
65
|
if ($knot->{_lock} & LOCK_EX) { |
|
294
|
1
|
|
|
|
|
2
|
$knot->{_was_changed} = 1; |
|
295
|
|
|
|
|
|
|
} |
|
296
|
|
|
|
|
|
|
else { |
|
297
|
5
|
100
|
|
|
|
15
|
if (! defined $knot->_encode($knot->seg, $knot->{_data})){ |
|
298
|
1
|
|
|
|
|
205
|
croak "Could not write to shared memory: $!"; |
|
299
|
|
|
|
|
|
|
} |
|
300
|
|
|
|
|
|
|
} |
|
301
|
|
|
|
|
|
|
|
|
302
|
5
|
|
|
|
|
28
|
return $val; |
|
303
|
|
|
|
|
|
|
} |
|
304
|
|
|
|
|
|
|
sub EXISTS { |
|
305
|
30
|
|
|
30
|
|
432
|
my $knot = shift; |
|
306
|
30
|
|
|
|
|
57
|
my $key = shift; |
|
307
|
|
|
|
|
|
|
|
|
308
|
30
|
100
|
|
|
|
119
|
$knot->{_data} = $knot->_decode($knot->seg) unless $knot->{_lock}; |
|
309
|
30
|
|
|
|
|
212
|
return exists $knot->{_data}->{$key}; |
|
310
|
|
|
|
|
|
|
} |
|
311
|
|
|
|
|
|
|
sub FIRSTKEY { |
|
312
|
28
|
|
|
28
|
|
24941
|
my $knot = shift; |
|
313
|
|
|
|
|
|
|
|
|
314
|
28
|
100
|
|
|
|
164
|
$knot->{_data} = $knot->_decode($knot->seg) unless $knot->{_lock}; |
|
315
|
|
|
|
|
|
|
|
|
316
|
28
|
|
|
|
|
85
|
$knot->{_hkey_list} = [ keys %{$knot->{_data}} ]; |
|
|
28
|
|
|
|
|
182
|
|
|
317
|
|
|
|
|
|
|
|
|
318
|
28
|
|
|
|
|
99
|
return $knot->NEXTKEY; |
|
319
|
|
|
|
|
|
|
} |
|
320
|
|
|
|
|
|
|
sub NEXTKEY { |
|
321
|
73
|
|
|
73
|
|
126
|
my ($knot, $last_key_accessed) = @_; |
|
322
|
|
|
|
|
|
|
|
|
323
|
|
|
|
|
|
|
# We don't use ordered hashes, so we don't need to use |
|
324
|
|
|
|
|
|
|
# the last key accessed parameter |
|
325
|
|
|
|
|
|
|
|
|
326
|
|
|
|
|
|
|
# Caveat emptor if hash was changed by another process |
|
327
|
|
|
|
|
|
|
|
|
328
|
73
|
|
|
|
|
88
|
return shift @{$knot->{_hkey_list}}; |
|
|
73
|
|
|
|
|
334
|
|
|
329
|
|
|
|
|
|
|
} |
|
330
|
|
|
|
40
|
|
|
sub EXTEND { |
|
331
|
|
|
|
|
|
|
#XXX Noop |
|
332
|
|
|
|
|
|
|
} |
|
333
|
|
|
|
|
|
|
sub PUSH { |
|
334
|
15
|
|
|
15
|
|
2206
|
my $knot = shift; |
|
335
|
|
|
|
|
|
|
|
|
336
|
15
|
100
|
|
|
|
64
|
return if ! _write_permitted($knot); |
|
337
|
|
|
|
|
|
|
|
|
338
|
14
|
100
|
|
|
|
44
|
$knot->{_data} = $knot->_decode($knot->seg, $knot->{_data}) unless $knot->{_lock}; |
|
339
|
|
|
|
|
|
|
|
|
340
|
14
|
|
|
|
|
39
|
push @{$knot->{_data}}, @_; |
|
|
14
|
|
|
|
|
49
|
|
|
341
|
14
|
100
|
|
|
|
45
|
if ($knot->{_lock} & LOCK_EX) { |
|
342
|
11
|
|
|
|
|
36
|
$knot->{_was_changed} = 1; |
|
343
|
|
|
|
|
|
|
} |
|
344
|
|
|
|
|
|
|
else { |
|
345
|
3
|
100
|
|
|
|
14
|
if (! defined $knot->_encode($knot->seg, $knot->{_data})){ |
|
346
|
1
|
|
|
|
|
230
|
croak "Could not write to shared memory: $!"; |
|
347
|
|
|
|
|
|
|
}; |
|
348
|
|
|
|
|
|
|
} |
|
349
|
|
|
|
|
|
|
} |
|
350
|
|
|
|
|
|
|
sub POP { |
|
351
|
5
|
|
|
5
|
|
1049
|
my $knot = shift; |
|
352
|
|
|
|
|
|
|
|
|
353
|
5
|
100
|
|
|
|
17
|
return if ! _write_permitted($knot); |
|
354
|
|
|
|
|
|
|
|
|
355
|
4
|
100
|
|
|
|
18
|
$knot->{_data} = $knot->_decode($knot->seg, $knot->{_data}) unless $knot->{_lock}; |
|
356
|
|
|
|
|
|
|
|
|
357
|
4
|
|
|
|
|
29
|
my $val = pop @{$knot->{_data}}; |
|
|
4
|
|
|
|
|
17
|
|
|
358
|
4
|
100
|
|
|
|
19
|
if ($knot->{_lock} & LOCK_EX) { |
|
359
|
1
|
|
|
|
|
2
|
$knot->{_was_changed} = 1; |
|
360
|
|
|
|
|
|
|
} |
|
361
|
|
|
|
|
|
|
else { |
|
362
|
3
|
100
|
|
|
|
13
|
if (! defined $knot->_encode($knot->seg, $knot->{_data})){ |
|
363
|
1
|
|
|
|
|
127
|
croak "Could not write to shared memory: $!"; |
|
364
|
|
|
|
|
|
|
} |
|
365
|
|
|
|
|
|
|
} |
|
366
|
3
|
|
|
|
|
19
|
return $val; |
|
367
|
|
|
|
|
|
|
} |
|
368
|
|
|
|
|
|
|
sub SHIFT { |
|
369
|
15
|
|
|
15
|
|
3496
|
my $knot = shift; |
|
370
|
|
|
|
|
|
|
|
|
371
|
15
|
100
|
|
|
|
49
|
return if ! _write_permitted($knot); |
|
372
|
|
|
|
|
|
|
|
|
373
|
14
|
100
|
|
|
|
47
|
$knot->{_data} = $knot->_decode($knot->seg, $knot->{_data}) unless $knot->{_lock}; |
|
374
|
14
|
|
|
|
|
26
|
my $val = shift @{$knot->{_data}}; |
|
|
14
|
|
|
|
|
40
|
|
|
375
|
14
|
100
|
|
|
|
44
|
if ($knot->{_lock} & LOCK_EX) { |
|
376
|
11
|
|
|
|
|
24
|
$knot->{_was_changed} = 1; |
|
377
|
|
|
|
|
|
|
} |
|
378
|
|
|
|
|
|
|
else { |
|
379
|
3
|
100
|
|
|
|
12
|
if (! defined $knot->_encode($knot->seg, $knot->{_data})){ |
|
380
|
1
|
|
|
|
|
116
|
croak "Could not write to shared memory: $!"; |
|
381
|
|
|
|
|
|
|
} |
|
382
|
|
|
|
|
|
|
} |
|
383
|
13
|
|
|
|
|
45
|
return $val; |
|
384
|
|
|
|
|
|
|
} |
|
385
|
|
|
|
|
|
|
sub UNSHIFT { |
|
386
|
5
|
|
|
5
|
|
4267
|
my $knot = shift; |
|
387
|
|
|
|
|
|
|
|
|
388
|
5
|
100
|
|
|
|
56
|
return if ! _write_permitted($knot); |
|
389
|
|
|
|
|
|
|
|
|
390
|
4
|
100
|
|
|
|
22
|
$knot->{_data} = $knot->_decode($knot->seg, $knot->{_data}) unless $knot->{_lock}; |
|
391
|
4
|
|
|
|
|
9
|
my $val = unshift @{$knot->{_data}}, @_; |
|
|
4
|
|
|
|
|
19
|
|
|
392
|
4
|
100
|
|
|
|
21
|
if ($knot->{_lock} & LOCK_EX) { |
|
393
|
1
|
|
|
|
|
2
|
$knot->{_was_changed} = 1; |
|
394
|
|
|
|
|
|
|
} |
|
395
|
|
|
|
|
|
|
else { |
|
396
|
3
|
100
|
|
|
|
11
|
if (! defined $knot->_encode($knot->seg, $knot->{_data})){ |
|
397
|
1
|
|
|
|
|
138
|
croak "Could not write to shared memory: $!"; |
|
398
|
|
|
|
|
|
|
} |
|
399
|
|
|
|
|
|
|
} |
|
400
|
3
|
|
|
|
|
13
|
return $val; |
|
401
|
|
|
|
|
|
|
} |
|
402
|
|
|
|
|
|
|
sub SPLICE { |
|
403
|
5
|
|
|
5
|
|
3595
|
my($knot, $off, $n, @av) = @_; |
|
404
|
|
|
|
|
|
|
|
|
405
|
5
|
100
|
|
|
|
21
|
return if ! _write_permitted($knot); |
|
406
|
|
|
|
|
|
|
|
|
407
|
4
|
100
|
|
|
|
20
|
$knot->{_data} = $knot->_decode($knot->seg, $knot->{_data}) unless $knot->{_lock}; |
|
408
|
4
|
|
|
|
|
11
|
my @val = splice @{$knot->{_data}}, $off, $n, @av; |
|
|
4
|
|
|
|
|
18
|
|
|
409
|
4
|
100
|
|
|
|
17
|
if ($knot->{_lock} & LOCK_EX) { |
|
410
|
1
|
|
|
|
|
1
|
$knot->{_was_changed} = 1; |
|
411
|
|
|
|
|
|
|
} |
|
412
|
|
|
|
|
|
|
else { |
|
413
|
3
|
100
|
|
|
|
11
|
if (! defined $knot->_encode($knot->seg, $knot->{_data})){ |
|
414
|
1
|
|
|
|
|
128
|
croak "Could not write to shared memory: $!"; |
|
415
|
|
|
|
|
|
|
} |
|
416
|
|
|
|
|
|
|
} |
|
417
|
3
|
|
|
|
|
21
|
return @val; |
|
418
|
|
|
|
|
|
|
} |
|
419
|
|
|
|
|
|
|
sub FETCHSIZE { |
|
420
|
60
|
|
|
60
|
|
2041222
|
my $knot = shift; |
|
421
|
|
|
|
|
|
|
|
|
422
|
60
|
100
|
|
|
|
328
|
$knot->{_data} = $knot->_decode($knot->seg) unless $knot->{_lock}; |
|
423
|
60
|
|
|
|
|
115
|
return scalar(@{$knot->{_data}}); |
|
|
60
|
|
|
|
|
328
|
|
|
424
|
|
|
|
|
|
|
} |
|
425
|
|
|
|
|
|
|
sub STORESIZE { |
|
426
|
5
|
|
|
5
|
|
2038
|
my $knot = shift; |
|
427
|
5
|
|
|
|
|
45
|
my $n = shift; |
|
428
|
|
|
|
|
|
|
|
|
429
|
5
|
100
|
|
|
|
19
|
return if ! _write_permitted($knot); |
|
430
|
|
|
|
|
|
|
|
|
431
|
4
|
100
|
|
|
|
21
|
$knot->{_data} = $knot->_decode($knot->seg) unless $knot->{_lock}; |
|
432
|
4
|
|
|
|
|
10
|
$#{$knot->{_data}} = $n - 1; |
|
|
4
|
|
|
|
|
20
|
|
|
433
|
4
|
100
|
|
|
|
15
|
if ($knot->{_lock} & LOCK_EX) { |
|
434
|
1
|
|
|
|
|
2
|
$knot->{_was_changed} = 1; |
|
435
|
|
|
|
|
|
|
} |
|
436
|
|
|
|
|
|
|
else { |
|
437
|
3
|
100
|
|
|
|
12
|
if (! defined $knot->_encode($knot->seg, $knot->{_data})){ |
|
438
|
1
|
|
|
|
|
165
|
croak "Could not write to shared memory: $!"; |
|
439
|
|
|
|
|
|
|
} |
|
440
|
|
|
|
|
|
|
} |
|
441
|
3
|
|
|
|
|
18
|
return $n; |
|
442
|
|
|
|
|
|
|
} |
|
443
|
|
|
|
|
|
|
|
|
444
|
|
|
|
|
|
|
# Public methods |
|
445
|
|
|
|
|
|
|
|
|
446
|
|
|
|
|
|
|
*shlock = \&lock; |
|
447
|
|
|
|
|
|
|
*shunlock = \&unlock; |
|
448
|
|
|
|
|
|
|
|
|
449
|
|
|
|
|
|
|
# End user methods |
|
450
|
|
|
|
|
|
|
|
|
451
|
|
|
|
|
|
|
sub new { |
|
452
|
7
|
|
|
7
|
1
|
30380
|
my ($class, %opts) = @_; |
|
453
|
|
|
|
|
|
|
|
|
454
|
7
|
|
100
|
|
|
332
|
my $type = $opts{var} || 'HASH'; |
|
455
|
|
|
|
|
|
|
|
|
456
|
7
|
100
|
|
|
|
167
|
if ($type eq 'HASH') { |
|
457
|
3
|
|
|
|
|
251
|
my $k = tie my %h, 'IPC::Shareable', \%opts; |
|
458
|
3
|
|
|
|
|
68
|
return \%h; |
|
459
|
|
|
|
|
|
|
} |
|
460
|
4
|
100
|
|
|
|
12
|
if ($type eq 'ARRAY') { |
|
461
|
2
|
|
|
|
|
15
|
my $k = tie my @a, 'IPC::Shareable', \%opts; |
|
462
|
2
|
|
|
|
|
12
|
return \@a; |
|
463
|
|
|
|
|
|
|
} |
|
464
|
2
|
50
|
|
|
|
12
|
if ($type eq 'SCALAR') { |
|
465
|
2
|
|
|
|
|
36
|
my $k = tie my $s, 'IPC::Shareable', \%opts; |
|
466
|
2
|
|
|
|
|
10
|
return \$s; |
|
467
|
|
|
|
|
|
|
} |
|
468
|
|
|
|
|
|
|
} |
|
469
|
|
|
|
|
|
|
sub lock { |
|
470
|
136
|
|
|
136
|
1
|
4792001
|
my $knot = shift; |
|
471
|
|
|
|
|
|
|
|
|
472
|
136
|
|
|
|
|
761
|
my ($flags, $code); |
|
473
|
|
|
|
|
|
|
|
|
474
|
136
|
100
|
|
|
|
1680
|
if (scalar @_ == 2) { |
|
475
|
5
|
|
|
|
|
21
|
($flags, $code) = @_; |
|
476
|
|
|
|
|
|
|
} |
|
477
|
|
|
|
|
|
|
|
|
478
|
136
|
100
|
|
|
|
626
|
if (defined $_[0]) { |
|
479
|
89
|
100
|
|
|
|
687
|
if (ref $_[0] eq 'CODE') { |
|
480
|
1
|
|
|
|
|
2
|
$code = shift; |
|
481
|
|
|
|
|
|
|
} |
|
482
|
|
|
|
|
|
|
else { |
|
483
|
88
|
|
|
|
|
669
|
$flags = shift; |
|
484
|
|
|
|
|
|
|
} |
|
485
|
|
|
|
|
|
|
} |
|
486
|
|
|
|
|
|
|
|
|
487
|
136
|
100
|
100
|
|
|
656
|
if (defined $code && ref $code ne 'CODE') { |
|
488
|
1
|
|
|
|
|
358
|
croak "\$code param to lock() must be a code ref" |
|
489
|
|
|
|
|
|
|
} |
|
490
|
|
|
|
|
|
|
|
|
491
|
135
|
100
|
|
|
|
582
|
$flags = LOCK_EX if ! defined $flags; |
|
492
|
|
|
|
|
|
|
|
|
493
|
135
|
50
|
|
|
|
634
|
return $knot->unlock if ($flags & LOCK_UN); |
|
494
|
|
|
|
|
|
|
|
|
495
|
135
|
50
|
|
|
|
806
|
return 1 if ($knot->{_lock} & $flags); |
|
496
|
|
|
|
|
|
|
|
|
497
|
|
|
|
|
|
|
# If they have a different lock than they want, release it first |
|
498
|
|
|
|
|
|
|
|
|
499
|
135
|
50
|
|
|
|
450
|
$knot->unlock if ($knot->{_lock}); |
|
500
|
|
|
|
|
|
|
|
|
501
|
135
|
|
|
|
|
1797
|
my $sem = $knot->sem; |
|
502
|
135
|
|
|
|
|
327
|
my $lock_success = $sem->op(@{ $semop_args{$flags} }); |
|
|
135
|
|
|
|
|
3798
|
|
|
503
|
|
|
|
|
|
|
|
|
504
|
135
|
100
|
|
|
|
5727100
|
if ($lock_success) { |
|
505
|
124
|
|
|
|
|
910
|
$knot->{_lock} = $flags; |
|
506
|
124
|
|
|
|
|
1026
|
$knot->{_data} = $knot->_decode($knot->seg); |
|
507
|
|
|
|
|
|
|
|
|
508
|
124
|
|
|
|
|
540
|
my @locked_children; |
|
509
|
124
|
|
|
|
|
593
|
my %seen = ($knot->seg->id => 1); |
|
510
|
|
|
|
|
|
|
|
|
511
|
124
|
100
|
|
|
|
1608
|
if (! _lock_children($knot, $flags, \@locked_children, \%seen)) { |
|
512
|
2
|
|
|
|
|
18
|
my $rflags = $knot->{_lock} | LOCK_UN; |
|
513
|
2
|
50
|
|
|
|
23
|
$rflags ^= LOCK_NB if $rflags & LOCK_NB; |
|
514
|
2
|
|
|
|
|
16
|
$knot->sem->op(@{ $semop_args{$rflags} }); |
|
|
2
|
|
|
|
|
44
|
|
|
515
|
2
|
|
|
|
|
33
|
$knot->{_lock} = 0; |
|
516
|
2
|
|
|
|
|
21
|
$lock_success = 0; |
|
517
|
|
|
|
|
|
|
} |
|
518
|
|
|
|
|
|
|
else { |
|
519
|
122
|
|
|
|
|
1519
|
$knot->{_locked_children} = \@locked_children; |
|
520
|
|
|
|
|
|
|
} |
|
521
|
|
|
|
|
|
|
} |
|
522
|
|
|
|
|
|
|
|
|
523
|
135
|
100
|
66
|
|
|
4107
|
if ($flags == LOCK_EX && $lock_success) { |
|
524
|
89
|
100
|
|
|
|
888
|
if ($code) { |
|
525
|
4
|
|
|
|
|
5
|
my $ok = eval { $code->(); 1 }; |
|
|
4
|
|
|
|
|
13
|
|
|
|
2
|
|
|
|
|
6
|
|
|
526
|
4
|
|
|
|
|
17
|
my $err = $@; |
|
527
|
4
|
|
|
|
|
10
|
$knot->unlock; |
|
528
|
4
|
100
|
|
|
|
19
|
die $err if ! $ok; |
|
529
|
2
|
|
|
|
|
6
|
return 1; |
|
530
|
|
|
|
|
|
|
} |
|
531
|
|
|
|
|
|
|
} |
|
532
|
131
|
|
|
|
|
571
|
return $lock_success; |
|
533
|
|
|
|
|
|
|
} |
|
534
|
|
|
|
|
|
|
sub unlock { |
|
535
|
263
|
|
|
263
|
1
|
6509597
|
my $knot = shift; |
|
536
|
|
|
|
|
|
|
|
|
537
|
263
|
100
|
|
|
|
1186
|
return 1 unless $knot->{_lock}; |
|
538
|
|
|
|
|
|
|
|
|
539
|
123
|
100
|
|
|
|
477
|
if ($knot->{_was_changed}) { |
|
540
|
53
|
100
|
|
|
|
265
|
if (! defined $knot->_encode($knot->seg, $knot->{_data})){ |
|
541
|
1
|
|
|
|
|
273
|
croak "Could not write to shared memory: $!\n"; |
|
542
|
|
|
|
|
|
|
} |
|
543
|
52
|
|
|
|
|
319
|
$knot->{_was_changed} = 0; |
|
544
|
|
|
|
|
|
|
} |
|
545
|
|
|
|
|
|
|
|
|
546
|
122
|
|
50
|
|
|
308
|
for my $child (reverse @{ $knot->{_locked_children} // [] }) { |
|
|
122
|
|
|
|
|
1269
|
|
|
547
|
24
|
100
|
|
|
|
143
|
if ($child->{_was_changed}) { |
|
548
|
3
|
|
|
|
|
30
|
$child->_encode($child->seg, $child->{_data}); |
|
549
|
3
|
|
|
|
|
21
|
$child->{_was_changed} = 0; |
|
550
|
|
|
|
|
|
|
} |
|
551
|
24
|
|
|
|
|
85
|
my $child_flags = $child->{_lock} | LOCK_UN; |
|
552
|
24
|
100
|
|
|
|
77
|
$child_flags ^= LOCK_NB if $child_flags & LOCK_NB; |
|
553
|
24
|
|
|
|
|
170
|
$child->sem->op(@{ $semop_args{$child_flags} }); |
|
|
24
|
|
|
|
|
357
|
|
|
554
|
24
|
|
|
|
|
707
|
$child->{_lock} = 0; |
|
555
|
|
|
|
|
|
|
} |
|
556
|
122
|
|
|
|
|
410
|
$knot->{_locked_children} = []; |
|
557
|
|
|
|
|
|
|
|
|
558
|
122
|
|
|
|
|
521
|
my $sem = $knot->sem; |
|
559
|
122
|
|
|
|
|
344
|
my $flags = $knot->{_lock} | LOCK_UN; |
|
560
|
|
|
|
|
|
|
|
|
561
|
122
|
100
|
|
|
|
483
|
$flags ^= LOCK_NB if ($flags & LOCK_NB); |
|
562
|
|
|
|
|
|
|
|
|
563
|
122
|
100
|
|
|
|
242
|
if (! $sem->op(@{ $semop_args{$flags} })) { |
|
|
122
|
|
|
|
|
1613
|
|
|
564
|
1
|
|
|
|
|
111
|
croak "Could not release semaphore lock: $!\n"; |
|
565
|
|
|
|
|
|
|
} |
|
566
|
|
|
|
|
|
|
|
|
567
|
121
|
|
|
|
|
2970
|
$knot->{_lock} = 0; |
|
568
|
|
|
|
|
|
|
|
|
569
|
121
|
|
|
|
|
480
|
1; |
|
570
|
|
|
|
|
|
|
} |
|
571
|
|
|
|
|
|
|
sub singleton { |
|
572
|
|
|
|
|
|
|
|
|
573
|
|
|
|
|
|
|
# If called with IPC::Shareable::singleton() as opposed to |
|
574
|
|
|
|
|
|
|
# IPC::Shareable->singleton(), the class isn't sent in. Check |
|
575
|
|
|
|
|
|
|
# for this and fix it if necessary |
|
576
|
|
|
|
|
|
|
|
|
577
|
8
|
100
|
100
|
8
|
1
|
16742
|
if (! defined $_[0] || $_[0] ne __PACKAGE__) { |
|
578
|
3
|
|
|
|
|
19
|
unshift @_, __PACKAGE__; |
|
579
|
|
|
|
|
|
|
} |
|
580
|
|
|
|
|
|
|
|
|
581
|
8
|
|
|
|
|
49
|
my ($class, $glue, $warn) = @_; |
|
582
|
|
|
|
|
|
|
|
|
583
|
8
|
100
|
|
|
|
128
|
if (! defined $glue) { |
|
584
|
2
|
|
|
|
|
712
|
croak "singleton() requires a GLUE parameter"; |
|
585
|
|
|
|
|
|
|
} |
|
586
|
|
|
|
|
|
|
|
|
587
|
6
|
100
|
|
|
|
47
|
$warn = 0 if ! defined $warn; |
|
588
|
|
|
|
|
|
|
|
|
589
|
6
|
|
|
|
|
173
|
tie my $lock, 'IPC::Shareable', { |
|
590
|
|
|
|
|
|
|
key => $glue, |
|
591
|
|
|
|
|
|
|
create => 1, |
|
592
|
|
|
|
|
|
|
exclusive => 1, |
|
593
|
|
|
|
|
|
|
graceful => 1, |
|
594
|
|
|
|
|
|
|
destroy => 1, |
|
595
|
|
|
|
|
|
|
warn => $warn |
|
596
|
|
|
|
|
|
|
}; |
|
597
|
|
|
|
|
|
|
|
|
598
|
3
|
|
|
|
|
29
|
return $$; |
|
599
|
|
|
|
|
|
|
} |
|
600
|
|
|
|
|
|
|
|
|
601
|
|
|
|
|
|
|
# Helper, maintenance and developer methods |
|
602
|
|
|
|
|
|
|
|
|
603
|
|
|
|
|
|
|
sub attributes { |
|
604
|
11872
|
|
|
11872
|
1
|
76078
|
my ($knot, $attr) = @_; |
|
605
|
|
|
|
|
|
|
|
|
606
|
11872
|
100
|
|
|
|
23328
|
if (defined $attr) { |
|
607
|
11658
|
|
|
|
|
54963
|
return $knot->{attributes}{$attr}; |
|
608
|
|
|
|
|
|
|
} |
|
609
|
|
|
|
|
|
|
else { |
|
610
|
214
|
|
|
|
|
4312
|
return $knot->{attributes}; |
|
611
|
|
|
|
|
|
|
} |
|
612
|
|
|
|
|
|
|
} |
|
613
|
|
|
|
|
|
|
sub global_register { |
|
614
|
105
|
|
|
105
|
1
|
1435675
|
return \%global_register; |
|
615
|
|
|
|
|
|
|
} |
|
616
|
|
|
|
|
|
|
sub process_register { |
|
617
|
11
|
|
|
11
|
1
|
66
|
return \%process_register; |
|
618
|
|
|
|
|
|
|
} |
|
619
|
|
|
|
|
|
|
sub uuid { |
|
620
|
526
|
|
|
526
|
1
|
10994
|
my ($knot) = @_; |
|
621
|
|
|
|
|
|
|
|
|
622
|
526
|
100
|
|
|
|
4031
|
if (! defined $knot->{_uuid}) { |
|
623
|
499
|
|
|
|
|
11020
|
$knot->{_uuid} = md5_hex(rand()); |
|
624
|
|
|
|
|
|
|
} |
|
625
|
|
|
|
|
|
|
|
|
626
|
526
|
|
|
|
|
1993
|
return $knot->{_uuid}; |
|
627
|
|
|
|
|
|
|
} |
|
628
|
|
|
|
|
|
|
|
|
629
|
|
|
|
|
|
|
sub seg { |
|
630
|
5848
|
|
|
5848
|
1
|
28254
|
my ($knot) = @_; |
|
631
|
5848
|
50
|
|
|
|
23800
|
return $knot->{_shm} if defined $knot->{_shm}; |
|
632
|
|
|
|
|
|
|
} |
|
633
|
|
|
|
|
|
|
sub sem { |
|
634
|
2477
|
|
|
2477
|
1
|
31923
|
my ($knot) = @_; |
|
635
|
2477
|
50
|
|
|
|
10929
|
return $knot->{_sem} if defined $knot->{_sem}; |
|
636
|
|
|
|
|
|
|
} |
|
637
|
|
|
|
|
|
|
|
|
638
|
|
|
|
|
|
|
sub shm_segments { |
|
639
|
26
|
50
|
66
|
26
|
1
|
14431
|
shift if ref($_[0]) || (defined $_[0] && !ref($_[0]) && UNIVERSAL::isa($_[0], __PACKAGE__)); |
|
|
|
|
66
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
640
|
|
|
|
|
|
|
|
|
641
|
26
|
|
|
|
|
77
|
my ($filter_key) = @_; |
|
642
|
|
|
|
|
|
|
|
|
643
|
26
|
100
|
|
|
|
70
|
my $filter_int = _key_str_to_int($filter_key) if defined $filter_key; |
|
644
|
|
|
|
|
|
|
|
|
645
|
26
|
|
|
|
|
55
|
my %segments; |
|
646
|
|
|
|
|
|
|
|
|
647
|
26
|
|
|
|
|
149251
|
for my $line (`ipcs -m`) { |
|
648
|
229
|
|
|
|
|
789
|
my ($id, $raw_key); |
|
649
|
|
|
|
|
|
|
|
|
650
|
|
|
|
|
|
|
# BSD/macOS format: m ... |
|
651
|
229
|
50
|
|
|
|
1719
|
if ($line =~ /^\s*m\s+(\d+)\s+(\S+)/) { |
|
|
|
100
|
|
|
|
|
|
|
652
|
0
|
|
|
|
|
0
|
($id, $raw_key) = ($1, $2); |
|
653
|
|
|
|
|
|
|
} |
|
654
|
|
|
|
|
|
|
# Linux format: ... |
|
655
|
|
|
|
|
|
|
elsif ($line =~ /^\s*(\S+)\s+(\d+)\s+\S+/) { |
|
656
|
125
|
|
|
|
|
1136
|
($raw_key, $id) = ($1, $2); |
|
657
|
|
|
|
|
|
|
} |
|
658
|
|
|
|
|
|
|
else { |
|
659
|
104
|
|
|
|
|
385
|
next; |
|
660
|
|
|
|
|
|
|
} |
|
661
|
|
|
|
|
|
|
|
|
662
|
125
|
0
|
|
|
|
950
|
my $key_int = $raw_key =~ /^0x[0-9a-fA-F]+$/ |
|
|
|
50
|
|
|
|
|
|
|
663
|
|
|
|
|
|
|
? hex($raw_key) |
|
664
|
|
|
|
|
|
|
: $raw_key =~ /^\d+$/ |
|
665
|
|
|
|
|
|
|
? int($raw_key) |
|
666
|
|
|
|
|
|
|
: next; |
|
667
|
|
|
|
|
|
|
|
|
668
|
125
|
|
|
|
|
693
|
my $hex_key = sprintf('0x%08x', $key_int); |
|
669
|
|
|
|
|
|
|
|
|
670
|
125
|
50
|
|
|
|
450
|
next if $key_int == 0; # IPC_PRIVATE segments can't be found by key |
|
671
|
|
|
|
|
|
|
|
|
672
|
|
|
|
|
|
|
# Get segment size via IPC_STAT |
|
673
|
125
|
|
|
|
|
289
|
my $stat_buf = ''; |
|
674
|
125
|
50
|
|
|
|
982
|
shmctl($id, IPC_STAT, $stat_buf) or next; |
|
675
|
|
|
|
|
|
|
|
|
676
|
|
|
|
|
|
|
my ($segsz) = $^O eq 'linux' |
|
677
|
|
|
|
|
|
|
? ( $Config{longsize} == 8 |
|
678
|
|
|
|
|
|
|
? unpack('x[48] Q', $stat_buf) # 64-bit Linux |
|
679
|
|
|
|
|
|
|
: unpack('x[36] L', $stat_buf) ) # 32-bit Linux |
|
680
|
125
|
50
|
0
|
|
|
5124
|
: ( $^O eq 'freebsd' && $Config{longsize} == 8 |
|
|
|
0
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
681
|
|
|
|
|
|
|
? unpack('x[32] Q', $stat_buf) # 64-bit FreeBSD (key_t=long=8, ipc_perm=32) |
|
682
|
|
|
|
|
|
|
: unpack('x[24] Q', $stat_buf) );# macOS/32-bit BSD |
|
683
|
|
|
|
|
|
|
|
|
684
|
125
|
50
|
|
|
|
567
|
next unless $segsz; |
|
685
|
|
|
|
|
|
|
|
|
686
|
125
|
|
|
|
|
262
|
my $data = ''; |
|
687
|
125
|
50
|
|
|
|
17800
|
shmread($id, $data, 0, $segsz) or next; |
|
688
|
|
|
|
|
|
|
|
|
689
|
|
|
|
|
|
|
# Strip trailing null bytes |
|
690
|
125
|
|
|
|
|
3069
|
$data =~ s/\x00+$//; |
|
691
|
|
|
|
|
|
|
|
|
692
|
|
|
|
|
|
|
# Skip segments not owned by IPC::Shareable (all our segments |
|
693
|
|
|
|
|
|
|
# are prefixed with the literal string 'IPC::Shareable') |
|
694
|
125
|
100
|
|
|
|
495
|
next unless substr($data, 0, 14) eq 'IPC::Shareable'; |
|
695
|
|
|
|
|
|
|
|
|
696
|
124
|
|
|
|
|
331
|
my $json_part = substr($data, 14); |
|
697
|
124
|
|
|
|
|
489
|
my @child_keys = ($json_part =~ /"child_key_hex":"([^"]+)"/g); |
|
698
|
|
|
|
|
|
|
|
|
699
|
|
|
|
|
|
|
$segments{$hex_key} = { |
|
700
|
|
|
|
|
|
|
child_keys => \@child_keys, |
|
701
|
|
|
|
|
|
|
content => $data, |
|
702
|
|
|
|
|
|
|
local_process => (exists $process_register{$id} ? 1 : 0), |
|
703
|
124
|
100
|
|
|
|
2583
|
known => (exists $global_register{$id} ? 1 : 0), |
|
|
|
100
|
|
|
|
|
|
|
704
|
|
|
|
|
|
|
}; |
|
705
|
|
|
|
|
|
|
} |
|
706
|
|
|
|
|
|
|
|
|
707
|
26
|
100
|
|
|
|
109
|
if (defined $filter_int) { |
|
708
|
|
|
|
|
|
|
# Walk the segment tree starting from the root whose key matches |
|
709
|
|
|
|
|
|
|
# $filter_int, collecting it and all its descendants. Use integer |
|
710
|
|
|
|
|
|
|
# comparison so that hex formatting differences (zero-padding, case) |
|
711
|
|
|
|
|
|
|
# between ipcs(1) output and child_key_hex values don't matter. |
|
712
|
2
|
|
|
|
|
89
|
my %int_to_hex = map { hex($_) => $_ } keys %segments; |
|
|
8
|
|
|
|
|
83
|
|
|
713
|
2
|
|
|
|
|
13
|
my (%related, @queue); |
|
714
|
2
|
|
|
|
|
7
|
push @queue, $filter_int; |
|
715
|
2
|
|
|
|
|
12
|
while (my $k_int = shift @queue) { |
|
716
|
3
|
|
100
|
|
|
19
|
my $k_hex = $int_to_hex{$k_int} // next; |
|
717
|
2
|
50
|
|
|
|
13
|
next if $related{$k_hex}++; |
|
718
|
2
|
|
|
|
|
5
|
push @queue, map { hex($_) } @{ $segments{$k_hex}{child_keys} }; |
|
|
1
|
|
|
|
|
7
|
|
|
|
2
|
|
|
|
|
8
|
|
|
719
|
|
|
|
|
|
|
} |
|
720
|
2
|
|
|
|
|
15
|
%segments = map { $_ => $segments{$_} } keys %related; |
|
|
2
|
|
|
|
|
12
|
|
|
721
|
|
|
|
|
|
|
} |
|
722
|
|
|
|
|
|
|
|
|
723
|
26
|
|
|
|
|
766
|
return \%segments; |
|
724
|
|
|
|
|
|
|
} |
|
725
|
|
|
|
|
|
|
sub unknown_segments { |
|
726
|
2
|
100
|
|
2
|
1
|
5242
|
shift if ref $_[0]; # Allow for object or class method call |
|
727
|
|
|
|
|
|
|
|
|
728
|
2
|
|
|
|
|
43
|
my $segs = shm_segments(); |
|
729
|
|
|
|
|
|
|
|
|
730
|
2
|
|
|
|
|
42
|
return grep { !$segs->{$_}{known} } keys %$segs; |
|
|
10
|
|
|
|
|
55
|
|
|
731
|
|
|
|
|
|
|
} |
|
732
|
|
|
|
|
|
|
sub seg_count { |
|
733
|
131
|
|
|
131
|
1
|
21113040
|
my $count = 0; |
|
734
|
|
|
|
|
|
|
|
|
735
|
131
|
|
|
|
|
806150
|
for my $line (`ipcs -m`) { |
|
736
|
|
|
|
|
|
|
# BSD/macOS format: m ... |
|
737
|
|
|
|
|
|
|
# Linux format: ... |
|
738
|
673
|
50
|
|
|
|
8956
|
$count++ if $line =~ /^\s*m\s+\d+\s+\S+/; |
|
739
|
673
|
100
|
|
|
|
4005
|
$count++ if $line =~ /^\s*(?:0x[0-9a-fA-F]+|\d+)\s+\d+\s+\S+/; |
|
740
|
|
|
|
|
|
|
} |
|
741
|
|
|
|
|
|
|
|
|
742
|
131
|
|
|
|
|
3110
|
return $count; |
|
743
|
|
|
|
|
|
|
} |
|
744
|
|
|
|
|
|
|
sub sem_count { |
|
745
|
122
|
|
|
122
|
1
|
86488
|
my $count = 0; |
|
746
|
|
|
|
|
|
|
|
|
747
|
122
|
|
|
|
|
644166
|
for my $line (`ipcs -s`) { |
|
748
|
|
|
|
|
|
|
# BSD/macOS format: s ... |
|
749
|
|
|
|
|
|
|
# Linux format: ... |
|
750
|
608
|
50
|
|
|
|
9229
|
$count++ if $line =~ /^\s*s\s+\d+\s+\S+/; |
|
751
|
608
|
100
|
|
|
|
4213
|
$count++ if $line =~ /^\s*(?:0x[0-9a-fA-F]+|\d+)\s+\d+\s+\S+/; |
|
752
|
|
|
|
|
|
|
} |
|
753
|
|
|
|
|
|
|
|
|
754
|
122
|
|
|
|
|
3203
|
return $count; |
|
755
|
|
|
|
|
|
|
} |
|
756
|
|
|
|
|
|
|
sub seg_map { |
|
757
|
6
|
100
|
|
6
|
1
|
564
|
croak "seg_map() must be called as an object method" unless ref $_[0]; |
|
758
|
5
|
|
|
|
|
15
|
my $knot_filter = shift; |
|
759
|
|
|
|
|
|
|
|
|
760
|
5
|
|
|
|
|
16
|
my $segs = shm_segments(); |
|
761
|
|
|
|
|
|
|
|
|
762
|
|
|
|
|
|
|
# Build hex_key -> OS segment ID from ipcs output |
|
763
|
5
|
|
|
|
|
9
|
my %id_by_hex; |
|
764
|
5
|
|
|
|
|
26264
|
for my $line (`ipcs -m`) { |
|
765
|
33
|
|
|
|
|
112
|
my ($id, $raw_key); |
|
766
|
33
|
50
|
|
|
|
229
|
if ($line =~ /^\s*m\s+(\d+)\s+(\S+)/) { ($id, $raw_key) = ($1, $2) } |
|
|
0
|
100
|
|
|
|
0
|
|
|
767
|
13
|
|
|
|
|
141
|
elsif ($line =~ /^\s*(\S+)\s+(\d+)\s+\S+/) { ($raw_key, $id) = ($1, $2) } |
|
768
|
20
|
|
|
|
|
55
|
else { next } |
|
769
|
|
|
|
|
|
|
|
|
770
|
13
|
0
|
|
|
|
61
|
my $key_int = $raw_key =~ /^0x[0-9a-fA-F]+$/ ? hex($raw_key) |
|
|
|
50
|
|
|
|
|
|
|
771
|
|
|
|
|
|
|
: $raw_key =~ /^\d+$/ ? int($raw_key) |
|
772
|
|
|
|
|
|
|
: next; |
|
773
|
13
|
|
|
|
|
125
|
$id_by_hex{ sprintf('0x%08x', $key_int) } = $id; |
|
774
|
|
|
|
|
|
|
} |
|
775
|
|
|
|
|
|
|
|
|
776
|
|
|
|
|
|
|
# Build hex_key -> knot from global_register (keyed by seg_id) |
|
777
|
5
|
|
|
|
|
26
|
my %knot_by_hex; |
|
778
|
5
|
|
|
|
|
48
|
for my $id (keys %global_register) { |
|
779
|
8
|
|
|
|
|
34
|
my $knot = $global_register{$id}; |
|
780
|
8
|
|
|
|
|
37
|
my $hex = $knot->{_key_hex}; |
|
781
|
8
|
50
|
|
|
|
39
|
$knot_by_hex{$hex} = $knot if defined $hex; |
|
782
|
|
|
|
|
|
|
} |
|
783
|
|
|
|
|
|
|
|
|
784
|
|
|
|
|
|
|
# Supplement child_keys from global_register for Storable segments. |
|
785
|
|
|
|
|
|
|
# shm_segments() only extracts child_key_hex from JSON segment content; |
|
786
|
|
|
|
|
|
|
# for Storable we walk each knot's _data looking for tied child references. |
|
787
|
5
|
|
|
|
|
16
|
my %extra_child_keys; # hex_key -> [ child_hex, ... ] |
|
788
|
5
|
|
|
|
|
21
|
for my $hex (keys %knot_by_hex) { |
|
789
|
8
|
|
|
|
|
27
|
my $knot = $knot_by_hex{$hex}; |
|
790
|
8
|
|
|
|
|
33
|
my $data = $knot->{_data}; |
|
791
|
8
|
|
50
|
|
|
69
|
my $rtype = Scalar::Util::reftype($data) // ''; |
|
792
|
|
|
|
|
|
|
|
|
793
|
8
|
50
|
|
|
|
49
|
my @vals = $rtype eq 'HASH' ? values %$data |
|
|
|
100
|
|
|
|
|
|
|
794
|
|
|
|
|
|
|
: $rtype eq 'ARRAY' ? @$data |
|
795
|
|
|
|
|
|
|
: (); |
|
796
|
|
|
|
|
|
|
|
|
797
|
8
|
|
|
|
|
30
|
for my $v (@vals) { |
|
798
|
3
|
100
|
|
|
|
21
|
next unless ref($v); |
|
799
|
1
|
|
50
|
|
|
20
|
my $vtype = Scalar::Util::reftype($v) // ''; |
|
800
|
1
|
|
|
|
|
6
|
my $child_knot; |
|
801
|
1
|
50
|
|
|
|
11
|
if ($vtype eq 'HASH') { $child_knot = tied(%$v) } |
|
|
1
|
0
|
|
|
|
6
|
|
|
|
|
0
|
|
|
|
|
|
|
802
|
0
|
|
|
|
|
0
|
elsif ($vtype eq 'ARRAY') { $child_knot = tied(@$v) } |
|
803
|
0
|
|
|
|
|
0
|
elsif ($vtype eq 'SCALAR') { $child_knot = tied($$v) } |
|
804
|
1
|
50
|
33
|
|
|
16
|
next unless $child_knot && $child_knot->{_key_hex}; |
|
805
|
1
|
|
|
|
|
3
|
push @{ $extra_child_keys{$hex} }, $child_knot->{_key_hex}; |
|
|
1
|
|
|
|
|
23
|
|
|
806
|
|
|
|
|
|
|
} |
|
807
|
|
|
|
|
|
|
} |
|
808
|
|
|
|
|
|
|
|
|
809
|
|
|
|
|
|
|
# If called as an object method, restrict output to just that knot's tree |
|
810
|
|
|
|
|
|
|
# by BFS through both child_keys (JSON) and extra_child_keys (Storable). |
|
811
|
5
|
50
|
33
|
|
|
70
|
if ($knot_filter && $knot_filter->{_key_hex}) { |
|
812
|
5
|
|
|
|
|
14
|
my $root_hex = $knot_filter->{_key_hex}; |
|
813
|
5
|
|
|
|
|
9
|
my (%in_tree, @queue); |
|
814
|
5
|
|
|
|
|
83
|
push @queue, $root_hex; |
|
815
|
5
|
|
|
|
|
36
|
while (my $h = shift @queue) { |
|
816
|
6
|
50
|
|
|
|
35
|
next if $in_tree{$h}++; |
|
817
|
6
|
|
50
|
|
|
12
|
push @queue, @{ $segs->{$h}{child_keys} // [] }; |
|
|
6
|
|
|
|
|
27
|
|
|
818
|
6
|
|
100
|
|
|
12
|
push @queue, @{ $extra_child_keys{$h} // [] }; |
|
|
6
|
|
|
|
|
61
|
|
|
819
|
|
|
|
|
|
|
} |
|
820
|
5
|
|
|
|
|
75
|
%$segs = map { $_ => $segs->{$_} } grep { $in_tree{$_} } keys %$segs; |
|
|
6
|
|
|
|
|
99
|
|
|
|
13
|
|
|
|
|
34
|
|
|
821
|
|
|
|
|
|
|
} |
|
822
|
|
|
|
|
|
|
|
|
823
|
|
|
|
|
|
|
# Identify root segments (not a child of any other segment) |
|
824
|
5
|
|
|
|
|
22
|
my %is_child; |
|
825
|
5
|
|
|
|
|
17
|
for my $hex (keys %$segs) { |
|
826
|
6
|
|
|
|
|
10
|
$is_child{$_}++ for @{ $segs->{$hex}{child_keys} }; |
|
|
6
|
|
|
|
|
21
|
|
|
827
|
|
|
|
|
|
|
} |
|
828
|
5
|
|
|
|
|
19
|
for my $hex (keys %extra_child_keys) { |
|
829
|
1
|
50
|
|
|
|
6
|
next unless exists $segs->{$hex}; |
|
830
|
1
|
|
|
|
|
5
|
$is_child{$_}++ for @{ $extra_child_keys{$hex} }; |
|
|
1
|
|
|
|
|
4
|
|
|
831
|
|
|
|
|
|
|
} |
|
832
|
5
|
|
|
|
|
12
|
my @roots = sort grep { !$is_child{$_} } keys %$segs; |
|
|
6
|
|
|
|
|
42
|
|
|
833
|
|
|
|
|
|
|
|
|
834
|
5
|
|
|
|
|
11
|
my @lines; |
|
835
|
5
|
|
|
|
|
17
|
push @lines, 'IPC::Shareable Segment Map'; |
|
836
|
5
|
|
|
|
|
10
|
push @lines, '=' x 26; |
|
837
|
|
|
|
|
|
|
|
|
838
|
5
|
50
|
|
|
|
17
|
if (!@roots) { |
|
839
|
0
|
|
|
|
|
0
|
push @lines, ''; |
|
840
|
0
|
|
|
|
|
0
|
push @lines, ' (no IPC::Shareable segments found)'; |
|
841
|
0
|
|
|
|
|
0
|
return join("\n", @lines) . "\n"; |
|
842
|
|
|
|
|
|
|
} |
|
843
|
|
|
|
|
|
|
|
|
844
|
5
|
|
|
|
|
10
|
my $render; |
|
845
|
|
|
|
|
|
|
$render = sub { |
|
846
|
6
|
|
|
6
|
|
14
|
my ($hex, $depth) = @_; |
|
847
|
6
|
|
|
|
|
21
|
my $indent = ' ' x $depth; |
|
848
|
6
|
|
50
|
|
|
28
|
my $seg = $segs->{$hex} // {}; |
|
849
|
|
|
|
|
|
|
|
|
850
|
6
|
|
|
|
|
9
|
my @tags; |
|
851
|
6
|
50
|
|
|
|
27
|
push @tags, $seg->{known} ? 'known' : 'unknown'; |
|
852
|
6
|
50
|
|
|
|
69
|
push @tags, 'owner' if $seg->{local_process}; |
|
853
|
6
|
|
|
|
|
27
|
my $tag_str = '[' . join(', ', @tags) . ']'; |
|
854
|
|
|
|
|
|
|
|
|
855
|
6
|
|
50
|
|
|
15
|
my $seg_id = $id_by_hex{$hex} // '?'; |
|
856
|
|
|
|
|
|
|
|
|
857
|
|
|
|
|
|
|
# Read semaphore slot values and ID; for segments not in |
|
858
|
|
|
|
|
|
|
# global_register attach with nsems=0 (avoids EINVAL on existing sets) |
|
859
|
6
|
|
|
|
|
11
|
my ($sem_str, $content_str); |
|
860
|
|
|
|
|
|
|
my $sem = $knot_by_hex{$hex} |
|
861
|
6
|
50
|
|
|
|
51
|
? $knot_by_hex{$hex}->sem |
|
862
|
|
|
|
|
|
|
: IPC::Semaphore->new(hex($hex), 0, 0); |
|
863
|
|
|
|
|
|
|
|
|
864
|
6
|
50
|
|
|
|
20
|
if (defined $sem) { |
|
865
|
6
|
|
50
|
|
|
94
|
my $sem_id = $sem->id // '?'; |
|
866
|
6
|
|
50
|
|
|
92
|
my $marker = $sem->getval(SEM_MARKER) // '?'; |
|
867
|
6
|
|
50
|
|
|
176
|
my $readers = $sem->getval(SEM_READERS) // '?'; |
|
868
|
6
|
|
50
|
|
|
167
|
my $writers = $sem->getval(SEM_WRITERS) // '?'; |
|
869
|
6
|
|
50
|
|
|
95
|
my $protected = $sem->getval(SEM_PROTECTED) // '?'; |
|
870
|
|
|
|
|
|
|
# Continuation indent: one tab (8 spaces) from the left margin |
|
871
|
6
|
|
|
|
|
84
|
my $cont = ' ' x 8; |
|
872
|
6
|
|
|
|
|
88
|
$sem_str = join("\n", |
|
873
|
|
|
|
|
|
|
"sem_id: $sem_id", |
|
874
|
|
|
|
|
|
|
"${cont}1: SEM_MARKER=$marker", |
|
875
|
|
|
|
|
|
|
"${cont}2: READERS=$readers", |
|
876
|
|
|
|
|
|
|
"${cont}3: WRITERS=$writers", |
|
877
|
|
|
|
|
|
|
"${cont}4: PROTECTED=$protected", |
|
878
|
|
|
|
|
|
|
); |
|
879
|
|
|
|
|
|
|
} |
|
880
|
|
|
|
|
|
|
else { |
|
881
|
0
|
|
|
|
|
0
|
$sem_str = '(not accessible)'; |
|
882
|
|
|
|
|
|
|
} |
|
883
|
|
|
|
|
|
|
|
|
884
|
|
|
|
|
|
|
$content_str = $knot_by_hex{$hex} |
|
885
|
6
|
50
|
|
|
|
67
|
? _shm_data_summary($knot_by_hex{$hex}) |
|
886
|
|
|
|
|
|
|
: '(not accessible - segment not tied in this process)'; |
|
887
|
|
|
|
|
|
|
|
|
888
|
|
|
|
|
|
|
# Merge child keys from shm_segments() and from global_register walk |
|
889
|
6
|
|
|
|
|
10
|
my %seen_child; |
|
890
|
1
|
|
|
|
|
5
|
my @child_keys = grep { !$seen_child{$_}++ } ( |
|
891
|
6
|
|
50
|
|
|
19
|
@{ $seg->{child_keys} // [] }, |
|
892
|
6
|
|
100
|
|
|
11
|
@{ $extra_child_keys{$hex} // [] }, |
|
|
6
|
|
|
|
|
39
|
|
|
893
|
|
|
|
|
|
|
); |
|
894
|
6
|
100
|
|
|
|
20
|
my $children = @child_keys ? join(', ', @child_keys) : '(none)'; |
|
895
|
|
|
|
|
|
|
|
|
896
|
6
|
|
|
|
|
11
|
push @lines, ''; |
|
897
|
6
|
|
|
|
|
39
|
push @lines, "${indent}${tag_str} key: ${hex} seg_id: ${seg_id}"; |
|
898
|
6
|
|
|
|
|
28
|
push @lines, "${indent} Semaphores: ${sem_str}"; |
|
899
|
6
|
|
|
|
|
28
|
push @lines, "${indent} Children: ${children}"; |
|
900
|
6
|
|
|
|
|
24
|
push @lines, "${indent} Content: ${content_str}"; |
|
901
|
|
|
|
|
|
|
|
|
902
|
6
|
|
|
|
|
42
|
$render->($_, $depth + 1) for @child_keys; |
|
903
|
5
|
|
|
|
|
108
|
}; |
|
904
|
|
|
|
|
|
|
|
|
905
|
5
|
|
|
|
|
30
|
$render->($_, 0) for @roots; |
|
906
|
|
|
|
|
|
|
|
|
907
|
5
|
|
|
|
|
70
|
push @lines, ''; |
|
908
|
5
|
|
|
|
|
172
|
return join("\n", @lines) . "\n"; |
|
909
|
|
|
|
|
|
|
} |
|
910
|
|
|
|
|
|
|
sub sysv_info { |
|
911
|
6
|
|
|
6
|
1
|
9889
|
shift; # Discard invocant (object ref or class name) |
|
912
|
6
|
|
|
|
|
71
|
my %opts = @_; |
|
913
|
6
|
|
100
|
|
|
68
|
my $proc_dir = delete $opts{_proc_dir} // '/proc/sys/kernel'; |
|
914
|
6
|
|
|
|
|
14
|
my $sysctl_out = delete $opts{_sysctl_out}; |
|
915
|
|
|
|
|
|
|
|
|
916
|
6
|
|
|
|
|
16
|
my %info; |
|
917
|
|
|
|
|
|
|
|
|
918
|
6
|
50
|
|
|
|
81
|
if ($^O eq 'darwin') { |
|
|
|
100
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
919
|
0
|
|
0
|
|
|
0
|
my $out = $sysctl_out // `sysctl kern.sysv 2>/dev/null`; |
|
920
|
0
|
|
|
|
|
0
|
for my $line (split /\n/, $out) { |
|
921
|
0
|
0
|
|
|
|
0
|
if ($line =~ /^kern\.sysv\.(\w+):\s*(\S+)/) { |
|
922
|
0
|
|
|
|
|
0
|
$info{$1} = $2; |
|
923
|
|
|
|
|
|
|
} |
|
924
|
|
|
|
|
|
|
} |
|
925
|
|
|
|
|
|
|
} |
|
926
|
|
|
|
|
|
|
elsif ($^O eq 'freebsd') { |
|
927
|
1
|
|
33
|
|
|
6
|
my $out = $sysctl_out // `sysctl kern.ipc 2>/dev/null`; |
|
928
|
1
|
|
|
|
|
14
|
for my $line (split /\n/, $out) { |
|
929
|
6
|
100
|
|
|
|
39
|
if ($line =~ /^kern\.ipc\.(shm\w+):\s*(\S+)/) { |
|
930
|
5
|
|
|
|
|
26
|
$info{$1} = $2; |
|
931
|
|
|
|
|
|
|
} |
|
932
|
|
|
|
|
|
|
} |
|
933
|
|
|
|
|
|
|
} |
|
934
|
|
|
|
|
|
|
elsif ($^O eq 'linux') { |
|
935
|
5
|
|
|
|
|
29
|
for my $key (qw(shmmax shmmin shmmni shmall)) { |
|
936
|
20
|
|
|
|
|
68
|
my $file = "$proc_dir/$key"; |
|
937
|
20
|
100
|
|
|
|
1034
|
if (open my $fh, '<', $file) { |
|
938
|
16
|
|
|
|
|
428
|
chomp(my $val = <$fh>); |
|
939
|
16
|
|
|
|
|
346
|
$info{$key} = $val; |
|
940
|
|
|
|
|
|
|
} |
|
941
|
|
|
|
|
|
|
} |
|
942
|
|
|
|
|
|
|
} |
|
943
|
|
|
|
|
|
|
|
|
944
|
6
|
50
|
|
|
|
66
|
return %info ? \%info : undef; |
|
945
|
|
|
|
|
|
|
} |
|
946
|
|
|
|
|
|
|
|
|
947
|
|
|
|
|
|
|
sub clean_up { |
|
948
|
5
|
|
|
5
|
1
|
16842
|
my $class = shift; |
|
949
|
|
|
|
|
|
|
|
|
950
|
5
|
|
|
|
|
26
|
for my $id (keys %process_register) { |
|
951
|
3
|
|
|
|
|
8
|
my $s = $process_register{$id}; |
|
952
|
3
|
50
|
|
|
|
12
|
next unless $s->attributes('owner') == $$; |
|
953
|
3
|
50
|
|
|
|
13
|
next if $s->attributes('protected'); |
|
954
|
3
|
|
|
|
|
16
|
remove($s); |
|
955
|
|
|
|
|
|
|
} |
|
956
|
|
|
|
|
|
|
} |
|
957
|
|
|
|
|
|
|
sub clean_up_all { |
|
958
|
82
|
|
|
82
|
1
|
14766808
|
my $class = shift; |
|
959
|
|
|
|
|
|
|
|
|
960
|
82
|
|
|
|
|
528
|
my $global_register = __PACKAGE__->global_register; |
|
961
|
|
|
|
|
|
|
|
|
962
|
82
|
|
|
|
|
637
|
for my $id (keys %$global_register) { |
|
963
|
219
|
|
|
|
|
561
|
my $s = $global_register->{$id}; |
|
964
|
219
|
100
|
|
|
|
817
|
next if $s->attributes('protected'); |
|
965
|
216
|
|
|
|
|
791
|
remove($s); |
|
966
|
|
|
|
|
|
|
} |
|
967
|
|
|
|
|
|
|
} |
|
968
|
|
|
|
|
|
|
sub clean_up_protected { |
|
969
|
9
|
|
|
9
|
1
|
5631
|
my ($knot, $protect_key); |
|
970
|
|
|
|
|
|
|
|
|
971
|
9
|
100
|
|
|
|
38
|
if (scalar @_ == 2) { |
|
972
|
5
|
|
|
|
|
17
|
($knot, $protect_key) = @_; |
|
973
|
|
|
|
|
|
|
} |
|
974
|
9
|
100
|
|
|
|
37
|
if (scalar @_ == 1) { |
|
975
|
3
|
|
|
|
|
12
|
($protect_key) = @_; |
|
976
|
|
|
|
|
|
|
} |
|
977
|
|
|
|
|
|
|
|
|
978
|
9
|
100
|
|
|
|
46
|
if (! defined $protect_key) { |
|
979
|
1
|
|
|
|
|
410
|
croak "clean_up_protected() requires a \$protect_key param"; |
|
980
|
|
|
|
|
|
|
} |
|
981
|
|
|
|
|
|
|
|
|
982
|
8
|
100
|
|
|
|
71
|
if ($protect_key !~ /^\d+$/) { |
|
983
|
1
|
|
|
|
|
170
|
croak |
|
984
|
|
|
|
|
|
|
"clean_up_protected() \$protect_key must be an integer. You sent $protect_key"; |
|
985
|
|
|
|
|
|
|
} |
|
986
|
|
|
|
|
|
|
|
|
987
|
7
|
|
|
|
|
30
|
my $global_register = __PACKAGE__->global_register; |
|
988
|
|
|
|
|
|
|
|
|
989
|
7
|
|
|
|
|
19
|
for my $id (keys %$global_register) { |
|
990
|
8
|
|
|
|
|
12
|
my $s = $global_register->{$id}; |
|
991
|
8
|
|
|
|
|
26
|
my $stored_key = $s->attributes('protected'); |
|
992
|
|
|
|
|
|
|
|
|
993
|
8
|
50
|
33
|
|
|
61
|
if ($stored_key && $stored_key == $protect_key) { |
|
994
|
8
|
|
|
|
|
28
|
remove($s); |
|
995
|
|
|
|
|
|
|
} |
|
996
|
|
|
|
|
|
|
} |
|
997
|
|
|
|
|
|
|
} |
|
998
|
|
|
|
|
|
|
sub remove { |
|
999
|
333
|
|
|
333
|
1
|
24175
|
my ($knot, $key) = @_; |
|
1000
|
|
|
|
|
|
|
|
|
1001
|
|
|
|
|
|
|
# If a key is passed, remove that specific segment by key rather than |
|
1002
|
|
|
|
|
|
|
# via an existing tied object |
|
1003
|
|
|
|
|
|
|
|
|
1004
|
333
|
100
|
|
|
|
920
|
if (defined $key) { |
|
1005
|
5
|
|
|
|
|
122
|
$key = $knot->_shm_key($key); |
|
1006
|
5
|
|
|
|
|
46
|
my $id = shmget($key, 0, 0); |
|
1007
|
|
|
|
|
|
|
|
|
1008
|
5
|
100
|
|
|
|
28
|
if (! defined $id) { |
|
1009
|
1
|
|
|
|
|
76
|
warn "remove(): shmget failed for key $key: $!"; |
|
1010
|
1
|
|
|
|
|
20
|
return; |
|
1011
|
|
|
|
|
|
|
} |
|
1012
|
|
|
|
|
|
|
|
|
1013
|
4
|
50
|
|
|
|
50
|
if (! shmctl($id, IPC_RMID, 0)) { |
|
1014
|
0
|
|
|
|
|
0
|
warn "Couldn't remove shm segment $id: $!"; |
|
1015
|
|
|
|
|
|
|
} |
|
1016
|
|
|
|
|
|
|
else { |
|
1017
|
4
|
|
|
|
|
281
|
delete $process_register{$id}; |
|
1018
|
4
|
|
|
|
|
10
|
delete $global_register{$id}; |
|
1019
|
|
|
|
|
|
|
} |
|
1020
|
|
|
|
|
|
|
|
|
1021
|
|
|
|
|
|
|
# Remove the associated semaphore set (same key, attach-only with nsems=0) |
|
1022
|
|
|
|
|
|
|
|
|
1023
|
4
|
|
|
|
|
80
|
my $sem = IPC::Semaphore->new($key, 0, 0); |
|
1024
|
4
|
50
|
|
|
|
75
|
if (defined $sem) { |
|
1025
|
0
|
0
|
|
|
|
0
|
$sem->remove or warn "Couldn't remove semaphore set for key $key: $!"; |
|
1026
|
|
|
|
|
|
|
} |
|
1027
|
|
|
|
|
|
|
|
|
1028
|
4
|
|
|
|
|
14
|
return; |
|
1029
|
|
|
|
|
|
|
} |
|
1030
|
|
|
|
|
|
|
|
|
1031
|
|
|
|
|
|
|
# Standard object based removal |
|
1032
|
|
|
|
|
|
|
|
|
1033
|
328
|
|
|
|
|
901
|
my $seg = $knot->seg; |
|
1034
|
328
|
|
|
|
|
1404
|
my $id = $seg->id; |
|
1035
|
|
|
|
|
|
|
|
|
1036
|
328
|
|
|
|
|
643
|
my $seg_removed = 0; |
|
1037
|
|
|
|
|
|
|
|
|
1038
|
328
|
50
|
|
|
|
1025
|
if (! $seg->remove) { |
|
1039
|
0
|
|
|
|
|
0
|
warn "Couldn't remove shm segment $id: $!"; |
|
1040
|
|
|
|
|
|
|
} |
|
1041
|
|
|
|
|
|
|
else { |
|
1042
|
328
|
|
|
|
|
684
|
$seg_removed = 1; |
|
1043
|
|
|
|
|
|
|
} |
|
1044
|
|
|
|
|
|
|
|
|
1045
|
|
|
|
|
|
|
# Semaphore cleanup |
|
1046
|
|
|
|
|
|
|
|
|
1047
|
328
|
|
|
|
|
908
|
my $sem = $knot->sem; |
|
1048
|
|
|
|
|
|
|
|
|
1049
|
328
|
|
|
|
|
775
|
my $sem_removed = 0; |
|
1050
|
328
|
|
|
|
|
1587
|
my $sem_remove_status = $sem->remove; |
|
1051
|
|
|
|
|
|
|
|
|
1052
|
328
|
100
|
66
|
|
|
8054
|
if ($sem_remove_status != 1 && $sem_remove_status ne '0 but true') { |
|
1053
|
1
|
|
|
|
|
111
|
warn "Couldn't remove semaphore set $id: $!"; |
|
1054
|
|
|
|
|
|
|
} |
|
1055
|
|
|
|
|
|
|
else { |
|
1056
|
327
|
|
|
|
|
588
|
$sem_removed = 1; |
|
1057
|
|
|
|
|
|
|
} |
|
1058
|
|
|
|
|
|
|
|
|
1059
|
|
|
|
|
|
|
# If the segment or semaphore couldn't be cleaned up, we need to |
|
1060
|
|
|
|
|
|
|
# keep state |
|
1061
|
|
|
|
|
|
|
|
|
1062
|
328
|
100
|
66
|
|
|
1485
|
if ($seg_removed && $sem_removed) { |
|
1063
|
327
|
|
|
|
|
1221
|
delete $process_register{$id}; |
|
1064
|
327
|
|
|
|
|
9743
|
delete $global_register{$id}; |
|
1065
|
|
|
|
|
|
|
} |
|
1066
|
|
|
|
|
|
|
} |
|
1067
|
|
|
|
|
|
|
|
|
1068
|
|
|
|
|
|
|
# Private methods |
|
1069
|
|
|
|
|
|
|
|
|
1070
|
|
|
|
|
|
|
# Encoding/Decoding |
|
1071
|
|
|
|
|
|
|
|
|
1072
|
|
|
|
|
|
|
sub _encode { |
|
1073
|
918
|
|
|
918
|
|
2149
|
my ($knot, $seg, $data) = @_; |
|
1074
|
|
|
|
|
|
|
|
|
1075
|
918
|
|
|
|
|
2040
|
my $serializer = $knot->attributes('serializer'); |
|
1076
|
|
|
|
|
|
|
|
|
1077
|
918
|
100
|
|
|
|
2649
|
if ($serializer eq 'storable') { |
|
1078
|
504
|
|
|
|
|
1445
|
return _freeze($seg, $data); |
|
1079
|
|
|
|
|
|
|
} |
|
1080
|
|
|
|
|
|
|
|
|
1081
|
414
|
|
|
|
|
1062
|
return _encode_json($seg, $data); |
|
1082
|
|
|
|
|
|
|
} |
|
1083
|
|
|
|
|
|
|
sub _decode { |
|
1084
|
2803
|
|
|
2803
|
|
5671
|
my ($knot, $seg) = @_; |
|
1085
|
|
|
|
|
|
|
|
|
1086
|
2803
|
|
|
|
|
6625
|
my $serializer = $knot->attributes('serializer'); |
|
1087
|
|
|
|
|
|
|
|
|
1088
|
2803
|
100
|
|
|
|
9942
|
my $data = $serializer eq 'storable' |
|
1089
|
|
|
|
|
|
|
? _thaw($seg) |
|
1090
|
|
|
|
|
|
|
: _decode_json($seg, $knot); |
|
1091
|
|
|
|
|
|
|
|
|
1092
|
2798
|
100
|
|
|
|
12078
|
return $data if defined $data; |
|
1093
|
|
|
|
|
|
|
|
|
1094
|
|
|
|
|
|
|
# Empty/never-written segment — return appropriate empty default so that |
|
1095
|
|
|
|
|
|
|
# aggregate tie methods (FETCHSIZE, PUSH, CLEAR, etc.) can deref safely. |
|
1096
|
489
|
100
|
|
|
|
1705
|
return [] if $knot->{_type_int} == TYPE_ARRAY; |
|
1097
|
406
|
100
|
|
|
|
1777
|
return {} if $knot->{_type_int} == TYPE_HASH; |
|
1098
|
100
|
|
|
|
|
332
|
return undef; |
|
1099
|
|
|
|
|
|
|
} |
|
1100
|
|
|
|
|
|
|
sub _encode_json { |
|
1101
|
414
|
|
|
414
|
|
810
|
my $seg = shift; |
|
1102
|
414
|
|
|
|
|
686
|
my $data = shift; |
|
1103
|
|
|
|
|
|
|
|
|
1104
|
414
|
|
|
|
|
1033
|
my $json = encode_json _encode_json_prepare($data); |
|
1105
|
|
|
|
|
|
|
|
|
1106
|
414
|
|
|
|
|
1573
|
substr $json, 0, 0, 'IPC::Shareable'; |
|
1107
|
|
|
|
|
|
|
|
|
1108
|
414
|
100
|
|
|
|
1436
|
if (length($json) > $seg->size) { |
|
1109
|
1
|
|
|
|
|
122
|
croak "Length of shared data exceeds shared segment size"; |
|
1110
|
|
|
|
|
|
|
} |
|
1111
|
|
|
|
|
|
|
|
|
1112
|
413
|
|
|
|
|
1157
|
$seg->shmwrite($json); |
|
1113
|
|
|
|
|
|
|
} |
|
1114
|
|
|
|
|
|
|
sub _encode_json_prepare { |
|
1115
|
415
|
|
|
415
|
|
2061
|
my ($data) = @_; |
|
1116
|
|
|
|
|
|
|
|
|
1117
|
415
|
50
|
|
|
|
1223
|
my $type = Scalar::Util::reftype($data) or return $data; |
|
1118
|
|
|
|
|
|
|
|
|
1119
|
|
|
|
|
|
|
# Replace direct IPC::Shareable child segments with __ics__ markers. |
|
1120
|
|
|
|
|
|
|
# All nested refs are tied children — no recursion needed; each child |
|
1121
|
|
|
|
|
|
|
# segment encodes its own children independently. We have to do this because |
|
1122
|
|
|
|
|
|
|
# JSON can't store blessed objects |
|
1123
|
|
|
|
|
|
|
|
|
1124
|
415
|
100
|
|
|
|
922
|
if ($type eq 'HASH') { |
|
1125
|
272
|
|
|
|
|
460
|
my %result; |
|
1126
|
272
|
|
|
|
|
1024
|
for my $key (keys %$data) { |
|
1127
|
1220
|
|
|
|
|
1681
|
my $val = $data->{$key}; |
|
1128
|
1220
|
|
66
|
|
|
2369
|
my $inner = ref($val) && _is_child($val); |
|
1129
|
|
|
|
|
|
|
$result{$key} = $inner |
|
1130
|
1220
|
100
|
|
|
|
3251
|
? { '__ics__' => { type => $inner->{_type}, child_key => $inner->{_key}, child_key_hex => sprintf('0x%08x', $inner->{_key}) } } |
|
1131
|
|
|
|
|
|
|
: $val; |
|
1132
|
|
|
|
|
|
|
} |
|
1133
|
272
|
|
|
|
|
2269
|
return \%result; |
|
1134
|
|
|
|
|
|
|
} |
|
1135
|
|
|
|
|
|
|
|
|
1136
|
143
|
100
|
|
|
|
357
|
if ($type eq 'ARRAY') { |
|
1137
|
|
|
|
|
|
|
return [ |
|
1138
|
|
|
|
|
|
|
map { |
|
1139
|
128
|
|
66
|
|
|
490
|
my $inner = ref($_) && _is_child($_); |
|
|
335
|
|
|
|
|
705
|
|
|
1140
|
|
|
|
|
|
|
$inner |
|
1141
|
335
|
100
|
|
|
|
1558
|
? { '__ics__' => { type => $inner->{_type}, child_key => $inner->{_key}, child_key_hex => sprintf('0x%08x', $inner->{_key}) } } |
|
1142
|
|
|
|
|
|
|
: $_ |
|
1143
|
|
|
|
|
|
|
} @$data |
|
1144
|
|
|
|
|
|
|
]; |
|
1145
|
|
|
|
|
|
|
} |
|
1146
|
|
|
|
|
|
|
|
|
1147
|
15
|
100
|
100
|
|
|
125
|
if ($type eq 'SCALAR' || $type eq 'REF') { |
|
1148
|
14
|
|
|
|
|
35
|
my $val = $$data; |
|
1149
|
14
|
|
66
|
|
|
53
|
my $inner = ref($val) && _is_child($val); |
|
1150
|
|
|
|
|
|
|
return $inner |
|
1151
|
14
|
100
|
|
|
|
225
|
? { '__ics__' => { type => $inner->{_type}, child_key => $inner->{_key}, child_key_hex => sprintf('0x%08x', $inner->{_key}) } } |
|
1152
|
|
|
|
|
|
|
: { '__sv__' => $val }; |
|
1153
|
|
|
|
|
|
|
} |
|
1154
|
|
|
|
|
|
|
|
|
1155
|
1
|
|
|
|
|
3
|
return $data; |
|
1156
|
|
|
|
|
|
|
} |
|
1157
|
|
|
|
|
|
|
sub _decode_json { |
|
1158
|
1478
|
|
|
1478
|
|
3339
|
my ($seg, $knot) = @_; |
|
1159
|
|
|
|
|
|
|
|
|
1160
|
1478
|
|
|
|
|
5345
|
my $json = $seg->data; |
|
1161
|
|
|
|
|
|
|
|
|
1162
|
1478
|
100
|
|
|
|
4115
|
return if ! $json; |
|
1163
|
|
|
|
|
|
|
|
|
1164
|
|
|
|
|
|
|
# The return of shmread() is the actual size of the defined size of the |
|
1165
|
|
|
|
|
|
|
# shared memory segment. Even if the return equates to an empty string |
|
1166
|
|
|
|
|
|
|
# (which it will if it contains no data), there will always be a length(). |
|
1167
|
|
|
|
|
|
|
# Therefore, we must see if we've tagged this data as a valid structure, |
|
1168
|
|
|
|
|
|
|
# or else decode will fail |
|
1169
|
|
|
|
|
|
|
|
|
1170
|
1202
|
|
|
|
|
3377
|
my $tag = substr $json, 0, 14, ''; |
|
1171
|
|
|
|
|
|
|
|
|
1172
|
1202
|
100
|
|
|
|
2990
|
if ($tag eq 'IPC::Shareable') { |
|
1173
|
1201
|
|
|
|
|
13626
|
my $data = decode_json $json; |
|
1174
|
|
|
|
|
|
|
|
|
1175
|
1198
|
100
|
|
|
|
2965
|
if (! defined($data)){ |
|
1176
|
1
|
|
|
|
|
922
|
croak "Munged shared memory segment (size exceeded?)"; |
|
1177
|
|
|
|
|
|
|
} |
|
1178
|
|
|
|
|
|
|
|
|
1179
|
1197
|
100
|
66
|
|
|
7178
|
_decode_json_restore($data, $knot) if defined $knot && index($json, '"__ics__"') >= 0; |
|
1180
|
|
|
|
|
|
|
|
|
1181
|
|
|
|
|
|
|
# Unwrap scalar-tie values encoded as { '__sv__' => val } or { '__ics__' => {...} } |
|
1182
|
1197
|
100
|
66
|
|
|
5827
|
if (defined $knot && $knot->{_type_int} == TYPE_SCALAR && ref($data) eq 'HASH') { |
|
|
|
|
100
|
|
|
|
|
|
1183
|
84
|
100
|
|
|
|
182
|
if (exists $data->{'__ics__'}) { |
|
1184
|
44
|
|
|
|
|
63
|
my $prev = $knot->{_data}; |
|
1185
|
44
|
100
|
66
|
|
|
201
|
my $prev_val = (defined $prev && ref($prev)) ? $$prev : undef; |
|
1186
|
44
|
|
|
|
|
117
|
my $resolved = _decode_json_resolve($data->{'__ics__'}, $prev_val, $knot); |
|
1187
|
44
|
|
|
|
|
195
|
return \$resolved; |
|
1188
|
|
|
|
|
|
|
} |
|
1189
|
40
|
100
|
|
|
|
114
|
if (exists $data->{'__sv__'}) { |
|
1190
|
23
|
|
|
|
|
47
|
my $val = $data->{'__sv__'}; |
|
1191
|
23
|
|
|
|
|
80
|
return \$val; |
|
1192
|
|
|
|
|
|
|
} |
|
1193
|
|
|
|
|
|
|
} |
|
1194
|
|
|
|
|
|
|
|
|
1195
|
1130
|
|
|
|
|
2909
|
return $data; |
|
1196
|
|
|
|
|
|
|
} else { |
|
1197
|
1
|
|
|
|
|
3
|
return; |
|
1198
|
|
|
|
|
|
|
} |
|
1199
|
|
|
|
|
|
|
} |
|
1200
|
|
|
|
|
|
|
sub _decode_json_restore { |
|
1201
|
375
|
|
|
375
|
|
831
|
my ($data, $knot) = @_; |
|
1202
|
|
|
|
|
|
|
|
|
1203
|
375
|
50
|
|
|
|
1237
|
my $type = Scalar::Util::reftype($data) or return; |
|
1204
|
|
|
|
|
|
|
|
|
1205
|
|
|
|
|
|
|
# Reuse existing tied child refs from previous decode where possible. |
|
1206
|
|
|
|
|
|
|
# This avoids a shmget+semget system call pair for each child on every |
|
1207
|
|
|
|
|
|
|
# decode cycle — only the first attach per segment incurs that cost. |
|
1208
|
|
|
|
|
|
|
|
|
1209
|
375
|
|
|
|
|
918
|
my $prev = $knot->{_data}; |
|
1210
|
|
|
|
|
|
|
|
|
1211
|
375
|
100
|
|
|
|
821
|
if ($type eq 'HASH') { |
|
|
|
50
|
|
|
|
|
|
|
1212
|
284
|
|
|
|
|
1298
|
for my $key (keys %$data) { |
|
1213
|
613
|
100
|
100
|
|
|
2528
|
next unless ref($data->{$key}) eq 'HASH' && exists $data->{$key}{'__ics__'}; |
|
1214
|
|
|
|
|
|
|
$data->{$key} = _decode_json_resolve( |
|
1215
|
|
|
|
|
|
|
$data->{$key}{'__ics__'}, |
|
1216
|
365
|
100
|
|
|
|
1475
|
ref($prev) eq 'HASH' ? $prev->{$key} : undef, |
|
1217
|
|
|
|
|
|
|
$knot, |
|
1218
|
|
|
|
|
|
|
); |
|
1219
|
|
|
|
|
|
|
} |
|
1220
|
|
|
|
|
|
|
} |
|
1221
|
|
|
|
|
|
|
elsif ($type eq 'ARRAY') { |
|
1222
|
91
|
|
|
|
|
319
|
for my $i (0 .. $#$data) { |
|
1223
|
192
|
100
|
66
|
|
|
817
|
next unless ref($data->[$i]) eq 'HASH' && exists $data->[$i]{'__ics__'}; |
|
1224
|
|
|
|
|
|
|
$data->[$i] = _decode_json_resolve( |
|
1225
|
171
|
100
|
100
|
|
|
758
|
$data->[$i]{'__ics__'}, |
|
1226
|
|
|
|
|
|
|
ref($prev) eq 'ARRAY' && $i <= $#$prev ? $prev->[$i] : undef, |
|
1227
|
|
|
|
|
|
|
$knot, |
|
1228
|
|
|
|
|
|
|
); |
|
1229
|
|
|
|
|
|
|
} |
|
1230
|
|
|
|
|
|
|
} |
|
1231
|
|
|
|
|
|
|
} |
|
1232
|
|
|
|
|
|
|
sub _decode_json_resolve { |
|
1233
|
580
|
|
|
580
|
|
1297
|
my ($info, $existing, $knot) = @_; |
|
1234
|
|
|
|
|
|
|
|
|
1235
|
580
|
100
|
|
|
|
1262
|
if (defined $existing) { |
|
1236
|
520
|
|
66
|
|
|
2192
|
my $inner = ref($existing) && _is_child($existing); |
|
1237
|
520
|
100
|
100
|
|
|
4418
|
return $existing if $inner && $inner->{_key} == $info->{child_key}; |
|
1238
|
|
|
|
|
|
|
} |
|
1239
|
|
|
|
|
|
|
|
|
1240
|
70
|
|
|
|
|
302
|
return _decode_json_reattach($info, $knot); |
|
1241
|
|
|
|
|
|
|
} |
|
1242
|
|
|
|
|
|
|
sub _decode_json_reattach { |
|
1243
|
70
|
|
|
70
|
|
138
|
my ($info, $knot) = @_; |
|
1244
|
|
|
|
|
|
|
|
|
1245
|
|
|
|
|
|
|
my %opts = ( |
|
1246
|
70
|
|
|
|
|
193
|
%{ $knot->attributes }, |
|
1247
|
|
|
|
|
|
|
key => $info->{child_key}, |
|
1248
|
70
|
|
|
|
|
114
|
exclusive => 0, |
|
1249
|
|
|
|
|
|
|
create => 0, |
|
1250
|
|
|
|
|
|
|
magic => 1, |
|
1251
|
|
|
|
|
|
|
); |
|
1252
|
|
|
|
|
|
|
|
|
1253
|
70
|
100
|
|
|
|
327
|
if ($info->{type} eq 'HASH') { |
|
|
|
100
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
1254
|
31
|
|
|
|
|
51
|
my %h; |
|
1255
|
31
|
|
|
|
|
224
|
tie %h, 'IPC::Shareable', \%opts; |
|
1256
|
31
|
|
|
|
|
313
|
return \%h; |
|
1257
|
|
|
|
|
|
|
} |
|
1258
|
|
|
|
|
|
|
elsif ($info->{type} eq 'ARRAY') { |
|
1259
|
38
|
|
|
|
|
58
|
my @a; |
|
1260
|
38
|
|
|
|
|
229
|
tie @a, 'IPC::Shareable', \%opts; |
|
1261
|
38
|
|
|
|
|
265
|
return \@a; |
|
1262
|
|
|
|
|
|
|
} |
|
1263
|
|
|
|
|
|
|
elsif ($info->{type} eq 'SCALAR') { |
|
1264
|
1
|
|
|
|
|
2
|
my $s; |
|
1265
|
1
|
|
|
|
|
8
|
tie $s, 'IPC::Shareable', \%opts; |
|
1266
|
1
|
|
|
|
|
4
|
return \$s; |
|
1267
|
|
|
|
|
|
|
} |
|
1268
|
|
|
|
|
|
|
} |
|
1269
|
|
|
|
|
|
|
sub _freeze { |
|
1270
|
504
|
|
|
504
|
|
819
|
my $seg = shift; |
|
1271
|
504
|
|
|
|
|
728
|
my $water = shift; |
|
1272
|
|
|
|
|
|
|
|
|
1273
|
504
|
|
|
|
|
2498
|
my $ice = freeze $water; |
|
1274
|
504
|
|
|
|
|
24986
|
substr $ice, 0, 0, 'IPC::Shareable'; |
|
1275
|
|
|
|
|
|
|
|
|
1276
|
504
|
100
|
|
|
|
1648
|
if (length($ice) > $seg->size) { |
|
1277
|
1
|
|
|
|
|
298
|
croak "Length of shared data exceeds shared segment size"; |
|
1278
|
|
|
|
|
|
|
} |
|
1279
|
|
|
|
|
|
|
|
|
1280
|
503
|
|
|
|
|
1529
|
$seg->shmwrite($ice); |
|
1281
|
|
|
|
|
|
|
} |
|
1282
|
|
|
|
|
|
|
sub _thaw { |
|
1283
|
1595
|
|
|
1595
|
|
2567
|
my $seg = shift; |
|
1284
|
|
|
|
|
|
|
|
|
1285
|
1595
|
|
|
|
|
7232
|
my $ice = $seg->shmread; |
|
1286
|
|
|
|
|
|
|
|
|
1287
|
1595
|
100
|
|
|
|
4301
|
return if ! $ice; |
|
1288
|
|
|
|
|
|
|
|
|
1289
|
1588
|
|
|
|
|
4656
|
my $tag = substr $ice, 0, 14, ''; |
|
1290
|
|
|
|
|
|
|
|
|
1291
|
1588
|
100
|
|
|
|
3892
|
if ($tag eq 'IPC::Shareable') { |
|
1292
|
1129
|
|
|
|
|
4825
|
my $water = thaw $ice; |
|
1293
|
1129
|
100
|
|
|
|
71128
|
if (! defined($water)){ |
|
1294
|
1
|
|
|
|
|
164
|
croak "Munged shared memory segment (size exceeded?)"; |
|
1295
|
|
|
|
|
|
|
} |
|
1296
|
1128
|
|
|
|
|
10010
|
return $water; |
|
1297
|
|
|
|
|
|
|
} else { |
|
1298
|
459
|
|
|
|
|
4543
|
return; |
|
1299
|
|
|
|
|
|
|
} |
|
1300
|
|
|
|
|
|
|
} |
|
1301
|
|
|
|
|
|
|
|
|
1302
|
|
|
|
|
|
|
# Data management |
|
1303
|
|
|
|
|
|
|
|
|
1304
|
|
|
|
|
|
|
sub _tie { |
|
1305
|
499
|
|
|
499
|
|
3168
|
my ($type, $class, $key_str, $opts); |
|
1306
|
|
|
|
|
|
|
|
|
1307
|
499
|
100
|
|
|
|
4059
|
if (scalar @_ == 4) { |
|
1308
|
|
|
|
|
|
|
# Legacy API allowed a string scalar key |
|
1309
|
176
|
|
|
|
|
1803
|
($type, $class, $key_str, $opts) = @_; |
|
1310
|
176
|
|
|
|
|
1179
|
$opts->{key} = $key_str; |
|
1311
|
|
|
|
|
|
|
} |
|
1312
|
|
|
|
|
|
|
else { |
|
1313
|
323
|
|
|
|
|
1977
|
($type, $class, $opts) = @_; |
|
1314
|
|
|
|
|
|
|
} |
|
1315
|
|
|
|
|
|
|
|
|
1316
|
499
|
|
|
|
|
4358
|
$opts = _parse_args($opts); |
|
1317
|
|
|
|
|
|
|
|
|
1318
|
499
|
|
|
|
|
3737
|
my $knot = bless { attributes => $opts }, $class; |
|
1319
|
|
|
|
|
|
|
|
|
1320
|
499
|
|
|
|
|
3655
|
$knot->uuid; |
|
1321
|
|
|
|
|
|
|
|
|
1322
|
499
|
|
|
|
|
2950
|
my $key = $knot->_shm_key; |
|
1323
|
499
|
|
|
|
|
2378
|
my $flags = $knot->_shm_flags; |
|
1324
|
499
|
|
|
|
|
1642
|
my $shm_size = $knot->attributes('size'); |
|
1325
|
|
|
|
|
|
|
|
|
1326
|
499
|
100
|
100
|
|
|
1463
|
if ($knot->attributes('limit') && $shm_size > SHMMAX_BYTES) { |
|
1327
|
2
|
|
|
|
|
494
|
croak |
|
1328
|
|
|
|
|
|
|
"Shared memory segment size '$shm_size' is larger than max size of " . |
|
1329
|
|
|
|
|
|
|
SHMMAX_BYTES; |
|
1330
|
|
|
|
|
|
|
} |
|
1331
|
|
|
|
|
|
|
|
|
1332
|
497
|
|
|
|
|
1503
|
my $seg; |
|
1333
|
|
|
|
|
|
|
|
|
1334
|
497
|
100
|
|
|
|
1583
|
if ($knot->attributes('graceful')) { |
|
1335
|
8
|
|
|
|
|
24
|
my $exclusive = eval { |
|
1336
|
8
|
|
|
|
|
90
|
$seg = IPC::Shareable::SharedMem->new( |
|
1337
|
|
|
|
|
|
|
key => $key, |
|
1338
|
|
|
|
|
|
|
size => $shm_size, |
|
1339
|
|
|
|
|
|
|
flags => $flags, |
|
1340
|
|
|
|
|
|
|
mode => $knot->attributes('mode'), |
|
1341
|
|
|
|
|
|
|
type => $type, |
|
1342
|
|
|
|
|
|
|
); |
|
1343
|
4
|
|
|
|
|
12
|
1; |
|
1344
|
|
|
|
|
|
|
}; |
|
1345
|
|
|
|
|
|
|
|
|
1346
|
8
|
100
|
|
|
|
44
|
if (! defined $exclusive) { |
|
1347
|
4
|
100
|
|
|
|
14
|
if ($knot->attributes('warn')) { |
|
1348
|
1
|
|
|
|
|
3
|
my $key = lc(sprintf("0x%X", $knot->_shm_key)); |
|
1349
|
|
|
|
|
|
|
|
|
1350
|
1
|
|
|
|
|
30
|
warn "Process ID $$ exited due to exclusive shared memory collision at segment/semaphore key '$key'\n"; |
|
1351
|
|
|
|
|
|
|
} |
|
1352
|
4
|
|
|
|
|
552
|
exit(0); |
|
1353
|
|
|
|
|
|
|
} |
|
1354
|
|
|
|
|
|
|
} |
|
1355
|
|
|
|
|
|
|
else { |
|
1356
|
489
|
|
|
|
|
1880
|
$seg = IPC::Shareable::SharedMem->new( |
|
1357
|
|
|
|
|
|
|
key => $key, |
|
1358
|
|
|
|
|
|
|
size => $shm_size, |
|
1359
|
|
|
|
|
|
|
flags => $flags, |
|
1360
|
|
|
|
|
|
|
mode => $knot->attributes('mode'), |
|
1361
|
|
|
|
|
|
|
type => $type, |
|
1362
|
|
|
|
|
|
|
); |
|
1363
|
|
|
|
|
|
|
} |
|
1364
|
|
|
|
|
|
|
|
|
1365
|
491
|
100
|
|
|
|
1385
|
if (! defined $seg) { |
|
1366
|
7
|
100
|
|
|
|
52
|
if ($! =~ /Cannot allocate memory/) { |
|
1367
|
2
|
|
|
|
|
287
|
croak "\nERROR: Could not create shared memory segment: $!\n\n" . |
|
1368
|
|
|
|
|
|
|
"Are you using too large a segment size, or spawning too many segments?"; |
|
1369
|
|
|
|
|
|
|
} |
|
1370
|
|
|
|
|
|
|
|
|
1371
|
5
|
50
|
|
|
|
34
|
if ($! =~ /No space left on device/) { |
|
1372
|
0
|
|
|
|
|
0
|
croak "\nERROR: Could not create shared memory segment: $!\n\n" . |
|
1373
|
|
|
|
|
|
|
"Are you spawning too many segments (in a loop perhaps)?"; |
|
1374
|
|
|
|
|
|
|
} |
|
1375
|
|
|
|
|
|
|
|
|
1376
|
5
|
100
|
66
|
|
|
17
|
if (! $knot->attributes('create')) { |
|
|
|
100
|
|
|
|
|
|
|
1377
|
3
|
|
|
|
|
1676
|
confess "ERROR: Could not acquire shared memory segment... 'create' ". |
|
1378
|
|
|
|
|
|
|
"option is not set, and the segment hasn't been created " . |
|
1379
|
|
|
|
|
|
|
"yet:\n\n $!"; |
|
1380
|
|
|
|
|
|
|
} |
|
1381
|
|
|
|
|
|
|
elsif ($knot->attributes('create') && $knot->attributes('exclusive')){ |
|
1382
|
1
|
|
|
|
|
115
|
croak "ERROR: Could not create shared memory segment. 'create' " . |
|
1383
|
|
|
|
|
|
|
"and 'exclusive' are set. Does the segment already exist? " . |
|
1384
|
|
|
|
|
|
|
"\n\n$!"; |
|
1385
|
|
|
|
|
|
|
} |
|
1386
|
|
|
|
|
|
|
else { |
|
1387
|
1
|
|
|
|
|
205
|
croak "ERROR: Could not create shared memory segment.\n\n$!"; |
|
1388
|
|
|
|
|
|
|
} |
|
1389
|
|
|
|
|
|
|
} |
|
1390
|
|
|
|
|
|
|
|
|
1391
|
|
|
|
|
|
|
# Try to attach to an existing semaphore set first using nsems=0, which |
|
1392
|
|
|
|
|
|
|
# avoids EINVAL on macOS/BSD when the existing set has fewer slots than |
|
1393
|
|
|
|
|
|
|
# the requested count. If the set does not exist yet, fall through to |
|
1394
|
|
|
|
|
|
|
# create a new 4-slot set (SEM_MARKER=0, SEM_PROTECTED=1, shared/write |
|
1395
|
|
|
|
|
|
|
# lock counters=2/3). |
|
1396
|
484
|
|
100
|
|
|
1790
|
my $sem = IPC::Semaphore->new($key, 0, $seg->flags & 0777) |
|
1397
|
|
|
|
|
|
|
// IPC::Semaphore->new($key, 4, $seg->flags); |
|
1398
|
|
|
|
|
|
|
|
|
1399
|
484
|
100
|
|
|
|
10164
|
if (! defined $sem){ |
|
1400
|
1
|
|
|
|
|
127
|
croak "Could not create semaphore set: $!\n"; |
|
1401
|
|
|
|
|
|
|
} |
|
1402
|
|
|
|
|
|
|
|
|
1403
|
483
|
100
|
|
|
|
909
|
if (! $sem->op(@{ $semop_args{(LOCK_SH)} }) ) { |
|
|
483
|
|
|
|
|
3852
|
|
|
1404
|
1
|
|
|
|
|
141
|
croak "Could not obtain semaphore set lock: $!\n"; |
|
1405
|
|
|
|
|
|
|
} |
|
1406
|
|
|
|
|
|
|
|
|
1407
|
482
|
100
|
|
|
|
13653
|
%$knot = ( |
|
|
|
100
|
|
|
|
|
|
|
1408
|
|
|
|
|
|
|
%$knot, |
|
1409
|
|
|
|
|
|
|
_hkey_list => undef, |
|
1410
|
|
|
|
|
|
|
_key => $key, |
|
1411
|
|
|
|
|
|
|
_key_hex => $seg->key_hex, |
|
1412
|
|
|
|
|
|
|
_lock => 0, |
|
1413
|
|
|
|
|
|
|
_shm => $seg, |
|
1414
|
|
|
|
|
|
|
_sem => $sem, |
|
1415
|
|
|
|
|
|
|
_type => $type, |
|
1416
|
|
|
|
|
|
|
_type_int => $type eq 'HASH' ? TYPE_HASH : $type eq 'ARRAY' ? TYPE_ARRAY : TYPE_SCALAR, |
|
1417
|
|
|
|
|
|
|
_was_changed => 0, |
|
1418
|
|
|
|
|
|
|
); |
|
1419
|
|
|
|
|
|
|
|
|
1420
|
482
|
|
|
|
|
1796
|
my $serializer = $knot->attributes('serializer'); |
|
1421
|
|
|
|
|
|
|
|
|
1422
|
482
|
100
|
|
|
|
1550
|
if ($serializer eq 'json') { |
|
1423
|
216
|
|
|
|
|
453
|
my $data; |
|
1424
|
216
|
|
|
|
|
868
|
my $decoded_ok = eval { $data = $knot->_decode($seg); 1 }; |
|
|
216
|
|
|
|
|
1165
|
|
|
|
213
|
|
|
|
|
534
|
|
|
1425
|
|
|
|
|
|
|
|
|
1426
|
216
|
100
|
|
|
|
705
|
if (! $decoded_ok) { |
|
1427
|
|
|
|
|
|
|
# JSON decode threw — the segment may contain legacy Storable data. |
|
1428
|
|
|
|
|
|
|
# Try Storable; if it succeeds, silently switch this session over |
|
1429
|
|
|
|
|
|
|
# and warn the caller so they know to migrate. |
|
1430
|
3
|
|
|
|
|
7
|
my $storable_data; |
|
1431
|
3
|
|
|
|
|
7
|
my $thaw_ok = eval { $storable_data = _thaw($seg); 1 }; |
|
|
3
|
|
|
|
|
11
|
|
|
|
3
|
|
|
|
|
7
|
|
|
1432
|
|
|
|
|
|
|
|
|
1433
|
3
|
50
|
33
|
|
|
22
|
if ($thaw_ok && defined $storable_data) { |
|
1434
|
3
|
|
|
|
|
867
|
carp sprintf( |
|
1435
|
|
|
|
|
|
|
"IPC::Shareable: segment 0x%08x contains Storable-encoded data; " |
|
1436
|
|
|
|
|
|
|
. "switching serializer to 'storable' for this session. " |
|
1437
|
|
|
|
|
|
|
. "Re-create the segment to migrate it to JSON.", |
|
1438
|
|
|
|
|
|
|
$key |
|
1439
|
|
|
|
|
|
|
); |
|
1440
|
3
|
|
|
|
|
33
|
$knot->{attributes}{serializer} = 'storable'; |
|
1441
|
3
|
|
|
|
|
10
|
$knot->{_data} = $storable_data; |
|
1442
|
|
|
|
|
|
|
} |
|
1443
|
|
|
|
|
|
|
else { |
|
1444
|
0
|
|
|
|
|
0
|
die $@; |
|
1445
|
|
|
|
|
|
|
} |
|
1446
|
|
|
|
|
|
|
} |
|
1447
|
|
|
|
|
|
|
else { |
|
1448
|
213
|
|
|
|
|
1183
|
$knot->{_data} = $data; |
|
1449
|
|
|
|
|
|
|
} |
|
1450
|
|
|
|
|
|
|
} |
|
1451
|
|
|
|
|
|
|
else { |
|
1452
|
266
|
|
|
|
|
979
|
$knot->{_data} = _thaw($seg); |
|
1453
|
|
|
|
|
|
|
} |
|
1454
|
|
|
|
|
|
|
|
|
1455
|
|
|
|
|
|
|
# Register unconditionally so any process that attaches to an existing |
|
1456
|
|
|
|
|
|
|
# segment (create=>0, re-attach, cross-process) is also tracked for |
|
1457
|
|
|
|
|
|
|
# clean_up_all(). Previously only new segments were registered here, |
|
1458
|
|
|
|
|
|
|
# requiring the Dumper hack in global_register() to catch the rest. |
|
1459
|
|
|
|
|
|
|
|
|
1460
|
482
|
100
|
|
|
|
1877
|
if (! exists $global_register{$knot->seg->id}) { |
|
1461
|
389
|
|
|
|
|
1184
|
$global_register{$knot->seg->id} = $knot; |
|
1462
|
|
|
|
|
|
|
} |
|
1463
|
|
|
|
|
|
|
|
|
1464
|
482
|
100
|
|
|
|
3362
|
if ($sem->getval(SEM_MARKER) != SHM_EXISTS) { |
|
1465
|
|
|
|
|
|
|
|
|
1466
|
366
|
|
33
|
|
|
12101
|
$process_register{$knot->seg->id} ||= $knot; |
|
1467
|
|
|
|
|
|
|
|
|
1468
|
366
|
|
|
|
|
1208
|
$sem->setval(SEM_PROTECTED, $knot->attributes('protected')); |
|
1469
|
|
|
|
|
|
|
|
|
1470
|
366
|
100
|
|
|
|
8013
|
if (! $sem->setval(SEM_MARKER, SHM_EXISTS)){ |
|
1471
|
1
|
|
|
|
|
157
|
croak "Couldn't set semaphore during object creation: $!"; |
|
1472
|
|
|
|
|
|
|
} |
|
1473
|
|
|
|
|
|
|
} |
|
1474
|
|
|
|
|
|
|
else { |
|
1475
|
|
|
|
|
|
|
# Segment already existed — restore the protected attribute from the |
|
1476
|
|
|
|
|
|
|
# semaphore so that clean_up_all() in this process correctly skips it |
|
1477
|
|
|
|
|
|
|
# even when the caller did not explicitly pass protected => N. |
|
1478
|
116
|
|
|
|
|
3047
|
my $stored_protected = $sem->getval(SEM_PROTECTED); |
|
1479
|
116
|
100
|
66
|
|
|
2166
|
$knot->{attributes}{protected} = $stored_protected |
|
1480
|
|
|
|
|
|
|
if defined $stored_protected && $stored_protected != 0; |
|
1481
|
|
|
|
|
|
|
} |
|
1482
|
|
|
|
|
|
|
|
|
1483
|
481
|
|
|
|
|
4680
|
$sem->op(@{ $semop_args{(LOCK_SH|LOCK_UN)} }); |
|
|
481
|
|
|
|
|
2575
|
|
|
1484
|
|
|
|
|
|
|
|
|
1485
|
481
|
|
|
|
|
10066
|
return $knot; |
|
1486
|
|
|
|
|
|
|
} |
|
1487
|
|
|
|
|
|
|
sub _magic_tie { |
|
1488
|
143
|
|
|
143
|
|
443
|
my ($parent, $val, $identifier) = @_; |
|
1489
|
|
|
|
|
|
|
|
|
1490
|
143
|
|
|
|
|
227
|
my $key; |
|
1491
|
|
|
|
|
|
|
|
|
1492
|
143
|
100
|
100
|
|
|
923
|
if ($parent->{_key} == IPC_PRIVATE && $parent->attributes('serializer') ne 'json') { |
|
1493
|
48
|
|
|
|
|
98
|
$key = IPC_PRIVATE; |
|
1494
|
|
|
|
|
|
|
} |
|
1495
|
|
|
|
|
|
|
else { |
|
1496
|
95
|
|
|
|
|
672
|
$key = _shm_key_rand(); |
|
1497
|
|
|
|
|
|
|
} |
|
1498
|
|
|
|
|
|
|
|
|
1499
|
|
|
|
|
|
|
# The individual options in the hash override any pre-set options that are |
|
1500
|
|
|
|
|
|
|
# being inherited from the parent |
|
1501
|
|
|
|
|
|
|
|
|
1502
|
|
|
|
|
|
|
my %opts = ( |
|
1503
|
142
|
|
|
|
|
343
|
%{ $parent->attributes }, |
|
|
142
|
|
|
|
|
417
|
|
|
1504
|
|
|
|
|
|
|
key => $key, |
|
1505
|
|
|
|
|
|
|
exclusive => 1, |
|
1506
|
|
|
|
|
|
|
create => 1, |
|
1507
|
|
|
|
|
|
|
magic => 1, |
|
1508
|
|
|
|
|
|
|
); |
|
1509
|
|
|
|
|
|
|
|
|
1510
|
|
|
|
|
|
|
# XXX I wish I didn't have to take a copy of data here and copy it back in |
|
1511
|
|
|
|
|
|
|
# XXX Also, have to peek inside potential objects to see their implementation |
|
1512
|
|
|
|
|
|
|
|
|
1513
|
142
|
|
|
|
|
469
|
my $child; |
|
1514
|
142
|
|
50
|
|
|
502
|
my $type = Scalar::Util::reftype($val) || ''; |
|
1515
|
|
|
|
|
|
|
|
|
1516
|
142
|
100
|
|
|
|
478
|
if ($type eq "HASH") { |
|
|
|
100
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
1517
|
101
|
|
|
|
|
402
|
my %copy = %$val; |
|
1518
|
101
|
|
|
|
|
2273
|
$child = tie %$val, 'IPC::Shareable', $key, { %opts }; |
|
1519
|
101
|
50
|
|
|
|
577
|
croak "Could not create inner tie" if ! $child; |
|
1520
|
|
|
|
|
|
|
|
|
1521
|
101
|
100
|
|
|
|
2243
|
_reset_segment($parent, $identifier) if $opts{tidy}; |
|
1522
|
|
|
|
|
|
|
|
|
1523
|
101
|
|
|
|
|
1046
|
%$val = %copy; |
|
1524
|
|
|
|
|
|
|
} |
|
1525
|
|
|
|
|
|
|
elsif ($type eq "ARRAY") { |
|
1526
|
38
|
|
|
|
|
104
|
my @copy = @$val; |
|
1527
|
38
|
|
|
|
|
703
|
$child = tie @$val, 'IPC::Shareable', $key, { %opts }; |
|
1528
|
38
|
50
|
|
|
|
181
|
croak "Could not create inner tie" if ! $child; |
|
1529
|
|
|
|
|
|
|
|
|
1530
|
38
|
100
|
|
|
|
204
|
_reset_segment($parent, $identifier) if $opts{tidy}; |
|
1531
|
|
|
|
|
|
|
|
|
1532
|
38
|
|
|
|
|
241
|
@$val = @copy; |
|
1533
|
|
|
|
|
|
|
} |
|
1534
|
|
|
|
|
|
|
elsif ($type eq "SCALAR") { |
|
1535
|
2
|
|
|
|
|
5
|
my $copy = $$val; |
|
1536
|
2
|
|
|
|
|
32
|
$child = tie $$val, 'IPC::Shareable', $key, { %opts }; |
|
1537
|
2
|
50
|
|
|
|
6
|
croak "Could not create inner tie" if ! $child; |
|
1538
|
|
|
|
|
|
|
|
|
1539
|
2
|
|
|
|
|
15
|
$$val = $copy; |
|
1540
|
|
|
|
|
|
|
} |
|
1541
|
|
|
|
|
|
|
else { |
|
1542
|
1
|
|
|
|
|
264
|
croak "Variables of type $type not implemented"; |
|
1543
|
|
|
|
|
|
|
} |
|
1544
|
|
|
|
|
|
|
|
|
1545
|
141
|
|
|
|
|
1027
|
return $child; |
|
1546
|
|
|
|
|
|
|
} |
|
1547
|
|
|
|
|
|
|
sub _is_child { |
|
1548
|
1152
|
50
|
|
1152
|
|
3224
|
my $data = shift or return; |
|
1549
|
|
|
|
|
|
|
|
|
1550
|
1152
|
|
|
|
|
2102
|
my $type = Scalar::Util::reftype( $data ); |
|
1551
|
1152
|
50
|
|
|
|
2379
|
return unless $type; |
|
1552
|
|
|
|
|
|
|
|
|
1553
|
1152
|
|
|
|
|
1592
|
my $obj; |
|
1554
|
|
|
|
|
|
|
|
|
1555
|
1152
|
100
|
|
|
|
2403
|
if ($type eq "HASH") { |
|
|
|
100
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
1556
|
886
|
|
|
|
|
1680
|
$obj = tied %$data; |
|
1557
|
|
|
|
|
|
|
} |
|
1558
|
|
|
|
|
|
|
elsif ($type eq "ARRAY") { |
|
1559
|
248
|
|
|
|
|
427
|
$obj = tied @$data; |
|
1560
|
|
|
|
|
|
|
} |
|
1561
|
|
|
|
|
|
|
elsif ($type eq "SCALAR") { |
|
1562
|
18
|
|
|
|
|
38
|
$obj = tied $$data; |
|
1563
|
|
|
|
|
|
|
} |
|
1564
|
|
|
|
|
|
|
|
|
1565
|
1152
|
100
|
|
|
|
2912
|
if (ref $obj eq 'IPC::Shareable') { |
|
1566
|
1151
|
|
|
|
|
3757
|
return $obj; |
|
1567
|
|
|
|
|
|
|
} |
|
1568
|
|
|
|
|
|
|
|
|
1569
|
1
|
|
|
|
|
2
|
return; |
|
1570
|
|
|
|
|
|
|
} |
|
1571
|
|
|
|
|
|
|
sub _need_tie { |
|
1572
|
143
|
|
|
143
|
|
438
|
my ($knot, $val, $identifier) = @_; |
|
1573
|
|
|
|
|
|
|
|
|
1574
|
143
|
|
|
|
|
392
|
my $type = Scalar::Util::reftype($val); |
|
1575
|
143
|
50
|
|
|
|
409
|
return 0 if ! $type; |
|
1576
|
|
|
|
|
|
|
|
|
1577
|
143
|
|
|
|
|
259
|
my $need_tie; |
|
1578
|
|
|
|
|
|
|
|
|
1579
|
143
|
100
|
|
|
|
477
|
if ($type eq "HASH") { |
|
|
|
100
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
1580
|
102
|
|
|
|
|
338
|
$need_tie = !(tied %$val); |
|
1581
|
|
|
|
|
|
|
} |
|
1582
|
|
|
|
|
|
|
elsif ($type eq "ARRAY") { |
|
1583
|
38
|
|
|
|
|
91
|
$need_tie = !(tied @$val); |
|
1584
|
|
|
|
|
|
|
} |
|
1585
|
|
|
|
|
|
|
elsif ($type eq "SCALAR") { |
|
1586
|
3
|
|
|
|
|
6
|
$need_tie = !(tied $$val); |
|
1587
|
|
|
|
|
|
|
} |
|
1588
|
|
|
|
|
|
|
|
|
1589
|
143
|
100
|
|
|
|
1283
|
return $need_tie ? 1 : 0; |
|
1590
|
|
|
|
|
|
|
} |
|
1591
|
|
|
|
|
|
|
|
|
1592
|
|
|
|
|
|
|
# Segment/semaphore operations |
|
1593
|
|
|
|
|
|
|
|
|
1594
|
|
|
|
|
|
|
sub _key_str_to_int { |
|
1595
|
|
|
|
|
|
|
# Convert any key format (hex string, decimal integer string, or arbitrary |
|
1596
|
|
|
|
|
|
|
# text) to a 32-bit integer using the same algorithm as _shm_key(), but |
|
1597
|
|
|
|
|
|
|
# without the %used_ids side effect. Safe to call any number of times. |
|
1598
|
5
|
|
|
5
|
|
2282
|
my ($key_str) = @_; |
|
1599
|
|
|
|
|
|
|
|
|
1600
|
5
|
100
|
|
|
|
35
|
return hex($key_str) if $key_str =~ /^0x[0-9a-fA-F]+$/i; |
|
1601
|
3
|
100
|
|
|
|
25
|
return $key_str + 0 if $key_str =~ /^\d+$/; |
|
1602
|
|
|
|
|
|
|
|
|
1603
|
2
|
|
|
|
|
13
|
my $int = crc32($key_str); |
|
1604
|
2
|
100
|
|
|
|
7
|
$int -= MAX_KEY_INT_SIZE if $int > MAX_KEY_INT_SIZE; |
|
1605
|
2
|
|
|
|
|
4
|
return $int; |
|
1606
|
|
|
|
|
|
|
} |
|
1607
|
|
|
|
|
|
|
sub _lock_children { |
|
1608
|
150
|
|
|
150
|
|
583
|
my ($knot, $flags, $accumulator, $seen) = @_; |
|
1609
|
|
|
|
|
|
|
|
|
1610
|
150
|
|
|
|
|
342
|
my $data = $knot->{_data}; |
|
1611
|
150
|
|
100
|
|
|
1083
|
my $rtype = Scalar::Util::reftype($data) // ''; |
|
1612
|
|
|
|
|
|
|
|
|
1613
|
150
|
100
|
|
|
|
805
|
my @vals = $rtype eq 'HASH' ? values %$data |
|
|
|
100
|
|
|
|
|
|
|
1614
|
|
|
|
|
|
|
: $rtype eq 'ARRAY' ? @$data |
|
1615
|
|
|
|
|
|
|
: (); |
|
1616
|
|
|
|
|
|
|
|
|
1617
|
150
|
|
|
|
|
733
|
for my $val (@vals) { |
|
1618
|
268
|
100
|
|
|
|
686
|
next unless ref($val); |
|
1619
|
28
|
|
|
|
|
91
|
my $child = _is_child($val); |
|
1620
|
28
|
50
|
33
|
|
|
319
|
next unless $child && $child->seg; |
|
1621
|
|
|
|
|
|
|
|
|
1622
|
28
|
|
|
|
|
135
|
my $id = $child->seg->id; |
|
1623
|
28
|
50
|
|
|
|
138
|
next if $seen->{$id}++; |
|
1624
|
|
|
|
|
|
|
|
|
1625
|
28
|
100
|
|
|
|
140
|
if (! $child->sem->op(@{ $semop_args{$flags} })) { |
|
|
28
|
|
|
|
|
161
|
|
|
1626
|
2
|
|
|
|
|
70
|
for my $locked (reverse @$accumulator) { |
|
1627
|
2
|
|
|
|
|
46
|
my $rflags = $locked->{_lock} | LOCK_UN; |
|
1628
|
2
|
50
|
|
|
|
33
|
$rflags ^= LOCK_NB if $rflags & LOCK_NB; |
|
1629
|
2
|
|
|
|
|
39
|
$locked->sem->op(@{ $semop_args{$rflags} }); |
|
|
2
|
|
|
|
|
30
|
|
|
1630
|
2
|
|
|
|
|
48
|
$locked->{_lock} = 0; |
|
1631
|
|
|
|
|
|
|
} |
|
1632
|
2
|
|
|
|
|
43
|
@$accumulator = (); |
|
1633
|
2
|
|
|
|
|
28
|
return; |
|
1634
|
|
|
|
|
|
|
} |
|
1635
|
|
|
|
|
|
|
|
|
1636
|
26
|
|
|
|
|
648
|
$child->{_data} = $child->_decode($child->seg); |
|
1637
|
26
|
|
|
|
|
103
|
$child->{_lock} = $flags; |
|
1638
|
26
|
|
|
|
|
73
|
push @$accumulator, $child; |
|
1639
|
|
|
|
|
|
|
|
|
1640
|
26
|
100
|
|
|
|
475
|
_lock_children($child, $flags, $accumulator, $seen) or return; |
|
1641
|
|
|
|
|
|
|
} |
|
1642
|
|
|
|
|
|
|
|
|
1643
|
146
|
|
|
|
|
711
|
return 1; |
|
1644
|
|
|
|
|
|
|
} |
|
1645
|
|
|
|
|
|
|
sub _reset_segment { |
|
1646
|
122
|
|
|
122
|
|
356
|
my ($parent, $id) = @_; |
|
1647
|
|
|
|
|
|
|
|
|
1648
|
122
|
|
100
|
|
|
546
|
my $parent_type = Scalar::Util::reftype($parent->{_data}) || ''; |
|
1649
|
|
|
|
|
|
|
|
|
1650
|
122
|
100
|
|
|
|
494
|
if ($parent_type eq 'HASH') { |
|
|
|
100
|
|
|
|
|
|
|
1651
|
80
|
|
|
|
|
183
|
my $data = $parent->{_data}; |
|
1652
|
80
|
100
|
|
|
|
294
|
if (exists $data->{$id}) { |
|
1653
|
9
|
|
100
|
|
|
68
|
my $child_type = Scalar::Util::reftype($data->{$id}) || ''; |
|
1654
|
9
|
100
|
66
|
|
|
66
|
if ($child_type eq 'HASH' && tied %{ $data->{$id} }) { |
|
|
7
|
50
|
33
|
|
|
28
|
|
|
1655
|
7
|
|
|
|
|
12
|
(tied %{ $parent->{_data}{$id} })->remove; |
|
|
7
|
|
|
|
|
30
|
|
|
1656
|
|
|
|
|
|
|
} |
|
1657
|
0
|
|
|
|
|
0
|
elsif ($child_type eq 'ARRAY' && tied @{ $data->{$id} }) { |
|
1658
|
0
|
|
|
|
|
0
|
(tied @{ $parent->{_data}{$id} })->remove; |
|
|
0
|
|
|
|
|
0
|
|
|
1659
|
|
|
|
|
|
|
} |
|
1660
|
|
|
|
|
|
|
} |
|
1661
|
|
|
|
|
|
|
} |
|
1662
|
|
|
|
|
|
|
elsif ($parent_type eq 'ARRAY') { |
|
1663
|
30
|
|
|
|
|
86
|
my $data = $parent->{_data}; |
|
1664
|
30
|
100
|
|
|
|
101
|
if (exists $data->[$id]) { |
|
1665
|
3
|
|
50
|
|
|
24
|
my $child_type = Scalar::Util::reftype($data->[$id]) || ''; |
|
1666
|
3
|
50
|
33
|
|
|
28
|
if ($child_type eq 'HASH' && tied %{ $data->[$id] }) { |
|
|
0
|
50
|
33
|
|
|
0
|
|
|
1667
|
0
|
|
|
|
|
0
|
(tied %{ $parent->{_data}[$id] })->remove; |
|
|
0
|
|
|
|
|
0
|
|
|
1668
|
|
|
|
|
|
|
} |
|
1669
|
3
|
|
|
|
|
13
|
elsif ($child_type eq 'ARRAY' && tied @{ $data->[$id] }) { |
|
1670
|
3
|
|
|
|
|
4
|
(tied @{ $parent->{_data}[$id] })->remove; |
|
|
3
|
|
|
|
|
20
|
|
|
1671
|
|
|
|
|
|
|
} |
|
1672
|
|
|
|
|
|
|
} |
|
1673
|
|
|
|
|
|
|
} |
|
1674
|
|
|
|
|
|
|
} |
|
1675
|
|
|
|
|
|
|
sub _shm_data_summary { |
|
1676
|
6
|
|
|
6
|
|
14
|
my ($knot) = @_; |
|
1677
|
|
|
|
|
|
|
|
|
1678
|
6
|
|
|
|
|
17
|
my $data = $knot->{_data}; |
|
1679
|
6
|
|
50
|
|
|
17
|
my $rtype = Scalar::Util::reftype($data) // ''; |
|
1680
|
|
|
|
|
|
|
|
|
1681
|
6
|
100
|
|
|
|
24
|
if ($rtype eq 'SCALAR') { |
|
1682
|
3
|
|
|
|
|
18
|
my $v = $$data; |
|
1683
|
3
|
50
|
|
|
|
18
|
return defined $v ? qq("$v") : '(undef)'; |
|
1684
|
|
|
|
|
|
|
} |
|
1685
|
|
|
|
|
|
|
|
|
1686
|
3
|
50
|
|
|
|
15
|
if ($rtype eq 'HASH') { |
|
1687
|
3
|
|
|
|
|
6
|
my @parts; |
|
1688
|
3
|
|
|
|
|
22
|
for my $k (sort keys %$data) { |
|
1689
|
3
|
|
|
|
|
9
|
my $v = $data->{$k}; |
|
1690
|
3
|
100
|
|
|
|
9
|
if (ref $v) { |
|
1691
|
1
|
|
50
|
|
|
10
|
my $vt = Scalar::Util::reftype($v) // ''; |
|
1692
|
1
|
0
|
|
|
|
9
|
my $child = $vt eq 'HASH' ? tied(%$v) |
|
|
|
0
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
1693
|
|
|
|
|
|
|
: $vt eq 'ARRAY' ? tied(@$v) |
|
1694
|
|
|
|
|
|
|
: $vt eq 'SCALAR' ? tied($$v) |
|
1695
|
|
|
|
|
|
|
: undef; |
|
1696
|
|
|
|
|
|
|
push @parts, $child && $child->{_key_hex} |
|
1697
|
1
|
50
|
33
|
|
|
37
|
? qq($k => {_key_hex}>) |
|
1698
|
|
|
|
|
|
|
: "$k => ["; ] |
|
1699
|
|
|
|
|
|
|
} |
|
1700
|
|
|
|
|
|
|
else { |
|
1701
|
2
|
50
|
|
|
|
11
|
push @parts, defined $v ? qq($k => "$v") : "$k => (undef)"; |
|
1702
|
|
|
|
|
|
|
} |
|
1703
|
|
|
|
|
|
|
} |
|
1704
|
3
|
50
|
|
|
|
20
|
return @parts ? '{ ' . join(', ', @parts) . ' }' : '{}'; |
|
1705
|
|
|
|
|
|
|
} |
|
1706
|
|
|
|
|
|
|
|
|
1707
|
0
|
0
|
|
|
|
0
|
if ($rtype eq 'ARRAY') { |
|
1708
|
0
|
|
|
|
|
0
|
my @parts; |
|
1709
|
0
|
|
|
|
|
0
|
for my $v (@$data) { |
|
1710
|
0
|
0
|
|
|
|
0
|
if (ref $v) { |
|
1711
|
0
|
|
0
|
|
|
0
|
my $vt = Scalar::Util::reftype($v) // ''; |
|
1712
|
0
|
0
|
|
|
|
0
|
my $child = $vt eq 'HASH' ? tied(%$v) |
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
1713
|
|
|
|
|
|
|
: $vt eq 'ARRAY' ? tied(@$v) |
|
1714
|
|
|
|
|
|
|
: $vt eq 'SCALAR' ? tied($$v) |
|
1715
|
|
|
|
|
|
|
: undef; |
|
1716
|
|
|
|
|
|
|
push @parts, $child && $child->{_key_hex} |
|
1717
|
0
|
0
|
0
|
|
|
0
|
? "{_key_hex}>" |
|
1718
|
|
|
|
|
|
|
: '['; ] |
|
1719
|
|
|
|
|
|
|
} |
|
1720
|
|
|
|
|
|
|
else { |
|
1721
|
0
|
0
|
|
|
|
0
|
push @parts, defined $v ? qq("$v") : '(undef)'; |
|
1722
|
|
|
|
|
|
|
} |
|
1723
|
|
|
|
|
|
|
} |
|
1724
|
0
|
|
|
|
|
0
|
return '[' . join(', ', @parts) . ']'; |
|
1725
|
|
|
|
|
|
|
} |
|
1726
|
|
|
|
|
|
|
|
|
1727
|
0
|
|
|
|
|
0
|
return '(unknown type)'; |
|
1728
|
|
|
|
|
|
|
} |
|
1729
|
|
|
|
|
|
|
sub _shm_flags { |
|
1730
|
|
|
|
|
|
|
# Parses the anonymous hash passed to constructors; returns a list |
|
1731
|
|
|
|
|
|
|
# of args suitable for passing to shmget |
|
1732
|
|
|
|
|
|
|
|
|
1733
|
499
|
|
|
499
|
|
1327
|
my ($knot) = @_; |
|
1734
|
|
|
|
|
|
|
|
|
1735
|
499
|
|
|
|
|
1345
|
my $flags = 0; |
|
1736
|
|
|
|
|
|
|
|
|
1737
|
499
|
100
|
|
|
|
1648
|
$flags |= IPC_CREAT if $knot->attributes('create'); |
|
1738
|
499
|
100
|
|
|
|
10591
|
$flags |= IPC_EXCL if $knot->attributes('exclusive'); |
|
1739
|
|
|
|
|
|
|
|
|
1740
|
499
|
|
|
|
|
2171
|
return $flags; |
|
1741
|
|
|
|
|
|
|
} |
|
1742
|
|
|
|
|
|
|
sub _shm_key { |
|
1743
|
|
|
|
|
|
|
# Generates a 32-bit CRC on the key string. The $key_str parameter is used |
|
1744
|
|
|
|
|
|
|
# for testing only, for purposes of testing various key strings |
|
1745
|
|
|
|
|
|
|
|
|
1746
|
516
|
|
|
516
|
|
2763
|
my ($knot, $key_str) = @_; |
|
1747
|
|
|
|
|
|
|
|
|
1748
|
516
|
|
100
|
|
|
4572
|
$key_str //= ($knot->attributes('key') || ''); |
|
|
|
|
66
|
|
|
|
|
|
1749
|
|
|
|
|
|
|
|
|
1750
|
516
|
|
|
|
|
1002
|
my $key; |
|
1751
|
|
|
|
|
|
|
|
|
1752
|
516
|
100
|
|
|
|
6488
|
if ($key_str eq '') { |
|
|
|
100
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
1753
|
96
|
|
|
|
|
458
|
$key = IPC_PRIVATE; |
|
1754
|
|
|
|
|
|
|
} |
|
1755
|
|
|
|
|
|
|
elsif ($key_str =~ /^0x[0-9a-fA-F]+$/i) { |
|
1756
|
|
|
|
|
|
|
# User specified an explicit hex string key (e.g. '0xDEADBEEF'); use the |
|
1757
|
|
|
|
|
|
|
# bit pattern as-is so the segment key seen by ipcs(1) matches exactly. |
|
1758
|
20
|
|
|
|
|
54
|
$key = hex($key_str); |
|
1759
|
20
|
|
|
|
|
115
|
$used_ids{$key}++; |
|
1760
|
20
|
|
|
|
|
77
|
return $key; |
|
1761
|
|
|
|
|
|
|
} |
|
1762
|
|
|
|
|
|
|
elsif ($key_str =~ /^\d+$/) { |
|
1763
|
|
|
|
|
|
|
# User specified an explicit decimal integer key; use it as-is. |
|
1764
|
183
|
|
|
|
|
385
|
$key = $key_str; |
|
1765
|
183
|
|
|
|
|
469
|
$used_ids{$key}++; |
|
1766
|
183
|
|
|
|
|
564
|
return $key; |
|
1767
|
|
|
|
|
|
|
} |
|
1768
|
|
|
|
|
|
|
else { |
|
1769
|
|
|
|
|
|
|
# String key: compute a 32-bit CRC and apply overflow correction so the |
|
1770
|
|
|
|
|
|
|
# result fits in a signed 32-bit key_t. |
|
1771
|
217
|
|
|
|
|
2392
|
$key = crc32($key_str); |
|
1772
|
|
|
|
|
|
|
} |
|
1773
|
|
|
|
|
|
|
|
|
1774
|
313
|
|
|
|
|
2390
|
$used_ids{$key}++; |
|
1775
|
|
|
|
|
|
|
|
|
1776
|
313
|
100
|
|
|
|
1366
|
if ($key > MAX_KEY_INT_SIZE) { |
|
1777
|
129
|
|
|
|
|
510
|
$key = $key - MAX_KEY_INT_SIZE; |
|
1778
|
|
|
|
|
|
|
|
|
1779
|
129
|
50
|
|
|
|
719
|
if ($key == 0) { |
|
1780
|
0
|
|
|
|
|
0
|
croak "We've calculated a key which equals 0. This is a fatal error"; |
|
1781
|
|
|
|
|
|
|
} |
|
1782
|
|
|
|
|
|
|
} |
|
1783
|
|
|
|
|
|
|
|
|
1784
|
313
|
|
|
|
|
1202
|
return $key; |
|
1785
|
|
|
|
|
|
|
} |
|
1786
|
|
|
|
|
|
|
sub _shm_key_rand { |
|
1787
|
95
|
|
|
95
|
|
156
|
my $key; |
|
1788
|
|
|
|
|
|
|
|
|
1789
|
|
|
|
|
|
|
# Unfortunatly, the only way I know how to check if a segment exists is |
|
1790
|
|
|
|
|
|
|
# to actually create it. We must do that here, then remove it just to |
|
1791
|
|
|
|
|
|
|
# ensure the slot is available |
|
1792
|
|
|
|
|
|
|
|
|
1793
|
95
|
|
|
|
|
190
|
my $verified_exclusive = 0; |
|
1794
|
|
|
|
|
|
|
|
|
1795
|
95
|
|
|
|
|
179
|
my $check_count = 0; |
|
1796
|
|
|
|
|
|
|
|
|
1797
|
95
|
|
100
|
|
|
706
|
while (! $verified_exclusive && $check_count < EXCLUSIVE_CHECK_LIMIT) { |
|
1798
|
104
|
|
|
|
|
182
|
$check_count++; |
|
1799
|
|
|
|
|
|
|
|
|
1800
|
104
|
|
|
|
|
278
|
$key = _shm_key_rand_int(); |
|
1801
|
|
|
|
|
|
|
|
|
1802
|
104
|
100
|
|
|
|
522
|
next if $used_ids{$key}; |
|
1803
|
|
|
|
|
|
|
|
|
1804
|
94
|
|
|
|
|
149
|
my $flags; |
|
1805
|
94
|
|
|
|
|
381
|
$flags |= IPC_CREAT; |
|
1806
|
94
|
|
|
|
|
726
|
$flags |= IPC_EXCL; |
|
1807
|
|
|
|
|
|
|
|
|
1808
|
94
|
|
|
|
|
993
|
my $seg; |
|
1809
|
|
|
|
|
|
|
|
|
1810
|
94
|
|
|
|
|
232
|
my $shm_slot_available = eval { |
|
1811
|
94
|
|
|
|
|
529
|
$seg = IPC::Shareable::SharedMem->new( |
|
1812
|
|
|
|
|
|
|
key => $key, |
|
1813
|
|
|
|
|
|
|
size => 1, |
|
1814
|
|
|
|
|
|
|
flags => $flags, |
|
1815
|
|
|
|
|
|
|
); |
|
1816
|
94
|
|
|
|
|
227
|
1; |
|
1817
|
|
|
|
|
|
|
}; |
|
1818
|
|
|
|
|
|
|
|
|
1819
|
94
|
50
|
|
|
|
254
|
if ($shm_slot_available) { |
|
1820
|
94
|
|
|
|
|
150
|
$verified_exclusive = 1; |
|
1821
|
94
|
50
|
|
|
|
448
|
$seg->remove if $seg; |
|
1822
|
|
|
|
|
|
|
} |
|
1823
|
|
|
|
|
|
|
} |
|
1824
|
|
|
|
|
|
|
|
|
1825
|
95
|
100
|
|
|
|
303
|
if (! $verified_exclusive) { |
|
1826
|
1
|
|
|
|
|
327
|
croak |
|
1827
|
|
|
|
|
|
|
"_shm_key_rand() can't get an available key after $check_count tries"; |
|
1828
|
|
|
|
|
|
|
} |
|
1829
|
|
|
|
|
|
|
|
|
1830
|
94
|
|
|
|
|
455
|
$used_ids{$key}++; |
|
1831
|
|
|
|
|
|
|
|
|
1832
|
94
|
|
|
|
|
496
|
return $key; |
|
1833
|
|
|
|
|
|
|
} |
|
1834
|
|
|
|
|
|
|
sub _shm_key_rand_int { |
|
1835
|
93
|
|
|
93
|
|
414
|
return int(rand(1_000_000)); |
|
1836
|
|
|
|
|
|
|
} |
|
1837
|
|
|
|
|
|
|
sub _read_check { |
|
1838
|
947
|
|
|
947
|
|
1816
|
my ($knot) = @_; |
|
1839
|
|
|
|
|
|
|
|
|
1840
|
|
|
|
|
|
|
# Advisory only: never blocks the read, only warns. Called from FETCH |
|
1841
|
|
|
|
|
|
|
# when this knot is unlocked (a locked FETCH uses _data cache and never |
|
1842
|
|
|
|
|
|
|
# touches shmem). Race window exists between this getval() and the |
|
1843
|
|
|
|
|
|
|
# subsequent _decode() — a writer could acquire in between — but this |
|
1844
|
|
|
|
|
|
|
# still catches the common case where a reader forgot to lock. |
|
1845
|
|
|
|
|
|
|
|
|
1846
|
947
|
100
|
|
|
|
2546
|
return unless $knot->attributes('enforced_read_locking'); |
|
1847
|
935
|
100
|
|
|
|
2083
|
return unless $knot->attributes('violated_read_lock_warn'); |
|
1848
|
|
|
|
|
|
|
|
|
1849
|
|
|
|
|
|
|
# getval() can return undef if the semaphore set has been removed (e.g. |
|
1850
|
|
|
|
|
|
|
# after clean_up_all). The check is advisory only, so silently skip when |
|
1851
|
|
|
|
|
|
|
# the semaphore is no longer reachable. |
|
1852
|
|
|
|
|
|
|
|
|
1853
|
932
|
|
|
|
|
2225
|
my $writers = $knot->sem->getval(SEM_WRITERS); |
|
1854
|
932
|
50
|
|
|
|
18433
|
return unless defined $writers; |
|
1855
|
|
|
|
|
|
|
|
|
1856
|
932
|
100
|
|
|
|
2295
|
if ($writers > 0) { |
|
1857
|
4
|
|
|
|
|
22
|
my $uuid = $knot->uuid; |
|
1858
|
4
|
|
|
|
|
28
|
my $seg_id = $knot->seg->id; |
|
1859
|
4
|
|
|
|
|
281
|
warn "Object with UUID $uuid attempted read from segment ID " |
|
1860
|
|
|
|
|
|
|
. "$seg_id which is exclusively locked (enforced read locking " |
|
1861
|
|
|
|
|
|
|
. "enabled); returned data may be stale or partially-written. " |
|
1862
|
|
|
|
|
|
|
. "Acquire LOCK_SH before reading to guarantee a coherent snapshot"; |
|
1863
|
|
|
|
|
|
|
} |
|
1864
|
|
|
|
|
|
|
|
|
1865
|
932
|
|
|
|
|
1718
|
return; |
|
1866
|
|
|
|
|
|
|
} |
|
1867
|
|
|
|
|
|
|
sub _write_permitted { |
|
1868
|
948
|
|
|
948
|
|
2001
|
my ($knot) = @_; |
|
1869
|
|
|
|
|
|
|
|
|
1870
|
948
|
100
|
|
|
|
2746
|
return 1 unless $knot->attributes('enforced_write_locking'); |
|
1871
|
|
|
|
|
|
|
|
|
1872
|
|
|
|
|
|
|
# If this knot itself holds LOCK_EX it is the owner of the lock and is |
|
1873
|
|
|
|
|
|
|
# permitted to write. |
|
1874
|
|
|
|
|
|
|
|
|
1875
|
920
|
100
|
|
|
|
3075
|
return 1 if $knot->{_lock} & LOCK_EX; |
|
1876
|
|
|
|
|
|
|
|
|
1877
|
863
|
|
|
|
|
2169
|
my $sem = $knot->sem; |
|
1878
|
|
|
|
|
|
|
|
|
1879
|
|
|
|
|
|
|
# Semaphore index 2 is the write-lock counter; it is 1 when any other knot |
|
1880
|
|
|
|
|
|
|
# holds LOCK_EX (set via SEM_UNDO so it auto-releases on process exit). |
|
1881
|
|
|
|
|
|
|
|
|
1882
|
|
|
|
|
|
|
# Block if any process holds LOCK_EX |
|
1883
|
|
|
|
|
|
|
|
|
1884
|
863
|
100
|
|
|
|
3460
|
if ($sem->getval(SEM_WRITERS) > 0) { |
|
1885
|
12
|
100
|
|
|
|
222
|
if ($knot->attributes('violated_write_lock_warn')) { |
|
1886
|
11
|
|
|
|
|
28
|
my $uuid = $knot->uuid; |
|
1887
|
11
|
|
|
|
|
28
|
my $seg_id = $knot->seg->id; |
|
1888
|
11
|
|
|
|
|
208
|
warn "Object with UUID $uuid attempted write to segment ID " |
|
1889
|
|
|
|
|
|
|
. "$seg_id which is exclusively locked (enforced write " |
|
1890
|
|
|
|
|
|
|
. "locking enabled). Your write was not accepted. Lock with " |
|
1891
|
|
|
|
|
|
|
. "LOCK_EX to ensure successful writes when a segment is " |
|
1892
|
|
|
|
|
|
|
. "already locked"; |
|
1893
|
|
|
|
|
|
|
} |
|
1894
|
|
|
|
|
|
|
|
|
1895
|
12
|
|
|
|
|
2363
|
return 0; |
|
1896
|
|
|
|
|
|
|
} |
|
1897
|
|
|
|
|
|
|
|
|
1898
|
|
|
|
|
|
|
# Block if any process holds LOCK_SH (active readers present) |
|
1899
|
|
|
|
|
|
|
|
|
1900
|
851
|
100
|
|
|
|
15429
|
if ($sem->getval(SEM_READERS) > 0) { |
|
1901
|
3
|
50
|
|
|
|
42
|
if ($knot->attributes('violated_write_lock_warn')) { |
|
1902
|
3
|
|
|
|
|
11
|
my $uuid = $knot->uuid; |
|
1903
|
3
|
|
|
|
|
10
|
my $seg_id = $knot->seg->id; |
|
1904
|
3
|
|
|
|
|
80
|
warn "Object with UUID $uuid attempted write to segment ID " |
|
1905
|
|
|
|
|
|
|
. "$seg_id which has active readers (enforced write locking " |
|
1906
|
|
|
|
|
|
|
. "enabled)"; |
|
1907
|
|
|
|
|
|
|
} |
|
1908
|
|
|
|
|
|
|
|
|
1909
|
3
|
|
|
|
|
2282
|
return 0; |
|
1910
|
|
|
|
|
|
|
} |
|
1911
|
|
|
|
|
|
|
|
|
1912
|
848
|
|
|
|
|
10868
|
return 1; |
|
1913
|
|
|
|
|
|
|
} |
|
1914
|
|
|
|
|
|
|
|
|
1915
|
|
|
|
|
|
|
# Misc |
|
1916
|
|
|
|
|
|
|
|
|
1917
|
|
|
|
|
|
|
sub _parse_args { |
|
1918
|
499
|
|
|
499
|
|
1725
|
my ($opts) = @_; |
|
1919
|
|
|
|
|
|
|
|
|
1920
|
499
|
100
|
|
|
|
2590
|
$opts = defined $opts ? $opts : { %default_options }; |
|
1921
|
|
|
|
|
|
|
|
|
1922
|
499
|
|
|
|
|
11206
|
for my $k (keys %default_options) { |
|
1923
|
7984
|
100
|
|
|
|
21715
|
if (not defined $opts->{$k}) { |
|
|
|
100
|
|
|
|
|
|
|
1924
|
3364
|
|
|
|
|
8744
|
$opts->{$k} = $default_options{$k}; |
|
1925
|
|
|
|
|
|
|
} |
|
1926
|
|
|
|
|
|
|
elsif ($opts->{$k} eq 'no') { |
|
1927
|
2
|
100
|
|
|
|
10
|
if ($^W) { |
|
1928
|
1
|
|
|
|
|
24
|
require Carp; |
|
1929
|
1
|
|
|
|
|
372
|
Carp::carp("Use of `no' in IPC::Shareable args is obsolete"); |
|
1930
|
|
|
|
|
|
|
} |
|
1931
|
|
|
|
|
|
|
|
|
1932
|
2
|
|
|
|
|
12
|
$opts->{$k} = 0; |
|
1933
|
|
|
|
|
|
|
} |
|
1934
|
|
|
|
|
|
|
} |
|
1935
|
499
|
|
66
|
|
|
8021
|
$opts->{owner} = ($opts->{owner} or $$); |
|
1936
|
499
|
|
100
|
|
|
4178
|
$opts->{magic} = ($opts->{magic} or 0); |
|
1937
|
499
|
|
|
|
|
1640
|
return $opts; |
|
1938
|
|
|
|
|
|
|
} |
|
1939
|
|
|
|
|
|
|
sub _end { |
|
1940
|
124
|
|
|
124
|
|
2605911
|
for my $s (values %process_register) { |
|
1941
|
140
|
|
|
|
|
608
|
eval { unlock($s) }; |
|
|
140
|
|
|
|
|
838
|
|
|
1942
|
140
|
50
|
|
|
|
591
|
next if $s->attributes('protected'); |
|
1943
|
140
|
100
|
|
|
|
524
|
next if ! $s->attributes('destroy'); |
|
1944
|
124
|
100
|
|
|
|
1048
|
next if $s->attributes('owner') != $$; |
|
1945
|
81
|
|
|
|
|
150
|
eval { remove($s) }; |
|
|
81
|
|
|
|
|
297
|
|
|
1946
|
|
|
|
|
|
|
} |
|
1947
|
|
|
|
|
|
|
} |
|
1948
|
|
|
|
|
|
|
END { |
|
1949
|
78
|
|
|
78
|
|
2029701
|
_end(); |
|
1950
|
|
|
|
|
|
|
} |
|
1951
|
|
|
|
|
|
|
|
|
1952
|
|
|
|
0
|
|
|
sub _placeholder {} |
|
1953
|
|
|
|
|
|
|
|
|
1954
|
|
|
|
|
|
|
1; |
|
1955
|
|
|
|
|
|
|
|
|
1956
|
|
|
|
|
|
|
__END__ |