blib/lib/Devel/RingBuffer.pm | |||
---|---|---|---|
Criterion | Covered | Total | % |
statement | 268 | 348 | 77.0 |
branch | 16 | 52 | 30.7 |
condition | 11 | 20 | 55.0 |
subroutine | 74 | 90 | 82.2 |
pod | 0 | 25 | 0.0 |
total | 369 | 535 | 68.9 |
line | stmt | bran | cond | sub | pod | time | code |
---|---|---|---|---|---|---|---|
1 | #/** | ||||||
2 | # Shared memory ring buffers for diagnosis/debug of Perl scripts. | ||||||
3 | # Uses IPC::Mmap to create/access/manage a memory mapped file (or namespace | ||||||
4 | # on Win32) as a ring buffer structure that can be used by "applications | ||||||
5 | # under test" that use an appropriate debug module (e.g., Devel::STrace) | ||||||
6 | # along with an external monitoring application (e.g., Devel::STrace::Monitor). | ||||||
7 | #
|
||||||
8 | # Note that significant functionality is written in XS/C in order to minimize | ||||||
9 | # tracing/debugging overhead. | ||||||
10 | #
|
||||||
11 | # Permission is granted to use this software under the same terms as Perl itself. | ||||||
12 | # Refer to the Perl Artistic License | ||||||
13 | # for details. | ||||||
14 | # | ||||||
15 | # @author D. Arnold | ||||||
16 | # @since 2006-05-01 | ||||||
17 | # @self $self | ||||||
18 | #*/ | ||||||
19 | package Devel::RingBuffer; | ||||||
20 | |||||||
21 | 7 | 7 | 86591 | use Carp qw(cluck carp confess); | |||
7 | 26 | ||||||
7 | 4334 | ||||||
22 | #use threads; | ||||||
23 | #use threads::shared; | ||||||
24 | 7 | 7 | 55 | use Config; | |||
7 | 15 | ||||||
7 | 2875 | ||||||
25 | 7 | 7 | 20887 | use IPC::Mmap; | |||
7 | 452366 | ||||||
7 | 599 | ||||||
26 | 7 | 7 | 72 | use DynaLoader; | |||
7 | 19 | ||||||
7 | 184 | ||||||
27 | 7 | 7 | 35 | use Exporter; | |||
7 | 20 | ||||||
7 | 366 | ||||||
28 | |||||||
29 | BEGIN { | ||||||
30 | 7 | 7 | 182 | our @ISA = qw(Exporter DynaLoader); | |||
31 | |||||||
32 | # | ||||||
33 | # offset of global fields | ||||||
34 | # | ||||||
35 | 7 | 7 | 41 | use constant RINGBUF_SINGLE => 0; | |||
7 | 9 | ||||||
7 | 442 | ||||||
36 | 7 | 7 | 36 | use constant RINGBUF_MSGAREA_SZ => 4; | |||
7 | 14 | ||||||
7 | 257 | ||||||
37 | 7 | 7 | 36 | use constant RINGBUF_BUFFERS => 8; | |||
7 | 8 | ||||||
7 | 268 | ||||||
38 | 7 | 7 | 29 | use constant RINGBUF_SLOTS => 12; | |||
7 | 13 | ||||||
7 | 255 | ||||||
39 | 7 | 7 | 83 | use constant RINGBUF_SLOT_SZ => 16; | |||
7 | 8 | ||||||
7 | 280 | ||||||
40 | 7 | 7 | 30 | use constant RINGBUF_CREATE_STOP => 20; | |||
7 | 14 | ||||||
7 | 243 | ||||||
41 | 7 | 7 | 29 | use constant RINGBUF_CREATE_TRACE => 24; | |||
7 | 8 | ||||||
7 | 242 | ||||||
42 | 7 | 7 | 29 | use constant RINGBUF_GLOBAL_SZ => 28; | |||
7 | 20 | ||||||
7 | 220 | ||||||
43 | 7 | 7 | 40 | use constant RINGBUF_TOTALMSG_SZ => 32; | |||
7 | 14 | ||||||
7 | 262 | ||||||
44 | 7 | 7 | 35 | use constant RINGBUF_GLOBMSG_SZ => 36; | |||
7 | 14 | ||||||
7 | 302 | ||||||
45 | 7 | 7 | 36 | use constant RINGBUF_GLOBAL_MSG => 40; | |||
7 | 14 | ||||||
7 | 272 | ||||||
46 | 7 | 7 | 29 | use constant RINGBUF_RINGHDR_SZ => 40; | |||
7 | 14 | ||||||
7 | 243 | ||||||
47 | # | ||||||
48 | # offsets of watchlist members | ||||||
49 | # | ||||||
50 | 7 | 7 | 29 | use constant RINGBUF_WATCH_INUSE => 0; | |||
7 | 13 | ||||||
7 | 326 | ||||||
51 | 7 | 7 | 35 | use constant RINGBUF_WATCH_EXPRLEN => 4; | |||
7 | 14 | ||||||
7 | 241 | ||||||
52 | 7 | 7 | 30 | use constant RINGBUF_WATCH_EXPR => 8; | |||
7 | 14 | ||||||
7 | 331 | ||||||
53 | 7 | 7 | 36 | use constant RINGBUF_WATCH_READY => 264; | |||
7 | 8 | ||||||
7 | 347 | ||||||
54 | 7 | 7 | 29 | use constant RINGBUF_WATCH_RESLEN => 268; | |||
7 | 14 | ||||||
7 | 233 | ||||||
55 | 7 | 7 | 65 | use constant RINGBUF_WATCH_RESULT => 272; | |||
7 | 14 | ||||||
7 | 256 | ||||||
56 | 7 | 7 | 35 | use constant RINGBUF_WATCH_SZ => 784; | |||
7 | 8 | ||||||
7 | 299 | ||||||
57 | 7 | 7 | 30 | use constant RINGBUF_WATCH_CNT => 4; | |||
7 | 13 | ||||||
7 | 248 | ||||||
58 | 7 | 7 | 34 | use constant RINGBUF_WATCH_EXPRSZ => 256; | |||
7 | 8 | ||||||
7 | 305 | ||||||
59 | 7 | 7 | 35 | use constant RINGBUF_WATCH_RESSZ => 512; | |||
7 | 14 | ||||||
7 | 353 | ||||||
60 | # | ||||||
61 | # offsets of ring buffer members | ||||||
62 | # | ||||||
63 | 7 | 7 | 35 | use constant RINGBUF_PID => 0; | |||
7 | 8 | ||||||
7 | 273 | ||||||
64 | 7 | 7 | 35 | use constant RINGBUF_TID => 4; | |||
7 | 14 | ||||||
7 | 301 | ||||||
65 | 7 | 7 | 41 | use constant RINGBUF_CURRSLOT => 8; | |||
7 | 13 | ||||||
7 | 319 | ||||||
66 | 7 | 7 | 36 | use constant RINGBUF_DEPTH => 12; | |||
7 | 104 | ||||||
7 | 342 | ||||||
67 | 7 | 7 | 41 | use constant RINGBUF_TRACE => 16; | |||
7 | 9992 | ||||||
7 | 6545 | ||||||
68 | 7 | 7 | 1049 | use constant RINGBUF_SIGNAL => 20; | |||
7 | 20 | ||||||
7 | 6648 | ||||||
69 | 7 | 7 | 1019 | use constant RINGBUF_BASEADDR => 24; | |||
7 | 987 | ||||||
7 | 8733 | ||||||
70 | 7 | 7 | 59 | use constant RINGBUF_WATCH_OFFSET => 28; | |||
7 | 23 | ||||||
7 | 355 | ||||||
71 | 7 | 7 | 34 | use constant RINGBUF_BUFHDR_SZ => 28; | |||
7 | 17 | ||||||
7 | 280 | ||||||
72 | |||||||
73 | 7 | 7 | 29 | use constant RINGBUF_DFLT_SLOTSZ => 214; | |||
7 | 14 | ||||||
7 | 387 | ||||||
74 | 7 | 7 | 29 | use constant RINGBUF_ENTRY_SZ => 200; | |||
7 | 14 | ||||||
7 | 339 | ||||||
75 | 7 | 7 | 34 | use constant RINGBUF_SLOT_PACKSTR => 'l d S/a*'; | |||
7 | 14 | ||||||
7 | 290 | ||||||
76 | # | ||||||
77 | # consts for member indexes | ||||||
78 | # | ||||||
79 | 7 | 7 | 35 | use constant RINGBUF_FILENAME => 0; | |||
7 | 8 | ||||||
7 | 291 | ||||||
80 | 7 | 7 | 29 | use constant RINGBUF_SIZE => 1; | |||
7 | 14 | ||||||
7 | 378 | ||||||
81 | 7 | 7 | 35 | use constant RINGBUF_COUNT => 2; | |||
7 | 19 | ||||||
7 | 671 | ||||||
82 | 7 | 7 | 59 | use constant RINGBUF_BUFSIZE => 3; | |||
7 | 14 | ||||||
7 | 324 | ||||||
83 | 7 | 7 | 48 | use constant RINGBUF_SLOT_CNT => 4; | |||
7 | 8 | ||||||
7 | 324 | ||||||
84 | 7 | 7 | 42 | use constant RINGBUF_FLD_TID => 5; | |||
7 | 14 | ||||||
7 | 336 | ||||||
85 | 7 | 7 | 79 | use constant RINGBUF_FLD_PID => 6; | |||
7 | 13 | ||||||
7 | 338 | ||||||
86 | 7 | 7 | 36 | use constant RINGBUF_RING => 7; | |||
7 | 14 | ||||||
7 | 342 | ||||||
87 | 7 | 7 | 35 | use constant RINGBUF_FH => 8; | |||
7 | 21 | ||||||
7 | 295 | ||||||
88 | 7 | 7 | 36 | use constant RINGBUF_FLD_MSGAREA_SZ => 9; | |||
7 | 8 | ||||||
7 | 319 | ||||||
89 | 7 | 7 | 36 | use constant RINGBUF_FLD_GLOBAL_SZ => 10; | |||
7 | 15 | ||||||
7 | 334 | ||||||
90 | 7 | 7 | 42 | use constant RINGBUF_MAP_OFFSET => 11; | |||
7 | 15 | ||||||
7 | 305 | ||||||
91 | 7 | 7 | 36 | use constant RINGBUF_RINGS_OFFSET => 12; | |||
7 | 8 | ||||||
7 | 391 | ||||||
92 | 7 | 7 | 41 | use constant RINGBUF_MAP_ADDR => 13; | |||
7 | 15 | ||||||
7 | 336 | ||||||
93 | 7 | 7 | 37 | use constant RINGBUF_RINGS_ADDR => 14; | |||
7 | 8 | ||||||
7 | 324 | ||||||
94 | 7 | 7 | 42 | use constant RINGBUF_ADDRESS => 15; | |||
7 | 13 | ||||||
7 | 342 | ||||||
95 | 7 | 7 | 41 | use constant RINGBUF_SLOT_SIZE => 16; | |||
7 | 15 | ||||||
7 | 445 | ||||||
96 | 7 | 7 | 37 | use constant RINGBUF_NEXT_IDX => 17; | |||
7 | 14 | ||||||
7 | 327 | ||||||
97 | |||||||
98 | 7 | 7 | 42 | use constant RINGBUF_RING_WAIT => 0.3; | |||
7 | 8 | ||||||
7 | 1263 | ||||||
99 | |||||||
100 | 7 | 20 | our @EXPORT = (); | ||||
101 | 7 | 15 | our @EXPORT_OK = (); | ||||
102 | 7 | 83 | our %EXPORT_TAGS = ( | ||||
103 | ringbuffer_consts => [ | ||||||
104 | qw/RINGBUF_SINGLE RINGBUF_MSGAREA_SZ RINGBUF_BUFFERS RINGBUF_SLOTS | ||||||
105 | RINGBUF_SLOT_SZ RINGBUF_CREATE_STOP RINGBUF_CREATE_TRACE RINGBUF_GLOBAL_SZ | ||||||
106 | RINGBUF_TOTALMSG_SZ RINGBUF_GLOBMSG_SZ | ||||||
107 | RINGBUF_GLOBAL_MSG RINGBUF_RINGHDR_SZ RINGBUF_WATCH_INUSE | ||||||
108 | RINGBUF_WATCH_EXPRLEN RINGBUF_WATCH_EXPR RINGBUF_WATCH_READY | ||||||
109 | RINGBUF_WATCH_RESLEN RINGBUF_WATCH_RESULT RINGBUF_WATCH_SZ | ||||||
110 | RINGBUF_WATCH_CNT RINGBUF_PID RINGBUF_TID RINGBUF_CURRSLOT | ||||||
111 | RINGBUF_DEPTH RINGBUF_TRACE RINGBUF_SIGNAL RINGBUF_WATCH_OFFSET | ||||||
112 | RINGBUF_BUFHDR_SZ RINGBUF_DFLT_SLOTSZ RINGBUF_ENTRY_SZ RINGBUF_SLOT_PACKSTR/ | ||||||
113 | ], | ||||||
114 | |||||||
115 | ringbuffer_members => [ | ||||||
116 | qw/RINGBUF_FILENAME RINGBUF_SIZE RINGBUF_COUNT RINGBUF_BUFSIZE RINGBUF_SLOT_CNT | ||||||
117 | RINGBUF_FLD_TID RINGBUF_FLD_PID RINGBUF_RING RINGBUF_FH | ||||||
118 | RINGBUF_FLD_MSGAREA_SZ RINGBUF_FLD_GLOBAL_SZ RINGBUF_MAP_OFFSET | ||||||
119 | RINGBUF_RINGS_OFFSET RINGBUF_MAP_ADDR RINGBUF_RINGS_ADDR RINGBUF_ADDRESS | ||||||
120 | RINGBUF_SLOT_SIZE RINGBUF_NEXT_IDX/ | ||||||
121 | ], | ||||||
122 | ); | ||||||
123 | |||||||
124 | 7 | 1330 | Exporter::export_tags(keys %EXPORT_TAGS); | ||||
125 | |||||||
126 | }; | ||||||
127 | |||||||
128 | our $VERSION = '0.31'; | ||||||
129 | our $hasThreads; | ||||||
130 | |||||||
131 | BEGIN { | ||||||
132 | 7 | 50 | 33 | 7 | 293 | if ($Config{useithreads} && (!$ENV{DEVEL_RINGBUF_NOTHREADS})) { | |
133 | 0 | 0 | require Devel::RingBuffer::ThreadFacade; | ||||
134 | 0 | 0 | $hasThreads = 1; | ||||
135 | } | ||||||
136 | } | ||||||
137 | |||||||
138 | 7 | 7 | 16065 | use threads::shared; | |||
7 | 16325 | ||||||
7 | 44 | ||||||
139 | |||||||
140 | 7 | 7 | 576 | use strict; | |||
7 | 9 | ||||||
7 | 206 | ||||||
141 | 7 | 7 | 42 | use warnings; | |||
7 | 15 | ||||||
7 | 437 | ||||||
142 | |||||||
143 | bootstrap Devel::RingBuffer $VERSION; | ||||||
144 | |||||||
145 | 7 | 7 | 4997 | use Devel::RingBuffer::Ring; | |||
7 | 27 | ||||||
7 | 29009 | ||||||
146 | |||||||
147 | our $thrdlock = undef; | ||||||
148 | |||||||
149 | #/** | ||||||
150 | # Constructor. Using a combination of the optional C<%args> and | ||||||
151 | # various environment variables, creates and initializes a | ||||||
152 | # mmap'ed file in read/write mode with the ring buffer structures. | ||||||
153 | # | ||||||
154 | # @param File name of the file to be created for memory mapping. | ||||||
155 | # @param GlobalSize size of global monitor <=> AUT message buffer. | ||||||
156 | # @param MessageSize size of per-thread monitor <=> AUT message buffer. | ||||||
157 | # @param Rings Number of rings to create in the ring buffer. | ||||||
158 | # @param Slots Number of slots per ring. | ||||||
159 | # @param SlotSize Slot size in bytes. | ||||||
160 | # @param StopOnCreate Initial value for stop_on_create flag. | ||||||
161 | # @param TraceOnCreate Initial value for trace_on_create flag. | ||||||
162 | # | ||||||
163 | # @return Devel::RingBuffer object on success; undef on failure | ||||||
164 | #*/ | ||||||
165 | sub new { | ||||||
166 | 7 | 7 | 0 | 5542 | my $class = shift; | ||
167 | |||||||
168 | 7 | 36 | my %args = @_; | ||||
169 | |||||||
170 | 7 | 66 | 77 | my $file = $args{File} || $ENV{DEVEL_RINGBUF_FILE}; | |||
171 | 7 | 14 | my $anon; | ||||
172 | 7 | 100 | 41 | unless (defined($file)) { | |||
173 | 6 | 60 | my @paths = split(/[\/\\]/, $0); | ||||
174 | 6 | 18 | $file = pop @paths; | ||||
175 | 6 | 50 | 54 | if ($^O eq 'MSWin32') { | |||
176 | 0 | 0 | $anon = 1; | ||||
177 | } | ||||||
178 | else { | ||||||
179 | 6 | 50 | 36 | $file = defined($ENV{TEMP}) ? "$ENV{TEMP}/$file" : "/tmp/$file"; | |||
180 | } | ||||||
181 | 6 | 54 | $file=~s/^(.+)\..+/$1/; | ||||
182 | # | ||||||
183 | # use timestamp sans weekday and year | ||||||
184 | # | ||||||
185 | 6 | 1278 | my @pieces = split(/\s+/, scalar localtime); | ||||
186 | 6 | 24 | pop @pieces; # get rid of year | ||||
187 | 6 | 48 | $pieces[0] = $$; # replace weekday w/ PID | ||||
188 | 6 | 18 | $pieces[-1]=~tr/:/_/; # Win32 can't handle colons in filenames | ||||
189 | 6 | 42 | $file .= '.' . join('_', @pieces); | ||||
190 | } | ||||||
191 | |||||||
192 | #print STDERR "RingBuffer new: args:", join(', ', keys %args), "\n"; | ||||||
193 | |||||||
194 | 7 | 50 | 99 | my $ringslots = $args{Slots} || $ENV{DEVEL_RINGBUF_SLOTS} || 10; | |||
195 | 7 | 50 | 22304 | my $slotsz = $args{SlotSize} || $ENV{DEVEL_RINGBUF_SLOTSZ} || 200; | |||
196 | 7 | 50 | 134 | my $ringcount = $args{Rings} || $ENV{DEVEL_RINGBUF_BUFFERS} || 20; | |||
197 | 7 | 50 | 102 | my $ringmsgsz = $args{MessageSize} || $ENV{DEVEL_RINGBUF_MSGSZ} || 256; | |||
198 | 7 | 50 | 89 | my $globmsgsz = $args{GlobalSize} || $ENV{DEVEL_RINGBUF_GLOBALSZ} || (16 * 1024); | |||
199 | 7 | 100 | 200 | my $create_stop = $args{StopOnCreate} || $ENV{DEVEL_RINGBUF_SOC} || 0; | |||
200 | 7 | 50 | 79 | my $create_trace = $args{TraceOnCreate} || $ENV{DEVEL_RINGBUF_TOC} || 0; | |||
201 | # | ||||||
202 | # in order to avoid issues with word alignment, we'll always | ||||||
203 | # force slotsz, msg size, and global size to be word aligned | ||||||
204 | # (who knows, we may need to be 8 byte aligned on some platforms) | ||||||
205 | # | ||||||
206 | 7 | 50 | 41 | $slotsz += (4 - ($slotsz & 3)) if ($slotsz & 3); | |||
207 | 7 | 50 | 42 | $ringmsgsz += (4 - ($ringmsgsz & 3)) if ($ringmsgsz & 3); | |||
208 | 7 | 50 | 28 | $globmsgsz += (4 - ($globmsgsz & 3)) if ($globmsgsz & 3); | |||
209 | |||||||
210 | 7 | 21 | my $freemap_offs = RINGBUF_RINGHDR_SZ + $globmsgsz; | ||||
211 | |||||||
212 | 7 | 55 | my $ringbufsz = _get_ring_size($ringslots, $slotsz, $ringmsgsz); | ||||
213 | |||||||
214 | 7 | 42 | my $ringsize = _get_total_size($ringcount, $ringslots, $slotsz, $ringmsgsz, $globmsgsz) + | ||||
215 | 1024; # Win32 needs some extra room | ||||||
216 | |||||||
217 | 7 | 50 | 149 | my $self = bless [ | |||
218 | $file, | ||||||
219 | $ringsize, | ||||||
220 | $ringcount, | ||||||
221 | $ringbufsz, | ||||||
222 | $ringslots, | ||||||
223 | ($hasThreads ? Devel::RingBuffer::ThreadFacade->tid() : 0), | ||||||
224 | $$, | ||||||
225 | undef, | ||||||
226 | undef, | ||||||
227 | $ringmsgsz, | ||||||
228 | $globmsgsz, | ||||||
229 | $freemap_offs, | ||||||
230 | _get_rings_addr(0, $ringcount, $globmsgsz), | ||||||
231 | $freemap_offs, | ||||||
232 | _get_rings_addr(0, $ringcount, $globmsgsz), | ||||||
233 | 0, | ||||||
234 | $slotsz | ||||||
235 | ], $class; | ||||||
236 | # | ||||||
237 | # create the mmap'ed ring | ||||||
238 | # | ||||||
239 | #cluck "file is $file\n"; | ||||||
240 | 7 | 50 | 41 | if ($anon) { | |||
241 | # | ||||||
242 | # on Win32 only...anonymous mmap is useless to us on POSIX | ||||||
243 | # | ||||||
244 | 0 | 0 | 0 | $self->[RINGBUF_RING] = IPC::Mmap->new($file, $ringsize, | |||
245 | PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANON) | ||||||
246 | or die "Can't open mmap file $file: $!"; | ||||||
247 | } | ||||||
248 | else { | ||||||
249 | 7 | 50 | 1578 | open(FH, ">$file") || | |||
250 | confess "Can't open mmap file $file: $!"; | ||||||
251 | 7 | 18569 | print FH "\0" x $ringsize; | ||||
252 | 7 | 386 | close FH; | ||||
253 | |||||||
254 | 7 | 50 | 76 | $self->[RINGBUF_RING] = IPC::Mmap->new($file, $ringsize, | |||
255 | PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FILE) | ||||||
256 | or die "Can't open mmap file $file: $!"; | ||||||
257 | } | ||||||
258 | # | ||||||
259 | # share the thrdlock | ||||||
260 | # | ||||||
261 | 7 | 50 | 76664 | if ($hasThreads) { | |||
262 | # print STDERR "we're shared\n"; | ||||||
263 | 0 | 0 | share($thrdlock); | ||||
264 | } | ||||||
265 | # | ||||||
266 | # clear the ringbuffer (Win32 needs this) | ||||||
267 | # | ||||||
268 | 7 | 38 | my $ringbuffer = $self->[RINGBUF_RING] ; | ||||
269 | 7 | 1685 | my $var = "\0" x ($ringsize - 1024); | ||||
270 | 7 | 121 | $ringbuffer->write($var, 0, $ringsize - 1024); | ||||
271 | 7 | 1281 | my $ringslotsz = $ringslots * $slotsz; | ||||
272 | # | ||||||
273 | # then init it | ||||||
274 | # | ||||||
275 | return undef | ||||||
276 | 7 | 50 | 77 | unless $ringbuffer->pack(0, 'l l l l l l l l l', | |||
277 | 0, $ringmsgsz, $ringcount, $ringslots, $slotsz, $create_stop, $create_trace, $globmsgsz, 0); | ||||||
278 | |||||||
279 | 7 | 251 | my $addr = $self->[RINGBUF_ADDRESS] = $self->[RINGBUF_RING]->getAddress(); | ||||
280 | |||||||
281 | 7 | 49 | $self->[RINGBUF_MAP_ADDR] += $addr; | ||||
282 | 7 | 14 | $self->[RINGBUF_RINGS_ADDR] += $addr; | ||||
283 | |||||||
284 | 7 | 27 | my $mapaddr = $self->[RINGBUF_MAP_ADDR]; | ||||
285 | 7 | 20 | my $ringsaddr = $self->[RINGBUF_RINGS_ADDR]; | ||||
286 | # | ||||||
287 | # let XS do init | ||||||
288 | # | ||||||
289 | _free_ring($mapaddr, $ringsaddr, $ringbufsz, $_) | ||||||
290 | 7 | 225 | foreach (0..$ringcount-1); | ||||
291 | # | ||||||
292 | # for unknown reasons, the first map doesn't take ... so remap | ||||||
293 | # | ||||||
294 | # $self->remmap(); | ||||||
295 | |||||||
296 | 7 | 73 | return $self; | ||||
297 | } | ||||||
298 | |||||||
299 | #/** | ||||||
300 | # Get the name of the mmap'ed file. | ||||||
301 | # | ||||||
302 | # @return the name of the mmap'ed file | ||||||
303 | #*/ | ||||||
304 | 6 | 6 | 0 | 78 | sub getName { return $_[0]->[RINGBUF_FILENAME]; } | ||
305 | |||||||
306 | #/** | ||||||
307 | # Get base address of the mmap'ed file. | ||||||
308 | # | ||||||
309 | # @return the address of the mmap'ed file | ||||||
310 | #*/ | ||||||
311 | 0 | 0 | 0 | 0 | sub getAddress { return $_[0]->[RINGBUF_ADDRESS]; } | ||
312 | |||||||
313 | #/** | ||||||
314 | # Allocate a ring buffer. Should only be used on ringbuffers created with new(). | ||||||
315 | # | ||||||
316 | # @return a Devel::RingBuffer::Ring object on success. | ||||||
317 | # If no rings are available, returns undef. | ||||||
318 | #*/ | ||||||
319 | sub allocate { | ||||||
320 | 24 | 24 | 0 | 130057 | my $self = shift; | ||
321 | # | ||||||
322 | # allocate a ring buffer and init it | ||||||
323 | # | ||||||
324 | # unless (($self->[RINGBUF_FLD_TID] == threads->self()->tid()) || | ||||||
325 | # ($self->[RINGBUF_FLD_PID] == $$)) { | ||||||
326 | # On Win32, the fork() emulation means we shouldn't remap!!! | ||||||
327 | # | ||||||
328 | 24 | 153 | if (0) { | ||||
329 | unless ($self->[RINGBUF_FLD_PID] == $$) { | ||||||
330 | # | ||||||
331 | # this probably isn't needed anymore for threads, but may be for | ||||||
332 | # processes... | ||||||
333 | # | ||||||
334 | my $file = $self->[RINGBUF_FILENAME]; | ||||||
335 | my $ringsize = $self->[RINGBUF_SIZE]; | ||||||
336 | $self->[RINGBUF_RING] = IPC::Mmap->new($file, $ringsize, | ||||||
337 | PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FILE) || | ||||||
338 | die "Can't mmap file $file: $!"; | ||||||
339 | $self->[RINGBUF_FLD_TID] = ($hasThreads ? Devel::RingBuffer::ThreadFacade->tid() : 0); | ||||||
340 | $self->[RINGBUF_FLD_PID] = $$; | ||||||
341 | } | ||||||
342 | } | ||||||
343 | |||||||
344 | 24 | 109 | my $ring = 0; | ||||
345 | 24 | 91 | my $ringbuffer = $self->[RINGBUF_RING]; | ||||
346 | 24 | 420 | $ringbuffer->lock(); | ||||
347 | { | ||||||
348 | 24 | 1105 | lock($thrdlock); | ||||
24 | 115 | ||||||
349 | # | ||||||
350 | # use XS to find free ring (for performance reasons) | ||||||
351 | # | ||||||
352 | 24 | 312 | $ring = _alloc_ring($self->[RINGBUF_MAP_ADDR], $self->[RINGBUF_COUNT]); | ||||
353 | } | ||||||
354 | 24 | 153 | $ringbuffer->unlock(); | ||||
355 | |||||||
356 | 24 | 346 | my $ringsaddr = $self->[RINGBUF_RINGS_ADDR]; | ||||
357 | |||||||
358 | 24 | 50 | 1210 | return defined($ring) ? | |||
359 | Devel::RingBuffer::Ring->new( | ||||||
360 | $self, | ||||||
361 | _get_ring_addr($self->[RINGBUF_RINGS_ADDR], | ||||||
362 | $ring, | ||||||
363 | $self->[RINGBUF_SLOT_CNT], | ||||||
364 | $self->[RINGBUF_SLOT_SIZE], | ||||||
365 | $self->[RINGBUF_FLD_MSGAREA_SZ]), | ||||||
366 | $self->[RINGBUF_ADDRESS], | ||||||
367 | $ring, | ||||||
368 | $self->[RINGBUF_SLOT_CNT], | ||||||
369 | $self->[RINGBUF_FLD_MSGAREA_SZ], | ||||||
370 | ) : | ||||||
371 | undef; | ||||||
372 | } | ||||||
373 | |||||||
374 | #/** | ||||||
375 | # Re-allocates a ring buffer. Required to handle threads' CLONE() | ||||||
376 | # of the existing ring buffer object when a new thread is created. | ||||||
377 | # C |
||||||
378 | # ring number, and its base address; the caller than updates | ||||||
379 | # an existing ring object with the returned values. | ||||||
380 | # | ||||||
381 | # @return the allocated ring index and address | ||||||
382 | #*/ | ||||||
383 | sub reallocate { | ||||||
384 | 0 | 0 | 0 | 0 | my $self = shift; | ||
385 | |||||||
386 | 0 | 0 | my $newring = 0; | ||||
387 | 0 | 0 | my $ringbuffer = $self->[RINGBUF_RING]; | ||||
388 | 0 | 0 | $ringbuffer->lock(); | ||||
389 | { | ||||||
390 | 0 | 0 | lock($thrdlock); | ||||
0 | 0 | ||||||
391 | # | ||||||
392 | # use XS to find free ring (for performance reasons) | ||||||
393 | # | ||||||
394 | 0 | 0 | $newring = _alloc_ring($self->[RINGBUF_MAP_ADDR], $self->[RINGBUF_COUNT]); | ||||
395 | } | ||||||
396 | 0 | 0 | $ringbuffer->unlock(); | ||||
397 | |||||||
398 | 0 | 0 | 0 | return defined($newring) ? | |||
399 | ($newring, | ||||||
400 | _get_ring_addr( | ||||||
401 | $self->[RINGBUF_RINGS_ADDR], | ||||||
402 | $newring, | ||||||
403 | $self->[RINGBUF_SLOT_CNT], | ||||||
404 | $self->[RINGBUF_SLOT_SIZE], | ||||||
405 | $self->[RINGBUF_FLD_MSGAREA_SZ])) : | ||||||
406 | (); | ||||||
407 | } | ||||||
408 | |||||||
409 | #/** | ||||||
410 | # Constructor. Opens an existing mmap'd file for read/write | ||||||
411 | # access (for interactive debuggers) | ||||||
412 | # | ||||||
413 | # @param $file optional name of mmap'ed file (or namespace for Win32) | ||||||
414 | # | ||||||
415 | # @return Devel::RingBuffer object on success; undef on failure | ||||||
416 | #*/ | ||||||
417 | sub open { | ||||||
418 | 0 | 0 | 0 | 0 | return _lcl_open(@_, PROT_READ|PROT_WRITE); | ||
419 | } | ||||||
420 | |||||||
421 | #/** | ||||||
422 | # Constructor. Opens an existing mmap'd file for read-only | ||||||
423 | # access (for simple monitor applications) | ||||||
424 | # | ||||||
425 | # @param $file optional name of mmap'ed file (or namespace for Win32) | ||||||
426 | # | ||||||
427 | # @return Devel::RingBuffer object on success; undef on failure | ||||||
428 | #*/ | ||||||
429 | sub monitor { | ||||||
430 | 0 | 0 | 0 | 0 | return _lcl_open(@_, PROT_READ); | ||
431 | } | ||||||
432 | |||||||
433 | sub _lcl_open { | ||||||
434 | 0 | 0 | 0 | my ($class, $file, $mode) = @_; | |||
435 | # | ||||||
436 | # open twice: first to get config params, then | ||||||
437 | # to map the whole file | ||||||
438 | # | ||||||
439 | # use anonymous open for Win32 | ||||||
440 | # | ||||||
441 | 0 | 0 | 0 | my $flags = ($^O eq 'MSWin32') ? | |||
442 | MAP_SHARED | MAP_ANON : | ||||||
443 | MAP_SHARED | MAP_FILE; | ||||||
444 | |||||||
445 | 0 | 0 | 0 | my $ringbuffer = | |||
446 | IPC::Mmap->new($file, RINGBUF_RINGHDR_SZ, PROT_READ, $flags) or | ||||||
447 | die "Can't mmap file $file: $!"; | ||||||
448 | |||||||
449 | 0 | 0 | my ($msgareasz, $count, $slots, $slotsz, $stop, $trace, $globmsgsz) = | ||||
450 | $ringbuffer->unpack(4, 28, 'l7'); | ||||||
451 | |||||||
452 | 0 | 0 | my $freemap_offs = RINGBUF_RINGHDR_SZ + $globmsgsz; | ||||
453 | |||||||
454 | 0 | 0 | my $ringbufsz = _get_ring_size($slots, $slotsz, $msgareasz); | ||||
455 | |||||||
456 | 0 | 0 | my $ringsize = _get_total_size($count, $slots, $slotsz, $msgareasz, $globmsgsz) + | ||||
457 | 1024; # Win32 needs some extra room | ||||||
458 | |||||||
459 | 0 | 0 | $ringbuffer->close(); | ||||
460 | |||||||
461 | 0 | 0 | 0 | $ringbuffer = IPC::Mmap->new($file, $ringsize, $mode, $flags) | |||
462 | or die "Can't mmap file $file: $!"; | ||||||
463 | |||||||
464 | 0 | 0 | 0 | return bless [ | |||
465 | $file, | ||||||
466 | $ringsize, | ||||||
467 | $count, | ||||||
468 | $ringbufsz, | ||||||
469 | $slots, | ||||||
470 | ($hasThreads ? Devel::RingBuffer::ThreadFacade->tid() : 0), | ||||||
471 | $$, | ||||||
472 | $ringbuffer, | ||||||
473 | undef, | ||||||
474 | $msgareasz, | ||||||
475 | $globmsgsz, | ||||||
476 | $freemap_offs, | ||||||
477 | _get_rings_addr(0, $count, $globmsgsz), | ||||||
478 | $ringbuffer->getAddress() + $freemap_offs, | ||||||
479 | _get_rings_addr($ringbuffer->getAddress(), $count, $globmsgsz), | ||||||
480 | $ringbuffer->getAddress(), | ||||||
481 | $slotsz | ||||||
482 | ], $class; | ||||||
483 | } | ||||||
484 | |||||||
485 | #/** | ||||||
486 | # Get the free buffer map | ||||||
487 | # | ||||||
488 | # @return list of bytes, one per ring; if and element is 'true', the associated | ||||||
489 | # ring is free; otherwise the ring is in use. | ||||||
490 | #*/ | ||||||
491 | sub getMap { | ||||||
492 | 1 | 1 | 0 | 29091 | return $_[0]->[RINGBUF_RING]->unpack( | ||
493 | $_[0]->[RINGBUF_MAP_OFFSET], | ||||||
494 | $_[0]->[RINGBUF_COUNT], | ||||||
495 | 'C' . $_[0]->[RINGBUF_COUNT] ); | ||||||
496 | } | ||||||
497 | |||||||
498 | #/** | ||||||
499 | # Get the RingBuffer global header fields. The fields | ||||||
500 | # returned include: | ||||||
501 | #
|
||||||
502 | # |
||||||
503 | # |
||||||
504 | # |
||||||
505 | # |
||||||
506 | # |
||||||
507 | # |
||||||
508 | # |
||||||
509 | # |
||||||
510 | # |
||||||
511 | # |
||||||
512 | # |
||||||
513 | # | ||||||
514 | # | ||||||
515 | # @return list of the specified header values | ||||||
516 | #*/ | ||||||
517 | sub getHeader { | ||||||
518 | 0 | 0 | 0 | 0 | return $_[0]->[RINGBUF_RING]->unpack(0, 40, 'l10'); | ||
519 | } | ||||||
520 | #/** | ||||||
521 | # Open and return a Devel::RingBuffer::Ring object | ||||||
522 | # for the specified ring number. | ||||||
523 | # | ||||||
524 | # @param $ringnum number of ring to be opened | ||||||
525 | # | ||||||
526 | # @return Devel::RingBuffer::Ring object | ||||||
527 | #*/ | ||||||
528 | sub getRing { | ||||||
529 | 19 | 19 | 0 | 214 | my ($self, $ringnum) = @_; | ||
530 | 19 | 171 | return Devel::RingBuffer::Ring->open( | ||||
531 | $self, | ||||||
532 | _get_ring_addr( | ||||||
533 | $self->[RINGBUF_RINGS_ADDR], | ||||||
534 | $ringnum, | ||||||
535 | $self->[RINGBUF_SLOT_CNT], | ||||||
536 | $self->[RINGBUF_SLOT_SIZE], | ||||||
537 | $self->[RINGBUF_FLD_MSGAREA_SZ]), | ||||||
538 | $self->[RINGBUF_ADDRESS], | ||||||
539 | $ringnum, | ||||||
540 | $self->[RINGBUF_SLOT_CNT], | ||||||
541 | $self->[RINGBUF_FLD_MSGAREA_SZ] | ||||||
542 | ); | ||||||
543 | } | ||||||
544 | |||||||
545 | #/** | ||||||
546 | # Get the configured number of slots per ring. | ||||||
547 | # | ||||||
548 | # @return the number of slots configured for the ring buffer. | ||||||
549 | #*/ | ||||||
550 | 0 | 0 | 0 | 0 | sub getSlots { return $_[0]->[RINGBUF_SLOT_CNT]; } | ||
551 | #/** | ||||||
552 | # Get the configured size of slots. | ||||||
553 | # | ||||||
554 | # @return the slot size | ||||||
555 | #*/ | ||||||
556 | 0 | 0 | 0 | 0 | sub getSlotSize { return $_[0]->[RINGBUF_SLOT_SIZE]; } | ||
557 | #/** | ||||||
558 | # Get the number of configured rings. | ||||||
559 | # | ||||||
560 | # @return the count of rings | ||||||
561 | #*/ | ||||||
562 | 6 | 6 | 0 | 2556 | sub getCount { return $_[0]->[RINGBUF_COUNT]; } | ||
563 | #/** | ||||||
564 | # Close the ring buffer. | ||||||
565 | # | ||||||
566 | # @deprecated | ||||||
567 | #*/ | ||||||
568 | sub close { | ||||||
569 | 1 | 1 | 0 | 2735359 | my $self = shift; | ||
570 | 1 | 18 | my $ring = delete $self->[RINGBUF_RING]; | ||||
571 | 1 | 32 | return 1; | ||||
572 | } | ||||||
573 | #/** | ||||||
574 | # Free a ring. Returns a ring to the free list | ||||||
575 | # | ||||||
576 | # @param $ring the ring object to be freed | ||||||
577 | #*/ | ||||||
578 | sub free { | ||||||
579 | 23 | 23 | 0 | 2711 | my ($self, $ring) = @_; | ||
580 | #print STDERR "freeing ring $ring\n"; | ||||||
581 | 23 | 50 | 88 | return 1 unless $self->[RINGBUF_RING]; | |||
582 | |||||||
583 | 23 | 42 | my $ringbuffer = $self->[RINGBUF_RING]; | ||||
584 | 23 | 180 | $ringbuffer->lock(); | ||||
585 | { | ||||||
586 | 23 | 488 | lock($thrdlock); | ||||
23 | 66 | ||||||
587 | # | ||||||
588 | # XS handles everything but the locks | ||||||
589 | # | ||||||
590 | 23 | 140 | _free_ring($self->[RINGBUF_MAP_ADDR], | ||||
591 | $self->[RINGBUF_RINGS_ADDR], | ||||||
592 | $self->[RINGBUF_BUFSIZE], | ||||||
593 | $ring); | ||||||
594 | } | ||||||
595 | |||||||
596 | 23 | 98 | $ringbuffer->unlock(); | ||||
597 | } | ||||||
598 | #/** | ||||||
599 | # Get the IPC::Mmap object used to store the ringbuffer. | ||||||
600 | # | ||||||
601 | # @return the IPC::Mmap object | ||||||
602 | #*/ | ||||||
603 | 0 | 0 | 0 | 0 | sub getMmap { return $_[0]->[RINGBUF_RING]; } | ||
604 | # | ||||||
605 | # just check for the current thread/process's ring instance; | ||||||
606 | # note this can be a lengthy process, since we must | ||||||
607 | # scan the mmap'd ring buffer headers for matching PID/TID, | ||||||
608 | # and then free it | ||||||
609 | # | ||||||
610 | # !!!DPERECATED!!! We can't permit DESTROY if cloned versions | ||||||
611 | # might destroy things; just let process run down deal with | ||||||
612 | # closing the file | ||||||
613 | # | ||||||
614 | sub OLDDESTROY { | ||||||
615 | 0 | 0 | 0 | 0 | my $self = shift; | ||
616 | 0 | 0 | 0 | my $tid = ($hasThreads ? Devel::RingBuffer::ThreadFacade->tid() : 0); | |||
617 | |||||||
618 | 0 | 0 | 0 | return unless $self->[RINGBUF_RING]; | |||
619 | |||||||
620 | 0 | 0 | print STDERR "RingBuffer DESTROYING in thread $tid\n"; | ||||
621 | |||||||
622 | 0 | 0 | my $ringbuffer = $self->[RINGBUF_RING]; | ||||
623 | 0 | 0 | $ringbuffer->lock(); | ||||
624 | { | ||||||
625 | 0 | 0 | lock($thrdlock); | ||||
0 | 0 | ||||||
626 | # | ||||||
627 | # XS handles everything but the locks | ||||||
628 | # | ||||||
629 | 0 | 0 | my $ring = _find_ring($self->[RINGBUF_RINGS_ADDR], | ||||
630 | $self->[RINGBUF_BUFSIZE], $self->[RINGBUF_COUNT], $$, $tid); | ||||||
631 | 0 | 0 | 0 | _free_ring($self->[RINGBUF_MAP_ADDR], | |||
632 | $self->[RINGBUF_RINGS_ADDR], | ||||||
633 | $self->[RINGBUF_BUFSIZE], | ||||||
634 | $ring) | ||||||
635 | if defined($ring); | ||||||
636 | } | ||||||
637 | 0 | 0 | $ringbuffer->unlock(); | ||||
638 | } | ||||||
639 | |||||||
640 | #/** | ||||||
641 | # Sets the value of the global single field. | ||||||
642 | # | ||||||
643 | # @param value to set | ||||||
644 | # | ||||||
645 | # @return the prior value of the field. | ||||||
646 | #*/ | ||||||
647 | sub setSingle { | ||||||
648 | 0 | 0 | 0 | 0 | return $_[0]->[RINGBUF_RING]->pack(0, 'l', $_[1]); | ||
649 | } | ||||||
650 | |||||||
651 | #/** | ||||||
652 | # Gets the value of the global single field. | ||||||
653 | # | ||||||
654 | # @return the value of the field. | ||||||
655 | #*/ | ||||||
656 | sub getSingle { | ||||||
657 | 0 | 0 | 0 | 0 | return $_[0]->[RINGBUF_RING]->unpack(0, 4, 'l'); | ||
658 | } | ||||||
659 | |||||||
660 | #/** | ||||||
661 | # Sets the value of the stop_on_create field. | ||||||
662 | # | ||||||
663 | # @return the prior value of the field. | ||||||
664 | #*/ | ||||||
665 | sub setStopOnCreate { | ||||||
666 | 0 | 0 | 0 | 0 | return $_[0]->[RINGBUF_RING]->pack(RINGBUF_CREATE_STOP, 'l', $_[1]); | ||
667 | } | ||||||
668 | |||||||
669 | #/** | ||||||
670 | # Get the value of the stop_on_create field. | ||||||
671 | # | ||||||
672 | # @return the current value of the field. | ||||||
673 | #*/ | ||||||
674 | sub getStopOnCreate { | ||||||
675 | 1 | 1 | 0 | 58 | return $_[0]->[RINGBUF_RING]->unpack(RINGBUF_CREATE_STOP, 4, 'l'); | ||
676 | } | ||||||
677 | |||||||
678 | #/** | ||||||
679 | # Sets the value of the trace_on_create field. | ||||||
680 | # | ||||||
681 | # @param $trace_on_create value to set | ||||||
682 | # @return the prior value of the field | ||||||
683 | #*/ | ||||||
684 | sub setTraceOnCreate { | ||||||
685 | 0 | 0 | 0 | 0 | return $_[0]->[RINGBUF_RING]->pack(RINGBUF_CREATE_TRACE, 'l', $_[1]); | ||
686 | } | ||||||
687 | |||||||
688 | #/** | ||||||
689 | # Get the value of the trace_on_create field. | ||||||
690 | # | ||||||
691 | # @return the value of the field | ||||||
692 | #*/ | ||||||
693 | sub getTraceOnCreate { | ||||||
694 | 1 | 1 | 0 | 36 | return $_[0]->[RINGBUF_RING]->unpack(RINGBUF_CREATE_TRACE, 4, 'l'); | ||
695 | } | ||||||
696 | |||||||
697 | #/** | ||||||
698 | # Sets a message into the global message area. Note that | ||||||
699 | # this operation requires locking the entire ring buffer | ||||||
700 | # header until the message is completely transfered. | ||||||
701 | # Messages larger than the configured global message size | ||||||
702 | # will be transfered in chunks; each chunk must back ACK'd by | ||||||
703 | # the message receiver. | ||||||
704 | # | ||||||
705 | # @param $msg the message to send | ||||||
706 | # | ||||||
707 | # @return the RingBuffer object | ||||||
708 | #*/ | ||||||
709 | sub setGlobalMsg { | ||||||
710 | 0 | 0 | 0 | my $self = shift; | |||
711 | 0 | my $ringbuffer = $self->[RINGBUF_RING]; | |||||
712 | 0 | my $globsz = $self->[RINGBUF_FLD_GLOBAL_SZ]; | |||||
713 | 0 | my $first = 1; | |||||
714 | 0 | $ringbuffer->lock(); | |||||
715 | { | ||||||
716 | 0 | lock($thrdlock); | |||||
0 | |||||||
717 | 0 | my ($t, $frag) = (0,0); | |||||
718 | 0 | my $len = length($_[0]); | |||||
719 | 0 | while ($len) { | |||||
720 | # | ||||||
721 | # may need to fragment | ||||||
722 | # | ||||||
723 | 0 | 0 | $t = ($len > $globsz) ? $globsz : $len; | ||||
724 | 0 | $ringbuffer->write(substr($_[0], $frag, $t), RINGBUF_GLOBAL_MSG, $t); | |||||
725 | 0 | $ringbuffer->pack(RINGBUF_GLOBMSG_SZ, 'l', $t); | |||||
726 | # | ||||||
727 | # set this last so reader doesn't read to soon | ||||||
728 | # | ||||||
729 | 0 | 0 | $ringbuffer->pack(RINGBUF_TOTALMSG_SZ, 'l', $len), | ||||
730 | $first = undef | ||||||
731 | if $first; | ||||||
732 | |||||||
733 | 0 | $len -= $t; | |||||
734 | 0 | $frag += $t; | |||||
735 | # | ||||||
736 | # wait for ACK that its been read | ||||||
737 | # | ||||||
738 | 0 | sleep RINGBUF_RING_WAIT, | |||||
739 | $t = $ringbuffer->unpack(RINGBUF_GLOBMSG_SZ, 4, 'l') | ||||||
740 | while $t; | ||||||
741 | } | ||||||
742 | 0 | $ringbuffer->pack(RINGBUF_TOTALMSG_SZ, 'l', 0); | |||||
743 | } | ||||||
744 | 0 | $ringbuffer->unlock(); | |||||
745 | 0 | return $self; | |||||
746 | } | ||||||
747 | |||||||
748 | #/** | ||||||
749 | # Gets a message from the global message area. Note that | ||||||
750 | # this operation B |
||||||
751 | # header, but instead relies on signalling of the message | ||||||
752 | # chunk lengths. | ||||||
753 | # Messages larger than the configured global message size | ||||||
754 | # will be received in chunks; each chunk must back ACK'd by | ||||||
755 | # the message receiver. | ||||||
756 | # | ||||||
757 | # @return the re-assembled global message buffer contents | ||||||
758 | # | ||||||
759 | #*/ | ||||||
760 | sub getGlobalMsg { | ||||||
761 | 0 | 0 | 0 | my $self = shift; | |||
762 | 0 | my $ringbuffer = $self->[RINGBUF_RING]; | |||||
763 | 0 | my $globsz = $self->[RINGBUF_FLD_GLOBAL_SZ]; | |||||
764 | 0 | my $result = ''; | |||||
765 | 0 | my $frag; | |||||
766 | my $t; | ||||||
767 | # | ||||||
768 | # wait for indication that msg is available | ||||||
769 | # | ||||||
770 | 0 | my $len = $ringbuffer->unpack(RINGBUF_TOTALMSG_SZ, 4, 'l'); | |||||
771 | |||||||
772 | 0 | sleep RINGBUF_RING_WAIT, | |||||
773 | $len = $ringbuffer->unpack(RINGBUF_TOTALMSG_SZ, 4, 'l') | ||||||
774 | until $len; | ||||||
775 | |||||||
776 | 0 | while ($len) { | |||||
777 | # | ||||||
778 | # may be fragmented | ||||||
779 | # wait for length field | ||||||
780 | # | ||||||
781 | 0 | sleep RINGBUF_RING_WAIT, | |||||
782 | $t = $ringbuffer->unpack(RINGBUF_GLOBMSG_SZ, 4, 'l') | ||||||
783 | until $t; | ||||||
784 | |||||||
785 | 0 | $ringbuffer->read($frag, RINGBUF_GLOBAL_MSG, $t); | |||||
786 | 0 | $len -= $t; | |||||
787 | 0 | $result .= $frag; | |||||
788 | # | ||||||
789 | # ACK it | ||||||
790 | # | ||||||
791 | 0 | $ringbuffer->pack(RINGBUF_GLOBMSG_SZ, 'l', 0); | |||||
792 | } | ||||||
793 | 0 | return $result; | |||||
794 | } | ||||||
795 | |||||||
796 | 1; |