AnyEvent-FDpasser

 view release on metacpan or  search on metacpan

README  view on Meta::CPAN

  Portability
    In order to use the SysV interface, set the "FDPASSER_SYSV" environment
    variable when running "Makefile.PL":

        $ FDPASSER_SYSV=1 perl Makefile.PL

    Currently the default is to always use the BSD interface. It will
    attempt to figure out which interface is appropriate (BSD4.4 or BSD4.3).
    Currently Solaris uses 4.3 and everything else uses 4.4. Patches and/or
    portability reports are welcome.

FULL DESCRIPTOR TABLES
    Any system call that creates new descriptors in your process can fail
    because your process has exceed its NOFILE resource limit. Also, it can
    fail because the system has run out of resources and can't handle new
    files or (more likely on modern systems) it has hit an artificial
    kernel-imposed limit like "kern.maxfiles" on BSD.

    In order to pass a file descriptor between processes, a new descriptor
    needs to be allocated in the receiving process. Therefore, the "recvmsg"
    and "ioctl" system calls used to implement descriptor passing can fail
    unexpectedly. Failing to create a descriptor is especially bad when
    transfering descriptors since the outcome is not well specified. Linux
    doesn't even mention this possible failure mode in the recvmsg() man
    page. BSD manuals indicate that EMSGSIZE will be returned and any
    descriptors in transit will be closed. If a descriptor is closed it can
    never be delivered to the application, even if the full descriptor table
    problem clears up.

    So what should we do? We could silently ignore it when a descriptor
    fails to transfer, but then we run the risk of desynchronising the
    descriptor stream. Another possibility is indicating to the application
    that this descriptor has failed to transfer and is now lost forever.
    Unfortunately this complicates the error handling an application must
    do, especially if the descriptor is linked to other descriptors which
    must then be received and (if they make it) closed. Finally, we could
    just give up, call the on_error callback, destory the passer object and
    punt the problem back to the application.

    None of the above "solutions" are very appealing so this module uses a
    trick known as the "close-dup slot reservation" trick. Actually I just
    made that name up now but it sounds pretty cool don't you think? The
    idea is that when the passer object is created, we "dup" a file
    descriptor and store it in the object. This module creates a pipe when
    the passer object is made, closes one side of the pipe and keeps the
    other around. This "sentinel" descriptor exists solely to take up an
    entry in our descriptor table: we will never write to it, read from it,
    or poll it.

    When it comes time to receive a descriptor, we close the sentinel
    descriptor, receive the descriptor from the sending process, and then
    attempt to dup another descriptor. Because we just cleared a descriptor
    table entry, there should always be a free descriptor to create.

    If duping fails, we stop trying to receive any further descriptors and
    instead retry at regular intervals (while not interrupting the event
    loop). Hopefully eventually the full descriptor table issue will clear
    up and we will be able to resume receiving descriptors.

    Note that a descriptor could be created between closing and receiving if
    your program uses asynchronous signal handlers or threads that create
    descriptors, so don't do that. Signals that are handled synchronously
    (like normal AnyEvent signal watchers) are fine.

    This trick is similar to a trick described in Marc Lehmann's libev POD
    document, section "special problem of accept()ing when you can't,"
    although the purpose of employing the trick in this module is somewhat
    different.

TESTS AND SYSTEM ASSUMPTIONS
    All the following tests should work with BSD4.4, BSD4.3, and SysV
    interfaces (where available).

  Bidirectional
    A passer is bidirectional and can be used to both send and receive
    descriptors, even simultaneously.

    There are tests (basic_socketpair.t and basic_filesystem.t) to verify
    this.

  Non-blocking
    A process may initiate push_recv_fh on a passer and this process will
    not block while it is waiting for the other end to call push_send_fh
    (and vice versa).

    There are tests (recv_before_send.t and send_before_recv.t) to verify
    this.

  FIFO ordering
    The order descriptors are sent with push_send_fh is the same order that
    they are received on at the other end with push_recv_fh.

    There is a test (buffer_exercise.t) to verify this and some other basic
    buffering properties.

  Preserves blocking status
    After a fork, the non-blocking status of a descriptor is preserved so if
    you are doing a socketpair followed by a fork it is acceptable to set
    the non-blocking status of both descriptors in the parent.

    Also, the non-blocking status of a descriptor passed with this module is
    preserved after it is passed so it is not necessary to reset nonblocking
    status on descriptors.

    There is a test (non_blocking_fhs.t) to verify this and some other
    assumptions for any given system.

  Passing passers
    Passing a descriptor and then using this descriptor as an argument to
    the existing_fh mode of this module to construct another passer is
    supported.

    There is a test (send_passer_over_passer.t) to verify this assumption
    for any given system.

  Descriptor table full
    Even when the descriptor table fills up intermittently, no descriptors
    being passed should be lost.

    There is a test (full_descriptor_table.t) to verify this.



( run in 0.518 second using v1.01-cache-2.11-cpan-39bf76dae61 )