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