NanoMsg-Raw-0.10/000755 000765 000024 00000000000 13030572462 013303 5ustar00bzstaff000000 000000 NanoMsg-Raw-0.10/Changes000644 000765 000024 00000001562 13030571536 014603 0ustar00bzstaff000000 000000 0.10 2016-12-28 - Add NAME sections to POD patched by gregor herrmann 0.09 2016-11-03 - Do not test textual error messages reported by SREZIC - Fix POD reported by gregoa 0.07 2016-05-16 - Fix t/inproc.t, t/separation.t, t/timeo.t to work with libnanomsg 0.8 and above. 0.06 2015-08-10 - t/survey.t works with libnanomsg-0.6 0.05 2015-05-04 - Remove t/threads.t - The test sometimes fails for misterious reason. - t/timeo.t accept timeouts between .1 and .12 0.04 2015-01-28 - Make sure the nanomsg library and the headers are available 0.03 2015-01-22 - Documentation improvements. - Fix OS X tests - Fix nn_getsockopt - An uninitialized variable results in unpredictable effects like segfaults or out of memory errors. - Fix rar segfault when using nn_recv. 0.02 2013-08-27 - Documentation improvements. 0.01 2013-08-24 - Initial release. NanoMsg-Raw-0.10/lib/000755 000765 000024 00000000000 13030572462 014051 5ustar00bzstaff000000 000000 NanoMsg-Raw-0.10/LICENSE000644 000765 000024 00000002212 13006736313 014305 0ustar00bzstaff000000 000000 This software is Copyright (c) 2015 by Florian Ragwitz. This is free software, licensed under: The MIT (X11) License The MIT License Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. NanoMsg-Raw-0.10/Makefile.PL000644 000765 000024 00000003040 13006736313 015252 0ustar00bzstaff000000 000000 use strict; use warnings; use Devel::CheckLib; check_lib_or_exit( header => 'nanomsg/nn.h', lib => 'nanomsg', ); use ExtUtils::MakeMaker; my %WriteMakefileArgs = ( "ABSTRACT" => "Low-level interface to the nanomsg scalability protocols library", "AUTHOR" => "Florian Ragwitz , Boris Zentner ", VERSION_FROM => 'lib/NanoMsg/Raw.pm', "CONFIGURE_REQUIRES" => { "Devel::CheckLib" => "0.9", "ExtUtils::MakeMaker" => 0 }, "DISTNAME" => "NanoMsg-Raw", "EXE_FILES" => [], "LIBS" => "-lnanomsg", "LICENSE" => "mit", "NAME" => "NanoMsg::Raw", "PREREQ_PM" => { "Exporter" => 0, "XSLoader" => 0, "overload" => 0, "strict" => 0, "warnings" => 0 }, "TEST_REQUIRES" => { "Test::Fatal" => 0, "Test::More" => "0.89", "Test::SharedFork" => 0, "Test::TCP" => 0, "Time::HiRes" => 0 }, "test" => { "TESTS" => "t/*.t" } ); my %FallbackPrereqs = ( "Devel::CheckLib" => "0.9", "Exporter" => 0, "ExtUtils::MakeMaker" => 0, "Test::Fatal" => 0, "Test::More" => "0.89", "Test::SharedFork" => 0, "Test::TCP" => 0, "Time::HiRes" => 0, "XSLoader" => 0, "overload" => 0, "strict" => 0, "warnings" => 0 ); unless ( eval { ExtUtils::MakeMaker->VERSION(6.63_03) } ) { delete $WriteMakefileArgs{TEST_REQUIRES}; delete $WriteMakefileArgs{BUILD_REQUIRES}; $WriteMakefileArgs{PREREQ_PM} = \%FallbackPrereqs; } delete $WriteMakefileArgs{CONFIGURE_REQUIRES} unless eval { ExtUtils::MakeMaker->VERSION(6.52) }; WriteMakefile(%WriteMakefileArgs); NanoMsg-Raw-0.10/MANIFEST000644 000765 000024 00000001261 13030572462 014434 0ustar00bzstaff000000 000000 Changes lib/NanoMsg/Raw.pm lib/NanoMsg/Raw/Message.pm LICENSE Makefile.PL MANIFEST This list of files MANIFEST.SKIP Raw.xs README.md t/00-report-prereqs.dd t/00-report-prereqs.t t/author-eol.t t/author-no-tabs.t t/block.t t/bus.t t/domain.t t/emfile.t t/export.t t/inproc.t t/iovec.t t/ipc.t t/msg.t t/pair.t t/pipeline.t t/poll.t t/prio.t t/pubsub.t t/recv-free-segv.t t/release-new-version.t t/release-pod-coverage.t t/release-pod-syntax.t t/reqrep.t t/send-recv.t t/separation.t t/survey.t t/tcp.t t/timeo.t typemap META.yml Module YAML meta-data (added by MakeMaker) META.json Module JSON meta-data (added by MakeMaker) NanoMsg-Raw-0.10/MANIFEST.SKIP000644 000765 000024 00000001727 13006736313 015210 0ustar00bzstaff000000 000000 .travis.yml # Avoid version control files. \bRCS\b \bCVS\b \bSCCS\b ,v$ \B\.svn\b \B\.git\b \B\.gitignore\b \b_darcs\b \B\.cvsignore$ # Avoid VMS specific MakeMaker generated files \bDescrip.MMS$ \bDESCRIP.MMS$ \bdescrip.mms$ # Avoid Makemaker generated and utility files. \bMANIFEST\.bak \bMakefile$ \bblib/ \bMakeMaker-\d \bpm_to_blib\.ts$ \bpm_to_blib$ \bblibdirs\.ts$ # 6.18 through 6.25 generated this \b_eumm/ # 7.05_05 and above # Avoid Module::Build generated and utility files. \bBuild$ \b_build/ \bBuild.bat$ \bBuild.COM$ \bBUILD.COM$ \bbuild.com$ # and Module::Build::Tiny generated files \b_build_params$ # Avoid temp and backup files. ~$ \.old$ \#$ \b\.# \.bak$ \.tmp$ \.# \.rej$ \..*\.sw.?$ # Avoid OS-specific files/dirs # Mac OSX metadata \B\.DS_Store # Mac OSX SMB mount metadata files \B\._ # Avoid Devel::Cover and Devel::CoverX::Covered files. \bcover_db\b \bcovered\b # Avoid prove files \B\.prove$ # Avoid MYMETA files ^MYMETA\. NanoMsg-Raw-0.10/META.json000644 000765 000024 00000002545 13030572462 014732 0ustar00bzstaff000000 000000 { "abstract" : "Low-level interface to the nanomsg scalability protocols library", "author" : [ "Florian Ragwitz , Boris Zentner " ], "dynamic_config" : 1, "generated_by" : "ExtUtils::MakeMaker version 7.24, CPAN::Meta::Converter version 2.150010", "license" : [ "mit" ], "meta-spec" : { "url" : "http://search.cpan.org/perldoc?CPAN::Meta::Spec", "version" : "2" }, "name" : "NanoMsg-Raw", "no_index" : { "directory" : [ "t", "inc" ] }, "prereqs" : { "build" : { "requires" : { "ExtUtils::MakeMaker" : "0" } }, "configure" : { "requires" : { "Devel::CheckLib" : "0.9", "ExtUtils::MakeMaker" : "0" } }, "runtime" : { "requires" : { "Exporter" : "0", "XSLoader" : "0", "overload" : "0", "strict" : "0", "warnings" : "0" } }, "test" : { "requires" : { "Test::Fatal" : "0", "Test::More" : "0.89", "Test::SharedFork" : "0", "Test::TCP" : "0", "Time::HiRes" : "0" } } }, "release_status" : "stable", "version" : "0.10", "x_serialization_backend" : "JSON::PP version 2.27400" } NanoMsg-Raw-0.10/META.yml000644 000765 000024 00000001444 13030572462 014557 0ustar00bzstaff000000 000000 --- abstract: 'Low-level interface to the nanomsg scalability protocols library' author: - 'Florian Ragwitz , Boris Zentner ' build_requires: ExtUtils::MakeMaker: '0' Test::Fatal: '0' Test::More: '0.89' Test::SharedFork: '0' Test::TCP: '0' Time::HiRes: '0' configure_requires: Devel::CheckLib: '0.9' ExtUtils::MakeMaker: '0' dynamic_config: 1 generated_by: 'ExtUtils::MakeMaker version 7.24, CPAN::Meta::Converter version 2.150010' license: mit meta-spec: url: http://module-build.sourceforge.net/META-spec-v1.4.html version: '1.4' name: NanoMsg-Raw no_index: directory: - t - inc requires: Exporter: '0' XSLoader: '0' overload: '0' strict: '0' warnings: '0' version: '0.10' x_serialization_backend: 'CPAN::Meta::YAML version 0.018' NanoMsg-Raw-0.10/Raw.xs000644 000765 000024 00000026247 12635104276 014427 0ustar00bzstaff000000 000000 #include "EXTERN.h" #include "perl.h" #include "XSUB.h" #include #include #ifndef XS_INTERNAL # define XS_INTERNAL(name) static XSPROTO(name) #endif #ifndef mg_findext # define mg_findext(sv, type, vtbl) S_mg_findext(aTHX_ sv, type, vtbl) static MAGIC * S_mg_findext(pTHX_ SV *sv, int type, const MGVTBL *vtbl) { MAGIC *mg; if (sv) { for (mg = SvMAGIC(sv); mg; mg = mg->mg_moremagic) { if (mg->mg_type == type && mg->mg_virtual == vtbl) return mg; } } return NULL; } #endif #define PERL_VERSION_DECIMAL(r,v,s) (r*1000000 + v*1000 + s) #define PERL_DECIMAL_VERSION \ PERL_VERSION_DECIMAL(PERL_REVISION,PERL_VERSION,PERL_SUBVERSION) #define PERL_VERSION_GE(r,v,s) \ (PERL_DECIMAL_VERSION >= PERL_VERSION_DECIMAL(r,v,s)) #if PERL_VERSION_GE(5,14,0) # define SIZETf "%zd" # define SIZETfARG(s) s #else # define SIZETf "%lu" # define SIZETfARG(s) (unsigned long)s #endif #ifndef dVAR # define dVAR dNOOP #endif #ifndef croak_xs_usage # define croak_xs_usage(a,b) S_croak_xs_usage(aTHX_ a,b) static void S_croak_xs_usage(pTHX_ const CV *const cv, const char *const params); #endif SV *errno_sv; HV *message_stash, *message_freed_stash; #define PERL_NN_SET_ERRNO STMT_START { \ sv_setpv(errno_sv, nn_strerror(errno)); \ SvIV_set(errno_sv, errno); \ SvIOK_on(errno_sv); \ } STMT_END typedef int perl_nn_int; typedef int perl_nn_int_bool; typedef void * perl_nn_messagebuf; struct perl_nn_message { void *buf; size_t len; }; static int perl_nn_message_mg_dup (pTHX_ MAGIC *mg, CLONE_PARAMS *param) { struct perl_nn_message *dst; struct perl_nn_message *src = (struct perl_nn_message *)mg->mg_ptr; PERL_UNUSED_ARG(param); Newx(dst, 1, struct perl_nn_message); dst->len = src->len; dst->buf = nn_allocmsg(src->len, 0); /* FIXME: alloc type */ memcpy(dst->buf, src->buf, src->len); mg->mg_ptr = (char *)dst; return 0; } static int perl_nn_message_mg_free (pTHX_ SV *sv, MAGIC *mg) { struct perl_nn_message *msg = (struct perl_nn_message *)mg->mg_ptr; PERL_UNUSED_ARG(sv); if (msg->buf) nn_freemsg(msg->buf); return 0; } static MGVTBL perl_nn_message_vtbl = { NULL, /* get */ NULL, /* set */ NULL, /* len */ NULL, /* clear */ perl_nn_message_mg_free, /* free */ NULL, /* copy */ perl_nn_message_mg_dup, /* dup */ NULL /* local */ }; static struct perl_nn_message * perl_nn_message_mg_find (pTHX_ SV *sv) { MAGIC *mg = mg_findext(sv, PERL_MAGIC_ext, &perl_nn_message_vtbl); return (struct perl_nn_message *)mg->mg_ptr; } AV *symbol_names; XS_INTERNAL(XS_NanoMsg__Raw_nn_constant); XS_INTERNAL(XS_NanoMsg__Raw_nn_constant) { dVAR; dXSARGS; IV ix = XSANY.any_iv; dXSTARG; if (items != 0) croak_xs_usage(cv, ""); XSprePUSH; PUSHi((IV)ix); XSRETURN(1); } static struct perl_nn_message * perl_nn_upgrade_to_message (pTHX_ SV *sv) { MAGIC *mg; struct perl_nn_message *msg; SV *obj = newSV(0); /* There's no sane way to prepare an arbitrary SV to be a reference, so we'll * have to go out of our way a bit. Also, we're forced to free any PV, if * present, as PV and RV are stored in the same place within an SV. */ if (SvROK(sv)) SvREFCNT_dec(SvRV(sv)); if (SvTYPE(sv) >= SVt_PV && SvPVX_const(sv) && SvLEN(sv)) { SvPV_free(sv); SvPV_set(sv, NULL); SvLEN_set(sv, 0); } SvUPGRADE(sv, SVt_RV); SvPOK_off(sv); SvRV_set(sv, obj); SvROK_on(sv); sv_upgrade(obj, SVt_PVMG); SvPOK_on(obj); SvCUR_set(obj, 0); SvLEN_set(obj, 0); sv_bless(sv, message_stash); SvREADONLY_on(obj); Newxz(msg, 1, struct perl_nn_message); mg = sv_magicext(obj, NULL, PERL_MAGIC_ext, &perl_nn_message_vtbl, (char *)msg, 0); mg->mg_flags |= MGf_DUP; return msg; } static struct perl_nn_message * perl_nn_invalidate_message (pTHX_ SV *sv) { MAGIC *mg, *prevmg, *moremg = NULL; struct perl_nn_message *msg = NULL; SV *obj = SvRV(sv); SvREADONLY_off(obj); SvPOK_off(obj); SvPVX(obj) = NULL; sv_bless(sv, message_freed_stash); for (prevmg = NULL, mg = SvMAGIC(obj); mg; prevmg = mg, mg = moremg) { moremg = mg->mg_moremagic; if (mg->mg_type == PERL_MAGIC_ext && mg->mg_virtual == &perl_nn_message_vtbl) { if (prevmg) prevmg->mg_moremagic = moremg; else SvMAGIC_set(obj, moremg); mg->mg_moremagic = NULL; msg = (struct perl_nn_message *)mg->mg_ptr; Safefree(mg); mg = prevmg; } } assert(msg); return msg; } static bool perl_nn_is_message (pTHX_ SV *sv) { return sv_isobject(sv) && SvSTASH(SvRV(sv)) == message_stash; } MODULE=NanoMsg::Raw PACKAGE=NanoMsg::Raw PROTOTYPES: DISABLE perl_nn_int nn_socket (domain, protocol) int domain int protocol perl_nn_int_bool nn_close (s) int s perl_nn_int_bool nn_setsockopt (s, level, option, optval) int s int level int option SV *optval PREINIT: const void *c_optval; size_t c_optvallen; int a_int; /* needed at this scope so we can pass a pointer to it in the generated CODE section */ INIT: if (SvPOKp(optval)) { c_optval = SvPV_const(optval, c_optvallen); } else { a_int = (int)SvIV(optval); c_optval = &a_int; c_optvallen = sizeof(int); } C_ARGS: s, level, option, c_optval, c_optvallen SV * nn_getsockopt (s, level, option) int s int level int option PREINIT: size_t optvallen = 256; /* maxlen of the return value without trailing \0 */ int ret; INIT: RETVAL = newSV(256 + 1); (void)SvPOK_only(RETVAL); CODE: ret = nn_getsockopt(s, level, option, SvPVX(RETVAL), &optvallen); POSTCALL: if (ret < 0) { PERL_NN_SET_ERRNO; XSRETURN_UNDEF; } SvCUR_set(RETVAL, optvallen); *SvEND(RETVAL) = '\0'; OUTPUT: RETVAL perl_nn_int nn_bind (s, addr) int s const char *addr perl_nn_int nn_connect (s, addr) int s const char *addr perl_nn_int_bool nn_shutdown (s, how) int s int how perl_nn_int nn_send (s, buf, flags = 0) int s SV *buf int flags PREINIT: void *c_buf; size_t len; INIT: if (perl_nn_is_message(aTHX_ buf)) { c_buf = &perl_nn_message_mg_find(aTHX_ SvRV(buf))->buf; len = NN_MSG; } else { c_buf = SvPV(buf, len); } C_ARGS: s, c_buf, len, flags POSTCALL: if (len == NN_MSG) perl_nn_invalidate_message(aTHX_ buf); int nn_recv (s, buf, len = NN_MSG, flags = 0) int s SV *buf size_t len int flags PREINIT: void *c_buf; struct perl_nn_message *msg; INIT: if (len == NN_MSG) { msg = perl_nn_upgrade_to_message(aTHX_ buf); c_buf = &msg->buf; } else { if (!SvOK(buf)) sv_setpvs(buf, ""); SvPV_force_nolen(buf); c_buf = SvGROW(buf, len); } C_ARGS: s, c_buf, len, flags POSTCALL: if (RETVAL < 0) { PERL_NN_SET_ERRNO; XSRETURN_UNDEF; } if (len == NN_MSG) { SV *obj = SvRV(buf); msg->len = RETVAL; SvPVX(obj) = msg->buf; SvCUR_set(obj, RETVAL); SvPOK_on(obj); } else { SvCUR_set(buf, (int)len < RETVAL ? (int)len : RETVAL); (void)SvPOK_only(buf); } # TODO: cmsg perl_nn_int nn_sendmsg (s, flags, ...) int s int flags PREINIT: struct nn_msghdr hdr; struct nn_iovec *iov; int iovlen, i; INIT: iovlen = items - 2; Newx(iov, iovlen, struct nn_iovec); for (i = 0; i < iovlen; i++) { SV *sv = ST(i + 2); if (perl_nn_is_message(aTHX_ sv)) { struct perl_nn_message *msg = perl_nn_message_mg_find(aTHX_ SvRV(sv)); iov[i].iov_base = &msg->buf; iov[i].iov_len = NN_MSG; } else { iov[i].iov_base = SvPV(sv, iov[i].iov_len); } } memset(&hdr, 0, sizeof(hdr)); hdr.msg_iov = iov; hdr.msg_iovlen = iovlen; C_ARGS: s, &hdr, flags POSTCALL: for (i = 0; i < iovlen; i++) if (iov[i].iov_len == NN_MSG) perl_nn_invalidate_message(aTHX_ ST(i + 2)); CLEANUP: Safefree(iov); int nn_recvmsg (s, flags, ...) int s int flags PREINIT: struct nn_msghdr hdr; struct nn_iovec *iov; int iovlen, i; size_t nbytes; struct perl_nn_message *msg; INIT: iovlen = (items - 2) / 2; Newx(iov, iovlen, struct nn_iovec); for (i = 0; i < iovlen; i++) { SV *svbuf = ST(i*2 + 2); UV len = SvUV(ST(i*2 + 3)); iov[i].iov_len = len; if (len == NN_MSG) { msg = perl_nn_upgrade_to_message(aTHX_ svbuf); iov[i].iov_base = &msg->buf; } else { if (!SvOK(svbuf)) sv_setpvs(svbuf, ""); SvPV_force_nolen(svbuf); SvGROW(svbuf, len); iov[i].iov_base = SvPVX(svbuf); } } memset (&hdr, 0, sizeof (hdr)); hdr.msg_iov = iov; hdr.msg_iovlen = iovlen; C_ARGS: s, &hdr, flags POSTCALL: if (RETVAL < 0) { PERL_NN_SET_ERRNO; XSRETURN_UNDEF; } nbytes = RETVAL; if (iovlen == 1 && iov[0].iov_len == NN_MSG) { SV *obj = SvRV(ST(2)); msg->len = RETVAL; SvPVX(obj) = msg->buf; SvCUR_set(obj, RETVAL); SvPOK_on(obj); } else { for (i = 0; i < iovlen; i++) { size_t max = iov[i].iov_len < nbytes ? iov[i].iov_len : nbytes; SvCUR_set(ST(i*2 + 2), max); if (nbytes > 0) nbytes -= max; } } CLEANUP: Safefree(iov); perl_nn_messagebuf nn_allocmsg (size, type) size_t size int type const char * nn_strerror (errnum) int errnum SV * nn_errno () CODE: RETVAL = SvREFCNT_inc(errno_sv); OUTPUT: RETVAL perl_nn_int_bool nn_device (s1, s2) int s1 int s2 INIT: if (ST(0) == &PL_sv_undef) s1 = -1; if (ST(1) == &PL_sv_undef) s2 = -1; void nn_term () void _symbols () PREINIT: int i; PPCODE: for (i = 0; i <= av_len(symbol_names); i++) mXPUSHs(SvREFCNT_inc(*av_fetch(symbol_names, i, 0))); BOOT: symbol_names = newAV(); errno_sv = newSV(0); sv_upgrade(errno_sv, SVt_PVIV); message_stash = gv_stashpvs("NanoMsg::Raw::Message", 0); message_freed_stash = gv_stashpvs("NanoMsg::Raw::Message::Freed", GV_ADD); { CV *cv; const char *sym; int val, i = 0; char name[4096] = "NanoMsg::Raw::"; size_t prefixlen = sizeof("NanoMsg::Raw::") - 1; while ((sym = nn_symbol(i++, &val)) != NULL) { size_t symlen = strlen(sym); if (strncmp(sym, "EFAULT", sizeof("EFAULT")-1) == 0) continue; av_push(symbol_names, newSVpv(sym, symlen)); memcpy(name + prefixlen, sym, symlen+1); cv = newXS(name, XS_NanoMsg__Raw_nn_constant, file); XSANY.any_iv = val; } memcpy(name + prefixlen, "NN_MSG", sizeof("NN_MSG")); cv = newXS(name, XS_NanoMsg__Raw_nn_constant, file); XSANY.any_iv = NN_MSG; } MODULE=NanoMsg::Raw PACKAGE=NanoMsg::Raw::Message void copy (sv, src) SV *sv SV *src PREINIT: const void *buf; STRLEN len; SV *obj; struct perl_nn_message *msg; INIT: obj = SvRV(sv); buf = SvPV(src, len); msg = perl_nn_message_mg_find(aTHX_ obj); if (len > msg->len) croak("Trying to copy "SIZETf" bytes into a message buffer of size "SIZETf, SIZETfARG(len), SIZETfARG(msg->len)); CODE: memcpy(msg->buf, buf, len); SvPVX(obj) = msg->buf; SvCUR_set(obj, len); SvPOK_on(obj); NanoMsg-Raw-0.10/README.md000644 000765 000024 00000101413 12557220277 014571 0ustar00bzstaff000000 000000 # SYNOPSIS use Test::More; use NanoMsg::Raw; my $sb = nn_socket(AF_SP, NN_PAIR); nn_bind($sb, 'inproc://foo'); my $sc = nn_socket(AF_SP, NN_PAIR); nn_connect($sc, 'inproc://foo'); nn_send($sb, 'bar'); nn_recv($sc, my $buf); is $buf, 'bar'; # WARNING **nanomsg, the c library this module is based on, is still in beta stage!** # DESCRIPTION `NanoMsg::Raw` is a binding to the `nanomsg` C library. The goal of this module is to provide a very low-level and manual interface to all the functionality of the nanomsg library. It doesn't intend to provide a convenient high-level API, integration with event loops, or the like. Those are intended to be implemented as separate abstractions on top of `NanoMsg::Raw`. The nanomsg C library is a high-performance implementation of several "scalability protocols". Scalability protocol's job is to define how multiple applications communicate to form a single distributed application. Implementation of following scalability protocols is available at the moment: ### * `PAIR` simple one-to-one communication * `BUS` simple many-to-many communication * `REQREP` allows one to build clusters of stateless services to process user requests * `PUBSUB` distributes messages to large sets of interested subscribers * `PIPELINE` aggregates messages from multiple sources and load balances them among many destinations * `SURVEY` allows one to query state of multiple applications in a single go Scalability protocols are layered on top of transport layer in the network stack. At the moment, nanomsg library supports following transports: ### * `INPROC` transport within a process (between threads, modules etc.) * `IPC` transport between processes on a single machine * `TCP` network transport via TCP ### nn\_socket($domain, $protocol) my $s = nn_socket(AF_SP, NN_PAIR); die nn_errno unless defined $s; Creates a nanomsg socket with specified `$domain` and `$protocol`. Returns a file descriptor for the newly created socket. Following domains are defined at the moment: ### * `AF_SP` Standard full-blown SP socket. * `AF_SP_RAW` Raw SP socket. Raw sockets omit the end-to-end functionality found in `AF_SP` sockets and thus can be used to implement intermediary devices in SP topologies. The `$protocol` parameter defines the type of the socket, which in turn determines the exact semantics of the socket. See ["Protocols"](#protocols) to get the list of available protocols and their socket types. The newly created socket is initially not associated with any endpoints. In order to establish a message flow at least one endpoint has to be added to the socket using `nn_bind` or `nn_connect`. Also note that type argument as found in standard `socket` function is omitted from `nn_socket`. All the SP sockets are message-based and thus of `SOCK_SEQPACKET` type. If the function succeeds file descriptor of the new socket is returned. Otherwise, `undef` is returned and `nn_errno` is set to to one of the values defined below. ### * `EAFNOSUPPORT` Specified address family is not supported. * `EINVAL` Unknown protocol. * `EMFILE` The limit on the total number of open SP sockets or OS limit for file descriptors has been reached. * `ETERM` The library is terminating. Note that file descriptors returned by `nn_socket` function are not standard file descriptors and will exhibit undefined behaviour when used with system functions. Moreover, it may happen that a system file descriptor and file descriptor of an SP socket will incidentally collide (be equal). ### nn\_close($s) nn_close($s) or die nn_errno; Closes the socket `$s`. Any buffered inbound messages that were not yet received by the application will be discarded. The library will try to deliver any outstanding outbound messages for the time specified by `NN_LINGER` socket option. The call will block in the meantime. If the function succeeds, a true value is returned. Otherwise, `undef` is returned and `nn_errno` is set to to one of the values defined below. ### * `EBADF` The provided socket is invalid. * `EINTR` Operation was interrupted by a signal. The socket is not fully closed yet. Operation can be re-started by calling `nn_close` again. ### nn\_setsockopt($s, $level, $option, $value) nn_setsockopt($s, NN_SOL_SOCKET, NN_LINGER, 1000) or die nn_errno; nn_setsockopt($s, NN_SOL_SOCKET, NN_SUB_SUBSCRIBE, 'ABC') or die nn_errno; Sets the `$value` of the socket option `$option`. The `$level` argument specifies the protocol level at which the option resides. For generic socket-level options use the `NN_SOL_SOCKET` level. For socket-type-specific options use the socket type for the `$level` argument (e.g. `NN_SUB`). For transport-specific options use the ID of the transport as the `$level` argument (e.g. `NN_TCP`). If the function succeeds a true value is returned. Otherwise, `undef` is returned and `nn_errno` is set to to one of the values defined below. ### * `EBADF` The provided socket is invalid. * `ENOPROTOOPT` The option is unknown at the level indicated. * `EINVAL` The specified option value is invalid. * `ETERM` The library is terminating. These are the generic socket-level (`NN_SOL_SOCKET` level) options: ### * `NN_LINGER` Specifies how long the socket should try to send pending outbound messages after `nn_close` has been called, in milliseconds. Negative values mean infinite linger. The type of the option is int. The default value is 1000 (1 second). * `NN_SNDBUF` Size of the send buffer, in bytes. To prevent blocking for messages larger than the buffer, exactly one message may be buffered in addition to the data in the send buffer. The type of this option is int. The default value is 128kB. * `NN_RCVBUF` Size of the receive buffer, in bytes. To prevent blocking for messages larger than the buffer, exactly one message may be buffered in addition to the data in the receive buffer. The type of this option is int. The default value is 128kB. * `NN_SNDTIMEO` The timeout for send operation on the socket, in milliseconds. If a message cannot be sent within the specified timeout, an `EAGAIN` error is returned. Negative values mean infinite timeout. The type of the option is int. The default value is -1. * `NN_RCVTIMEO` The timeout for recv operation on the socket, in milliseconds. If a message cannot be received within the specified timeout, an `EAGAIN` error is returned. Negative values mean infinite timeout. The type of the option is int. The default value is -1. * `NN_RECONNECT_IVL` For connection-based transports such as TCP, this option specifies how long to wait, in milliseconds, when connection is broken before trying to re-establish it. Note that actual reconnect interval may be randomised to some extent to prevent severe reconnection storms. The type of the option is int. The default value is 100 (0.1 second). * `NN_RECONNECT_IVL_MAX` This option is to be used only in addition to `NN_RECONNECT_IVL` option. It specifies maximum reconnection interval. On each reconnect attempt, the previous interval is doubled until `NN_RECONNECT_IVL_MAX` is reached. A value of zero means that no exponential backoff is performed and reconnect interval is based only on `NN_RECONNECT_IVL`. If `NN_RECONNECT_IVL_MAX` is less than `NN_RECONNECT_IVL`, it is ignored. The type of the option is int. The default value is 0. * `NN_SNDPRIO` Sets outbound priority for endpoints subsequently added to the socket. This option has no effect on socket types that send messages to all the peers. However, if the socket type sends each message to a single peer (or a limited set of peers), peers with high priority take precedence over peers with low priority. The type of the option is int. The highest priority is 1, the lowest priority is 16. The default value is 8. * `NN_IPV4ONLY` If set to 1, only IPv4 addresses are used. If set to 0, both IPv4 and IPv6 addresses are used. The default value is 1. ### nn\_getsockopt($s, $level, $option) my $linger = unpack 'i', nn_getsockopt($s, NN_SOL_SOCKET, NN_LINGER) || die nn_errno; Retrieves the value for the socket option `$option`. The `$level` argument specifies the protocol level at which the option resides. For generic socket-level options use the `NN_SOL_SOCKET` level. For socket-type-specific options use the socket type for the `$level` argument (e.g. `NN_SUB`). For transport-specific options use ID of the transport as the `$level` argument (e.g. `NN_TCP`). The function returns a packed string representing the requested socket option, or `undef` on error, with one of the following reasons for the error placed in `nn_errno`. ### * `EBADF` The provided socket is invalid. * `ENOPROTOOPT` The option is unknown at the `$level` indicated. * `ETERM` The library is terminating. Just what is in the packed string depends on `$level` and `$option`; see the list of socket options for details; A common case is that the option is an integer, in which case the result is a packed integer, which you can decode using `unpack` with the `i` (or `I`) format. This function can be used to retrieve the values for all the generic socket-level (`NN_SOL_SOCKET`) options documented in `nn_getsockopt` and also supports these additional generic socket-level options that can only be retrieved but not set: ### * `NN_DOMAIN` Returns the domain constant as it was passed to `nn_socket`. * `NN_PROTOCOL` Returns the protocol constant as it was passed to `nn_socket`. * `NN_SNDFD` Retrieves a file descriptor that is readable when a message can be sent to the socket. The descriptor should be used only for polling and never read from or written to. The type of the option is int. The descriptor becomes invalid and should not be used any more once the socket is closed. This socket option is not available for unidirectional recv-only socket types. * `NN_RCVFD` Retrieves a file descriptor that is readable when a message can be received from the socket. The descriptor should be used only for polling and never read from or written to. The type of the option is int. The descriptor becomes invalid and should not be used any more once the socket is closed. This socket option is not available for unidirectional send-only socket types. ### nn\_bind($s, $addr) my $eid = nn_bind($s, 'inproc://test'); die nn_errno unless defined $eid; Adds a local endpoint to the socket `$s`. The endpoint can be then used by other applications to connect to. The `$addr` argument consists of two parts as follows: `transport://address`. The `transport` specifies the underlying transport protocol to use. The meaning of the `address` part is specific to the underlying transport protocol. See ["Transports"](#transports) for a list of available transport protocols. The maximum length of the `$addr` parameter is specified by `NN_SOCKADDR_MAX` constant. Note that `nn_bind` and `nn_connect` may be called multiple times on the same socket thus allowing the socket to communicate with multiple heterogeneous endpoints. If the function succeeds, an endpoint ID is returned. Endpoint ID can be later used to remove the endpoint from the socket via `nn_shutdown` function. If the function fails, `undef` is returned and `nn_errno` is set to to one of the values defined below. ### * `EBADF` The provided socket is invalid. * `EMFILE` Maximum number of active endpoints was reached. * `EINVAL` The syntax of the supplied address is invalid. * `ENAMETOOLONG` The supplied address is too long. * `EPROTONOSUPPORT` The requested transport protocol is not supported. * `EADDRNOTAVAIL` The requested endpoint is not local. * `ENODEV` Address specifies a nonexistent interface. * `EADDRINUSE` The requested local endpoint is already in use. * `ETERM` The library is terminating. ### nn\_connect($s, $addr) my $eid = nn_connect($s, 'inproc://test'); die nn_errno unless defined $eid; Adds a remote endpoint to the socket `$s`. The library would then try to connect to the specified remote endpoint. The `$addr` argument consists of two parts as follows: `transport://address`. The `transport` specifies the underlying transport protocol to use. The meaning of the `address` part is specific to the underlying transport protocol. See ["Protocols"](#protocols) for a list of available transport protocols. The maximum length of the `$addr` parameter is specified by `NN_SOCKADDR_MAX` constant. Note that `nn_connect` and `nn_bind` may be called multiple times on the same socket thus allowing the socket to communicate with multiple heterogeneous endpoints. If the function succeeds, an endpoint ID is returned. Endpoint ID can be later used to remove the endpoint from the socket via `nn_shutdown` function. If the function fails, `undef` is returned and `nn_errno` is set to to one of the values defined below. ### * `EBADF` The provided socket is invalid. * `EMFILE` Maximum number of active endpoints was reached. * `EINVAL` The syntax of the supplied address is invalid. * `ENAMETOOLONG` The supplied address is too long. * `EPROTONOSUPPORT` The requested transport protocol is not supported. * `ENODEV` Address specifies a nonexistent interface. * `ETERM` The library is terminating. ### nn\_shutdown($s, $eid) nn_shutdown($s, $eid) or die nn_errno; Removes an endpoint from socket `$s`. The `eid` parameter specifies the ID of the endpoint to remove as returned by prior call to `nn_bind` or `nn_connect`. The `nn_shutdown` call will return immediately. However, the library will try to deliver any outstanding outbound messages to the endpoint for the time specified by the `NN_LINGER` socket option. If the function succeeds, a true value is returned. Otherwise, `undef` is returned and `nn_errno` is set to to one of the values defined below. ### * `EBADF` The provided socket is invalid. * `EINVAL` The how parameter doesn't correspond to an active endpoint. * `EINTR` Operation was interrupted by a signal. The endpoint is not fully closed yet. Operation can be re-started by calling `nn_shutdown` again. * `ETERM` The library is terminating. ### nn\_send($s, $data, $flags=0) my $bytes_sent = nn_send($s, 'foo'); die nn_errno unless defined $bytes_sent; This function will send a message containing the provided `$data` to the socket `$s`. `$data` can either be anything that can be used as a byte string in perl or a message buffer instance allocated by `nn_allocmsg`. In case of a message buffer instance the instance will be deallocated and invalidated by the `nn_send` function. The buffer will be an instance of `NanoMsg::Raw::Message::Freed` after the call to `nn_send`. Which of the peers the message will be sent to is determined by the particular socket type. The `$flags` argument, which defaults to `0`, is a combination of the flags defined below: ### * `NN_DONTWAIT` Specifies that the operation should be performed in non-blocking mode. If the message cannot be sent straight away, the function will fail with `nn_errno` set to `EAGAIN`. If the function succeeds, the number of bytes in the message is returned. Otherwise, a `undef` is returned and `nn_errno` is set to to one of the values defined below. ### * `EBADF` The provided socket is invalid. * `ENOTSUP` The operation is not supported by this socket type. * `EFSM` The operation cannot be performed on this socket at the moment because the socket is not in the appropriate state. This error may occur with socket types that switch between several states. * `EAGAIN` Non-blocking mode was requested and the message cannot be sent at the moment. * `EINTR` The operation was interrupted by delivery of a signal before the message was sent. * `ETIMEDOUT` Individual socket types may define their own specific timeouts. If such timeout is hit, this error will be returned. * `ETERM` The library is terminating. ### nn\_recv($s, $data, $length=NN\_MSG, $flags=0) my $bytes_received = nn_recv($s, my $buf, 256); die nn_errno unless defined $bytes_received; Receive a message from the socket `$s` and store it in the buffer `$buf`. Any bytes exceeding the length specified by the `$length` argument will be truncated. Alternatively, `nn_recv` can allocate a message buffer instance for you. To do so, set the `$length` parameter to `NN_MSG` (the default). The `$flags` argument, which defaults to `0`, is a combination of the flags defined below: ### * `NN_DONTWAIT` Specifies that the operation should be performed in non-blocking mode. If the message cannot be received straight away, the function will fail with `nn_errno` set to `EAGAIN`. If the function succeeds number of bytes in the message is returned. Otherwise, `undef` is returned and `nn_errno` is set to to one of the values defined below. ### * `EBADF` The provided socket is invalid. * `ENOTSUP` The operation is not supported by this socket type. * `EFSM` The operation cannot be performed on this socket at the moment because socket is not in the appropriate state. This error may occur with socket types that switch between several states. * `EAGAIN` Non-blocking mode was requested and there's no message to receive at the moment. * `EINTR` The operation was interrupted by delivery of a signal before the message was received. * `ETIMEDOUT` Individual socket types may define their own specific timeouts. If such timeout is hit this error will be returned. * `ETERM` The library is terminating. ### nn\_sendmsg($s, $flags, $data1, $data2, ..., $dataN) my $bytes_sent = nn_sendmsg($s, 0, 'foo', 'bar'); die nn_errno unless defined $bytes_sent; This function is a fine-grained alternative to `nn_send`. It allows sending multiple data buffers that make up a single message without having to create another temporary buffer to hold the concatenation of the different message parts. The scalars containing the data to be sent (`$data1`, `$data2`, ..., `$dataN`) can either be anything that can be used as a byte string in perl or a message buffer instance allocated by `nn_allocmsg`. In case of a message buffer instance the instance will be deallocated and invalidated by the `nn_sendmsg` function. The buffers will be a instances of `NanoMsg::Raw::Message::Freed` after the call to `nn_sendmsg`. When using message buffer instances, only one buffer may be provided. To which of the peers will the message be sent to is determined by the particular socket type. The `$flags` argument is a combination of the flags defined below: ### * `NN_DONTWAIT` Specifies that the operation should be performed in non-blocking mode. If the message cannot be sent straight away, the function will fail with `nn_errno` set to `EAGAIN`. If the function succeeds number of bytes in the message is returned. Otherwise, `undef` is returned and `nn_errno` is set to to one of the values defined below. ### * `EBADF` The provided socket is invalid. * `ENOTSUP` The operation is not supported by this socket type. * `EFSM` The operation cannot be performed on this socket at the moment because socket is not in the appropriate state. This error may occur with socket types that switch between several states. * `EAGAIN` Non-blocking mode was requested and the message cannot be sent at the moment. * `EINTR` The operation was interrupted by delivery of a signal before the message was sent. * `ETIMEDOUT` Individual socket types may define their own specific timeouts. If such timeout is hit this error will be returned. * `ETERM` The library is terminating. In the future, `nn_sendmsg` might allow for sending along additional control data. ### nn\_recvmsg($s, $flags, $data1 => $len1, $data2 => $len2, ..., $dataN => $lenN) my $bytes_received = nn_recvmsg($s, 0, my $buf1 => 256, my $buf2 => 1024); die nn_errno unless defined $bytes_received; This function is a fine-grained alternative to `nn_recv`. It allows receiving a single message into multiple data buffers of different sizes, eliminating the need to create copies of part of the received message in some cases. The scalars in which to receive the message data (`$buf1`, `$buf2`, ..., `$bufN`) will be filled with as many bytes of data as is specified by the length parameter following them in the argument list (`$len1`, `$len2`, ..., `$lenN`). Alternatively, `nn_recvmsg` can allocate a message buffer instance for you. To do so, set the length parameter of a buffer to to `NN_MSG`. In this case, only one receive buffer can be provided. The `$flags` argument is a combination of the flags defined below: ### * `NN_DONTWAIT` Specifies that the operation should be performed in non-blocking mode. If the message cannot be received straight away, the function will fail with `nn_errno` set to `EAGAIN`. In the future, `nn_recvmsg` might allow for receiving additional control data. ### nn\_allocmsg($size, $type) my $msg = nn_allocmsg(3, 0) or die nn_errno; $msg->copy('foo'); nn_send($s, $msg); Allocate a message of the specified `$size` to be sent in zero-copy fashion. The content of the message is undefined after allocation and it should be filled in by the user. While `nn_send` and `nn_sendmsg` allow to send arbitrary buffers, buffers allocated using `nn_allocmsg` can be more efficient for large messages as they allow for using zero-copy techniques. The `$type` parameter specifies type of allocation mechanism to use. Zero is the default one. However, individual transport mechanisms may define their own allocation mechanisms, such as allocating in shared memory or allocating a memory block pinned down to a physical memory address. Such allocation, when used with the transport that defines them, should be more efficient than the default allocation mechanism. If the function succeeds a newly allocated message buffer instance (an object instance of the class [NanoMsg::Raw::Message](https://metacpan.org/pod/NanoMsg::Raw::Message)) is returned. Otherwise, `undef` is returned and `nn_errno` is set to to one of the values defined below. ### * `EINVAL` Supplied allocation type is invalid. * `ENOMEM` Not enough memory to allocate the message. ### nn\_errno() Returns value of `errno` after the last call to any nanomsg function in the current thread. This function can be used in the same way the `$!` global variable is be used for many other system and library calls. The return value can be used in numeric context, for example to compare it with error code constants such as `EAGAIN`, or in a string context, to retrieve a textual message describing the error. ### nn\_strerror($errno) Returns a textual representation of the error described by the nummeric `$errno` provided. It shouldn't normally be necessary to ever call this function, as using `nn_errno` in string context is basically equivalent to `nn_strerror(nn_errno)`. ### nn\_device($s1, $s2) nn_device($s1, $s2) or die; Starts a device to forward messages between two sockets. If both sockets are valid, the `nn_device` function loops and sends and messages received from `$s1` to `$s2` and vice versa. If only one socket is valid and the other is `undef`, `nn_device` works in a loopback mode - it loops and sends any messages received from the socket back to itself. The function loops until it hits an error. In such case it returns `undef` and sets `nn_errno` to one of the values defined below. ### * `EBADF` One of the provided sockets is invalid. * `EINVAL` Either one of the socket is not an `AF_SP_RAW` socket; or the two sockets don't belong to the same protocol; or the directionality of the sockets doesn't fit (e.g. attempt to join two SINK sockets to form a device). * `EINTR` The operation was interrupted by delivery of a signal. * `ETERM` The library is terminating. ### nn\_term() nn_term(); To help with shutdown of multi-threaded programs the `nn_term` function is provided. It informs all the open sockets that process termination is underway. If a socket is blocked inside a blocking function, such as `nn_recv`, it will be unblocked and the `ETERM` error will be returned to the user. Similarly, any subsequent attempt to invoke a socket function other than `nn_close` after `nn_term` was called will result in an `ETERM` error. If waiting for `NN_SNDFD` or `NN_RCVFD` using a polling function, such as `poll` or `select`, the call will unblock with both `NN_SNDFD` and `NN_RCVFD` signaled. The `nn_term` function itself is non-blocking. # Protocols ## One-to-one protocol Pair protocol is the simplest and least scalable scalability protocol. It allows scaling by breaking the application in exactly two pieces. For example, if a monolithic application handles both accounting and agenda of HR department, it can be split into two applications (accounting vs. HR) that are run on two separate servers. These applications can then communicate via PAIR sockets. The downside of this protocol is that its scaling properties are very limited. Splitting the application into two pieces allows one to scale to two servers. To add the third server to the cluster, application has to be split once more, say be separating HR functionality into hiring module and salary computation module. Whenever possible, try to use one of the more scalable protocols instead. ### Socket Types ### * `NN_PAIR` Socket for communication with exactly one peer. Each party can send messages at any time. If the peer is not available or send buffer is full subsequent calls to `nn_send` will block until it's possible to send the message. ### Socket Options No protocol-specific socket options are defined at the moment. ## Request/reply protocol This protocol is used to distribute the workload among multiple stateless workers. ### Socket Types ### * `NN_REQ` Used to implement the client application that sends requests and receives replies. * `NN_REP` Used to implement the stateless worker that receives requests and sends replies. ### Socket Options ### * `NN_REQ_RESEND_IVL` This option is defined on the full REQ socket. If a reply is not received in specified amount of milliseconds, the request will be automatically resent. The type of this option is int. Default value is 60000 (1 minute). ## Publish/subscribe protocol Broadcasts messages to multiple destinations. ### Socket Types ### * `NN_PUB` This socket is used to distribute messages to multiple destinations. Receive operation is not defined. * `NN_SUB` Receives messages from the publisher. Only messages that the socket is subscribed to are received. When the socket is created there are no subscriptions and thus no messages will be received. Send operation is not defined on this socket. ### Socket Options ### * `NN_SUB_SUBSCRIBE` Defined on full SUB socket. Subscribes for a particular topic. Type of the option is string. * `NN_SUB_UNSUBSCRIBE` Defined on full SUB socket. Unsubscribes from a particular topic. Type of the option is string. ## Survey protocol Allows one to broadcast a survey to multiple locations and gather the responses. ### Socket Types ### * `NN_SURVEYOR` Used to send the survey. The survey is delivered to all the connected respondents. Once the query is sent, the socket can be used to receive the responses. When the survey deadline expires, receive will return the `ETIMEDOUT` error. * `NN_RESPONDENT` Use to respond to the survey. Survey is received using receive function, response is sent using send function. This socket can be connected to at most one peer. ### Socket Options ### * `NN_SURVEYOR_DEADLINE` Specifies how long to wait for responses to the survey. Once the deadline expires, receive function will return the `ETIMEDOUT` error and all subsequent responses to the survey will be silently dropped. The deadline is measured in milliseconds. Option type is int. Default value is 1000 (1 second). ## Pipeline protocol Fair queues messages from the previous processing step and load balances them among instances of the next processing step. ### Socket Types ### * `NN_PUSH` This socket is used to send messages to a cluster of load-balanced nodes. Receive operation is not implemented on this socket type. * `NN_PULL` This socket is used to receive a message from a cluster of nodes. Send operation is not implemented on this socket type. ### Socket Options No protocol-specific socket options are defined at the moment. ## Message bus protocol Broadcasts messages from any node to all other nodes in the topology. The socket should never receives messages that it sent itself. This pattern scales only to local level (within a single machine or within a single LAN). Trying to scale it further can result in overloading individual nodes with messages. **WARNING**: For bus topology to function correctly, the user is responsible for ensuring that path from each node to any other node exists within the topology. Raw (`AF_SP_RAW`) BUS socket never send the message to the peer it was received from. ### Socket Types ### * `NN_BUS` Sent messages are distributed to all nodes in the topology. Incoming messages from all other nodes in the topology are fair-queued in the socket. ### Socket Options There are no options defined at the moment. # Transports ## In-process transport The in-process transport allows one to send messages between threads or modules inside a process. In-process address is an arbitrary case-sensitive string preceded by `inproc://` protocol specifier. All in-process addresses are visible from any module within the process. They are not visible from outside of the process. The overall buffer size for an inproc connection is determined by the `NN_RCVBUF` socket option on the receiving end of the connection. The `NN_SNDBUF` socket option is ignored. In addition to the buffer, one message of arbitrary size will fit into the buffer. That way, even messages larger than the buffer can be transferred via inproc connection. This transport's ID is `NN_INPROC`. ## Inter-process transport The inter-process transport allows for sending messages between processes within a single box. The implementation uses native IPC mechanism provided by the local operating system and the IPC addresses are thus OS-specific. On POSIX-compliant systems, UNIX domain sockets are used and IPC addresses are file references. Note that both relative (`ipc://test.ipc`) and absolute (`ipc:///tmp/test.ipc`) paths may be used. Also note that access rights on the IPC files must be set in such a way that the appropriate applications can actually use them. On Windows, named pipes are used for IPC. IPC address is an arbitrary case-insensitive string containing any character except for backslash. Internally, address `ipc://test` means that named pipe `\\.\pipe\test` will be used. This transport's ID is `NN_IPC`. ## TCP transport The TCP transport allows for passing message over the network using simple reliable one-to-one connections. TCP is the most widely used transport protocol, it is virtually ubiquitous and thus the transport of choice for communication over the network. When binding a TCP socket address of the form `tcp://interface:port` should be used. Port is the TCP port number to use. Interface is one of the following (optionally placed within square brackets): ### * Asterisk character (\*) meaning all local network interfaces. * IPv4 address of a local network interface in numeric form (192.168.0.111). * IPv6 address of a local network interface in numeric form (::1). * Interface name, as defined by operating system. When connecting a TCP socket address of the form `tcp://interface;address:port` should be used. Port is the TCP port number to use. Interface is optional and specifies which local network interface to use. If not specified, OS will select an appropriate interface itself. If specified it can be one of the following (optionally placed within square brackets): ### * IPv4 address of a local network interface in numeric form (192.168.0.111). * IPv6 address of a local network interface in numeric form (::1). * Interface name, as defined by operating system (eth0). Finally, address specifies the remote address to connect to. It can be one of the following (optionally placed within square brackets): ### * IPv4 address of a remote network interface in numeric form (192.168.0.111). * IPv6 address of a remote network interface in numeric form (::1). * The DNS name of the remote box. This transport's ID is `NN_TCP`. ### Socket Options ### * `NN_TCP_NODELAY` This option, when set to 1, disables Nagle's algorithm. It also disables delaying of TCP acknowledgments. Using this option improves latency at the expense of throughput. Type of this option is int. The default value is 0. # Constants In addition to all the error constants and `NN_` constants used in the documentation of the individual functions, protocols, and transports, the following constants are available: ### * `NN_VERSION_CURRENT` The current interface version. * `NN_VERSION_REVISION` The latest revision of the current interface. * `NN_VERSION_AGE` How many past interface versions are still supported. # SEE ALSO ### * The nanomsg C library documentation at [http://nanomsg.org/v0.1/nanomsg.7.html](http://nanomsg.org/v0.1/nanomsg.7.html) The API this module provides is very close to the C library's interface, so the C documentation is likely to be useful to developers using Perl, too. Additionally, most of this module's documentation is copied from the C library documentation, so the upstream documentation might be somewhat more recent. * [NanoMsg::Raw::Message](https://metacpan.org/pod/NanoMsg::Raw::Message) NanoMsg-Raw-0.10/t/000755 000765 000024 00000000000 13030572462 013546 5ustar00bzstaff000000 000000 NanoMsg-Raw-0.10/typemap000644 000765 000024 00000000747 12557220277 014724 0ustar00bzstaff000000 000000 perl_nn_int T_PERL_NN_INT perl_nn_int_bool T_PERL_NN_INT_BOOL perl_nn_messagebuf T_PERL_NN_MESSAGEBUF OUTPUT T_PERL_NN_INT if ($var < 0) PERL_NN_SET_ERRNO; else sv_setiv($arg, (IV)$var); T_PERL_NN_INT_BOOL if ($var < 0) PERL_NN_SET_ERRNO; else $arg = &PL_sv_yes; T_PERL_NN_MESSAGEBUF if ($var == NULL) PERL_NN_SET_ERRNO; else { struct perl_nn_message *msg = perl_nn_upgrade_to_message(aTHX_ $arg); msg->buf = $var; msg->len = size; } NanoMsg-Raw-0.10/t/00-report-prereqs.dd000644 000765 000024 00000004517 13006736313 017275 0ustar00bzstaff000000 000000 do { my $x = { 'configure' => { 'requires' => { 'Devel::CheckLib' => '0.9', 'ExtUtils::MakeMaker' => '0', 'perl' => '5.006' } }, 'develop' => { 'requires' => { 'CPAN::Meta' => '2.120920', 'Encode' => '0', 'HTTP::Tiny' => '0', 'JSON' => '0', 'List::Util' => '0', 'Module::Metadata' => '0', 'Pod::Coverage::TrustPod' => '0', 'Test::EOL' => '0', 'Test::More' => '0.88', 'Test::NoTabs' => '0', 'Test::Pod' => '1.41', 'Test::Pod::Coverage' => '1.08', 'version' => '0' } }, 'runtime' => { 'requires' => { 'Exporter' => '0', 'XSLoader' => '0', 'overload' => '0', 'perl' => '5.006', 'strict' => '0', 'warnings' => '0' } }, 'test' => { 'recommends' => { 'CPAN::Meta' => '2.120900' }, 'requires' => { 'ExtUtils::MakeMaker' => '0', 'File::Spec' => '0', 'Test::Fatal' => '0', 'Test::More' => '0.89', 'Test::TCP' => '0', 'Time::HiRes' => '0', 'perl' => '5.006' } } }; $x; }NanoMsg-Raw-0.10/t/00-report-prereqs.t000644 000765 000024 00000012731 13006736313 017146 0ustar00bzstaff000000 000000 #!perl use strict; use warnings; # This test was generated by Dist::Zilla::Plugin::Test::ReportPrereqs 0.021 use Test::More tests => 1; use ExtUtils::MakeMaker; use File::Spec; # from $version::LAX my $lax_version_re = qr/(?: undef | (?: (?:[0-9]+) (?: \. | (?:\.[0-9]+) (?:_[0-9]+)? )? | (?:\.[0-9]+) (?:_[0-9]+)? ) | (?: v (?:[0-9]+) (?: (?:\.[0-9]+)+ (?:_[0-9]+)? )? | (?:[0-9]+)? (?:\.[0-9]+){2,} (?:_[0-9]+)? ) )/x; # hide optional CPAN::Meta modules from prereq scanner # and check if they are available my $cpan_meta = "CPAN::Meta"; my $cpan_meta_pre = "CPAN::Meta::Prereqs"; my $HAS_CPAN_META = eval "require $cpan_meta; $cpan_meta->VERSION('2.120900')" && eval "require $cpan_meta_pre"; ## no critic # Verify requirements? my $DO_VERIFY_PREREQS = 1; sub _max { my $max = shift; $max = ( $_ > $max ) ? $_ : $max for @_; return $max; } sub _merge_prereqs { my ($collector, $prereqs) = @_; # CPAN::Meta::Prereqs object if (ref $collector eq $cpan_meta_pre) { return $collector->with_merged_prereqs( CPAN::Meta::Prereqs->new( $prereqs ) ); } # Raw hashrefs for my $phase ( keys %$prereqs ) { for my $type ( keys %{ $prereqs->{$phase} } ) { for my $module ( keys %{ $prereqs->{$phase}{$type} } ) { $collector->{$phase}{$type}{$module} = $prereqs->{$phase}{$type}{$module}; } } } return $collector; } my @include = qw( ); my @exclude = qw( ); # Add static prereqs to the included modules list my $static_prereqs = do 't/00-report-prereqs.dd'; # Merge all prereqs (either with ::Prereqs or a hashref) my $full_prereqs = _merge_prereqs( ( $HAS_CPAN_META ? $cpan_meta_pre->new : {} ), $static_prereqs ); # Add dynamic prereqs to the included modules list (if we can) my ($source) = grep { -f } 'MYMETA.json', 'MYMETA.yml'; if ( $source && $HAS_CPAN_META ) { if ( my $meta = eval { CPAN::Meta->load_file($source) } ) { $full_prereqs = _merge_prereqs($full_prereqs, $meta->prereqs); } } else { $source = 'static metadata'; } my @full_reports; my @dep_errors; my $req_hash = $HAS_CPAN_META ? $full_prereqs->as_string_hash : $full_prereqs; # Add static includes into a fake section for my $mod (@include) { $req_hash->{other}{modules}{$mod} = 0; } for my $phase ( qw(configure build test runtime develop other) ) { next unless $req_hash->{$phase}; next if ($phase eq 'develop' and not $ENV{AUTHOR_TESTING}); for my $type ( qw(requires recommends suggests conflicts modules) ) { next unless $req_hash->{$phase}{$type}; my $title = ucfirst($phase).' '.ucfirst($type); my @reports = [qw/Module Want Have/]; for my $mod ( sort keys %{ $req_hash->{$phase}{$type} } ) { next if $mod eq 'perl'; next if grep { $_ eq $mod } @exclude; my $file = $mod; $file =~ s{::}{/}g; $file .= ".pm"; my ($prefix) = grep { -e File::Spec->catfile($_, $file) } @INC; my $want = $req_hash->{$phase}{$type}{$mod}; $want = "undef" unless defined $want; $want = "any" if !$want && $want == 0; my $req_string = $want eq 'any' ? 'any version required' : "version '$want' required"; if ($prefix) { my $have = MM->parse_version( File::Spec->catfile($prefix, $file) ); $have = "undef" unless defined $have; push @reports, [$mod, $want, $have]; if ( $DO_VERIFY_PREREQS && $HAS_CPAN_META && $type eq 'requires' ) { if ( $have !~ /\A$lax_version_re\z/ ) { push @dep_errors, "$mod version '$have' cannot be parsed ($req_string)"; } elsif ( ! $full_prereqs->requirements_for( $phase, $type )->accepts_module( $mod => $have ) ) { push @dep_errors, "$mod version '$have' is not in required range '$want'"; } } } else { push @reports, [$mod, $want, "missing"]; if ( $DO_VERIFY_PREREQS && $type eq 'requires' ) { push @dep_errors, "$mod is not installed ($req_string)"; } } } if ( @reports ) { push @full_reports, "=== $title ===\n\n"; my $ml = _max( map { length $_->[0] } @reports ); my $wl = _max( map { length $_->[1] } @reports ); my $hl = _max( map { length $_->[2] } @reports ); if ($type eq 'modules') { splice @reports, 1, 0, ["-" x $ml, "", "-" x $hl]; push @full_reports, map { sprintf(" %*s %*s\n", -$ml, $_->[0], $hl, $_->[2]) } @reports; } else { splice @reports, 1, 0, ["-" x $ml, "-" x $wl, "-" x $hl]; push @full_reports, map { sprintf(" %*s %*s %*s\n", -$ml, $_->[0], $wl, $_->[1], $hl, $_->[2]) } @reports; } push @full_reports, "\n"; } } } if ( @full_reports ) { diag "\nVersions for all modules listed in $source (including optional ones):\n\n", @full_reports; } if ( @dep_errors ) { diag join("\n", "\n*** WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING ***\n", "The following REQUIRED prerequisites were not satisfied:\n", @dep_errors, "\n" ); } pass; # vim: ts=4 sts=4 sw=4 et: NanoMsg-Raw-0.10/t/author-eol.t000644 000765 000024 00000001764 13006736313 016022 0ustar00bzstaff000000 000000 BEGIN { unless ($ENV{AUTHOR_TESTING}) { require Test::More; Test::More::plan(skip_all => 'these tests are for testing by the author'); } } use strict; use warnings; # this test was generated with Dist::Zilla::Plugin::Test::EOL 0.18 use Test::More 0.88; use Test::EOL; my @files = ( 'lib/NanoMsg/Raw.pm', 'lib/NanoMsg/Raw/Message.pm', 't/00-report-prereqs.dd', 't/00-report-prereqs.t', 't/author-eol.t', 't/author-no-tabs.t', 't/block.t', 't/bus.t', 't/domain.t', 't/emfile.t', 't/export.t', 't/inproc.t', 't/iovec.t', 't/ipc.t', 't/msg.t', 't/pair.t', 't/pipeline.t', 't/poll.t', 't/prio.t', 't/pubsub.t', 't/recv-free-segv.t', 't/release-new-version.t', 't/release-pod-coverage.t', 't/release-pod-syntax.t', 't/reqrep.t', 't/send-recv.t', 't/separation.t', 't/survey.t', 't/tcp.t', 't/timeo.t' ); eol_unix_ok($_, { trailing_whitespace => 1 }) foreach @files; done_testing; NanoMsg-Raw-0.10/t/author-no-tabs.t000644 000765 000024 00000001732 13006736313 016601 0ustar00bzstaff000000 000000 BEGIN { unless ($ENV{AUTHOR_TESTING}) { require Test::More; Test::More::plan(skip_all => 'these tests are for testing by the author'); } } use strict; use warnings; # this test was generated with Dist::Zilla::Plugin::Test::NoTabs 0.15 use Test::More 0.88; use Test::NoTabs; my @files = ( 'lib/NanoMsg/Raw.pm', 'lib/NanoMsg/Raw/Message.pm', 't/00-report-prereqs.dd', 't/00-report-prereqs.t', 't/author-eol.t', 't/author-no-tabs.t', 't/block.t', 't/bus.t', 't/domain.t', 't/emfile.t', 't/export.t', 't/inproc.t', 't/iovec.t', 't/ipc.t', 't/msg.t', 't/pair.t', 't/pipeline.t', 't/poll.t', 't/prio.t', 't/pubsub.t', 't/recv-free-segv.t', 't/release-new-version.t', 't/release-pod-coverage.t', 't/release-pod-syntax.t', 't/reqrep.t', 't/send-recv.t', 't/separation.t', 't/survey.t', 't/tcp.t', 't/timeo.t' ); notabs_ok($_) foreach @files; done_testing; NanoMsg-Raw-0.10/t/block.t000644 000765 000024 00000002250 12727365626 015042 0ustar00bzstaff000000 000000 use strict; use warnings; use Test::More 0.89; use Test::TCP; use NanoMsg::Raw; # This test checks whether blocking on send/recv works as expected. test_tcp( server => sub { my ($port) = @_; my $sb = nn_socket(AF_SP, NN_PAIR); cmp_ok $sb, '>=', 0, 'new socket'; cmp_ok nn_bind($sb, "tcp://127.0.0.1:$port"), '>=', 0, 'bind'; is nn_recv($sb, my $buf, 3, 0), 3, 'recv 1'; is $buf, 'ABC', 'right buffer contents'; is nn_recv($sb, $buf, 3, 0), 3, 'recv 2'; is $buf, 'ABC', 'right buffer contents'; ok nn_close($sb), 'close'; }, client => sub { my ($port) = @_; my $sc = nn_socket(AF_SP, NN_PAIR); cmp_ok $sc, '>=', 0, 'new socket in worker'; cmp_ok nn_connect($sc, "tcp://127.0.0.1:$port"), '>=', 0, 'worker connect'; # Wait for the main process to block. sleep 1; is nn_send($sc, 'ABC', 0), 3, 'worker send 1'; # Wait for the main thread to process the previous message and block # once again. sleep 1; is nn_send($sc, 'ABC', 0), 3, 'worker send 2'; ok nn_close($sc), 'close'; }, ); done_testing; NanoMsg-Raw-0.10/t/bus.t000644 000765 000024 00000002347 12727365626 014550 0ustar00bzstaff000000 000000 use strict; use warnings; use Test::More 0.89; use NanoMsg::Raw; my $socket_address_a = 'inproc://a'; my $socket_address_b = 'inproc://b'; # Create a simple bus topology consisting of 3 nodes. my $bus1 = nn_socket(AF_SP, NN_BUS); cmp_ok $bus1, '>=', 0; cmp_ok nn_bind($bus1, $socket_address_a), '>=', 0; my $bus2 = nn_socket(AF_SP, NN_BUS); cmp_ok $bus2, '>=', 0; cmp_ok nn_bind($bus2, $socket_address_b), '>=', 0; cmp_ok nn_connect($bus2, $socket_address_a), '>=', 0; my $bus3 = nn_socket(AF_SP, NN_BUS); cmp_ok $bus3, '>=', 0; cmp_ok nn_connect($bus3, $socket_address_a), '>=', 0; cmp_ok nn_connect($bus3, $socket_address_b), '>=', 0; # Send a message from each node. is nn_send($bus1, 'A', 0), 1; is nn_send($bus2, 'AB', 0), 2; is nn_send($bus3, 'ABC', 0), 3; # Check that two messages arrived at each node. my $ret = nn_recv($bus1, my $buf, 3, 0); ok $ret == 2 || $ret == 3; $ret = nn_recv($bus1, $buf, 3, 0); ok $ret == 2 || $ret == 3; $ret = nn_recv($bus2, $buf, 3, 0); ok $ret == 1 || $ret == 3; $ret = nn_recv($bus2, $buf, 3, 0); ok $ret == 1 || $ret == 3; $ret = nn_recv($bus3, $buf, 3, 0); ok $ret == 1 || $ret == 2; $ret = nn_recv($bus3, $buf, 3, 0); ok $ret == 1 || $ret == 2; ok nn_close($_) for $bus3, $bus2, $bus1; done_testing; NanoMsg-Raw-0.10/t/domain.t000644 000765 000024 00000000401 12727365626 015213 0ustar00bzstaff000000 000000 use strict; use warnings; use Test::More 0.89; use NanoMsg::Raw; my $s = nn_socket AF_SP, NN_PAIR; is unpack('I', nn_getsockopt($s, NN_SOL_SOCKET, NN_DOMAIN)), AF_SP; is unpack('I', nn_getsockopt($s, NN_SOL_SOCKET, NN_PROTOCOL)), NN_PAIR; done_testing; NanoMsg-Raw-0.10/t/emfile.t000644 000765 000024 00000000673 13006736313 015202 0ustar00bzstaff000000 000000 use strict; use warnings; use Test::More 0.89; use NanoMsg::Raw; my @socks; while (1) { my $s = nn_socket AF_SP, NN_PAIR; if (!defined $s) { ok nn_errno == EMFILE; # does not work with german/any locale other than C #like nn_errno, qr/^too many open files/i; #like nn_strerror(nn_errno), qr/^too many open files/i; last; } push @socks, $s; } ok nn_close $_ for @socks; done_testing; NanoMsg-Raw-0.10/t/export.t000644 000765 000024 00000001065 12727365626 015274 0ustar00bzstaff000000 000000 use strict; use warnings; use Test::More 0.89; { package Foo; use NanoMsg::Raw; ::ok exists $::Foo::{NN_PAIR}; ::ok exists $::Foo::{nn_socket}; } { package Bar; use NanoMsg::Raw ':constants'; ::ok exists $::Bar::{NN_PAIR}; ::ok !exists $::Bar::{nn_socket}; } { package Baz; use NanoMsg::Raw ':functions'; ::ok !exists $::Baz::{NN_PAIR}; ::ok exists $::Baz::{nn_socket}; } { package Moo; use NanoMsg::Raw ':all'; ::ok exists $::Foo::{NN_PAIR}; ::ok exists $::Foo::{nn_socket}; } done_testing; NanoMsg-Raw-0.10/t/inproc.t000644 000765 000024 00000003627 12757132557 015251 0ustar00bzstaff000000 000000 use strict; use warnings; use Test::More 0.89; use NanoMsg::Raw; # Tests inproc transport. my $socket_address = 'inproc://test'; { my $sc = nn_socket AF_SP, NN_PAIR; ok defined $sc; ok defined nn_connect $sc, $socket_address; my $sb = nn_socket AF_SP, NN_PAIR; ok defined $sb; ok defined nn_bind $sb, $socket_address; # Try a duplicate bind. It should fail. ok !defined nn_bind $sc, $socket_address; ok nn_errno == EADDRINUSE; # Ping-pong test. for (1 .. 100) { is nn_send($sc, 'ABC', 0), 3; is nn_recv($sb, my $buf, 256, 0), 3; is nn_send($sb, 'DEFG', 0), 4; is nn_recv($sc, $buf, 256, 0), 4; } # Batch transfer test. is nn_send($sc, 'XYZ', 0), 3 for 1 .. 100; is nn_recv($sb, my $buf, 256, 0), 3 for 1 .. 100; ok nn_close $_ for $sc, $sb; } { # Test whether queue limits are observed. my $sb = nn_socket AF_SP, NN_PAIR; ok defined $sb; ok nn_setsockopt $sb, NN_SOL_SOCKET, NN_RCVBUF, 200; ok defined nn_bind $sb, $socket_address; my $sc = nn_socket AF_SP, NN_PAIR; ok defined $sc; ok defined nn_connect $sc, $socket_address; ok nn_setsockopt $sc, NN_SOL_SOCKET, NN_SNDTIMEO, 200; my $i = 0; while (1) { my $ret = nn_send $sc, '0123456789', 0; if (!defined $ret) { ok ( nn_errno == EAGAIN or nn_errno == ETIMEDOUT ); last; } is $ret, 10; $i++; } is $i, 20; is nn_recv($sb, my $buf, 256, 0), 10; is nn_send($sc, '0123456789', 0), 10; ok !defined nn_send($sc, '0123456789', 0); ok ( nn_errno == EAGAIN or nn_errno == ETIMEDOUT ); is nn_recv($sb, $buf, 256, 0), 10 for 1 .. 20; # Make sure that even a message that doesn't fit into the buffers gets # across. is nn_send($sc, 'A' x 256, 0), 256; is nn_recv($sb, $buf, 256, 0), 256; ok nn_close $_ for $sc, $sb; } done_testing; NanoMsg-Raw-0.10/t/iovec.t000644 000765 000024 00000001264 12727365626 015061 0ustar00bzstaff000000 000000 use strict; use warnings; use Test::More 0.89; use NanoMsg::Raw; my $socket_address = 'inproc://a'; my $sb = nn_socket AF_SP, NN_PAIR; cmp_ok $sb, '>=', 0; cmp_ok nn_bind($sb, $socket_address), '>=', 0; my $sc = nn_socket AF_SP, NN_PAIR; cmp_ok $sc, '>=', 0; cmp_ok nn_connect($sc, $socket_address), '>=', 0; my $buf1 = 'ABCDEFGHIJKLMNO'; my $buf2 = 'PQRSTUVWXYZ'; is nn_sendmsg($sc, 0, $buf1, $buf2), 26; is nn_recvmsg($sb, 0, my $buf3 => 15, my $buf4 => 15), 26; is $buf3, $buf1; is $buf4, $buf2; is nn_sendmsg($sc, 0, $buf1, $buf2), 26; is nn_recvmsg($sb, 0, my $buf5 => 26, my $buf6 => 26, my $buf7 => 26), 26; is $buf5, $buf1 . $buf2; is $buf6, ''; is $buf7, ''; done_testing; NanoMsg-Raw-0.10/t/ipc.t000644 000765 000024 00000002037 12727365626 014526 0ustar00bzstaff000000 000000 use strict; use warnings; use Test::More 0.89; use NanoMsg::Raw; # Test IPC transport. my $socket_address = 'ipc://test.ipc'; { # Try closing a IPC socket while it not connected. my $sc = nn_socket AF_SP, NN_PAIR; ok defined $sc; ok defined nn_connect $sc, $socket_address; ok nn_close $sc; } { my $sc = nn_socket AF_SP, NN_PAIR; ok defined $sc; ok defined nn_connect $sc, $socket_address; # Leave enough time for at least on re-connect attempt. sleep 1; my $sb = nn_socket AF_SP, NN_PAIR; ok defined $sb; ok defined nn_bind $sb, $socket_address; # Ping-pong test. for (0 .. 1) { my $p = join('' => 0 .. 9) x 4; is nn_send($sc, $p, 0), 40; is nn_recv($sb, my $buf, 3, 0), 40; is $buf, '012'; is nn_send($sb, $p, 0), 40; is nn_recv($sc, $buf, 3, 0), 40; } # Batch transfer test. is nn_send($sc, 'XYZ', 0), 3 for 1 .. 100; is nn_recv($sb, my $buf, 3, 0), 3 for 1 .. 100; ok nn_close $_ for $sc, $sb; } done_testing; NanoMsg-Raw-0.10/t/msg.t000644 000765 000024 00000003406 12727365626 014542 0ustar00bzstaff000000 000000 use strict; use warnings; use Test::More 0.89; use Test::Fatal; use NanoMsg::Raw; my $socket_address = 'inproc://test'; { my $sb = nn_socket AF_SP, NN_PAIR; ok defined $sb; ok defined nn_bind $sb, $socket_address; my $sc = nn_socket AF_SP, NN_PAIR; ok defined $sc; ok defined nn_connect $sc, $socket_address; my $buf1 = nn_allocmsg 256, 0; ok defined $buf1; $buf1->copy(join '' => map chr, 0 .. 255); is nn_send($sc, $buf1, 0), 256; is nn_recv($sb, my $buf2, NN_MSG, 0), 256; is $buf2, join '' => map chr, 0 .. 255; $buf1 = nn_allocmsg 256, 0; ok defined $buf1; $buf1->copy(join '' => map chr, 0 .. 255); is nn_sendmsg($sc, 0, $buf1), 256; is nn_recvmsg($sb, 0, $buf2 => NN_MSG), 256; is $buf2, join '' => map chr, 0 .. 255; ok nn_close $_ for $sc, $sb; } { my $sb = nn_socket AF_SP, NN_PAIR; ok defined $sb; ok defined nn_bind $sb, $socket_address; my $sc = nn_socket AF_SP, NN_PAIR; ok defined $sc; ok defined nn_connect $sc, $socket_address; my $m = NanoMsg::Raw::nn_allocmsg(3, 0); isa_ok $m, 'NanoMsg::Raw::Message'; like exception { ${ $m } = 'asd'; }, qr/^Modification of a read-only value attempted/; like exception { $m->copy('fooo'); }, qr/^Trying to copy 4 bytes into a message buffer of size 3/; $m->copy('foo'); is $m, 'foo'; is nn_send($sc, $m, 0), 3; isa_ok $m, 'NanoMsg::Raw::Message::Freed'; { my $destroyed = 0; my $buf = ScopeGuard->new(sub { $destroyed++ }); is nn_recv($sb, $buf, NN_MSG, 0), 3; is $buf, 'foo'; is $destroyed, 1; } { package ScopeGuard; sub new { bless $_[1] } sub DESTROY { shift->() } } } done_testing; NanoMsg-Raw-0.10/t/pair.t000644 000765 000024 00000000725 12727365626 014710 0ustar00bzstaff000000 000000 use strict; use warnings; use Test::More 0.89; use NanoMsg::Raw; my $socket_address = 'inproc://a'; my $sb = nn_socket(AF_SP, NN_PAIR); ok defined $sb; ok defined nn_bind($sb, $socket_address); my $sc = nn_socket(AF_SP, NN_PAIR); ok defined $sc; ok defined nn_connect($sc, $socket_address); is nn_send($sc, 'ABC', 0), 3; is nn_recv($sb, my $buf, 3, 0), 3; is nn_send($sb, 'DEF', 0), 3; is nn_recv($sc, $buf, 3, 0), 3; ok nn_close $_ for $sc, $sb; done_testing; NanoMsg-Raw-0.10/t/pipeline.t000644 000765 000024 00000002573 12727365626 015565 0ustar00bzstaff000000 000000 use strict; use warnings; use Test::More 0.89; use NanoMsg::Raw; my $socket_address = 'inproc://a'; # Test fan-out. { my $push1 = nn_socket(AF_SP, NN_PUSH); ok defined $push1; ok defined nn_bind($push1, $socket_address); my $pull1 = nn_socket(AF_SP, NN_PULL); ok defined $pull1; ok defined nn_connect($pull1, $socket_address); my $pull2 = nn_socket(AF_SP, NN_PULL); ok defined $pull2; ok defined nn_connect($pull2, $socket_address); # Wait till both connections are established to get messages spread evenly # between the two pull sockets. sleep 1; is nn_send($push1, 'ABC', 0), 3; is nn_send($push1, 'DEF', 0), 3; is nn_recv($pull1, my $buf, 3, 0), 3; is nn_recv($pull2, $buf, 3, 0), 3; ok nn_close $_ for $push1, $pull1, $pull2; } # Test fan-in. { my $pull1 = nn_socket(AF_SP, NN_PULL); ok defined $pull1; ok defined nn_bind($pull1, $socket_address); my $push1 = nn_socket(AF_SP, NN_PUSH); ok defined $push1; ok defined nn_connect($push1, $socket_address); my $push2 = nn_socket(AF_SP, NN_PUSH); ok defined $push2; ok defined nn_connect($push2, $socket_address); is nn_send($push1, 'ABC', 0), 3; is nn_send($push2, 'DEF', 0), 3; is nn_recv($pull1, my $buf, 3, 0), 3; is nn_recv($pull1, $buf, 3, 0), 3; ok nn_close $_ for $pull1, $push1, $push2; } done_testing; NanoMsg-Raw-0.10/t/poll.t000644 000765 000024 00000003276 12727365626 014727 0ustar00bzstaff000000 000000 use strict; use warnings; use Test::More 0.89; use NanoMsg::Raw; # Test of polling via NN_SNDFD/NN_RCVFD mechanism. sub getevents { my ($s, $events, $timeout) = @_; my ($rin, $win, $ein) = (('') x 3); my ($rcvfd, $sndfd); if ($events->{in}) { $rcvfd = unpack 'I', nn_getsockopt($s, NN_SOL_SOCKET, NN_RCVFD); ok defined $rcvfd; vec($rin, $rcvfd, 1) = 1; } if ($events->{out}) { $sndfd = unpack 'I', nn_getsockopt($s, NN_SOL_SOCKET, NN_SNDFD); ok defined $sndfd; vec($win, $sndfd, 1) = 1; } my $fds = select $rin, $win, undef, $timeout; cmp_ok $fds, '>=', 0; +{ ($events->{in} && vec($rin, $rcvfd, 1) ? (in => 1) : ()), ($events->{out} && vec($win, $sndfd, 1) ? (out => 1) : ()), }; } my $socket_address = 'inproc://a'; my $sb = nn_socket AF_SP, NN_PAIR; cmp_ok $sb, '>=', 0; cmp_ok nn_bind($sb, $socket_address), '>=', 0; my $sc = nn_socket AF_SP, NN_PAIR; cmp_ok $sc, '>=', 0; cmp_ok nn_connect($sc, $socket_address), '>=', 0; # Check the initial state of the socket. my $events = getevents($sb, { in => 1, out => 1 }, 1); ok $events->{out}; ok !$events->{in}; # Poll for IN when there's no message available. The call should time out. $events = getevents($sb, { in => 1 }, .001); ok !$events->{in}; # Send a message and start polling. This time IN event should be signaled. is nn_send($sc, 'ABC', 0), 3; $events = getevents($sb, { in => 1 }, 1); ok $events->{in}; # Receive the message and make sure that IN is no longer signaled. is nn_recv($sb, my $buf, 3, 0), 3; $events = getevents($sb, { in => 1 }, .001); ok !$events->{in}; ok nn_close($_) for $sc, $sb; done_testing; NanoMsg-Raw-0.10/t/prio.t000644 000765 000024 00000001461 12727365626 014724 0ustar00bzstaff000000 000000 use strict; use warnings; use Test::More 0.89; use NanoMsg::Raw; my $socket_address_a = 'inproc://a'; my $socket_address_b = 'inproc://b'; my $pull1 = nn_socket(AF_SP, NN_PULL); ok defined $pull1; ok defined nn_bind($pull1, $socket_address_a); my $pull2 = nn_socket(AF_SP, NN_PULL); ok defined $pull2; ok defined nn_bind($pull2, $socket_address_b); my $push = nn_socket(AF_SP, NN_PUSH); ok defined $push; ok nn_setsockopt($push, NN_SOL_SOCKET, NN_SNDPRIO, 1); ok defined nn_connect($push, $socket_address_a); ok nn_setsockopt($push, NN_SOL_SOCKET, NN_SNDPRIO, 2); ok defined nn_connect($push, $socket_address_b); is nn_send($push, 'ABC', 0), 3; is nn_send($push, 'DEF', 0), 3; is nn_recv($pull1, my $buf, 3, 0), 3; is nn_recv($pull1, $buf, 3, 0), 3; ok nn_close $_ for $pull1, $push, $pull2; done_testing; NanoMsg-Raw-0.10/t/pubsub.t000644 000765 000024 00000001401 12727365626 015245 0ustar00bzstaff000000 000000 use strict; use warnings; use Test::More 0.89; use NanoMsg::Raw; my $socket_address = 'inproc://a'; my $pub = nn_socket(AF_SP, NN_PUB); ok defined $pub; ok defined nn_bind($pub, $socket_address); my $sub1 = nn_socket(AF_SP, NN_SUB); ok defined $sub1; ok nn_setsockopt($sub1, NN_SUB, NN_SUB_SUBSCRIBE, ''); ok defined nn_connect($sub1, $socket_address); my $sub2 = nn_socket(AF_SP, NN_SUB); ok defined $sub2; ok nn_setsockopt($sub2, NN_SUB, NN_SUB_SUBSCRIBE, ''); ok defined nn_connect($sub2, $socket_address); # Wait till connections are established to prevent message loss. sleep 1; is nn_send($pub, join('' => 0 .. 9) x 4, 0), 40; is nn_recv($sub1, my $buf, 3, 0), 40; is nn_recv($sub2, $buf, 3, 0), 40; ok nn_close $_ for $pub, $sub1, $sub2; done_testing; NanoMsg-Raw-0.10/t/recv-free-segv.t000644 000765 000024 00000001121 12727365626 016564 0ustar00bzstaff000000 000000 use strict; use warnings; use Test::More tests => 6; use NanoMsg::Raw; my $url = 'ipc:///tmp/survey.ipc'; ok defined( my $sock = nn_socket( AF_SP, NN_SURVEYOR ) ); ok nn_setsockopt( $sock, NN_SURVEYOR, NN_SURVEYOR_DEADLINE, 1000 ); ok defined nn_bind( $sock, $url ); is nn_send( $sock, 'MESSAGE' ), 7; # $res is upgraded to NanoMsg::Raw::Message but we do not receive data # in this test and NanoMsg::Raw::Message segfault on destruction. { ok !defined( nn_recv( $sock, my $res ) ); } # give it some time sleep 2; # do one last test otherwise all tests pass but we segfault at the end. ok 1; NanoMsg-Raw-0.10/t/release-new-version.t000644 000765 000024 00000004703 13006736313 017631 0ustar00bzstaff000000 000000 BEGIN { unless ($ENV{RELEASE_TESTING}) { require Test::More; Test::More::plan(skip_all => 'these tests are for release candidate testing'); } } # this test was generated with Dist::Zilla::Plugin::Test::NewVersion 0.009 use strict; use warnings FATAL => 'all'; use Test::More 0.88; use Encode; use HTTP::Tiny; use JSON; use version; use Module::Metadata; use List::Util 'first'; use CPAN::Meta 2.120920; # 'provides' field from dist metadata, if needed my $dist_provides; # returns bool, detailed message sub version_is_bumped { my ($module_metadata, $pkg) = @_; my $res = HTTP::Tiny->new->get("http://cpanidx.org/cpanidx/json/mod/$pkg"); return (0, 'index could not be queried?') if not $res->{success}; # JSON wants UTF-8 bytestreams, so we need to re-encode no matter what # encoding we got. -- rjbs, 2011-08-18 (in # Dist::Zilla::Plugin::CheckPrereqsIndexed) my $json_octets = Encode::encode_utf8($res->{content}); my $payload = JSON::->new->decode($json_octets); return (0, 'no valid JSON returned') unless $payload; return (1, 'not indexed') if not defined $payload->[0]{mod_vers}; return (1, 'VERSION is not set in index') if $payload->[0]{mod_vers} eq 'undef'; my $indexed_version = version->parse($payload->[0]{mod_vers}); my $current_version = $module_metadata->version($pkg); if (not defined $current_version) { $dist_provides ||= do { my $metafile = first { -e $_ } qw(MYMETA.json MYMETA.yml META.json META.yml); my $dist_metadata = $metafile ? CPAN::Meta->load_file($metafile) : undef; $dist_metadata->provides if $dist_metadata; }; $current_version = $dist_provides->{$pkg}{version}; return (0, 'VERSION is not set; indexed version is ' . $indexed_version) if not $dist_provides or not $current_version; } return ( $indexed_version < $current_version, 'indexed at ' . $indexed_version . '; local version is ' . $current_version, ); } foreach my $filename ( "lib\/NanoMsg\/Raw\.pm", "lib\/NanoMsg\/Raw\/Message\.pm" ) { my $module_metadata = Module::Metadata->new_from_file($filename); foreach my $pkg ($module_metadata->packages_inside) { my ($bumped, $message) = version_is_bumped($module_metadata, $pkg); ok($bumped, $pkg . ' (' . $filename . ') VERSION is ok' . ( $message ? (' (' . $message . ')') : '' ) ); } } done_testing; NanoMsg-Raw-0.10/t/release-pod-coverage.t000644 000765 000024 00000000572 13006736313 017730 0ustar00bzstaff000000 000000 #!perl BEGIN { unless ($ENV{RELEASE_TESTING}) { require Test::More; Test::More::plan(skip_all => 'these tests are for release candidate testing'); } } # This file was automatically generated by Dist::Zilla::Plugin::PodCoverageTests. use Test::Pod::Coverage 1.08; use Pod::Coverage::TrustPod; all_pod_coverage_ok({ coverage_class => 'Pod::Coverage::TrustPod' }); NanoMsg-Raw-0.10/t/release-pod-syntax.t000644 000765 000024 00000000456 13006736313 017464 0ustar00bzstaff000000 000000 #!perl BEGIN { unless ($ENV{RELEASE_TESTING}) { require Test::More; Test::More::plan(skip_all => 'these tests are for release candidate testing'); } } # This file was automatically generated by Dist::Zilla::Plugin::PodSyntaxTests. use Test::More; use Test::Pod 1.41; all_pod_files_ok(); NanoMsg-Raw-0.10/t/reqrep.t000644 000765 000024 00000004673 12727365626 015261 0ustar00bzstaff000000 000000 use strict; use warnings; use Test::More 0.89; use NanoMsg::Raw; my $socket_address = 'inproc://test'; { my $rep1 = nn_socket(AF_SP, NN_REP); ok defined $rep1; ok defined nn_bind($rep1, $socket_address); my $req1 = nn_socket(AF_SP, NN_REQ); ok defined $req1; ok defined nn_connect($req1, $socket_address); my $req2 = nn_socket(AF_SP, NN_REQ); ok defined $req2; ok defined nn_connect($req2, $socket_address); is nn_send($rep1, 'ABC', 0), undef; ok nn_errno == EFSM; is nn_recv($req1, my $buf, 7, 0), undef; ok nn_errno == EFSM; is nn_send($req2, 'ABC', 0), 3; is nn_recv($rep1, $buf, 3, 0), 3; is nn_send($rep1, $buf, 0), 3; is nn_recv($req2, $buf, 3, 0), 3; is nn_send($req1, 'ABC', 0), 3; is nn_recv($rep1, $buf, 3, 0), 3; is nn_send($rep1, $buf, 0), 3; is nn_recv($req1, $buf, 3, 0), 3; ok nn_close $_ for $rep1, $req1, $req2; } { my $req1 = nn_socket(AF_SP, NN_REQ); ok defined $req1; ok defined nn_bind($req1, $socket_address); my $rep1 = nn_socket(AF_SP, NN_REP); ok defined $rep1; ok defined nn_connect($rep1, $socket_address); my $rep2 = nn_socket(AF_SP, NN_REP); ok defined $rep2; ok defined nn_connect($rep2, $socket_address); is nn_send($req1, 'ABC', 0), 3; is nn_recv($rep1, my $buf, 3, 0), 3; is nn_send($rep1, $buf, 0), 3; is nn_recv($req1, $buf, 3, 0), 3; is nn_send($req1, 'ABC', 0), 3; is nn_recv($rep2, $buf, 3, 0), 3; is nn_send($rep2, $buf, 0), 3; is nn_recv($req1, $buf, 3, 0), 3; ok nn_close $_ for $rep2, $rep1, $req1; } { my $rep1 = nn_socket(AF_SP, NN_REP); ok defined $rep1; ok defined nn_bind($rep1, $socket_address); my $req1 = nn_socket(AF_SP, NN_REQ); ok defined $req1; ok defined nn_connect($req1, $socket_address); ok nn_setsockopt($req1, NN_REQ, NN_REQ_RESEND_IVL, 100); is nn_send($req1, 'ABC', 0), 3; is nn_recv($rep1, my $buf, 3, 0), 3; is nn_recv($rep1, $buf, 3, 0), 3; ok nn_close $_ for $req1, $rep1; } { my $req1 = nn_socket(AF_SP, NN_REQ); ok defined $req1; ok defined nn_connect($req1, $socket_address); is nn_send($req1, 'ABC', 0), 3; my $rep1 = nn_socket(AF_SP, NN_REP); ok defined $rep1; ok defined nn_bind($rep1, $socket_address); ok nn_setsockopt($rep1, NN_SOL_SOCKET, NN_RCVTIMEO, 100); is nn_recv($rep1, my $buf, 3, 0), 3; ok nn_close $_ for $req1, $rep1; } done_testing; NanoMsg-Raw-0.10/t/send-recv.t000644 000765 000024 00000005104 12727365626 015637 0ustar00bzstaff000000 000000 use strict; use warnings; use Test::More 0.89; use NanoMsg::Raw; # Tests all posssible variations of nn_send and nn_recv # It is also stress test for our receive buffer. # # nn_send( $s, $buf ) # nn_send( $s, $buf, $flags ) # # nn_recv( $s, $buf ) # nn_recv( $s, $buf, NN_MSG ) # nn_recv( $s, $buf, 123 ) # nn_recv( $s, $buf, 123, 0 ) # nn_recv( $s, $buf, NN_MSG, 0 ) # nn_recv( $s, $buf, 123, NN_DONTWAIT ) # nn_recv( $s, $buf, NN_MSG, NN_DONTWAIT ) # reusing the buf variable might trigger # # perl(91339,0x7fff75f71180) malloc: *** error for object 0x7fa722024a70: pointer being realloc'd was not allocated # *** set a breakpoint in malloc_error_break to debug # # and # sv_upgrade from type 5 down to type 2 at t/send-recv.t line 66. my $socket_address = 'inproc://test'; my $pub = nn_socket AF_SP, NN_PUB; ok defined nn_bind $pub, $socket_address; my @subs; for ( 1 .. 7 ) { push @subs, my $sub = nn_socket AF_SP, NN_SUB; ok defined $sub; ok defined nn_setsockopt( $sub, NN_SUB, NN_SUB_SUBSCRIBE, '' ); ok defined nn_connect $sub, $socket_address; } my $msg = 'Hi there'; my $mlen = length($msg); is nn_send( $pub, $msg ), $mlen; is nn_send( $pub, $msg, 0 ), $mlen; my $buf; # buffer is large enough for the string is nn_recv( $subs[0], $buf ), $mlen; is length($buf), $mlen; is nn_recv( $subs[1], $buf, NN_MSG ), $mlen; is length($buf), $mlen; is nn_recv( $subs[2], $buf, $mlen ), $mlen; is length($buf), $mlen; is nn_recv( $subs[3], $buf, NN_MSG, 0 ), $mlen; is length($buf), $mlen; is nn_recv( $subs[4], $buf, $mlen, 0 ), $mlen; is length($buf), $mlen; is nn_recv( $subs[5], $buf, NN_MSG, NN_DONTWAIT ), $mlen; is length($buf), $mlen; is nn_recv( $subs[6], $buf, $mlen, NN_DONTWAIT ), $mlen; is length($buf), $mlen; # receive the whole message is nn_recv( $subs[0], $buf ), $mlen; is length($buf), $mlen; # receive only a few bytes - mixed with requests for the whole message # while reusing the buffer. my $bsize = int( $mlen / 2 ); is nn_recv( $subs[1], $buf, NN_MSG ), $mlen; is length($buf), $mlen; is nn_recv( $subs[2], $buf, $bsize ), $mlen; is length($buf), $bsize; is nn_recv( $subs[3], $buf, NN_MSG, 0 ), $mlen; is length($buf), $mlen; is nn_recv( $subs[4], $buf, $bsize, 0 ), $mlen; is length($buf), $bsize; is nn_recv( $subs[5], $buf, NN_MSG, NN_DONTWAIT ), $mlen; is length($buf), $mlen; is nn_recv( $subs[6], $buf, $bsize, NN_DONTWAIT ), $mlen; is length($buf), $bsize; # there is nothing left to receive my $str = $buf = 'This buffer should not change'; ok !defined nn_recv( $subs[3], $buf, $bsize, NN_DONTWAIT ); is $buf, $str; ok nn_close $_ for $pub, @subs; done_testing; NanoMsg-Raw-0.10/t/separation.t000644 000765 000024 00000003743 12757132557 016123 0ustar00bzstaff000000 000000 use strict; use warnings; use Test::More 0.89; use Test::TCP; use NanoMsg::Raw; my $socket_address_inproc = 'inproc://a'; my $socket_address_ipc = 'ipc://test-separation.ipc'; my $socket_address_tcp = 'tcp://127.0.0.1:' . empty_port; { my $pair = nn_socket(AF_SP, NN_PAIR); ok defined $pair; ok defined nn_bind($pair, $socket_address_inproc); my $pull = nn_socket(AF_SP, NN_PULL); ok defined $pull; ok defined nn_connect($pull, $socket_address_inproc); ok nn_setsockopt($pair, NN_SOL_SOCKET, NN_SNDTIMEO, 100); is nn_send($pair, 'ABC', 0), undef; ok ( nn_errno == EAGAIN or nn_errno == ETIMEDOUT ); ok nn_close $_ for $pull, $pair; } { my $pull = nn_socket(AF_SP, NN_PULL); ok defined $pull; ok defined nn_connect($pull, $socket_address_inproc); my $pair = nn_socket(AF_SP, NN_PAIR); ok defined $pair; ok defined nn_bind($pair, $socket_address_inproc); ok nn_setsockopt($pair, NN_SOL_SOCKET, NN_SNDTIMEO, 100); is nn_send($pair, 'ABC', 0), undef; ok ( nn_errno == EAGAIN or nn_errno == ETIMEDOUT ); ok nn_close $_ for $pull, $pair; } { my $pair = nn_socket(AF_SP, NN_PAIR); ok defined $pair; ok defined nn_bind($pair, $socket_address_ipc); my $pull = nn_socket(AF_SP, NN_PULL); ok defined $pull; ok defined nn_connect($pull, $socket_address_ipc); ok nn_setsockopt($pair, NN_SOL_SOCKET, NN_SNDTIMEO, 100); is nn_send($pair, 'ABC', 0), undef; ok ( nn_errno == EAGAIN or nn_errno == ETIMEDOUT ); ok nn_close $_ for $pull, $pair; } { my $pair = nn_socket(AF_SP, NN_PAIR); ok defined $pair; ok defined nn_bind($pair, $socket_address_tcp); my $pull = nn_socket(AF_SP, NN_PULL); ok defined $pull; ok defined nn_connect($pull, $socket_address_tcp); ok nn_setsockopt($pair, NN_SOL_SOCKET, NN_SNDTIMEO, 100); is nn_send($pair, 'ABC', 0), undef; ok ( nn_errno == EAGAIN or nn_errno == ETIMEDOUT ); ok nn_close $_ for $pull, $pair; } done_testing; NanoMsg-Raw-0.10/t/survey.t000644 000765 000024 00000002310 12757132557 015300 0ustar00bzstaff000000 000000 use strict; use warnings; use Test::More 0.89; use NanoMsg::Raw; my $socket_address = 'inproc://test'; my $surveyor = nn_socket(AF_SP, NN_SURVEYOR); ok defined $surveyor; ok nn_setsockopt($surveyor, NN_SURVEYOR, NN_SURVEYOR_DEADLINE, 500); ok defined nn_bind($surveyor, $socket_address); my @respondent = map { my $s = nn_socket(AF_SP, NN_RESPONDENT); ok defined $s; ok defined nn_connect($s, $socket_address); $s; } 1 .. 3; is nn_send($surveyor, 'ABC', 0), 3; is nn_recv($respondent[0], my $buf, 3, 0), 3; is nn_send($respondent[0], 'DEF', 0), 3; is nn_recv($respondent[1], $buf, 3, 0), 3; is nn_send($respondent[1], 'DEF', 0), 3; is nn_recv($surveyor, $buf, 3, 0), 3; is nn_recv($surveyor, $buf, 3, 0), 3; is nn_recv($surveyor, $buf, 3, 0), undef; # libnanmsg-0.5 returns EFSM libnanomsg-0.6 returns ETIMEDOUT ok ( nn_errno == EFSM or nn_errno == ETIMEDOUT ); is nn_recv($respondent[2], $buf, 3, 0), 3; is nn_send($respondent[2], 'GHI', 0), 3; is nn_send($surveyor, 'ABC', 0), 3; is nn_recv($surveyor, $buf, 3, 0), undef; # libnanmsg-0.5 returns EFSM libnanomsg-0.6 returns ETIMEDOUT ok ( nn_errno == EFSM or nn_errno == ETIMEDOUT ); ok nn_close $_ for $surveyor, @respondent; done_testing; NanoMsg-Raw-0.10/t/tcp.t000644 000765 000024 00000004035 12727365626 014541 0ustar00bzstaff000000 000000 use strict; use warnings; use Test::More 0.89; use Test::TCP; use NanoMsg::Raw; my $host = '127.0.0.1'; my $port = empty_port; my $socket_address = "tcp://$host:$port"; { my $sb = nn_socket(AF_SP, NN_PAIR); ok defined $sb; ok defined nn_bind($sb, $socket_address); ok nn_close($sb); } { my $sb = nn_socket(AF_SP, NN_PAIR); ok defined $sb; ok defined nn_connect($sb, "tcp://$host;$host:$port"); ok nn_close($sb); } { my $sc = nn_socket(AF_SP, NN_PAIR); ok defined $sc; is unpack('I', nn_getsockopt($sc, NN_TCP, NN_TCP_NODELAY)), 0; ok !nn_setsockopt($sc, NN_TCP, NN_TCP_NODELAY, 2); ok nn_errno == EINVAL; ok nn_setsockopt($sc, NN_TCP, NN_TCP_NODELAY, 1); is unpack('I', nn_getsockopt($sc, NN_TCP, NN_TCP_NODELAY)), 1; my %invalid_connect_addresses = ( 'tcp://*:' => EINVAL, 'tcp://*:1000000' => EINVAL, 'tcp://*:some_port' => EINVAL, 'tcp://eth10000;127.0.0.1:5555' => ENODEV, 'tcp://127.0.0.1' => EINVAL, ); my %invalid_bind_addresses = ( 'tcp://127.0.0.1:' => EINVAL, 'tcp://127.0.0.1:1000000' => EINVAL, 'tcp://eth10000:5555' => ENODEV, ); for my $t ([\&nn_connect, \%invalid_connect_addresses], [\&nn_bind, \%invalid_bind_addresses]) { for my $addr (keys %{ $t->[1] }) { is $t->[0]->($sc, $addr), undef; ok nn_errno == $t->[1]->{$addr}; } } ok defined nn_connect($sc, $socket_address); sleep 1; my $sb = nn_socket(AF_SP, NN_PAIR); ok defined $sb; ok defined nn_bind($sb, $socket_address); for (1 .. 100) { is nn_send($sc, 'ABC', 0), 3; is nn_recv($sb, my $buf, 3, 0), 3; is nn_send($sb, 'DEF', 0), 3; is nn_recv($sc, $buf, 3, 0), 3; } is nn_send($sc, join('' => 0 .. 9) x 4, 0), 40 for 0 ..99; is nn_recv($sb, my $buf, 3, 0), 40 for 0 .. 99; ok nn_close $_ for $sc, $sb; } done_testing; NanoMsg-Raw-0.10/t/timeo.t000644 000765 000024 00000001522 12757132557 015064 0ustar00bzstaff000000 000000 use strict; use warnings; use Test::More 0.89; use Time::HiRes 'gettimeofday', 'tv_interval'; use NanoMsg::Raw; sub timeit (&) { my ($cb) = @_; my $started = [gettimeofday]; my @ret = $cb->(); (tv_interval($started), @ret); } my $s = nn_socket AF_SP, NN_PAIR; cmp_ok $s, '>=', 0; my $timeo = 100; ok nn_setsockopt($s, NN_SOL_SOCKET, NN_RCVTIMEO, $timeo); my ($elapsed, $ret) = timeit { nn_recv($s, my $buf, 3, 0); }; ok !defined $ret; ok ( nn_errno == EAGAIN or nn_errno == ETIMEDOUT ); cmp_ok $elapsed, '>=', 0.1; cmp_ok $elapsed, '<=', 0.12; ok nn_setsockopt($s, NN_SOL_SOCKET, NN_SNDTIMEO, $timeo); ($elapsed, $ret) = timeit { nn_send($s, 'ABC', 0); }; ok !defined $ret; ok ( nn_errno == EAGAIN or nn_errno == ETIMEDOUT ); cmp_ok $elapsed, '>=', 0.1; cmp_ok $elapsed, '<=', 0.12; ok nn_close $s; done_testing; NanoMsg-Raw-0.10/lib/NanoMsg/000755 000765 000024 00000000000 13030572462 015413 5ustar00bzstaff000000 000000 NanoMsg-Raw-0.10/lib/NanoMsg/Raw/000755 000765 000024 00000000000 13030572462 016144 5ustar00bzstaff000000 000000 NanoMsg-Raw-0.10/lib/NanoMsg/Raw.pm000644 000765 000024 00000107617 13030571603 016512 0ustar00bzstaff000000 000000 package NanoMsg::Raw; # ABSTRACT: Low-level interface to the nanomsg scalability protocols library $NanoMsg::Raw::VERSION = '0.10'; use strict; use warnings; use XSLoader; use NanoMsg::Raw::Message; XSLoader::load( 'NanoMsg::Raw', exists $NanoMsg::Raw::{VERSION} ? ${ $NanoMsg::Raw::{VERSION} } : (), ); use Exporter 'import'; my @constants = (_symbols(), 'NN_MSG'); my @functions = ( map { "nn_$_" } qw( socket close setsockopt getsockopt bind connect shutdown send recv sendmsg recvmsg allocmsg strerror device term errno ) ); our @EXPORT_OK = (@constants, @functions); our @EXPORT = @EXPORT_OK; our %EXPORT_TAGS = ( all => \@EXPORT_OK, constants => \@constants, functions => \@functions, ); =head1 NAME NanoMsg::Raw - Low-level interface to the nanomsg scalability protocols library =head1 SYNOPSIS use Test::More; use NanoMsg::Raw; my $sb = nn_socket(AF_SP, NN_PAIR); nn_bind($sb, 'inproc://foo'); my $sc = nn_socket(AF_SP, NN_PAIR); nn_connect($sc, 'inproc://foo'); nn_send($sb, 'bar'); nn_recv($sc, my $buf); is $buf, 'bar'; =head1 WARNING B =head1 DESCRIPTION C is a binding to the C C library. The goal of this module is to provide a very low-level and manual interface to all the functionality of the nanomsg library. It doesn't intend to provide a convenient high-level API, integration with event loops, or the like. Those are intended to be implemented as separate abstractions on top of C. The nanomsg C library is a high-performance implementation of several "scalability protocols". Scalability protocol's job is to define how multiple applications communicate to form a single distributed application. Implementation of following scalability protocols is available at the moment: =over =item * C simple one-to-one communication =item * C simple many-to-many communication =item * C allows one to build clusters of stateless services to process user requests =item * C distributes messages to large sets of interested subscribers =item * C aggregates messages from multiple sources and load balances them among many destinations =item * C allows one to query state of multiple applications in a single go =back Scalability protocols are layered on top of transport layer in the network stack. At the moment, nanomsg library supports following transports: =over =item * C transport within a process (between threads, modules etc.) =item * C transport between processes on a single machine =item * C network transport via TCP =back =over =item nn_socket($domain, $protocol) my $s = nn_socket(AF_SP, NN_PAIR); die nn_errno unless defined $s; Creates a nanomsg socket with specified C<$domain> and C<$protocol>. Returns a file descriptor for the newly created socket. Following domains are defined at the moment: =over =item * C Standard full-blown SP socket. =item * C Raw SP socket. Raw sockets omit the end-to-end functionality found in C sockets and thus can be used to implement intermediary devices in SP topologies. =back The C<$protocol> parameter defines the type of the socket, which in turn determines the exact semantics of the socket. See L to get the list of available protocols and their socket types. The newly created socket is initially not associated with any endpoints. In order to establish a message flow at least one endpoint has to be added to the socket using C or C. Also note that type argument as found in standard C function is omitted from C. All the SP sockets are message-based and thus of C type. If the function succeeds file descriptor of the new socket is returned. Otherwise, C is returned and C is set to to one of the values defined below. =over =item * C Specified address family is not supported. =item * C Unknown protocol. =item * C The limit on the total number of open SP sockets or OS limit for file descriptors has been reached. =item * C The library is terminating. Note that file descriptors returned by C function are not standard file descriptors and will exhibit undefined behaviour when used with system functions. Moreover, it may happen that a system file descriptor and file descriptor of an SP socket will incidentally collide (be equal). =back =item nn_close($s) nn_close($s) or die nn_errno; Closes the socket C<$s>. Any buffered inbound messages that were not yet received by the application will be discarded. The library will try to deliver any outstanding outbound messages for the time specified by C socket option. The call will block in the meantime. If the function succeeds, a true value is returned. Otherwise, C is returned and C is set to to one of the values defined below. =over =item * C The provided socket is invalid. =item * C Operation was interrupted by a signal. The socket is not fully closed yet. Operation can be re-started by calling C again. =back =item nn_setsockopt($s, $level, $option, $value) nn_setsockopt($s, NN_SOL_SOCKET, NN_LINGER, 1000) or die nn_errno; nn_setsockopt($s, NN_SOL_SOCKET, NN_SUB_SUBSCRIBE, 'ABC') or die nn_errno; Sets the C<$value> of the socket option C<$option>. The C<$level> argument specifies the protocol level at which the option resides. For generic socket-level options use the C level. For socket-type-specific options use the socket type for the C<$level> argument (e.g. C). For transport-specific options use the ID of the transport as the C<$level> argument (e.g. C). If the function succeeds a true value is returned. Otherwise, C is returned and C is set to to one of the values defined below. =over =item * C The provided socket is invalid. =item * C The option is unknown at the level indicated. =item * C The specified option value is invalid. =item * C The library is terminating. =back These are the generic socket-level (C level) options: =over =item * C Specifies how long the socket should try to send pending outbound messages after C has been called, in milliseconds. Negative values mean infinite linger. The type of the option is int. The default value is 1000 (1 second). =item * C Size of the send buffer, in bytes. To prevent blocking for messages larger than the buffer, exactly one message may be buffered in addition to the data in the send buffer. The type of this option is int. The default value is 128kB. =item * C Size of the receive buffer, in bytes. To prevent blocking for messages larger than the buffer, exactly one message may be buffered in addition to the data in the receive buffer. The type of this option is int. The default value is 128kB. =item * C The timeout for send operation on the socket, in milliseconds. If a message cannot be sent within the specified timeout, an C error is returned. Negative values mean infinite timeout. The type of the option is int. The default value is -1. =item * C The timeout for recv operation on the socket, in milliseconds. If a message cannot be received within the specified timeout, an C error is returned. Negative values mean infinite timeout. The type of the option is int. The default value is -1. =item * C For connection-based transports such as TCP, this option specifies how long to wait, in milliseconds, when connection is broken before trying to re-establish it. Note that actual reconnect interval may be randomised to some extent to prevent severe reconnection storms. The type of the option is int. The default value is 100 (0.1 second). =item * C This option is to be used only in addition to C option. It specifies maximum reconnection interval. On each reconnect attempt, the previous interval is doubled until C is reached. A value of zero means that no exponential backoff is performed and reconnect interval is based only on C. If C is less than C, it is ignored. The type of the option is int. The default value is 0. =item * C Sets outbound priority for endpoints subsequently added to the socket. This option has no effect on socket types that send messages to all the peers. However, if the socket type sends each message to a single peer (or a limited set of peers), peers with high priority take precedence over peers with low priority. The type of the option is int. The highest priority is 1, the lowest priority is 16. The default value is 8. =item * C If set to 1, only IPv4 addresses are used. If set to 0, both IPv4 and IPv6 addresses are used. The default value is 1. =back =item nn_getsockopt($s, $level, $option) my $linger = unpack 'i', nn_getsockopt($s, NN_SOL_SOCKET, NN_LINGER) || die nn_errno; Retrieves the value for the socket option C<$option>. The C<$level> argument specifies the protocol level at which the option resides. For generic socket-level options use the C level. For socket-type-specific options use the socket type for the C<$level> argument (e.g. C). For transport-specific options use ID of the transport as the C<$level> argument (e.g. C). The function returns a packed string representing the requested socket option, or C on error, with one of the following reasons for the error placed in C. =over =item * C The provided socket is invalid. =item * C The option is unknown at the C<$level> indicated. =item * C The library is terminating. =back Just what is in the packed string depends on C<$level> and C<$option>; see the list of socket options for details; A common case is that the option is an integer, in which case the result is a packed integer, which you can decode using C with the C (or C) format. This function can be used to retrieve the values for all the generic socket-level (C) options documented in C and also supports these additional generic socket-level options that can only be retrieved but not set: =over =item * C Returns the domain constant as it was passed to C. =item * C Returns the protocol constant as it was passed to C. =item * C Retrieves a file descriptor that is readable when a message can be sent to the socket. The descriptor should be used only for polling and never read from or written to. The type of the option is int. The descriptor becomes invalid and should not be used any more once the socket is closed. This socket option is not available for unidirectional recv-only socket types. =item * C Retrieves a file descriptor that is readable when a message can be received from the socket. The descriptor should be used only for polling and never read from or written to. The type of the option is int. The descriptor becomes invalid and should not be used any more once the socket is closed. This socket option is not available for unidirectional send-only socket types. =back =item nn_bind($s, $addr) my $eid = nn_bind($s, 'inproc://test'); die nn_errno unless defined $eid; Adds a local endpoint to the socket C<$s>. The endpoint can be then used by other applications to connect to. The C<$addr> argument consists of two parts as follows: C. The C specifies the underlying transport protocol to use. The meaning of the C
part is specific to the underlying transport protocol. See L for a list of available transport protocols. The maximum length of the C<$addr> parameter is specified by C constant. Note that C and C may be called multiple times on the same socket thus allowing the socket to communicate with multiple heterogeneous endpoints. If the function succeeds, an endpoint ID is returned. Endpoint ID can be later used to remove the endpoint from the socket via C function. If the function fails, C is returned and C is set to to one of the values defined below. =over =item * C The provided socket is invalid. =item * C Maximum number of active endpoints was reached. =item * C The syntax of the supplied address is invalid. =item * C The supplied address is too long. =item * C The requested transport protocol is not supported. =item * C The requested endpoint is not local. =item * C Address specifies a nonexistent interface. =item * C The requested local endpoint is already in use. =item * C The library is terminating. =back =item nn_connect($s, $addr) my $eid = nn_connect($s, 'inproc://test'); die nn_errno unless defined $eid; Adds a remote endpoint to the socket C<$s>. The library would then try to connect to the specified remote endpoint. The C<$addr> argument consists of two parts as follows: C. The C specifies the underlying transport protocol to use. The meaning of the C
part is specific to the underlying transport protocol. See L for a list of available transport protocols. The maximum length of the C<$addr> parameter is specified by C constant. Note that C and C may be called multiple times on the same socket thus allowing the socket to communicate with multiple heterogeneous endpoints. If the function succeeds, an endpoint ID is returned. Endpoint ID can be later used to remove the endpoint from the socket via C function. If the function fails, C is returned and C is set to to one of the values defined below. =over =item * C The provided socket is invalid. =item * C Maximum number of active endpoints was reached. =item * C The syntax of the supplied address is invalid. =item * C The supplied address is too long. =item * C The requested transport protocol is not supported. =item * C Address specifies a nonexistent interface. =item * C The library is terminating. =back =item nn_shutdown($s, $eid) nn_shutdown($s, $eid) or die nn_errno; Removes an endpoint from socket C<$s>. The C parameter specifies the ID of the endpoint to remove as returned by prior call to C or C. The C call will return immediately. However, the library will try to deliver any outstanding outbound messages to the endpoint for the time specified by the C socket option. If the function succeeds, a true value is returned. Otherwise, C is returned and C is set to to one of the values defined below. =over =item * C The provided socket is invalid. =item * C The how parameter doesn't correspond to an active endpoint. =item * C Operation was interrupted by a signal. The endpoint is not fully closed yet. Operation can be re-started by calling C again. =item * C The library is terminating. =back =item nn_send($s, $data, $flags=0) my $bytes_sent = nn_send($s, 'foo'); die nn_errno unless defined $bytes_sent; This function will send a message containing the provided C<$data> to the socket C<$s>. C<$data> can either be anything that can be used as a byte string in perl or a message buffer instance allocated by C. In case of a message buffer instance the instance will be deallocated and invalidated by the C function. The buffer will be an instance of C after the call to C. Which of the peers the message will be sent to is determined by the particular socket type. The C<$flags> argument, which defaults to C<0>, is a combination of the flags defined below: =over =item * C Specifies that the operation should be performed in non-blocking mode. If the message cannot be sent straight away, the function will fail with C set to C. =back If the function succeeds, the number of bytes in the message is returned. Otherwise, a C is returned and C is set to to one of the values defined below. =over =item * C The provided socket is invalid. =item * C The operation is not supported by this socket type. =item * C The operation cannot be performed on this socket at the moment because the socket is not in the appropriate state. This error may occur with socket types that switch between several states. =item * C Non-blocking mode was requested and the message cannot be sent at the moment. =item * C The operation was interrupted by delivery of a signal before the message was sent. =item * C Individual socket types may define their own specific timeouts. If such timeout is hit, this error will be returned. =item * C The library is terminating. =back =item nn_recv($s, $data, $length=NN_MSG, $flags=0) my $bytes_received = nn_recv($s, my $buf, 256); die nn_errno unless defined $bytes_received; Receive a message from the socket C<$s> and store it in the buffer C<$buf>. Any bytes exceeding the length specified by the C<$length> argument will be truncated. Alternatively, C can allocate a message buffer instance for you. To do so, set the C<$length> parameter to C (the default). The C<$flags> argument, which defaults to C<0>, is a combination of the flags defined below: =over =item * C Specifies that the operation should be performed in non-blocking mode. If the message cannot be received straight away, the function will fail with C set to C. =back If the function succeeds number of bytes in the message is returned. Otherwise, C is returned and C is set to to one of the values defined below. =over =item * C The provided socket is invalid. =item * C The operation is not supported by this socket type. =item * C The operation cannot be performed on this socket at the moment because socket is not in the appropriate state. This error may occur with socket types that switch between several states. =item * C Non-blocking mode was requested and there's no message to receive at the moment. =item * C The operation was interrupted by delivery of a signal before the message was received. =item * C Individual socket types may define their own specific timeouts. If such timeout is hit this error will be returned. =item * C The library is terminating. =back =item nn_sendmsg($s, $flags, $data1, $data2, ..., $dataN) my $bytes_sent = nn_sendmsg($s, 0, 'foo', 'bar'); die nn_errno unless defined $bytes_sent; This function is a fine-grained alternative to C. It allows sending multiple data buffers that make up a single message without having to create another temporary buffer to hold the concatenation of the different message parts. The scalars containing the data to be sent (C<$data1>, C<$data2>, ..., C<$dataN>) can either be anything that can be used as a byte string in perl or a message buffer instance allocated by C. In case of a message buffer instance the instance will be deallocated and invalidated by the C function. The buffers will be a instances of C after the call to C. When using message buffer instances, only one buffer may be provided. To which of the peers will the message be sent to is determined by the particular socket type. The C<$flags> argument is a combination of the flags defined below: =over =item * C Specifies that the operation should be performed in non-blocking mode. If the message cannot be sent straight away, the function will fail with C set to C. =back If the function succeeds number of bytes in the message is returned. Otherwise, C is returned and C is set to to one of the values defined below. =over =item * C The provided socket is invalid. =item * C The operation is not supported by this socket type. =item * C The operation cannot be performed on this socket at the moment because socket is not in the appropriate state. This error may occur with socket types that switch between several states. =item * C Non-blocking mode was requested and the message cannot be sent at the moment. =item * C The operation was interrupted by delivery of a signal before the message was sent. =item * C Individual socket types may define their own specific timeouts. If such timeout is hit this error will be returned. =item * C The library is terminating. =back In the future, C might allow for sending along additional control data. =item nn_recvmsg($s, $flags, $data1 => $len1, $data2 => $len2, ..., $dataN => $lenN) my $bytes_received = nn_recvmsg($s, 0, my $buf1 => 256, my $buf2 => 1024); die nn_errno unless defined $bytes_received; This function is a fine-grained alternative to C. It allows receiving a single message into multiple data buffers of different sizes, eliminating the need to create copies of part of the received message in some cases. The scalars in which to receive the message data (C<$buf1>, C<$buf2>, ..., C<$bufN>) will be filled with as many bytes of data as is specified by the length parameter following them in the argument list (C<$len1>, C<$len2>, ..., C<$lenN>). Alternatively, C can allocate a message buffer instance for you. To do so, set the length parameter of a buffer to to C. In this case, only one receive buffer can be provided. The C<$flags> argument is a combination of the flags defined below: =over =item * C Specifies that the operation should be performed in non-blocking mode. If the message cannot be received straight away, the function will fail with C set to C. =back In the future, C might allow for receiving additional control data. =item nn_allocmsg($size, $type) my $msg = nn_allocmsg(3, 0) or die nn_errno; $msg->copy('foo'); nn_send($s, $msg); Allocate a message of the specified C<$size> to be sent in zero-copy fashion. The content of the message is undefined after allocation and it should be filled in by the user. While C and C allow one to send arbitrary buffers, buffers allocated using C can be more efficient for large messages as they allow for using zero-copy techniques. The C<$type> parameter specifies type of allocation mechanism to use. Zero is the default one. However, individual transport mechanisms may define their own allocation mechanisms, such as allocating in shared memory or allocating a memory block pinned down to a physical memory address. Such allocation, when used with the transport that defines them, should be more efficient than the default allocation mechanism. If the function succeeds a newly allocated message buffer instance (an object instance of the class L) is returned. Otherwise, C is returned and C is set to to one of the values defined below. =over =item * C Supplied allocation type is invalid. =item * C Not enough memory to allocate the message. =back =item nn_errno() Returns value of C after the last call to any nanomsg function in the current thread. This function can be used in the same way the C<$!> global variable is be used for many other system and library calls. The return value can be used in numeric context, for example to compare it with error code constants such as C, or in a string context, to retrieve a textual message describing the error. =item nn_strerror($errno) Returns a textual representation of the error described by the nummeric C<$errno> provided. It shouldn't normally be necessary to ever call this function, as using C in string context is basically equivalent to C. =item nn_device($s1, $s2) nn_device($s1, $s2) or die; Starts a device to forward messages between two sockets. If both sockets are valid, the C function loops and sends and messages received from C<$s1> to C<$s2> and vice versa. If only one socket is valid and the other is C, C works in a loopback mode - it loops and sends any messages received from the socket back to itself. The function loops until it hits an error. In such case it returns C and sets C to one of the values defined below. =over =item * C One of the provided sockets is invalid. =item * C Either one of the socket is not an C socket; or the two sockets don't belong to the same protocol; or the directionality of the sockets doesn't fit (e.g. attempt to join two SINK sockets to form a device). =item * C The operation was interrupted by delivery of a signal. =item * C The library is terminating. =back =item nn_term() nn_term(); To help with shutdown of multi-threaded programs the C function is provided. It informs all the open sockets that process termination is underway. If a socket is blocked inside a blocking function, such as C, it will be unblocked and the C error will be returned to the user. Similarly, any subsequent attempt to invoke a socket function other than C after C was called will result in an C error. If waiting for C or C using a polling function, such as C or C