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