diff options
author | Martin Lucina <martin@lucina.net> | 2012-01-23 08:53:19 +0100 |
---|---|---|
committer | Martin Lucina <martin@lucina.net> | 2012-01-23 08:53:19 +0100 |
commit | a15854bd92db69fcd0b4444fe1b8fe3610a7acf6 (patch) | |
tree | 1214b945d0f0033ff318de367c70525ea141ef56 /src |
Imported Upstream version 2.0.7.dfsgupstream/2.0.7.dfsg
Diffstat (limited to 'src')
123 files changed, 18804 insertions, 0 deletions
diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 0000000..fa97ca3 --- /dev/null +++ b/src/Makefile.am @@ -0,0 +1,244 @@ +lib_LTLIBRARIES = libzmq.la + +pkgconfigdir = $(libdir)/pkgconfig +pkgconfig_DATA = libzmq.pc + +include_HEADERS = ../include/zmq.h ../include/zmq.hpp + +if BUILD_PGM +pgm_sources = ../foreign/openpgm/@pgm_basename@/openpgm/pgm/packet.c \ + ../foreign/openpgm/@pgm_basename@/openpgm/pgm/time.c \ + ../foreign/openpgm/@pgm_basename@/openpgm/pgm/if.c \ + ../foreign/openpgm/@pgm_basename@/openpgm/pgm/getifaddrs.c \ + ../foreign/openpgm/@pgm_basename@/openpgm/pgm/getnodeaddr.c \ + ../foreign/openpgm/@pgm_basename@/openpgm/pgm/indextoaddr.c \ + ../foreign/openpgm/@pgm_basename@/openpgm/pgm/indextoname.c \ + ../foreign/openpgm/@pgm_basename@/openpgm/pgm/nametoindex.c \ + ../foreign/openpgm/@pgm_basename@/openpgm/pgm/inet_network.c \ + ../foreign/openpgm/@pgm_basename@/openpgm/pgm/md5.c \ + ../foreign/openpgm/@pgm_basename@/openpgm/pgm/gsi.c \ + ../foreign/openpgm/@pgm_basename@/openpgm/pgm/tsi.c \ + ../foreign/openpgm/@pgm_basename@/openpgm/pgm/signal.c \ + ../foreign/openpgm/@pgm_basename@/openpgm/pgm/txwi.c \ + ../foreign/openpgm/@pgm_basename@/openpgm/pgm/rxwi.c \ + ../foreign/openpgm/@pgm_basename@/openpgm/pgm/transport.c \ + ../foreign/openpgm/@pgm_basename@/openpgm/pgm/source.c \ + ../foreign/openpgm/@pgm_basename@/openpgm/pgm/receiver.c \ + ../foreign/openpgm/@pgm_basename@/openpgm/pgm/recv.c \ + ../foreign/openpgm/@pgm_basename@/openpgm/pgm/pgm.c \ + ../foreign/openpgm/@pgm_basename@/openpgm/pgm/timer.c \ + ../foreign/openpgm/@pgm_basename@/openpgm/pgm/net.c \ + ../foreign/openpgm/@pgm_basename@/openpgm/pgm/rate_control.c \ + ../foreign/openpgm/@pgm_basename@/openpgm/pgm/async.c \ + ../foreign/openpgm/@pgm_basename@/openpgm/pgm/checksum.c \ + ../foreign/openpgm/@pgm_basename@/openpgm/pgm/reed_solomon.c \ + ../foreign/openpgm/@pgm_basename@/openpgm/pgm/galois_tables.c \ + ../foreign/openpgm/@pgm_basename@/openpgm/pgm/wsastrerror.c \ + ../foreign/openpgm/@pgm_basename@/openpgm/pgm/glib-compat.c \ + ../foreign/openpgm/@pgm_basename@/openpgm/pgm/backtrace.c \ + ../foreign/openpgm/@pgm_basename@/openpgm/pgm/log.c \ + ../foreign/openpgm/@pgm_basename@/openpgm/pgm/sockaddr.c \ + ../foreign/openpgm/@pgm_basename@/openpgm/pgm/version.c + +../foreign/openpgm/@pgm_basename@/openpgm/pgm/version.c: ../foreign/openpgm/@pgm_basename@/openpgm/pgm/version_generator.py + python ../foreign/openpgm/@pgm_basename@/openpgm/pgm/version_generator.py > $@ + +../foreign/openpgm/@pgm_basename@/openpgm/pgm/galois_tables.c: ../foreign/openpgm/@pgm_basename@/openpgm/pgm/galois_generator.pl + perl ../foreign/openpgm/@pgm_basename@/openpgm/pgm/galois_generator.pl > $@ +endif + +nodist_libzmq_la_SOURCES = $(pgm_sources) + +libzmq_la_SOURCES = app_thread.hpp \ + atomic_counter.hpp \ + atomic_ptr.hpp \ + blob.hpp \ + command.hpp \ + config.hpp \ + ctx.hpp \ + decoder.hpp \ + devpoll.hpp \ + downstream.hpp \ + encoder.hpp \ + epoll.hpp \ + err.hpp \ + fd.hpp \ + forwarder.hpp \ + fq.hpp \ + i_inout.hpp \ + io_object.hpp \ + io_thread.hpp \ + ip.hpp \ + i_endpoint.hpp \ + i_engine.hpp \ + i_poll_events.hpp \ + kqueue.hpp \ + lb.hpp \ + likely.hpp \ + msg_content.hpp \ + mutex.hpp \ + object.hpp \ + options.hpp \ + owned.hpp \ + pgm_receiver.hpp \ + pgm_sender.hpp \ + pgm_socket.hpp \ + pipe.hpp \ + platform.hpp \ + poll.hpp \ + poller.hpp \ + pair.hpp \ + prefix_tree.hpp \ + pub.hpp \ + queue.hpp \ + rep.hpp \ + req.hpp \ + select.hpp \ + session.hpp \ + signaler.hpp \ + socket_base.hpp \ + stdint.hpp \ + streamer.hpp \ + sub.hpp \ + tcp_connecter.hpp \ + tcp_listener.hpp \ + tcp_socket.hpp \ + thread.hpp \ + upstream.hpp \ + uuid.hpp \ + windows.hpp \ + wire.hpp \ + xrep.hpp \ + xreq.hpp \ + yarray.hpp \ + yarray_item.hpp \ + ypipe.hpp \ + yqueue.hpp \ + zmq_connecter.hpp \ + zmq_decoder.hpp \ + zmq_encoder.hpp \ + zmq_engine.hpp \ + zmq_init.hpp \ + zmq_listener.hpp \ + app_thread.cpp \ + command.cpp \ + ctx.cpp \ + devpoll.cpp \ + downstream.cpp \ + epoll.cpp \ + err.cpp \ + forwarder.cpp \ + fq.cpp \ + io_object.cpp \ + io_thread.cpp \ + ip.cpp \ + kqueue.cpp \ + lb.cpp \ + object.cpp \ + options.cpp \ + owned.cpp \ + pgm_receiver.cpp \ + pgm_sender.cpp \ + pgm_socket.cpp \ + pair.cpp \ + prefix_tree.cpp \ + pipe.cpp \ + poll.cpp \ + pub.cpp \ + queue.cpp \ + rep.cpp \ + req.cpp \ + select.cpp \ + session.cpp \ + signaler.cpp \ + socket_base.cpp \ + streamer.cpp \ + sub.cpp \ + tcp_connecter.cpp \ + tcp_listener.cpp \ + tcp_socket.cpp \ + thread.cpp \ + upstream.cpp \ + uuid.cpp \ + xrep.cpp \ + xreq.cpp \ + zmq.cpp \ + zmq_connecter.cpp \ + zmq_decoder.cpp \ + zmq_encoder.cpp \ + zmq_engine.cpp \ + zmq_init.cpp \ + zmq_listener.cpp + +if ON_MINGW +libzmq_la_LDFLAGS = -no-undefined -avoid-version -version-info @LTVER@ @LIBZMQ_EXTRA_LDFLAGS@ +else +libzmq_la_LDFLAGS = -version-info @LTVER@ @LIBZMQ_EXTRA_LDFLAGS@ +endif + +if BUILD_PGM + +if ON_MINGW +libpgm_diff_flags = \ + -D_WIN32_WINNT=0x0501 \ + -DCONFIG_16BIT_CHECKSUM \ + -DCONFIG_HAVE_IFR_NETMASK \ + -DCONFIG_BIND_INADDR_ANY \ + -DCONFIG_GALOIS_MUL_LUT \ + -DIF_NAMESIZE=256 \ + -DPGM_GNUC_INTERNAL=G_GNUC_INTERNAL \ + -DCONFIG_HAVE_WSACMSGHDR \ + -DGETTEXT_PACKAGE='"pgm"' \ + -DG_LOG_DOMAIN='"Pgm"' + +else +libpgm_diff_flags = \ + -D__need_IOV_MAX \ + -DCONFIG_16BIT_CHECKSUM \ + -DCONFIG_HAVE_PSELECT \ + -DCONFIG_HAVE_POLL \ + -DCONFIG_HAVE_PPOLL \ + -DCONFIG_HAVE_EPOLL \ + -DCONFIG_HAVE_CLOCK_GETTIME \ + -DCONFIG_HAVE_CLOCK_NANOSLEEP \ + -DCONFIG_HAVE_NANOSLEEP \ + -DCONFIG_HAVE_USLEEP \ + -DCONFIG_HAVE_RTC \ + -DCONFIG_HAVE_TSC \ + -DCONFIG_HAVE_IFR_NETMASK \ + -DCONFIG_HAVE_GETIFADDRS \ + -DCONFIG_HAVE_GETHOSTBYNAME2 \ + -DCONFIG_HAVE_GETPROTOBYNAME_R \ + -DCONFIG_BIND_INADDR_ANY \ + -DCONFIG_GALOIS_MUL_LUT \ + -DCONFIG_HAVE_MCAST_JOIN \ + -DCONFIG_HAVE_IP_MREQN \ + -DCONFIG_HAVE_SPRINTF_GROUPING \ + -DCONFIG_HAVE_HPET \ + -DPGM_GNUC_INTERNAL=G_GNUC_INTERNAL \ + -DGETTEXT_PACKAGE='"pgm"' \ + -DG_LOG_DOMAIN='"Pgm"' +endif + +libzmq_la_CFLAGS = -I$(top_srcdir)/foreign/openpgm/@pgm_basename@/openpgm/pgm/include/ @LIBZMQ_EXTRA_CXXFLAGS@ \ + -Wall \ + -pedantic \ + -std=gnu99 \ + -fno-strict-aliasing \ + --param max-inline-insns-single=600 \ + -D_REENTRANT \ + -D_GNU_SOURCE \ + ${libpgm_diff_flags} + +libzmq_la_CXXFLAGS = -I$(top_srcdir)/foreign/openpgm/@pgm_basename@/openpgm/pgm/include/ \ + @LIBZMQ_EXTRA_CXXFLAGS@ +endif + +if BUILD_NO_PGM +libzmq_la_CXXFLAGS = @LIBZMQ_EXTRA_CXXFLAGS@ +endif + +dist-hook: + -rm $(distdir)/platform.hpp + + diff --git a/src/Makefile.in b/src/Makefile.in new file mode 100644 index 0000000..bc3f00f --- /dev/null +++ b/src/Makefile.in @@ -0,0 +1,1510 @@ +# Makefile.in generated by automake 1.10.1 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + + + +VPATH = @srcdir@ +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +subdir = src +DIST_COMMON = $(include_HEADERS) $(srcdir)/Makefile.am \ + $(srcdir)/Makefile.in $(srcdir)/libzmq.pc.in \ + $(srcdir)/platform.hpp.in +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/config/libtool.m4 \ + $(top_srcdir)/config/ltoptions.m4 \ + $(top_srcdir)/config/ltsugar.m4 \ + $(top_srcdir)/config/ltversion.m4 \ + $(top_srcdir)/config/lt~obsolete.m4 $(top_srcdir)/configure.in +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = platform.hpp +CONFIG_CLEAN_FILES = libzmq.pc +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = `echo $$p | sed -e 's|^.*/||'`; +am__installdirs = "$(DESTDIR)$(libdir)" "$(DESTDIR)$(pkgconfigdir)" \ + "$(DESTDIR)$(includedir)" +libLTLIBRARIES_INSTALL = $(INSTALL) +LTLIBRARIES = $(lib_LTLIBRARIES) +libzmq_la_LIBADD = +am_libzmq_la_OBJECTS = libzmq_la-app_thread.lo libzmq_la-command.lo \ + libzmq_la-ctx.lo libzmq_la-devpoll.lo libzmq_la-downstream.lo \ + libzmq_la-epoll.lo libzmq_la-err.lo libzmq_la-forwarder.lo \ + libzmq_la-fq.lo libzmq_la-io_object.lo libzmq_la-io_thread.lo \ + libzmq_la-ip.lo libzmq_la-kqueue.lo libzmq_la-lb.lo \ + libzmq_la-object.lo libzmq_la-options.lo libzmq_la-owned.lo \ + libzmq_la-pgm_receiver.lo libzmq_la-pgm_sender.lo \ + libzmq_la-pgm_socket.lo libzmq_la-pair.lo \ + libzmq_la-prefix_tree.lo libzmq_la-pipe.lo libzmq_la-poll.lo \ + libzmq_la-pub.lo libzmq_la-queue.lo libzmq_la-rep.lo \ + libzmq_la-req.lo libzmq_la-select.lo libzmq_la-session.lo \ + libzmq_la-signaler.lo libzmq_la-socket_base.lo \ + libzmq_la-streamer.lo libzmq_la-sub.lo \ + libzmq_la-tcp_connecter.lo libzmq_la-tcp_listener.lo \ + libzmq_la-tcp_socket.lo libzmq_la-thread.lo \ + libzmq_la-upstream.lo libzmq_la-uuid.lo libzmq_la-xrep.lo \ + libzmq_la-xreq.lo libzmq_la-zmq.lo libzmq_la-zmq_connecter.lo \ + libzmq_la-zmq_decoder.lo libzmq_la-zmq_encoder.lo \ + libzmq_la-zmq_engine.lo libzmq_la-zmq_init.lo \ + libzmq_la-zmq_listener.lo +@BUILD_PGM_TRUE@am__objects_1 = libzmq_la-packet.lo libzmq_la-time.lo \ +@BUILD_PGM_TRUE@ libzmq_la-if.lo libzmq_la-getifaddrs.lo \ +@BUILD_PGM_TRUE@ libzmq_la-getnodeaddr.lo \ +@BUILD_PGM_TRUE@ libzmq_la-indextoaddr.lo \ +@BUILD_PGM_TRUE@ libzmq_la-indextoname.lo \ +@BUILD_PGM_TRUE@ libzmq_la-nametoindex.lo \ +@BUILD_PGM_TRUE@ libzmq_la-inet_network.lo libzmq_la-md5.lo \ +@BUILD_PGM_TRUE@ libzmq_la-gsi.lo libzmq_la-tsi.lo \ +@BUILD_PGM_TRUE@ libzmq_la-signal.lo libzmq_la-txwi.lo \ +@BUILD_PGM_TRUE@ libzmq_la-rxwi.lo libzmq_la-transport.lo \ +@BUILD_PGM_TRUE@ libzmq_la-source.lo libzmq_la-receiver.lo \ +@BUILD_PGM_TRUE@ libzmq_la-recv.lo libzmq_la-pgm.lo \ +@BUILD_PGM_TRUE@ libzmq_la-timer.lo libzmq_la-net.lo \ +@BUILD_PGM_TRUE@ libzmq_la-rate_control.lo libzmq_la-async.lo \ +@BUILD_PGM_TRUE@ libzmq_la-checksum.lo \ +@BUILD_PGM_TRUE@ libzmq_la-reed_solomon.lo \ +@BUILD_PGM_TRUE@ libzmq_la-galois_tables.lo \ +@BUILD_PGM_TRUE@ libzmq_la-wsastrerror.lo \ +@BUILD_PGM_TRUE@ libzmq_la-glib-compat.lo \ +@BUILD_PGM_TRUE@ libzmq_la-backtrace.lo libzmq_la-log.lo \ +@BUILD_PGM_TRUE@ libzmq_la-sockaddr.lo libzmq_la-version.lo +nodist_libzmq_la_OBJECTS = $(am__objects_1) +libzmq_la_OBJECTS = $(am_libzmq_la_OBJECTS) \ + $(nodist_libzmq_la_OBJECTS) +libzmq_la_LINK = $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CXXLD) $(libzmq_la_CXXFLAGS) \ + $(CXXFLAGS) $(libzmq_la_LDFLAGS) $(LDFLAGS) -o $@ +DEFAULT_INCLUDES = -I.@am__isrc@ +depcomp = $(SHELL) $(top_srcdir)/config/depcomp +am__depfiles_maybe = depfiles +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ + --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +CCLD = $(CC) +LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ + --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \ + $(LDFLAGS) -o $@ +CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) +LTCXXCOMPILE = $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ + --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) +CXXLD = $(CXX) +CXXLINK = $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ + --mode=link $(CXXLD) $(AM_CXXFLAGS) $(CXXFLAGS) $(AM_LDFLAGS) \ + $(LDFLAGS) -o $@ +SOURCES = $(libzmq_la_SOURCES) $(nodist_libzmq_la_SOURCES) +DIST_SOURCES = $(libzmq_la_SOURCES) +pkgconfigDATA_INSTALL = $(INSTALL_DATA) +DATA = $(pkgconfig_DATA) +includeHEADERS_INSTALL = $(INSTALL_HEADER) +HEADERS = $(include_HEADERS) +ETAGS = etags +CTAGS = ctags +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AR = @AR@ +AS = @AS@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DLLTOOL = @DLLTOOL@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +GLIB_CFLAGS = @GLIB_CFLAGS@ +GLIB_LIBS = @GLIB_LIBS@ +GREP = @GREP@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LIBZMQ_EXTRA_CXXFLAGS = @LIBZMQ_EXTRA_CXXFLAGS@ +LIBZMQ_EXTRA_LDFLAGS = @LIBZMQ_EXTRA_LDFLAGS@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +LTVER = @LTVER@ +MAKEINFO = @MAKEINFO@ +MKDIR_P = @MKDIR_P@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PKG_CONFIG = @PKG_CONFIG@ +RANLIB = @RANLIB@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +VERSION = @VERSION@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +have_asciidoc = @have_asciidoc@ +have_gzip = @have_gzip@ +have_perl = @have_perl@ +have_pkg_config = @have_pkg_config@ +have_python = @have_python@ +have_xmlto = @have_xmlto@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +inttypes = @inttypes@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +lt_ECHO = @lt_ECHO@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +pgm_basename = @pgm_basename@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +stdint = @stdint@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +lib_LTLIBRARIES = libzmq.la +pkgconfigdir = $(libdir)/pkgconfig +pkgconfig_DATA = libzmq.pc +include_HEADERS = ../include/zmq.h ../include/zmq.hpp +@BUILD_PGM_TRUE@pgm_sources = ../foreign/openpgm/@pgm_basename@/openpgm/pgm/packet.c \ +@BUILD_PGM_TRUE@ ../foreign/openpgm/@pgm_basename@/openpgm/pgm/time.c \ +@BUILD_PGM_TRUE@ ../foreign/openpgm/@pgm_basename@/openpgm/pgm/if.c \ +@BUILD_PGM_TRUE@ ../foreign/openpgm/@pgm_basename@/openpgm/pgm/getifaddrs.c \ +@BUILD_PGM_TRUE@ ../foreign/openpgm/@pgm_basename@/openpgm/pgm/getnodeaddr.c \ +@BUILD_PGM_TRUE@ ../foreign/openpgm/@pgm_basename@/openpgm/pgm/indextoaddr.c \ +@BUILD_PGM_TRUE@ ../foreign/openpgm/@pgm_basename@/openpgm/pgm/indextoname.c \ +@BUILD_PGM_TRUE@ ../foreign/openpgm/@pgm_basename@/openpgm/pgm/nametoindex.c \ +@BUILD_PGM_TRUE@ ../foreign/openpgm/@pgm_basename@/openpgm/pgm/inet_network.c \ +@BUILD_PGM_TRUE@ ../foreign/openpgm/@pgm_basename@/openpgm/pgm/md5.c \ +@BUILD_PGM_TRUE@ ../foreign/openpgm/@pgm_basename@/openpgm/pgm/gsi.c \ +@BUILD_PGM_TRUE@ ../foreign/openpgm/@pgm_basename@/openpgm/pgm/tsi.c \ +@BUILD_PGM_TRUE@ ../foreign/openpgm/@pgm_basename@/openpgm/pgm/signal.c \ +@BUILD_PGM_TRUE@ ../foreign/openpgm/@pgm_basename@/openpgm/pgm/txwi.c \ +@BUILD_PGM_TRUE@ ../foreign/openpgm/@pgm_basename@/openpgm/pgm/rxwi.c \ +@BUILD_PGM_TRUE@ ../foreign/openpgm/@pgm_basename@/openpgm/pgm/transport.c \ +@BUILD_PGM_TRUE@ ../foreign/openpgm/@pgm_basename@/openpgm/pgm/source.c \ +@BUILD_PGM_TRUE@ ../foreign/openpgm/@pgm_basename@/openpgm/pgm/receiver.c \ +@BUILD_PGM_TRUE@ ../foreign/openpgm/@pgm_basename@/openpgm/pgm/recv.c \ +@BUILD_PGM_TRUE@ ../foreign/openpgm/@pgm_basename@/openpgm/pgm/pgm.c \ +@BUILD_PGM_TRUE@ ../foreign/openpgm/@pgm_basename@/openpgm/pgm/timer.c \ +@BUILD_PGM_TRUE@ ../foreign/openpgm/@pgm_basename@/openpgm/pgm/net.c \ +@BUILD_PGM_TRUE@ ../foreign/openpgm/@pgm_basename@/openpgm/pgm/rate_control.c \ +@BUILD_PGM_TRUE@ ../foreign/openpgm/@pgm_basename@/openpgm/pgm/async.c \ +@BUILD_PGM_TRUE@ ../foreign/openpgm/@pgm_basename@/openpgm/pgm/checksum.c \ +@BUILD_PGM_TRUE@ ../foreign/openpgm/@pgm_basename@/openpgm/pgm/reed_solomon.c \ +@BUILD_PGM_TRUE@ ../foreign/openpgm/@pgm_basename@/openpgm/pgm/galois_tables.c \ +@BUILD_PGM_TRUE@ ../foreign/openpgm/@pgm_basename@/openpgm/pgm/wsastrerror.c \ +@BUILD_PGM_TRUE@ ../foreign/openpgm/@pgm_basename@/openpgm/pgm/glib-compat.c \ +@BUILD_PGM_TRUE@ ../foreign/openpgm/@pgm_basename@/openpgm/pgm/backtrace.c \ +@BUILD_PGM_TRUE@ ../foreign/openpgm/@pgm_basename@/openpgm/pgm/log.c \ +@BUILD_PGM_TRUE@ ../foreign/openpgm/@pgm_basename@/openpgm/pgm/sockaddr.c \ +@BUILD_PGM_TRUE@ ../foreign/openpgm/@pgm_basename@/openpgm/pgm/version.c + +nodist_libzmq_la_SOURCES = $(pgm_sources) +libzmq_la_SOURCES = app_thread.hpp \ + atomic_counter.hpp \ + atomic_ptr.hpp \ + blob.hpp \ + command.hpp \ + config.hpp \ + ctx.hpp \ + decoder.hpp \ + devpoll.hpp \ + downstream.hpp \ + encoder.hpp \ + epoll.hpp \ + err.hpp \ + fd.hpp \ + forwarder.hpp \ + fq.hpp \ + i_inout.hpp \ + io_object.hpp \ + io_thread.hpp \ + ip.hpp \ + i_endpoint.hpp \ + i_engine.hpp \ + i_poll_events.hpp \ + kqueue.hpp \ + lb.hpp \ + likely.hpp \ + msg_content.hpp \ + mutex.hpp \ + object.hpp \ + options.hpp \ + owned.hpp \ + pgm_receiver.hpp \ + pgm_sender.hpp \ + pgm_socket.hpp \ + pipe.hpp \ + platform.hpp \ + poll.hpp \ + poller.hpp \ + pair.hpp \ + prefix_tree.hpp \ + pub.hpp \ + queue.hpp \ + rep.hpp \ + req.hpp \ + select.hpp \ + session.hpp \ + signaler.hpp \ + socket_base.hpp \ + stdint.hpp \ + streamer.hpp \ + sub.hpp \ + tcp_connecter.hpp \ + tcp_listener.hpp \ + tcp_socket.hpp \ + thread.hpp \ + upstream.hpp \ + uuid.hpp \ + windows.hpp \ + wire.hpp \ + xrep.hpp \ + xreq.hpp \ + yarray.hpp \ + yarray_item.hpp \ + ypipe.hpp \ + yqueue.hpp \ + zmq_connecter.hpp \ + zmq_decoder.hpp \ + zmq_encoder.hpp \ + zmq_engine.hpp \ + zmq_init.hpp \ + zmq_listener.hpp \ + app_thread.cpp \ + command.cpp \ + ctx.cpp \ + devpoll.cpp \ + downstream.cpp \ + epoll.cpp \ + err.cpp \ + forwarder.cpp \ + fq.cpp \ + io_object.cpp \ + io_thread.cpp \ + ip.cpp \ + kqueue.cpp \ + lb.cpp \ + object.cpp \ + options.cpp \ + owned.cpp \ + pgm_receiver.cpp \ + pgm_sender.cpp \ + pgm_socket.cpp \ + pair.cpp \ + prefix_tree.cpp \ + pipe.cpp \ + poll.cpp \ + pub.cpp \ + queue.cpp \ + rep.cpp \ + req.cpp \ + select.cpp \ + session.cpp \ + signaler.cpp \ + socket_base.cpp \ + streamer.cpp \ + sub.cpp \ + tcp_connecter.cpp \ + tcp_listener.cpp \ + tcp_socket.cpp \ + thread.cpp \ + upstream.cpp \ + uuid.cpp \ + xrep.cpp \ + xreq.cpp \ + zmq.cpp \ + zmq_connecter.cpp \ + zmq_decoder.cpp \ + zmq_encoder.cpp \ + zmq_engine.cpp \ + zmq_init.cpp \ + zmq_listener.cpp + +@ON_MINGW_FALSE@libzmq_la_LDFLAGS = -version-info @LTVER@ @LIBZMQ_EXTRA_LDFLAGS@ +@ON_MINGW_TRUE@libzmq_la_LDFLAGS = -no-undefined -avoid-version -version-info @LTVER@ @LIBZMQ_EXTRA_LDFLAGS@ +@BUILD_PGM_TRUE@@ON_MINGW_FALSE@libpgm_diff_flags = \ +@BUILD_PGM_TRUE@@ON_MINGW_FALSE@ -D__need_IOV_MAX \ +@BUILD_PGM_TRUE@@ON_MINGW_FALSE@ -DCONFIG_16BIT_CHECKSUM \ +@BUILD_PGM_TRUE@@ON_MINGW_FALSE@ -DCONFIG_HAVE_PSELECT \ +@BUILD_PGM_TRUE@@ON_MINGW_FALSE@ -DCONFIG_HAVE_POLL \ +@BUILD_PGM_TRUE@@ON_MINGW_FALSE@ -DCONFIG_HAVE_PPOLL \ +@BUILD_PGM_TRUE@@ON_MINGW_FALSE@ -DCONFIG_HAVE_EPOLL \ +@BUILD_PGM_TRUE@@ON_MINGW_FALSE@ -DCONFIG_HAVE_CLOCK_GETTIME \ +@BUILD_PGM_TRUE@@ON_MINGW_FALSE@ -DCONFIG_HAVE_CLOCK_NANOSLEEP \ +@BUILD_PGM_TRUE@@ON_MINGW_FALSE@ -DCONFIG_HAVE_NANOSLEEP \ +@BUILD_PGM_TRUE@@ON_MINGW_FALSE@ -DCONFIG_HAVE_USLEEP \ +@BUILD_PGM_TRUE@@ON_MINGW_FALSE@ -DCONFIG_HAVE_RTC \ +@BUILD_PGM_TRUE@@ON_MINGW_FALSE@ -DCONFIG_HAVE_TSC \ +@BUILD_PGM_TRUE@@ON_MINGW_FALSE@ -DCONFIG_HAVE_IFR_NETMASK \ +@BUILD_PGM_TRUE@@ON_MINGW_FALSE@ -DCONFIG_HAVE_GETIFADDRS \ +@BUILD_PGM_TRUE@@ON_MINGW_FALSE@ -DCONFIG_HAVE_GETHOSTBYNAME2 \ +@BUILD_PGM_TRUE@@ON_MINGW_FALSE@ -DCONFIG_HAVE_GETPROTOBYNAME_R \ +@BUILD_PGM_TRUE@@ON_MINGW_FALSE@ -DCONFIG_BIND_INADDR_ANY \ +@BUILD_PGM_TRUE@@ON_MINGW_FALSE@ -DCONFIG_GALOIS_MUL_LUT \ +@BUILD_PGM_TRUE@@ON_MINGW_FALSE@ -DCONFIG_HAVE_MCAST_JOIN \ +@BUILD_PGM_TRUE@@ON_MINGW_FALSE@ -DCONFIG_HAVE_IP_MREQN \ +@BUILD_PGM_TRUE@@ON_MINGW_FALSE@ -DCONFIG_HAVE_SPRINTF_GROUPING \ +@BUILD_PGM_TRUE@@ON_MINGW_FALSE@ -DCONFIG_HAVE_HPET \ +@BUILD_PGM_TRUE@@ON_MINGW_FALSE@ -DPGM_GNUC_INTERNAL=G_GNUC_INTERNAL \ +@BUILD_PGM_TRUE@@ON_MINGW_FALSE@ -DGETTEXT_PACKAGE='"pgm"' \ +@BUILD_PGM_TRUE@@ON_MINGW_FALSE@ -DG_LOG_DOMAIN='"Pgm"' + +@BUILD_PGM_TRUE@@ON_MINGW_TRUE@libpgm_diff_flags = \ +@BUILD_PGM_TRUE@@ON_MINGW_TRUE@ -D_WIN32_WINNT=0x0501 \ +@BUILD_PGM_TRUE@@ON_MINGW_TRUE@ -DCONFIG_16BIT_CHECKSUM \ +@BUILD_PGM_TRUE@@ON_MINGW_TRUE@ -DCONFIG_HAVE_IFR_NETMASK \ +@BUILD_PGM_TRUE@@ON_MINGW_TRUE@ -DCONFIG_BIND_INADDR_ANY \ +@BUILD_PGM_TRUE@@ON_MINGW_TRUE@ -DCONFIG_GALOIS_MUL_LUT \ +@BUILD_PGM_TRUE@@ON_MINGW_TRUE@ -DIF_NAMESIZE=256 \ +@BUILD_PGM_TRUE@@ON_MINGW_TRUE@ -DPGM_GNUC_INTERNAL=G_GNUC_INTERNAL \ +@BUILD_PGM_TRUE@@ON_MINGW_TRUE@ -DCONFIG_HAVE_WSACMSGHDR \ +@BUILD_PGM_TRUE@@ON_MINGW_TRUE@ -DGETTEXT_PACKAGE='"pgm"' \ +@BUILD_PGM_TRUE@@ON_MINGW_TRUE@ -DG_LOG_DOMAIN='"Pgm"' + +@BUILD_PGM_TRUE@libzmq_la_CFLAGS = -I$(top_srcdir)/foreign/openpgm/@pgm_basename@/openpgm/pgm/include/ @LIBZMQ_EXTRA_CXXFLAGS@ \ +@BUILD_PGM_TRUE@ -Wall \ +@BUILD_PGM_TRUE@ -pedantic \ +@BUILD_PGM_TRUE@ -std=gnu99 \ +@BUILD_PGM_TRUE@ -fno-strict-aliasing \ +@BUILD_PGM_TRUE@ --param max-inline-insns-single=600 \ +@BUILD_PGM_TRUE@ -D_REENTRANT \ +@BUILD_PGM_TRUE@ -D_GNU_SOURCE \ +@BUILD_PGM_TRUE@ ${libpgm_diff_flags} + +@BUILD_NO_PGM_TRUE@libzmq_la_CXXFLAGS = @LIBZMQ_EXTRA_CXXFLAGS@ +@BUILD_PGM_TRUE@libzmq_la_CXXFLAGS = -I$(top_srcdir)/foreign/openpgm/@pgm_basename@/openpgm/pgm/include/ \ +@BUILD_PGM_TRUE@ @LIBZMQ_EXTRA_CXXFLAGS@ + +all: platform.hpp + $(MAKE) $(AM_MAKEFLAGS) all-am + +.SUFFIXES: +.SUFFIXES: .c .cpp .lo .o .obj +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \ + && exit 0; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/Makefile'; \ + cd $(top_srcdir) && \ + $(AUTOMAKE) --gnu src/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +platform.hpp: stamp-h1 + @if test ! -f $@; then \ + rm -f stamp-h1; \ + $(MAKE) $(AM_MAKEFLAGS) stamp-h1; \ + else :; fi + +stamp-h1: $(srcdir)/platform.hpp.in $(top_builddir)/config.status + @rm -f stamp-h1 + cd $(top_builddir) && $(SHELL) ./config.status src/platform.hpp +$(srcdir)/platform.hpp.in: $(am__configure_deps) + cd $(top_srcdir) && $(AUTOHEADER) + rm -f stamp-h1 + touch $@ + +distclean-hdr: + -rm -f platform.hpp stamp-h1 +libzmq.pc: $(top_builddir)/config.status $(srcdir)/libzmq.pc.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +install-libLTLIBRARIES: $(lib_LTLIBRARIES) + @$(NORMAL_INSTALL) + test -z "$(libdir)" || $(MKDIR_P) "$(DESTDIR)$(libdir)" + @list='$(lib_LTLIBRARIES)'; for p in $$list; do \ + if test -f $$p; then \ + f=$(am__strip_dir) \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(libLTLIBRARIES_INSTALL) $(INSTALL_STRIP_FLAG) '$$p' '$(DESTDIR)$(libdir)/$$f'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(libLTLIBRARIES_INSTALL) $(INSTALL_STRIP_FLAG) "$$p" "$(DESTDIR)$(libdir)/$$f"; \ + else :; fi; \ + done + +uninstall-libLTLIBRARIES: + @$(NORMAL_UNINSTALL) + @list='$(lib_LTLIBRARIES)'; for p in $$list; do \ + p=$(am__strip_dir) \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$p'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$p"; \ + done + +clean-libLTLIBRARIES: + -test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES) + @list='$(lib_LTLIBRARIES)'; for p in $$list; do \ + dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \ + test "$$dir" != "$$p" || dir=.; \ + echo "rm -f \"$${dir}/so_locations\""; \ + rm -f "$${dir}/so_locations"; \ + done +libzmq.la: $(libzmq_la_OBJECTS) $(libzmq_la_DEPENDENCIES) + $(libzmq_la_LINK) -rpath $(libdir) $(libzmq_la_OBJECTS) $(libzmq_la_LIBADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libzmq_la-app_thread.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libzmq_la-async.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libzmq_la-backtrace.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libzmq_la-checksum.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libzmq_la-command.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libzmq_la-ctx.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libzmq_la-devpoll.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libzmq_la-downstream.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libzmq_la-epoll.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libzmq_la-err.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libzmq_la-forwarder.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libzmq_la-fq.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libzmq_la-galois_tables.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libzmq_la-getifaddrs.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libzmq_la-getnodeaddr.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libzmq_la-glib-compat.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libzmq_la-gsi.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libzmq_la-if.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libzmq_la-indextoaddr.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libzmq_la-indextoname.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libzmq_la-inet_network.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libzmq_la-io_object.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libzmq_la-io_thread.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libzmq_la-ip.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libzmq_la-kqueue.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libzmq_la-lb.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libzmq_la-log.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libzmq_la-md5.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libzmq_la-nametoindex.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libzmq_la-net.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libzmq_la-object.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libzmq_la-options.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libzmq_la-owned.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libzmq_la-packet.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libzmq_la-pair.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libzmq_la-pgm.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libzmq_la-pgm_receiver.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libzmq_la-pgm_sender.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libzmq_la-pgm_socket.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libzmq_la-pipe.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libzmq_la-poll.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libzmq_la-prefix_tree.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libzmq_la-pub.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libzmq_la-queue.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libzmq_la-rate_control.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libzmq_la-receiver.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libzmq_la-recv.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libzmq_la-reed_solomon.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libzmq_la-rep.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libzmq_la-req.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libzmq_la-rxwi.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libzmq_la-select.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libzmq_la-session.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libzmq_la-signal.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libzmq_la-signaler.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libzmq_la-sockaddr.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libzmq_la-socket_base.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libzmq_la-source.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libzmq_la-streamer.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libzmq_la-sub.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libzmq_la-tcp_connecter.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libzmq_la-tcp_listener.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libzmq_la-tcp_socket.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libzmq_la-thread.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libzmq_la-time.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libzmq_la-timer.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libzmq_la-transport.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libzmq_la-tsi.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libzmq_la-txwi.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libzmq_la-upstream.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libzmq_la-uuid.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libzmq_la-version.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libzmq_la-wsastrerror.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libzmq_la-xrep.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libzmq_la-xreq.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libzmq_la-zmq.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libzmq_la-zmq_connecter.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libzmq_la-zmq_decoder.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libzmq_la-zmq_encoder.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libzmq_la-zmq_engine.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libzmq_la-zmq_init.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libzmq_la-zmq_listener.Plo@am__quote@ + +.c.o: +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(COMPILE) -c $< + +.c.obj: +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` +@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'` + +.c.lo: +@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $< + +libzmq_la-packet.lo: ../foreign/openpgm/@pgm_basename@/openpgm/pgm/packet.c +@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzmq_la_CFLAGS) $(CFLAGS) -MT libzmq_la-packet.lo -MD -MP -MF $(DEPDIR)/libzmq_la-packet.Tpo -c -o libzmq_la-packet.lo `test -f '../foreign/openpgm/@pgm_basename@/openpgm/pgm/packet.c' || echo '$(srcdir)/'`../foreign/openpgm/@pgm_basename@/openpgm/pgm/packet.c +@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/libzmq_la-packet.Tpo $(DEPDIR)/libzmq_la-packet.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='../foreign/openpgm/@pgm_basename@/openpgm/pgm/packet.c' object='libzmq_la-packet.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzmq_la_CFLAGS) $(CFLAGS) -c -o libzmq_la-packet.lo `test -f '../foreign/openpgm/@pgm_basename@/openpgm/pgm/packet.c' || echo '$(srcdir)/'`../foreign/openpgm/@pgm_basename@/openpgm/pgm/packet.c + +libzmq_la-time.lo: ../foreign/openpgm/@pgm_basename@/openpgm/pgm/time.c +@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzmq_la_CFLAGS) $(CFLAGS) -MT libzmq_la-time.lo -MD -MP -MF $(DEPDIR)/libzmq_la-time.Tpo -c -o libzmq_la-time.lo `test -f '../foreign/openpgm/@pgm_basename@/openpgm/pgm/time.c' || echo '$(srcdir)/'`../foreign/openpgm/@pgm_basename@/openpgm/pgm/time.c +@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/libzmq_la-time.Tpo $(DEPDIR)/libzmq_la-time.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='../foreign/openpgm/@pgm_basename@/openpgm/pgm/time.c' object='libzmq_la-time.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzmq_la_CFLAGS) $(CFLAGS) -c -o libzmq_la-time.lo `test -f '../foreign/openpgm/@pgm_basename@/openpgm/pgm/time.c' || echo '$(srcdir)/'`../foreign/openpgm/@pgm_basename@/openpgm/pgm/time.c + +libzmq_la-if.lo: ../foreign/openpgm/@pgm_basename@/openpgm/pgm/if.c +@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzmq_la_CFLAGS) $(CFLAGS) -MT libzmq_la-if.lo -MD -MP -MF $(DEPDIR)/libzmq_la-if.Tpo -c -o libzmq_la-if.lo `test -f '../foreign/openpgm/@pgm_basename@/openpgm/pgm/if.c' || echo '$(srcdir)/'`../foreign/openpgm/@pgm_basename@/openpgm/pgm/if.c +@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/libzmq_la-if.Tpo $(DEPDIR)/libzmq_la-if.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='../foreign/openpgm/@pgm_basename@/openpgm/pgm/if.c' object='libzmq_la-if.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzmq_la_CFLAGS) $(CFLAGS) -c -o libzmq_la-if.lo `test -f '../foreign/openpgm/@pgm_basename@/openpgm/pgm/if.c' || echo '$(srcdir)/'`../foreign/openpgm/@pgm_basename@/openpgm/pgm/if.c + +libzmq_la-getifaddrs.lo: ../foreign/openpgm/@pgm_basename@/openpgm/pgm/getifaddrs.c +@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzmq_la_CFLAGS) $(CFLAGS) -MT libzmq_la-getifaddrs.lo -MD -MP -MF $(DEPDIR)/libzmq_la-getifaddrs.Tpo -c -o libzmq_la-getifaddrs.lo `test -f '../foreign/openpgm/@pgm_basename@/openpgm/pgm/getifaddrs.c' || echo '$(srcdir)/'`../foreign/openpgm/@pgm_basename@/openpgm/pgm/getifaddrs.c +@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/libzmq_la-getifaddrs.Tpo $(DEPDIR)/libzmq_la-getifaddrs.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='../foreign/openpgm/@pgm_basename@/openpgm/pgm/getifaddrs.c' object='libzmq_la-getifaddrs.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzmq_la_CFLAGS) $(CFLAGS) -c -o libzmq_la-getifaddrs.lo `test -f '../foreign/openpgm/@pgm_basename@/openpgm/pgm/getifaddrs.c' || echo '$(srcdir)/'`../foreign/openpgm/@pgm_basename@/openpgm/pgm/getifaddrs.c + +libzmq_la-getnodeaddr.lo: ../foreign/openpgm/@pgm_basename@/openpgm/pgm/getnodeaddr.c +@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzmq_la_CFLAGS) $(CFLAGS) -MT libzmq_la-getnodeaddr.lo -MD -MP -MF $(DEPDIR)/libzmq_la-getnodeaddr.Tpo -c -o libzmq_la-getnodeaddr.lo `test -f '../foreign/openpgm/@pgm_basename@/openpgm/pgm/getnodeaddr.c' || echo '$(srcdir)/'`../foreign/openpgm/@pgm_basename@/openpgm/pgm/getnodeaddr.c +@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/libzmq_la-getnodeaddr.Tpo $(DEPDIR)/libzmq_la-getnodeaddr.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='../foreign/openpgm/@pgm_basename@/openpgm/pgm/getnodeaddr.c' object='libzmq_la-getnodeaddr.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzmq_la_CFLAGS) $(CFLAGS) -c -o libzmq_la-getnodeaddr.lo `test -f '../foreign/openpgm/@pgm_basename@/openpgm/pgm/getnodeaddr.c' || echo '$(srcdir)/'`../foreign/openpgm/@pgm_basename@/openpgm/pgm/getnodeaddr.c + +libzmq_la-indextoaddr.lo: ../foreign/openpgm/@pgm_basename@/openpgm/pgm/indextoaddr.c +@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzmq_la_CFLAGS) $(CFLAGS) -MT libzmq_la-indextoaddr.lo -MD -MP -MF $(DEPDIR)/libzmq_la-indextoaddr.Tpo -c -o libzmq_la-indextoaddr.lo `test -f '../foreign/openpgm/@pgm_basename@/openpgm/pgm/indextoaddr.c' || echo '$(srcdir)/'`../foreign/openpgm/@pgm_basename@/openpgm/pgm/indextoaddr.c +@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/libzmq_la-indextoaddr.Tpo $(DEPDIR)/libzmq_la-indextoaddr.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='../foreign/openpgm/@pgm_basename@/openpgm/pgm/indextoaddr.c' object='libzmq_la-indextoaddr.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzmq_la_CFLAGS) $(CFLAGS) -c -o libzmq_la-indextoaddr.lo `test -f '../foreign/openpgm/@pgm_basename@/openpgm/pgm/indextoaddr.c' || echo '$(srcdir)/'`../foreign/openpgm/@pgm_basename@/openpgm/pgm/indextoaddr.c + +libzmq_la-indextoname.lo: ../foreign/openpgm/@pgm_basename@/openpgm/pgm/indextoname.c +@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzmq_la_CFLAGS) $(CFLAGS) -MT libzmq_la-indextoname.lo -MD -MP -MF $(DEPDIR)/libzmq_la-indextoname.Tpo -c -o libzmq_la-indextoname.lo `test -f '../foreign/openpgm/@pgm_basename@/openpgm/pgm/indextoname.c' || echo '$(srcdir)/'`../foreign/openpgm/@pgm_basename@/openpgm/pgm/indextoname.c +@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/libzmq_la-indextoname.Tpo $(DEPDIR)/libzmq_la-indextoname.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='../foreign/openpgm/@pgm_basename@/openpgm/pgm/indextoname.c' object='libzmq_la-indextoname.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzmq_la_CFLAGS) $(CFLAGS) -c -o libzmq_la-indextoname.lo `test -f '../foreign/openpgm/@pgm_basename@/openpgm/pgm/indextoname.c' || echo '$(srcdir)/'`../foreign/openpgm/@pgm_basename@/openpgm/pgm/indextoname.c + +libzmq_la-nametoindex.lo: ../foreign/openpgm/@pgm_basename@/openpgm/pgm/nametoindex.c +@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzmq_la_CFLAGS) $(CFLAGS) -MT libzmq_la-nametoindex.lo -MD -MP -MF $(DEPDIR)/libzmq_la-nametoindex.Tpo -c -o libzmq_la-nametoindex.lo `test -f '../foreign/openpgm/@pgm_basename@/openpgm/pgm/nametoindex.c' || echo '$(srcdir)/'`../foreign/openpgm/@pgm_basename@/openpgm/pgm/nametoindex.c +@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/libzmq_la-nametoindex.Tpo $(DEPDIR)/libzmq_la-nametoindex.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='../foreign/openpgm/@pgm_basename@/openpgm/pgm/nametoindex.c' object='libzmq_la-nametoindex.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzmq_la_CFLAGS) $(CFLAGS) -c -o libzmq_la-nametoindex.lo `test -f '../foreign/openpgm/@pgm_basename@/openpgm/pgm/nametoindex.c' || echo '$(srcdir)/'`../foreign/openpgm/@pgm_basename@/openpgm/pgm/nametoindex.c + +libzmq_la-inet_network.lo: ../foreign/openpgm/@pgm_basename@/openpgm/pgm/inet_network.c +@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzmq_la_CFLAGS) $(CFLAGS) -MT libzmq_la-inet_network.lo -MD -MP -MF $(DEPDIR)/libzmq_la-inet_network.Tpo -c -o libzmq_la-inet_network.lo `test -f '../foreign/openpgm/@pgm_basename@/openpgm/pgm/inet_network.c' || echo '$(srcdir)/'`../foreign/openpgm/@pgm_basename@/openpgm/pgm/inet_network.c +@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/libzmq_la-inet_network.Tpo $(DEPDIR)/libzmq_la-inet_network.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='../foreign/openpgm/@pgm_basename@/openpgm/pgm/inet_network.c' object='libzmq_la-inet_network.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzmq_la_CFLAGS) $(CFLAGS) -c -o libzmq_la-inet_network.lo `test -f '../foreign/openpgm/@pgm_basename@/openpgm/pgm/inet_network.c' || echo '$(srcdir)/'`../foreign/openpgm/@pgm_basename@/openpgm/pgm/inet_network.c + +libzmq_la-md5.lo: ../foreign/openpgm/@pgm_basename@/openpgm/pgm/md5.c +@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzmq_la_CFLAGS) $(CFLAGS) -MT libzmq_la-md5.lo -MD -MP -MF $(DEPDIR)/libzmq_la-md5.Tpo -c -o libzmq_la-md5.lo `test -f '../foreign/openpgm/@pgm_basename@/openpgm/pgm/md5.c' || echo '$(srcdir)/'`../foreign/openpgm/@pgm_basename@/openpgm/pgm/md5.c +@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/libzmq_la-md5.Tpo $(DEPDIR)/libzmq_la-md5.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='../foreign/openpgm/@pgm_basename@/openpgm/pgm/md5.c' object='libzmq_la-md5.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzmq_la_CFLAGS) $(CFLAGS) -c -o libzmq_la-md5.lo `test -f '../foreign/openpgm/@pgm_basename@/openpgm/pgm/md5.c' || echo '$(srcdir)/'`../foreign/openpgm/@pgm_basename@/openpgm/pgm/md5.c + +libzmq_la-gsi.lo: ../foreign/openpgm/@pgm_basename@/openpgm/pgm/gsi.c +@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzmq_la_CFLAGS) $(CFLAGS) -MT libzmq_la-gsi.lo -MD -MP -MF $(DEPDIR)/libzmq_la-gsi.Tpo -c -o libzmq_la-gsi.lo `test -f '../foreign/openpgm/@pgm_basename@/openpgm/pgm/gsi.c' || echo '$(srcdir)/'`../foreign/openpgm/@pgm_basename@/openpgm/pgm/gsi.c +@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/libzmq_la-gsi.Tpo $(DEPDIR)/libzmq_la-gsi.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='../foreign/openpgm/@pgm_basename@/openpgm/pgm/gsi.c' object='libzmq_la-gsi.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzmq_la_CFLAGS) $(CFLAGS) -c -o libzmq_la-gsi.lo `test -f '../foreign/openpgm/@pgm_basename@/openpgm/pgm/gsi.c' || echo '$(srcdir)/'`../foreign/openpgm/@pgm_basename@/openpgm/pgm/gsi.c + +libzmq_la-tsi.lo: ../foreign/openpgm/@pgm_basename@/openpgm/pgm/tsi.c +@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzmq_la_CFLAGS) $(CFLAGS) -MT libzmq_la-tsi.lo -MD -MP -MF $(DEPDIR)/libzmq_la-tsi.Tpo -c -o libzmq_la-tsi.lo `test -f '../foreign/openpgm/@pgm_basename@/openpgm/pgm/tsi.c' || echo '$(srcdir)/'`../foreign/openpgm/@pgm_basename@/openpgm/pgm/tsi.c +@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/libzmq_la-tsi.Tpo $(DEPDIR)/libzmq_la-tsi.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='../foreign/openpgm/@pgm_basename@/openpgm/pgm/tsi.c' object='libzmq_la-tsi.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzmq_la_CFLAGS) $(CFLAGS) -c -o libzmq_la-tsi.lo `test -f '../foreign/openpgm/@pgm_basename@/openpgm/pgm/tsi.c' || echo '$(srcdir)/'`../foreign/openpgm/@pgm_basename@/openpgm/pgm/tsi.c + +libzmq_la-signal.lo: ../foreign/openpgm/@pgm_basename@/openpgm/pgm/signal.c +@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzmq_la_CFLAGS) $(CFLAGS) -MT libzmq_la-signal.lo -MD -MP -MF $(DEPDIR)/libzmq_la-signal.Tpo -c -o libzmq_la-signal.lo `test -f '../foreign/openpgm/@pgm_basename@/openpgm/pgm/signal.c' || echo '$(srcdir)/'`../foreign/openpgm/@pgm_basename@/openpgm/pgm/signal.c +@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/libzmq_la-signal.Tpo $(DEPDIR)/libzmq_la-signal.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='../foreign/openpgm/@pgm_basename@/openpgm/pgm/signal.c' object='libzmq_la-signal.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzmq_la_CFLAGS) $(CFLAGS) -c -o libzmq_la-signal.lo `test -f '../foreign/openpgm/@pgm_basename@/openpgm/pgm/signal.c' || echo '$(srcdir)/'`../foreign/openpgm/@pgm_basename@/openpgm/pgm/signal.c + +libzmq_la-txwi.lo: ../foreign/openpgm/@pgm_basename@/openpgm/pgm/txwi.c +@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzmq_la_CFLAGS) $(CFLAGS) -MT libzmq_la-txwi.lo -MD -MP -MF $(DEPDIR)/libzmq_la-txwi.Tpo -c -o libzmq_la-txwi.lo `test -f '../foreign/openpgm/@pgm_basename@/openpgm/pgm/txwi.c' || echo '$(srcdir)/'`../foreign/openpgm/@pgm_basename@/openpgm/pgm/txwi.c +@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/libzmq_la-txwi.Tpo $(DEPDIR)/libzmq_la-txwi.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='../foreign/openpgm/@pgm_basename@/openpgm/pgm/txwi.c' object='libzmq_la-txwi.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzmq_la_CFLAGS) $(CFLAGS) -c -o libzmq_la-txwi.lo `test -f '../foreign/openpgm/@pgm_basename@/openpgm/pgm/txwi.c' || echo '$(srcdir)/'`../foreign/openpgm/@pgm_basename@/openpgm/pgm/txwi.c + +libzmq_la-rxwi.lo: ../foreign/openpgm/@pgm_basename@/openpgm/pgm/rxwi.c +@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzmq_la_CFLAGS) $(CFLAGS) -MT libzmq_la-rxwi.lo -MD -MP -MF $(DEPDIR)/libzmq_la-rxwi.Tpo -c -o libzmq_la-rxwi.lo `test -f '../foreign/openpgm/@pgm_basename@/openpgm/pgm/rxwi.c' || echo '$(srcdir)/'`../foreign/openpgm/@pgm_basename@/openpgm/pgm/rxwi.c +@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/libzmq_la-rxwi.Tpo $(DEPDIR)/libzmq_la-rxwi.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='../foreign/openpgm/@pgm_basename@/openpgm/pgm/rxwi.c' object='libzmq_la-rxwi.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzmq_la_CFLAGS) $(CFLAGS) -c -o libzmq_la-rxwi.lo `test -f '../foreign/openpgm/@pgm_basename@/openpgm/pgm/rxwi.c' || echo '$(srcdir)/'`../foreign/openpgm/@pgm_basename@/openpgm/pgm/rxwi.c + +libzmq_la-transport.lo: ../foreign/openpgm/@pgm_basename@/openpgm/pgm/transport.c +@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzmq_la_CFLAGS) $(CFLAGS) -MT libzmq_la-transport.lo -MD -MP -MF $(DEPDIR)/libzmq_la-transport.Tpo -c -o libzmq_la-transport.lo `test -f '../foreign/openpgm/@pgm_basename@/openpgm/pgm/transport.c' || echo '$(srcdir)/'`../foreign/openpgm/@pgm_basename@/openpgm/pgm/transport.c +@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/libzmq_la-transport.Tpo $(DEPDIR)/libzmq_la-transport.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='../foreign/openpgm/@pgm_basename@/openpgm/pgm/transport.c' object='libzmq_la-transport.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzmq_la_CFLAGS) $(CFLAGS) -c -o libzmq_la-transport.lo `test -f '../foreign/openpgm/@pgm_basename@/openpgm/pgm/transport.c' || echo '$(srcdir)/'`../foreign/openpgm/@pgm_basename@/openpgm/pgm/transport.c + +libzmq_la-source.lo: ../foreign/openpgm/@pgm_basename@/openpgm/pgm/source.c +@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzmq_la_CFLAGS) $(CFLAGS) -MT libzmq_la-source.lo -MD -MP -MF $(DEPDIR)/libzmq_la-source.Tpo -c -o libzmq_la-source.lo `test -f '../foreign/openpgm/@pgm_basename@/openpgm/pgm/source.c' || echo '$(srcdir)/'`../foreign/openpgm/@pgm_basename@/openpgm/pgm/source.c +@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/libzmq_la-source.Tpo $(DEPDIR)/libzmq_la-source.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='../foreign/openpgm/@pgm_basename@/openpgm/pgm/source.c' object='libzmq_la-source.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzmq_la_CFLAGS) $(CFLAGS) -c -o libzmq_la-source.lo `test -f '../foreign/openpgm/@pgm_basename@/openpgm/pgm/source.c' || echo '$(srcdir)/'`../foreign/openpgm/@pgm_basename@/openpgm/pgm/source.c + +libzmq_la-receiver.lo: ../foreign/openpgm/@pgm_basename@/openpgm/pgm/receiver.c +@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzmq_la_CFLAGS) $(CFLAGS) -MT libzmq_la-receiver.lo -MD -MP -MF $(DEPDIR)/libzmq_la-receiver.Tpo -c -o libzmq_la-receiver.lo `test -f '../foreign/openpgm/@pgm_basename@/openpgm/pgm/receiver.c' || echo '$(srcdir)/'`../foreign/openpgm/@pgm_basename@/openpgm/pgm/receiver.c +@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/libzmq_la-receiver.Tpo $(DEPDIR)/libzmq_la-receiver.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='../foreign/openpgm/@pgm_basename@/openpgm/pgm/receiver.c' object='libzmq_la-receiver.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzmq_la_CFLAGS) $(CFLAGS) -c -o libzmq_la-receiver.lo `test -f '../foreign/openpgm/@pgm_basename@/openpgm/pgm/receiver.c' || echo '$(srcdir)/'`../foreign/openpgm/@pgm_basename@/openpgm/pgm/receiver.c + +libzmq_la-recv.lo: ../foreign/openpgm/@pgm_basename@/openpgm/pgm/recv.c +@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzmq_la_CFLAGS) $(CFLAGS) -MT libzmq_la-recv.lo -MD -MP -MF $(DEPDIR)/libzmq_la-recv.Tpo -c -o libzmq_la-recv.lo `test -f '../foreign/openpgm/@pgm_basename@/openpgm/pgm/recv.c' || echo '$(srcdir)/'`../foreign/openpgm/@pgm_basename@/openpgm/pgm/recv.c +@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/libzmq_la-recv.Tpo $(DEPDIR)/libzmq_la-recv.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='../foreign/openpgm/@pgm_basename@/openpgm/pgm/recv.c' object='libzmq_la-recv.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzmq_la_CFLAGS) $(CFLAGS) -c -o libzmq_la-recv.lo `test -f '../foreign/openpgm/@pgm_basename@/openpgm/pgm/recv.c' || echo '$(srcdir)/'`../foreign/openpgm/@pgm_basename@/openpgm/pgm/recv.c + +libzmq_la-pgm.lo: ../foreign/openpgm/@pgm_basename@/openpgm/pgm/pgm.c +@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzmq_la_CFLAGS) $(CFLAGS) -MT libzmq_la-pgm.lo -MD -MP -MF $(DEPDIR)/libzmq_la-pgm.Tpo -c -o libzmq_la-pgm.lo `test -f '../foreign/openpgm/@pgm_basename@/openpgm/pgm/pgm.c' || echo '$(srcdir)/'`../foreign/openpgm/@pgm_basename@/openpgm/pgm/pgm.c +@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/libzmq_la-pgm.Tpo $(DEPDIR)/libzmq_la-pgm.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='../foreign/openpgm/@pgm_basename@/openpgm/pgm/pgm.c' object='libzmq_la-pgm.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzmq_la_CFLAGS) $(CFLAGS) -c -o libzmq_la-pgm.lo `test -f '../foreign/openpgm/@pgm_basename@/openpgm/pgm/pgm.c' || echo '$(srcdir)/'`../foreign/openpgm/@pgm_basename@/openpgm/pgm/pgm.c + +libzmq_la-timer.lo: ../foreign/openpgm/@pgm_basename@/openpgm/pgm/timer.c +@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzmq_la_CFLAGS) $(CFLAGS) -MT libzmq_la-timer.lo -MD -MP -MF $(DEPDIR)/libzmq_la-timer.Tpo -c -o libzmq_la-timer.lo `test -f '../foreign/openpgm/@pgm_basename@/openpgm/pgm/timer.c' || echo '$(srcdir)/'`../foreign/openpgm/@pgm_basename@/openpgm/pgm/timer.c +@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/libzmq_la-timer.Tpo $(DEPDIR)/libzmq_la-timer.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='../foreign/openpgm/@pgm_basename@/openpgm/pgm/timer.c' object='libzmq_la-timer.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzmq_la_CFLAGS) $(CFLAGS) -c -o libzmq_la-timer.lo `test -f '../foreign/openpgm/@pgm_basename@/openpgm/pgm/timer.c' || echo '$(srcdir)/'`../foreign/openpgm/@pgm_basename@/openpgm/pgm/timer.c + +libzmq_la-net.lo: ../foreign/openpgm/@pgm_basename@/openpgm/pgm/net.c +@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzmq_la_CFLAGS) $(CFLAGS) -MT libzmq_la-net.lo -MD -MP -MF $(DEPDIR)/libzmq_la-net.Tpo -c -o libzmq_la-net.lo `test -f '../foreign/openpgm/@pgm_basename@/openpgm/pgm/net.c' || echo '$(srcdir)/'`../foreign/openpgm/@pgm_basename@/openpgm/pgm/net.c +@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/libzmq_la-net.Tpo $(DEPDIR)/libzmq_la-net.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='../foreign/openpgm/@pgm_basename@/openpgm/pgm/net.c' object='libzmq_la-net.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzmq_la_CFLAGS) $(CFLAGS) -c -o libzmq_la-net.lo `test -f '../foreign/openpgm/@pgm_basename@/openpgm/pgm/net.c' || echo '$(srcdir)/'`../foreign/openpgm/@pgm_basename@/openpgm/pgm/net.c + +libzmq_la-rate_control.lo: ../foreign/openpgm/@pgm_basename@/openpgm/pgm/rate_control.c +@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzmq_la_CFLAGS) $(CFLAGS) -MT libzmq_la-rate_control.lo -MD -MP -MF $(DEPDIR)/libzmq_la-rate_control.Tpo -c -o libzmq_la-rate_control.lo `test -f '../foreign/openpgm/@pgm_basename@/openpgm/pgm/rate_control.c' || echo '$(srcdir)/'`../foreign/openpgm/@pgm_basename@/openpgm/pgm/rate_control.c +@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/libzmq_la-rate_control.Tpo $(DEPDIR)/libzmq_la-rate_control.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='../foreign/openpgm/@pgm_basename@/openpgm/pgm/rate_control.c' object='libzmq_la-rate_control.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzmq_la_CFLAGS) $(CFLAGS) -c -o libzmq_la-rate_control.lo `test -f '../foreign/openpgm/@pgm_basename@/openpgm/pgm/rate_control.c' || echo '$(srcdir)/'`../foreign/openpgm/@pgm_basename@/openpgm/pgm/rate_control.c + +libzmq_la-async.lo: ../foreign/openpgm/@pgm_basename@/openpgm/pgm/async.c +@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzmq_la_CFLAGS) $(CFLAGS) -MT libzmq_la-async.lo -MD -MP -MF $(DEPDIR)/libzmq_la-async.Tpo -c -o libzmq_la-async.lo `test -f '../foreign/openpgm/@pgm_basename@/openpgm/pgm/async.c' || echo '$(srcdir)/'`../foreign/openpgm/@pgm_basename@/openpgm/pgm/async.c +@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/libzmq_la-async.Tpo $(DEPDIR)/libzmq_la-async.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='../foreign/openpgm/@pgm_basename@/openpgm/pgm/async.c' object='libzmq_la-async.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzmq_la_CFLAGS) $(CFLAGS) -c -o libzmq_la-async.lo `test -f '../foreign/openpgm/@pgm_basename@/openpgm/pgm/async.c' || echo '$(srcdir)/'`../foreign/openpgm/@pgm_basename@/openpgm/pgm/async.c + +libzmq_la-checksum.lo: ../foreign/openpgm/@pgm_basename@/openpgm/pgm/checksum.c +@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzmq_la_CFLAGS) $(CFLAGS) -MT libzmq_la-checksum.lo -MD -MP -MF $(DEPDIR)/libzmq_la-checksum.Tpo -c -o libzmq_la-checksum.lo `test -f '../foreign/openpgm/@pgm_basename@/openpgm/pgm/checksum.c' || echo '$(srcdir)/'`../foreign/openpgm/@pgm_basename@/openpgm/pgm/checksum.c +@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/libzmq_la-checksum.Tpo $(DEPDIR)/libzmq_la-checksum.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='../foreign/openpgm/@pgm_basename@/openpgm/pgm/checksum.c' object='libzmq_la-checksum.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzmq_la_CFLAGS) $(CFLAGS) -c -o libzmq_la-checksum.lo `test -f '../foreign/openpgm/@pgm_basename@/openpgm/pgm/checksum.c' || echo '$(srcdir)/'`../foreign/openpgm/@pgm_basename@/openpgm/pgm/checksum.c + +libzmq_la-reed_solomon.lo: ../foreign/openpgm/@pgm_basename@/openpgm/pgm/reed_solomon.c +@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzmq_la_CFLAGS) $(CFLAGS) -MT libzmq_la-reed_solomon.lo -MD -MP -MF $(DEPDIR)/libzmq_la-reed_solomon.Tpo -c -o libzmq_la-reed_solomon.lo `test -f '../foreign/openpgm/@pgm_basename@/openpgm/pgm/reed_solomon.c' || echo '$(srcdir)/'`../foreign/openpgm/@pgm_basename@/openpgm/pgm/reed_solomon.c +@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/libzmq_la-reed_solomon.Tpo $(DEPDIR)/libzmq_la-reed_solomon.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='../foreign/openpgm/@pgm_basename@/openpgm/pgm/reed_solomon.c' object='libzmq_la-reed_solomon.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzmq_la_CFLAGS) $(CFLAGS) -c -o libzmq_la-reed_solomon.lo `test -f '../foreign/openpgm/@pgm_basename@/openpgm/pgm/reed_solomon.c' || echo '$(srcdir)/'`../foreign/openpgm/@pgm_basename@/openpgm/pgm/reed_solomon.c + +libzmq_la-galois_tables.lo: ../foreign/openpgm/@pgm_basename@/openpgm/pgm/galois_tables.c +@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzmq_la_CFLAGS) $(CFLAGS) -MT libzmq_la-galois_tables.lo -MD -MP -MF $(DEPDIR)/libzmq_la-galois_tables.Tpo -c -o libzmq_la-galois_tables.lo `test -f '../foreign/openpgm/@pgm_basename@/openpgm/pgm/galois_tables.c' || echo '$(srcdir)/'`../foreign/openpgm/@pgm_basename@/openpgm/pgm/galois_tables.c +@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/libzmq_la-galois_tables.Tpo $(DEPDIR)/libzmq_la-galois_tables.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='../foreign/openpgm/@pgm_basename@/openpgm/pgm/galois_tables.c' object='libzmq_la-galois_tables.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzmq_la_CFLAGS) $(CFLAGS) -c -o libzmq_la-galois_tables.lo `test -f '../foreign/openpgm/@pgm_basename@/openpgm/pgm/galois_tables.c' || echo '$(srcdir)/'`../foreign/openpgm/@pgm_basename@/openpgm/pgm/galois_tables.c + +libzmq_la-wsastrerror.lo: ../foreign/openpgm/@pgm_basename@/openpgm/pgm/wsastrerror.c +@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzmq_la_CFLAGS) $(CFLAGS) -MT libzmq_la-wsastrerror.lo -MD -MP -MF $(DEPDIR)/libzmq_la-wsastrerror.Tpo -c -o libzmq_la-wsastrerror.lo `test -f '../foreign/openpgm/@pgm_basename@/openpgm/pgm/wsastrerror.c' || echo '$(srcdir)/'`../foreign/openpgm/@pgm_basename@/openpgm/pgm/wsastrerror.c +@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/libzmq_la-wsastrerror.Tpo $(DEPDIR)/libzmq_la-wsastrerror.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='../foreign/openpgm/@pgm_basename@/openpgm/pgm/wsastrerror.c' object='libzmq_la-wsastrerror.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzmq_la_CFLAGS) $(CFLAGS) -c -o libzmq_la-wsastrerror.lo `test -f '../foreign/openpgm/@pgm_basename@/openpgm/pgm/wsastrerror.c' || echo '$(srcdir)/'`../foreign/openpgm/@pgm_basename@/openpgm/pgm/wsastrerror.c + +libzmq_la-glib-compat.lo: ../foreign/openpgm/@pgm_basename@/openpgm/pgm/glib-compat.c +@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzmq_la_CFLAGS) $(CFLAGS) -MT libzmq_la-glib-compat.lo -MD -MP -MF $(DEPDIR)/libzmq_la-glib-compat.Tpo -c -o libzmq_la-glib-compat.lo `test -f '../foreign/openpgm/@pgm_basename@/openpgm/pgm/glib-compat.c' || echo '$(srcdir)/'`../foreign/openpgm/@pgm_basename@/openpgm/pgm/glib-compat.c +@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/libzmq_la-glib-compat.Tpo $(DEPDIR)/libzmq_la-glib-compat.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='../foreign/openpgm/@pgm_basename@/openpgm/pgm/glib-compat.c' object='libzmq_la-glib-compat.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzmq_la_CFLAGS) $(CFLAGS) -c -o libzmq_la-glib-compat.lo `test -f '../foreign/openpgm/@pgm_basename@/openpgm/pgm/glib-compat.c' || echo '$(srcdir)/'`../foreign/openpgm/@pgm_basename@/openpgm/pgm/glib-compat.c + +libzmq_la-backtrace.lo: ../foreign/openpgm/@pgm_basename@/openpgm/pgm/backtrace.c +@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzmq_la_CFLAGS) $(CFLAGS) -MT libzmq_la-backtrace.lo -MD -MP -MF $(DEPDIR)/libzmq_la-backtrace.Tpo -c -o libzmq_la-backtrace.lo `test -f '../foreign/openpgm/@pgm_basename@/openpgm/pgm/backtrace.c' || echo '$(srcdir)/'`../foreign/openpgm/@pgm_basename@/openpgm/pgm/backtrace.c +@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/libzmq_la-backtrace.Tpo $(DEPDIR)/libzmq_la-backtrace.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='../foreign/openpgm/@pgm_basename@/openpgm/pgm/backtrace.c' object='libzmq_la-backtrace.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzmq_la_CFLAGS) $(CFLAGS) -c -o libzmq_la-backtrace.lo `test -f '../foreign/openpgm/@pgm_basename@/openpgm/pgm/backtrace.c' || echo '$(srcdir)/'`../foreign/openpgm/@pgm_basename@/openpgm/pgm/backtrace.c + +libzmq_la-log.lo: ../foreign/openpgm/@pgm_basename@/openpgm/pgm/log.c +@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzmq_la_CFLAGS) $(CFLAGS) -MT libzmq_la-log.lo -MD -MP -MF $(DEPDIR)/libzmq_la-log.Tpo -c -o libzmq_la-log.lo `test -f '../foreign/openpgm/@pgm_basename@/openpgm/pgm/log.c' || echo '$(srcdir)/'`../foreign/openpgm/@pgm_basename@/openpgm/pgm/log.c +@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/libzmq_la-log.Tpo $(DEPDIR)/libzmq_la-log.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='../foreign/openpgm/@pgm_basename@/openpgm/pgm/log.c' object='libzmq_la-log.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzmq_la_CFLAGS) $(CFLAGS) -c -o libzmq_la-log.lo `test -f '../foreign/openpgm/@pgm_basename@/openpgm/pgm/log.c' || echo '$(srcdir)/'`../foreign/openpgm/@pgm_basename@/openpgm/pgm/log.c + +libzmq_la-sockaddr.lo: ../foreign/openpgm/@pgm_basename@/openpgm/pgm/sockaddr.c +@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzmq_la_CFLAGS) $(CFLAGS) -MT libzmq_la-sockaddr.lo -MD -MP -MF $(DEPDIR)/libzmq_la-sockaddr.Tpo -c -o libzmq_la-sockaddr.lo `test -f '../foreign/openpgm/@pgm_basename@/openpgm/pgm/sockaddr.c' || echo '$(srcdir)/'`../foreign/openpgm/@pgm_basename@/openpgm/pgm/sockaddr.c +@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/libzmq_la-sockaddr.Tpo $(DEPDIR)/libzmq_la-sockaddr.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='../foreign/openpgm/@pgm_basename@/openpgm/pgm/sockaddr.c' object='libzmq_la-sockaddr.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzmq_la_CFLAGS) $(CFLAGS) -c -o libzmq_la-sockaddr.lo `test -f '../foreign/openpgm/@pgm_basename@/openpgm/pgm/sockaddr.c' || echo '$(srcdir)/'`../foreign/openpgm/@pgm_basename@/openpgm/pgm/sockaddr.c + +libzmq_la-version.lo: ../foreign/openpgm/@pgm_basename@/openpgm/pgm/version.c +@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzmq_la_CFLAGS) $(CFLAGS) -MT libzmq_la-version.lo -MD -MP -MF $(DEPDIR)/libzmq_la-version.Tpo -c -o libzmq_la-version.lo `test -f '../foreign/openpgm/@pgm_basename@/openpgm/pgm/version.c' || echo '$(srcdir)/'`../foreign/openpgm/@pgm_basename@/openpgm/pgm/version.c +@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/libzmq_la-version.Tpo $(DEPDIR)/libzmq_la-version.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='../foreign/openpgm/@pgm_basename@/openpgm/pgm/version.c' object='libzmq_la-version.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzmq_la_CFLAGS) $(CFLAGS) -c -o libzmq_la-version.lo `test -f '../foreign/openpgm/@pgm_basename@/openpgm/pgm/version.c' || echo '$(srcdir)/'`../foreign/openpgm/@pgm_basename@/openpgm/pgm/version.c + +.cpp.o: +@am__fastdepCXX_TRUE@ $(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXXCOMPILE) -c -o $@ $< + +.cpp.obj: +@am__fastdepCXX_TRUE@ $(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` +@am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.cpp.lo: +@am__fastdepCXX_TRUE@ $(LTCXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(LTCXXCOMPILE) -c -o $@ $< + +libzmq_la-app_thread.lo: app_thread.cpp +@am__fastdepCXX_TRUE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzmq_la_CXXFLAGS) $(CXXFLAGS) -MT libzmq_la-app_thread.lo -MD -MP -MF $(DEPDIR)/libzmq_la-app_thread.Tpo -c -o libzmq_la-app_thread.lo `test -f 'app_thread.cpp' || echo '$(srcdir)/'`app_thread.cpp +@am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/libzmq_la-app_thread.Tpo $(DEPDIR)/libzmq_la-app_thread.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='app_thread.cpp' object='libzmq_la-app_thread.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzmq_la_CXXFLAGS) $(CXXFLAGS) -c -o libzmq_la-app_thread.lo `test -f 'app_thread.cpp' || echo '$(srcdir)/'`app_thread.cpp + +libzmq_la-command.lo: command.cpp +@am__fastdepCXX_TRUE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzmq_la_CXXFLAGS) $(CXXFLAGS) -MT libzmq_la-command.lo -MD -MP -MF $(DEPDIR)/libzmq_la-command.Tpo -c -o libzmq_la-command.lo `test -f 'command.cpp' || echo '$(srcdir)/'`command.cpp +@am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/libzmq_la-command.Tpo $(DEPDIR)/libzmq_la-command.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='command.cpp' object='libzmq_la-command.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzmq_la_CXXFLAGS) $(CXXFLAGS) -c -o libzmq_la-command.lo `test -f 'command.cpp' || echo '$(srcdir)/'`command.cpp + +libzmq_la-ctx.lo: ctx.cpp +@am__fastdepCXX_TRUE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzmq_la_CXXFLAGS) $(CXXFLAGS) -MT libzmq_la-ctx.lo -MD -MP -MF $(DEPDIR)/libzmq_la-ctx.Tpo -c -o libzmq_la-ctx.lo `test -f 'ctx.cpp' || echo '$(srcdir)/'`ctx.cpp +@am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/libzmq_la-ctx.Tpo $(DEPDIR)/libzmq_la-ctx.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='ctx.cpp' object='libzmq_la-ctx.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzmq_la_CXXFLAGS) $(CXXFLAGS) -c -o libzmq_la-ctx.lo `test -f 'ctx.cpp' || echo '$(srcdir)/'`ctx.cpp + +libzmq_la-devpoll.lo: devpoll.cpp +@am__fastdepCXX_TRUE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzmq_la_CXXFLAGS) $(CXXFLAGS) -MT libzmq_la-devpoll.lo -MD -MP -MF $(DEPDIR)/libzmq_la-devpoll.Tpo -c -o libzmq_la-devpoll.lo `test -f 'devpoll.cpp' || echo '$(srcdir)/'`devpoll.cpp +@am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/libzmq_la-devpoll.Tpo $(DEPDIR)/libzmq_la-devpoll.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='devpoll.cpp' object='libzmq_la-devpoll.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzmq_la_CXXFLAGS) $(CXXFLAGS) -c -o libzmq_la-devpoll.lo `test -f 'devpoll.cpp' || echo '$(srcdir)/'`devpoll.cpp + +libzmq_la-downstream.lo: downstream.cpp +@am__fastdepCXX_TRUE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzmq_la_CXXFLAGS) $(CXXFLAGS) -MT libzmq_la-downstream.lo -MD -MP -MF $(DEPDIR)/libzmq_la-downstream.Tpo -c -o libzmq_la-downstream.lo `test -f 'downstream.cpp' || echo '$(srcdir)/'`downstream.cpp +@am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/libzmq_la-downstream.Tpo $(DEPDIR)/libzmq_la-downstream.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='downstream.cpp' object='libzmq_la-downstream.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzmq_la_CXXFLAGS) $(CXXFLAGS) -c -o libzmq_la-downstream.lo `test -f 'downstream.cpp' || echo '$(srcdir)/'`downstream.cpp + +libzmq_la-epoll.lo: epoll.cpp +@am__fastdepCXX_TRUE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzmq_la_CXXFLAGS) $(CXXFLAGS) -MT libzmq_la-epoll.lo -MD -MP -MF $(DEPDIR)/libzmq_la-epoll.Tpo -c -o libzmq_la-epoll.lo `test -f 'epoll.cpp' || echo '$(srcdir)/'`epoll.cpp +@am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/libzmq_la-epoll.Tpo $(DEPDIR)/libzmq_la-epoll.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='epoll.cpp' object='libzmq_la-epoll.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzmq_la_CXXFLAGS) $(CXXFLAGS) -c -o libzmq_la-epoll.lo `test -f 'epoll.cpp' || echo '$(srcdir)/'`epoll.cpp + +libzmq_la-err.lo: err.cpp +@am__fastdepCXX_TRUE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzmq_la_CXXFLAGS) $(CXXFLAGS) -MT libzmq_la-err.lo -MD -MP -MF $(DEPDIR)/libzmq_la-err.Tpo -c -o libzmq_la-err.lo `test -f 'err.cpp' || echo '$(srcdir)/'`err.cpp +@am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/libzmq_la-err.Tpo $(DEPDIR)/libzmq_la-err.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='err.cpp' object='libzmq_la-err.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzmq_la_CXXFLAGS) $(CXXFLAGS) -c -o libzmq_la-err.lo `test -f 'err.cpp' || echo '$(srcdir)/'`err.cpp + +libzmq_la-forwarder.lo: forwarder.cpp +@am__fastdepCXX_TRUE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzmq_la_CXXFLAGS) $(CXXFLAGS) -MT libzmq_la-forwarder.lo -MD -MP -MF $(DEPDIR)/libzmq_la-forwarder.Tpo -c -o libzmq_la-forwarder.lo `test -f 'forwarder.cpp' || echo '$(srcdir)/'`forwarder.cpp +@am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/libzmq_la-forwarder.Tpo $(DEPDIR)/libzmq_la-forwarder.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='forwarder.cpp' object='libzmq_la-forwarder.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzmq_la_CXXFLAGS) $(CXXFLAGS) -c -o libzmq_la-forwarder.lo `test -f 'forwarder.cpp' || echo '$(srcdir)/'`forwarder.cpp + +libzmq_la-fq.lo: fq.cpp +@am__fastdepCXX_TRUE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzmq_la_CXXFLAGS) $(CXXFLAGS) -MT libzmq_la-fq.lo -MD -MP -MF $(DEPDIR)/libzmq_la-fq.Tpo -c -o libzmq_la-fq.lo `test -f 'fq.cpp' || echo '$(srcdir)/'`fq.cpp +@am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/libzmq_la-fq.Tpo $(DEPDIR)/libzmq_la-fq.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='fq.cpp' object='libzmq_la-fq.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzmq_la_CXXFLAGS) $(CXXFLAGS) -c -o libzmq_la-fq.lo `test -f 'fq.cpp' || echo '$(srcdir)/'`fq.cpp + +libzmq_la-io_object.lo: io_object.cpp +@am__fastdepCXX_TRUE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzmq_la_CXXFLAGS) $(CXXFLAGS) -MT libzmq_la-io_object.lo -MD -MP -MF $(DEPDIR)/libzmq_la-io_object.Tpo -c -o libzmq_la-io_object.lo `test -f 'io_object.cpp' || echo '$(srcdir)/'`io_object.cpp +@am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/libzmq_la-io_object.Tpo $(DEPDIR)/libzmq_la-io_object.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='io_object.cpp' object='libzmq_la-io_object.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzmq_la_CXXFLAGS) $(CXXFLAGS) -c -o libzmq_la-io_object.lo `test -f 'io_object.cpp' || echo '$(srcdir)/'`io_object.cpp + +libzmq_la-io_thread.lo: io_thread.cpp +@am__fastdepCXX_TRUE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzmq_la_CXXFLAGS) $(CXXFLAGS) -MT libzmq_la-io_thread.lo -MD -MP -MF $(DEPDIR)/libzmq_la-io_thread.Tpo -c -o libzmq_la-io_thread.lo `test -f 'io_thread.cpp' || echo '$(srcdir)/'`io_thread.cpp +@am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/libzmq_la-io_thread.Tpo $(DEPDIR)/libzmq_la-io_thread.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='io_thread.cpp' object='libzmq_la-io_thread.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzmq_la_CXXFLAGS) $(CXXFLAGS) -c -o libzmq_la-io_thread.lo `test -f 'io_thread.cpp' || echo '$(srcdir)/'`io_thread.cpp + +libzmq_la-ip.lo: ip.cpp +@am__fastdepCXX_TRUE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzmq_la_CXXFLAGS) $(CXXFLAGS) -MT libzmq_la-ip.lo -MD -MP -MF $(DEPDIR)/libzmq_la-ip.Tpo -c -o libzmq_la-ip.lo `test -f 'ip.cpp' || echo '$(srcdir)/'`ip.cpp +@am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/libzmq_la-ip.Tpo $(DEPDIR)/libzmq_la-ip.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='ip.cpp' object='libzmq_la-ip.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzmq_la_CXXFLAGS) $(CXXFLAGS) -c -o libzmq_la-ip.lo `test -f 'ip.cpp' || echo '$(srcdir)/'`ip.cpp + +libzmq_la-kqueue.lo: kqueue.cpp +@am__fastdepCXX_TRUE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzmq_la_CXXFLAGS) $(CXXFLAGS) -MT libzmq_la-kqueue.lo -MD -MP -MF $(DEPDIR)/libzmq_la-kqueue.Tpo -c -o libzmq_la-kqueue.lo `test -f 'kqueue.cpp' || echo '$(srcdir)/'`kqueue.cpp +@am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/libzmq_la-kqueue.Tpo $(DEPDIR)/libzmq_la-kqueue.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='kqueue.cpp' object='libzmq_la-kqueue.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzmq_la_CXXFLAGS) $(CXXFLAGS) -c -o libzmq_la-kqueue.lo `test -f 'kqueue.cpp' || echo '$(srcdir)/'`kqueue.cpp + +libzmq_la-lb.lo: lb.cpp +@am__fastdepCXX_TRUE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzmq_la_CXXFLAGS) $(CXXFLAGS) -MT libzmq_la-lb.lo -MD -MP -MF $(DEPDIR)/libzmq_la-lb.Tpo -c -o libzmq_la-lb.lo `test -f 'lb.cpp' || echo '$(srcdir)/'`lb.cpp +@am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/libzmq_la-lb.Tpo $(DEPDIR)/libzmq_la-lb.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='lb.cpp' object='libzmq_la-lb.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzmq_la_CXXFLAGS) $(CXXFLAGS) -c -o libzmq_la-lb.lo `test -f 'lb.cpp' || echo '$(srcdir)/'`lb.cpp + +libzmq_la-object.lo: object.cpp +@am__fastdepCXX_TRUE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzmq_la_CXXFLAGS) $(CXXFLAGS) -MT libzmq_la-object.lo -MD -MP -MF $(DEPDIR)/libzmq_la-object.Tpo -c -o libzmq_la-object.lo `test -f 'object.cpp' || echo '$(srcdir)/'`object.cpp +@am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/libzmq_la-object.Tpo $(DEPDIR)/libzmq_la-object.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='object.cpp' object='libzmq_la-object.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzmq_la_CXXFLAGS) $(CXXFLAGS) -c -o libzmq_la-object.lo `test -f 'object.cpp' || echo '$(srcdir)/'`object.cpp + +libzmq_la-options.lo: options.cpp +@am__fastdepCXX_TRUE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzmq_la_CXXFLAGS) $(CXXFLAGS) -MT libzmq_la-options.lo -MD -MP -MF $(DEPDIR)/libzmq_la-options.Tpo -c -o libzmq_la-options.lo `test -f 'options.cpp' || echo '$(srcdir)/'`options.cpp +@am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/libzmq_la-options.Tpo $(DEPDIR)/libzmq_la-options.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='options.cpp' object='libzmq_la-options.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzmq_la_CXXFLAGS) $(CXXFLAGS) -c -o libzmq_la-options.lo `test -f 'options.cpp' || echo '$(srcdir)/'`options.cpp + +libzmq_la-owned.lo: owned.cpp +@am__fastdepCXX_TRUE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzmq_la_CXXFLAGS) $(CXXFLAGS) -MT libzmq_la-owned.lo -MD -MP -MF $(DEPDIR)/libzmq_la-owned.Tpo -c -o libzmq_la-owned.lo `test -f 'owned.cpp' || echo '$(srcdir)/'`owned.cpp +@am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/libzmq_la-owned.Tpo $(DEPDIR)/libzmq_la-owned.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='owned.cpp' object='libzmq_la-owned.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzmq_la_CXXFLAGS) $(CXXFLAGS) -c -o libzmq_la-owned.lo `test -f 'owned.cpp' || echo '$(srcdir)/'`owned.cpp + +libzmq_la-pgm_receiver.lo: pgm_receiver.cpp +@am__fastdepCXX_TRUE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzmq_la_CXXFLAGS) $(CXXFLAGS) -MT libzmq_la-pgm_receiver.lo -MD -MP -MF $(DEPDIR)/libzmq_la-pgm_receiver.Tpo -c -o libzmq_la-pgm_receiver.lo `test -f 'pgm_receiver.cpp' || echo '$(srcdir)/'`pgm_receiver.cpp +@am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/libzmq_la-pgm_receiver.Tpo $(DEPDIR)/libzmq_la-pgm_receiver.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='pgm_receiver.cpp' object='libzmq_la-pgm_receiver.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzmq_la_CXXFLAGS) $(CXXFLAGS) -c -o libzmq_la-pgm_receiver.lo `test -f 'pgm_receiver.cpp' || echo '$(srcdir)/'`pgm_receiver.cpp + +libzmq_la-pgm_sender.lo: pgm_sender.cpp +@am__fastdepCXX_TRUE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzmq_la_CXXFLAGS) $(CXXFLAGS) -MT libzmq_la-pgm_sender.lo -MD -MP -MF $(DEPDIR)/libzmq_la-pgm_sender.Tpo -c -o libzmq_la-pgm_sender.lo `test -f 'pgm_sender.cpp' || echo '$(srcdir)/'`pgm_sender.cpp +@am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/libzmq_la-pgm_sender.Tpo $(DEPDIR)/libzmq_la-pgm_sender.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='pgm_sender.cpp' object='libzmq_la-pgm_sender.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzmq_la_CXXFLAGS) $(CXXFLAGS) -c -o libzmq_la-pgm_sender.lo `test -f 'pgm_sender.cpp' || echo '$(srcdir)/'`pgm_sender.cpp + +libzmq_la-pgm_socket.lo: pgm_socket.cpp +@am__fastdepCXX_TRUE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzmq_la_CXXFLAGS) $(CXXFLAGS) -MT libzmq_la-pgm_socket.lo -MD -MP -MF $(DEPDIR)/libzmq_la-pgm_socket.Tpo -c -o libzmq_la-pgm_socket.lo `test -f 'pgm_socket.cpp' || echo '$(srcdir)/'`pgm_socket.cpp +@am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/libzmq_la-pgm_socket.Tpo $(DEPDIR)/libzmq_la-pgm_socket.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='pgm_socket.cpp' object='libzmq_la-pgm_socket.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzmq_la_CXXFLAGS) $(CXXFLAGS) -c -o libzmq_la-pgm_socket.lo `test -f 'pgm_socket.cpp' || echo '$(srcdir)/'`pgm_socket.cpp + +libzmq_la-pair.lo: pair.cpp +@am__fastdepCXX_TRUE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzmq_la_CXXFLAGS) $(CXXFLAGS) -MT libzmq_la-pair.lo -MD -MP -MF $(DEPDIR)/libzmq_la-pair.Tpo -c -o libzmq_la-pair.lo `test -f 'pair.cpp' || echo '$(srcdir)/'`pair.cpp +@am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/libzmq_la-pair.Tpo $(DEPDIR)/libzmq_la-pair.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='pair.cpp' object='libzmq_la-pair.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzmq_la_CXXFLAGS) $(CXXFLAGS) -c -o libzmq_la-pair.lo `test -f 'pair.cpp' || echo '$(srcdir)/'`pair.cpp + +libzmq_la-prefix_tree.lo: prefix_tree.cpp +@am__fastdepCXX_TRUE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzmq_la_CXXFLAGS) $(CXXFLAGS) -MT libzmq_la-prefix_tree.lo -MD -MP -MF $(DEPDIR)/libzmq_la-prefix_tree.Tpo -c -o libzmq_la-prefix_tree.lo `test -f 'prefix_tree.cpp' || echo '$(srcdir)/'`prefix_tree.cpp +@am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/libzmq_la-prefix_tree.Tpo $(DEPDIR)/libzmq_la-prefix_tree.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='prefix_tree.cpp' object='libzmq_la-prefix_tree.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzmq_la_CXXFLAGS) $(CXXFLAGS) -c -o libzmq_la-prefix_tree.lo `test -f 'prefix_tree.cpp' || echo '$(srcdir)/'`prefix_tree.cpp + +libzmq_la-pipe.lo: pipe.cpp +@am__fastdepCXX_TRUE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzmq_la_CXXFLAGS) $(CXXFLAGS) -MT libzmq_la-pipe.lo -MD -MP -MF $(DEPDIR)/libzmq_la-pipe.Tpo -c -o libzmq_la-pipe.lo `test -f 'pipe.cpp' || echo '$(srcdir)/'`pipe.cpp +@am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/libzmq_la-pipe.Tpo $(DEPDIR)/libzmq_la-pipe.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='pipe.cpp' object='libzmq_la-pipe.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzmq_la_CXXFLAGS) $(CXXFLAGS) -c -o libzmq_la-pipe.lo `test -f 'pipe.cpp' || echo '$(srcdir)/'`pipe.cpp + +libzmq_la-poll.lo: poll.cpp +@am__fastdepCXX_TRUE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzmq_la_CXXFLAGS) $(CXXFLAGS) -MT libzmq_la-poll.lo -MD -MP -MF $(DEPDIR)/libzmq_la-poll.Tpo -c -o libzmq_la-poll.lo `test -f 'poll.cpp' || echo '$(srcdir)/'`poll.cpp +@am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/libzmq_la-poll.Tpo $(DEPDIR)/libzmq_la-poll.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='poll.cpp' object='libzmq_la-poll.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzmq_la_CXXFLAGS) $(CXXFLAGS) -c -o libzmq_la-poll.lo `test -f 'poll.cpp' || echo '$(srcdir)/'`poll.cpp + +libzmq_la-pub.lo: pub.cpp +@am__fastdepCXX_TRUE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzmq_la_CXXFLAGS) $(CXXFLAGS) -MT libzmq_la-pub.lo -MD -MP -MF $(DEPDIR)/libzmq_la-pub.Tpo -c -o libzmq_la-pub.lo `test -f 'pub.cpp' || echo '$(srcdir)/'`pub.cpp +@am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/libzmq_la-pub.Tpo $(DEPDIR)/libzmq_la-pub.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='pub.cpp' object='libzmq_la-pub.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzmq_la_CXXFLAGS) $(CXXFLAGS) -c -o libzmq_la-pub.lo `test -f 'pub.cpp' || echo '$(srcdir)/'`pub.cpp + +libzmq_la-queue.lo: queue.cpp +@am__fastdepCXX_TRUE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzmq_la_CXXFLAGS) $(CXXFLAGS) -MT libzmq_la-queue.lo -MD -MP -MF $(DEPDIR)/libzmq_la-queue.Tpo -c -o libzmq_la-queue.lo `test -f 'queue.cpp' || echo '$(srcdir)/'`queue.cpp +@am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/libzmq_la-queue.Tpo $(DEPDIR)/libzmq_la-queue.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='queue.cpp' object='libzmq_la-queue.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzmq_la_CXXFLAGS) $(CXXFLAGS) -c -o libzmq_la-queue.lo `test -f 'queue.cpp' || echo '$(srcdir)/'`queue.cpp + +libzmq_la-rep.lo: rep.cpp +@am__fastdepCXX_TRUE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzmq_la_CXXFLAGS) $(CXXFLAGS) -MT libzmq_la-rep.lo -MD -MP -MF $(DEPDIR)/libzmq_la-rep.Tpo -c -o libzmq_la-rep.lo `test -f 'rep.cpp' || echo '$(srcdir)/'`rep.cpp +@am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/libzmq_la-rep.Tpo $(DEPDIR)/libzmq_la-rep.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='rep.cpp' object='libzmq_la-rep.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzmq_la_CXXFLAGS) $(CXXFLAGS) -c -o libzmq_la-rep.lo `test -f 'rep.cpp' || echo '$(srcdir)/'`rep.cpp + +libzmq_la-req.lo: req.cpp +@am__fastdepCXX_TRUE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzmq_la_CXXFLAGS) $(CXXFLAGS) -MT libzmq_la-req.lo -MD -MP -MF $(DEPDIR)/libzmq_la-req.Tpo -c -o libzmq_la-req.lo `test -f 'req.cpp' || echo '$(srcdir)/'`req.cpp +@am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/libzmq_la-req.Tpo $(DEPDIR)/libzmq_la-req.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='req.cpp' object='libzmq_la-req.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzmq_la_CXXFLAGS) $(CXXFLAGS) -c -o libzmq_la-req.lo `test -f 'req.cpp' || echo '$(srcdir)/'`req.cpp + +libzmq_la-select.lo: select.cpp +@am__fastdepCXX_TRUE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzmq_la_CXXFLAGS) $(CXXFLAGS) -MT libzmq_la-select.lo -MD -MP -MF $(DEPDIR)/libzmq_la-select.Tpo -c -o libzmq_la-select.lo `test -f 'select.cpp' || echo '$(srcdir)/'`select.cpp +@am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/libzmq_la-select.Tpo $(DEPDIR)/libzmq_la-select.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='select.cpp' object='libzmq_la-select.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzmq_la_CXXFLAGS) $(CXXFLAGS) -c -o libzmq_la-select.lo `test -f 'select.cpp' || echo '$(srcdir)/'`select.cpp + +libzmq_la-session.lo: session.cpp +@am__fastdepCXX_TRUE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzmq_la_CXXFLAGS) $(CXXFLAGS) -MT libzmq_la-session.lo -MD -MP -MF $(DEPDIR)/libzmq_la-session.Tpo -c -o libzmq_la-session.lo `test -f 'session.cpp' || echo '$(srcdir)/'`session.cpp +@am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/libzmq_la-session.Tpo $(DEPDIR)/libzmq_la-session.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='session.cpp' object='libzmq_la-session.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzmq_la_CXXFLAGS) $(CXXFLAGS) -c -o libzmq_la-session.lo `test -f 'session.cpp' || echo '$(srcdir)/'`session.cpp + +libzmq_la-signaler.lo: signaler.cpp +@am__fastdepCXX_TRUE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzmq_la_CXXFLAGS) $(CXXFLAGS) -MT libzmq_la-signaler.lo -MD -MP -MF $(DEPDIR)/libzmq_la-signaler.Tpo -c -o libzmq_la-signaler.lo `test -f 'signaler.cpp' || echo '$(srcdir)/'`signaler.cpp +@am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/libzmq_la-signaler.Tpo $(DEPDIR)/libzmq_la-signaler.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='signaler.cpp' object='libzmq_la-signaler.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzmq_la_CXXFLAGS) $(CXXFLAGS) -c -o libzmq_la-signaler.lo `test -f 'signaler.cpp' || echo '$(srcdir)/'`signaler.cpp + +libzmq_la-socket_base.lo: socket_base.cpp +@am__fastdepCXX_TRUE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzmq_la_CXXFLAGS) $(CXXFLAGS) -MT libzmq_la-socket_base.lo -MD -MP -MF $(DEPDIR)/libzmq_la-socket_base.Tpo -c -o libzmq_la-socket_base.lo `test -f 'socket_base.cpp' || echo '$(srcdir)/'`socket_base.cpp +@am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/libzmq_la-socket_base.Tpo $(DEPDIR)/libzmq_la-socket_base.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='socket_base.cpp' object='libzmq_la-socket_base.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzmq_la_CXXFLAGS) $(CXXFLAGS) -c -o libzmq_la-socket_base.lo `test -f 'socket_base.cpp' || echo '$(srcdir)/'`socket_base.cpp + +libzmq_la-streamer.lo: streamer.cpp +@am__fastdepCXX_TRUE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzmq_la_CXXFLAGS) $(CXXFLAGS) -MT libzmq_la-streamer.lo -MD -MP -MF $(DEPDIR)/libzmq_la-streamer.Tpo -c -o libzmq_la-streamer.lo `test -f 'streamer.cpp' || echo '$(srcdir)/'`streamer.cpp +@am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/libzmq_la-streamer.Tpo $(DEPDIR)/libzmq_la-streamer.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='streamer.cpp' object='libzmq_la-streamer.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzmq_la_CXXFLAGS) $(CXXFLAGS) -c -o libzmq_la-streamer.lo `test -f 'streamer.cpp' || echo '$(srcdir)/'`streamer.cpp + +libzmq_la-sub.lo: sub.cpp +@am__fastdepCXX_TRUE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzmq_la_CXXFLAGS) $(CXXFLAGS) -MT libzmq_la-sub.lo -MD -MP -MF $(DEPDIR)/libzmq_la-sub.Tpo -c -o libzmq_la-sub.lo `test -f 'sub.cpp' || echo '$(srcdir)/'`sub.cpp +@am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/libzmq_la-sub.Tpo $(DEPDIR)/libzmq_la-sub.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='sub.cpp' object='libzmq_la-sub.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzmq_la_CXXFLAGS) $(CXXFLAGS) -c -o libzmq_la-sub.lo `test -f 'sub.cpp' || echo '$(srcdir)/'`sub.cpp + +libzmq_la-tcp_connecter.lo: tcp_connecter.cpp +@am__fastdepCXX_TRUE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzmq_la_CXXFLAGS) $(CXXFLAGS) -MT libzmq_la-tcp_connecter.lo -MD -MP -MF $(DEPDIR)/libzmq_la-tcp_connecter.Tpo -c -o libzmq_la-tcp_connecter.lo `test -f 'tcp_connecter.cpp' || echo '$(srcdir)/'`tcp_connecter.cpp +@am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/libzmq_la-tcp_connecter.Tpo $(DEPDIR)/libzmq_la-tcp_connecter.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='tcp_connecter.cpp' object='libzmq_la-tcp_connecter.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzmq_la_CXXFLAGS) $(CXXFLAGS) -c -o libzmq_la-tcp_connecter.lo `test -f 'tcp_connecter.cpp' || echo '$(srcdir)/'`tcp_connecter.cpp + +libzmq_la-tcp_listener.lo: tcp_listener.cpp +@am__fastdepCXX_TRUE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzmq_la_CXXFLAGS) $(CXXFLAGS) -MT libzmq_la-tcp_listener.lo -MD -MP -MF $(DEPDIR)/libzmq_la-tcp_listener.Tpo -c -o libzmq_la-tcp_listener.lo `test -f 'tcp_listener.cpp' || echo '$(srcdir)/'`tcp_listener.cpp +@am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/libzmq_la-tcp_listener.Tpo $(DEPDIR)/libzmq_la-tcp_listener.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='tcp_listener.cpp' object='libzmq_la-tcp_listener.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzmq_la_CXXFLAGS) $(CXXFLAGS) -c -o libzmq_la-tcp_listener.lo `test -f 'tcp_listener.cpp' || echo '$(srcdir)/'`tcp_listener.cpp + +libzmq_la-tcp_socket.lo: tcp_socket.cpp +@am__fastdepCXX_TRUE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzmq_la_CXXFLAGS) $(CXXFLAGS) -MT libzmq_la-tcp_socket.lo -MD -MP -MF $(DEPDIR)/libzmq_la-tcp_socket.Tpo -c -o libzmq_la-tcp_socket.lo `test -f 'tcp_socket.cpp' || echo '$(srcdir)/'`tcp_socket.cpp +@am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/libzmq_la-tcp_socket.Tpo $(DEPDIR)/libzmq_la-tcp_socket.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='tcp_socket.cpp' object='libzmq_la-tcp_socket.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzmq_la_CXXFLAGS) $(CXXFLAGS) -c -o libzmq_la-tcp_socket.lo `test -f 'tcp_socket.cpp' || echo '$(srcdir)/'`tcp_socket.cpp + +libzmq_la-thread.lo: thread.cpp +@am__fastdepCXX_TRUE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzmq_la_CXXFLAGS) $(CXXFLAGS) -MT libzmq_la-thread.lo -MD -MP -MF $(DEPDIR)/libzmq_la-thread.Tpo -c -o libzmq_la-thread.lo `test -f 'thread.cpp' || echo '$(srcdir)/'`thread.cpp +@am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/libzmq_la-thread.Tpo $(DEPDIR)/libzmq_la-thread.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='thread.cpp' object='libzmq_la-thread.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzmq_la_CXXFLAGS) $(CXXFLAGS) -c -o libzmq_la-thread.lo `test -f 'thread.cpp' || echo '$(srcdir)/'`thread.cpp + +libzmq_la-upstream.lo: upstream.cpp +@am__fastdepCXX_TRUE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzmq_la_CXXFLAGS) $(CXXFLAGS) -MT libzmq_la-upstream.lo -MD -MP -MF $(DEPDIR)/libzmq_la-upstream.Tpo -c -o libzmq_la-upstream.lo `test -f 'upstream.cpp' || echo '$(srcdir)/'`upstream.cpp +@am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/libzmq_la-upstream.Tpo $(DEPDIR)/libzmq_la-upstream.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='upstream.cpp' object='libzmq_la-upstream.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzmq_la_CXXFLAGS) $(CXXFLAGS) -c -o libzmq_la-upstream.lo `test -f 'upstream.cpp' || echo '$(srcdir)/'`upstream.cpp + +libzmq_la-uuid.lo: uuid.cpp +@am__fastdepCXX_TRUE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzmq_la_CXXFLAGS) $(CXXFLAGS) -MT libzmq_la-uuid.lo -MD -MP -MF $(DEPDIR)/libzmq_la-uuid.Tpo -c -o libzmq_la-uuid.lo `test -f 'uuid.cpp' || echo '$(srcdir)/'`uuid.cpp +@am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/libzmq_la-uuid.Tpo $(DEPDIR)/libzmq_la-uuid.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='uuid.cpp' object='libzmq_la-uuid.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzmq_la_CXXFLAGS) $(CXXFLAGS) -c -o libzmq_la-uuid.lo `test -f 'uuid.cpp' || echo '$(srcdir)/'`uuid.cpp + +libzmq_la-xrep.lo: xrep.cpp +@am__fastdepCXX_TRUE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzmq_la_CXXFLAGS) $(CXXFLAGS) -MT libzmq_la-xrep.lo -MD -MP -MF $(DEPDIR)/libzmq_la-xrep.Tpo -c -o libzmq_la-xrep.lo `test -f 'xrep.cpp' || echo '$(srcdir)/'`xrep.cpp +@am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/libzmq_la-xrep.Tpo $(DEPDIR)/libzmq_la-xrep.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='xrep.cpp' object='libzmq_la-xrep.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzmq_la_CXXFLAGS) $(CXXFLAGS) -c -o libzmq_la-xrep.lo `test -f 'xrep.cpp' || echo '$(srcdir)/'`xrep.cpp + +libzmq_la-xreq.lo: xreq.cpp +@am__fastdepCXX_TRUE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzmq_la_CXXFLAGS) $(CXXFLAGS) -MT libzmq_la-xreq.lo -MD -MP -MF $(DEPDIR)/libzmq_la-xreq.Tpo -c -o libzmq_la-xreq.lo `test -f 'xreq.cpp' || echo '$(srcdir)/'`xreq.cpp +@am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/libzmq_la-xreq.Tpo $(DEPDIR)/libzmq_la-xreq.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='xreq.cpp' object='libzmq_la-xreq.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzmq_la_CXXFLAGS) $(CXXFLAGS) -c -o libzmq_la-xreq.lo `test -f 'xreq.cpp' || echo '$(srcdir)/'`xreq.cpp + +libzmq_la-zmq.lo: zmq.cpp +@am__fastdepCXX_TRUE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzmq_la_CXXFLAGS) $(CXXFLAGS) -MT libzmq_la-zmq.lo -MD -MP -MF $(DEPDIR)/libzmq_la-zmq.Tpo -c -o libzmq_la-zmq.lo `test -f 'zmq.cpp' || echo '$(srcdir)/'`zmq.cpp +@am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/libzmq_la-zmq.Tpo $(DEPDIR)/libzmq_la-zmq.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='zmq.cpp' object='libzmq_la-zmq.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzmq_la_CXXFLAGS) $(CXXFLAGS) -c -o libzmq_la-zmq.lo `test -f 'zmq.cpp' || echo '$(srcdir)/'`zmq.cpp + +libzmq_la-zmq_connecter.lo: zmq_connecter.cpp +@am__fastdepCXX_TRUE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzmq_la_CXXFLAGS) $(CXXFLAGS) -MT libzmq_la-zmq_connecter.lo -MD -MP -MF $(DEPDIR)/libzmq_la-zmq_connecter.Tpo -c -o libzmq_la-zmq_connecter.lo `test -f 'zmq_connecter.cpp' || echo '$(srcdir)/'`zmq_connecter.cpp +@am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/libzmq_la-zmq_connecter.Tpo $(DEPDIR)/libzmq_la-zmq_connecter.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='zmq_connecter.cpp' object='libzmq_la-zmq_connecter.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzmq_la_CXXFLAGS) $(CXXFLAGS) -c -o libzmq_la-zmq_connecter.lo `test -f 'zmq_connecter.cpp' || echo '$(srcdir)/'`zmq_connecter.cpp + +libzmq_la-zmq_decoder.lo: zmq_decoder.cpp +@am__fastdepCXX_TRUE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzmq_la_CXXFLAGS) $(CXXFLAGS) -MT libzmq_la-zmq_decoder.lo -MD -MP -MF $(DEPDIR)/libzmq_la-zmq_decoder.Tpo -c -o libzmq_la-zmq_decoder.lo `test -f 'zmq_decoder.cpp' || echo '$(srcdir)/'`zmq_decoder.cpp +@am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/libzmq_la-zmq_decoder.Tpo $(DEPDIR)/libzmq_la-zmq_decoder.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='zmq_decoder.cpp' object='libzmq_la-zmq_decoder.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzmq_la_CXXFLAGS) $(CXXFLAGS) -c -o libzmq_la-zmq_decoder.lo `test -f 'zmq_decoder.cpp' || echo '$(srcdir)/'`zmq_decoder.cpp + +libzmq_la-zmq_encoder.lo: zmq_encoder.cpp +@am__fastdepCXX_TRUE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzmq_la_CXXFLAGS) $(CXXFLAGS) -MT libzmq_la-zmq_encoder.lo -MD -MP -MF $(DEPDIR)/libzmq_la-zmq_encoder.Tpo -c -o libzmq_la-zmq_encoder.lo `test -f 'zmq_encoder.cpp' || echo '$(srcdir)/'`zmq_encoder.cpp +@am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/libzmq_la-zmq_encoder.Tpo $(DEPDIR)/libzmq_la-zmq_encoder.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='zmq_encoder.cpp' object='libzmq_la-zmq_encoder.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzmq_la_CXXFLAGS) $(CXXFLAGS) -c -o libzmq_la-zmq_encoder.lo `test -f 'zmq_encoder.cpp' || echo '$(srcdir)/'`zmq_encoder.cpp + +libzmq_la-zmq_engine.lo: zmq_engine.cpp +@am__fastdepCXX_TRUE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzmq_la_CXXFLAGS) $(CXXFLAGS) -MT libzmq_la-zmq_engine.lo -MD -MP -MF $(DEPDIR)/libzmq_la-zmq_engine.Tpo -c -o libzmq_la-zmq_engine.lo `test -f 'zmq_engine.cpp' || echo '$(srcdir)/'`zmq_engine.cpp +@am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/libzmq_la-zmq_engine.Tpo $(DEPDIR)/libzmq_la-zmq_engine.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='zmq_engine.cpp' object='libzmq_la-zmq_engine.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzmq_la_CXXFLAGS) $(CXXFLAGS) -c -o libzmq_la-zmq_engine.lo `test -f 'zmq_engine.cpp' || echo '$(srcdir)/'`zmq_engine.cpp + +libzmq_la-zmq_init.lo: zmq_init.cpp +@am__fastdepCXX_TRUE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzmq_la_CXXFLAGS) $(CXXFLAGS) -MT libzmq_la-zmq_init.lo -MD -MP -MF $(DEPDIR)/libzmq_la-zmq_init.Tpo -c -o libzmq_la-zmq_init.lo `test -f 'zmq_init.cpp' || echo '$(srcdir)/'`zmq_init.cpp +@am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/libzmq_la-zmq_init.Tpo $(DEPDIR)/libzmq_la-zmq_init.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='zmq_init.cpp' object='libzmq_la-zmq_init.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzmq_la_CXXFLAGS) $(CXXFLAGS) -c -o libzmq_la-zmq_init.lo `test -f 'zmq_init.cpp' || echo '$(srcdir)/'`zmq_init.cpp + +libzmq_la-zmq_listener.lo: zmq_listener.cpp +@am__fastdepCXX_TRUE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzmq_la_CXXFLAGS) $(CXXFLAGS) -MT libzmq_la-zmq_listener.lo -MD -MP -MF $(DEPDIR)/libzmq_la-zmq_listener.Tpo -c -o libzmq_la-zmq_listener.lo `test -f 'zmq_listener.cpp' || echo '$(srcdir)/'`zmq_listener.cpp +@am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/libzmq_la-zmq_listener.Tpo $(DEPDIR)/libzmq_la-zmq_listener.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='zmq_listener.cpp' object='libzmq_la-zmq_listener.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzmq_la_CXXFLAGS) $(CXXFLAGS) -c -o libzmq_la-zmq_listener.lo `test -f 'zmq_listener.cpp' || echo '$(srcdir)/'`zmq_listener.cpp + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs +install-pkgconfigDATA: $(pkgconfig_DATA) + @$(NORMAL_INSTALL) + test -z "$(pkgconfigdir)" || $(MKDIR_P) "$(DESTDIR)$(pkgconfigdir)" + @list='$(pkgconfig_DATA)'; for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + f=$(am__strip_dir) \ + echo " $(pkgconfigDATA_INSTALL) '$$d$$p' '$(DESTDIR)$(pkgconfigdir)/$$f'"; \ + $(pkgconfigDATA_INSTALL) "$$d$$p" "$(DESTDIR)$(pkgconfigdir)/$$f"; \ + done + +uninstall-pkgconfigDATA: + @$(NORMAL_UNINSTALL) + @list='$(pkgconfig_DATA)'; for p in $$list; do \ + f=$(am__strip_dir) \ + echo " rm -f '$(DESTDIR)$(pkgconfigdir)/$$f'"; \ + rm -f "$(DESTDIR)$(pkgconfigdir)/$$f"; \ + done +install-includeHEADERS: $(include_HEADERS) + @$(NORMAL_INSTALL) + test -z "$(includedir)" || $(MKDIR_P) "$(DESTDIR)$(includedir)" + @list='$(include_HEADERS)'; for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + f=$(am__strip_dir) \ + echo " $(includeHEADERS_INSTALL) '$$d$$p' '$(DESTDIR)$(includedir)/$$f'"; \ + $(includeHEADERS_INSTALL) "$$d$$p" "$(DESTDIR)$(includedir)/$$f"; \ + done + +uninstall-includeHEADERS: + @$(NORMAL_UNINSTALL) + @list='$(include_HEADERS)'; for p in $$list; do \ + f=$(am__strip_dir) \ + echo " rm -f '$(DESTDIR)$(includedir)/$$f'"; \ + rm -f "$(DESTDIR)$(includedir)/$$f"; \ + done + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonemtpy = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + mkid -fID $$unique +tags: TAGS + +TAGS: $(HEADERS) $(SOURCES) platform.hpp.in $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) platform.hpp.in $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$tags $$unique; \ + fi +ctags: CTAGS +CTAGS: $(HEADERS) $(SOURCES) platform.hpp.in $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + list='$(SOURCES) $(HEADERS) platform.hpp.in $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + test -z "$(CTAGS_ARGS)$$tags$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$tags $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && cd $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) $$here + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \ + fi; \ + cp -pR $$d/$$file $(distdir)$$dir || exit 1; \ + else \ + test -f $(distdir)/$$file \ + || cp -p $$d/$$file $(distdir)/$$file \ + || exit 1; \ + fi; \ + done + $(MAKE) $(AM_MAKEFLAGS) \ + top_distdir="$(top_distdir)" distdir="$(distdir)" \ + dist-hook +check-am: all-am +check: check-am +all-am: Makefile $(LTLIBRARIES) $(DATA) $(HEADERS) platform.hpp +installdirs: + for dir in "$(DESTDIR)$(libdir)" "$(DESTDIR)$(pkgconfigdir)" "$(DESTDIR)$(includedir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic clean-libLTLIBRARIES clean-libtool \ + mostlyclean-am + +distclean: distclean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-hdr distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +info: info-am + +info-am: + +install-data-am: install-includeHEADERS install-pkgconfigDATA + +install-dvi: install-dvi-am + +install-exec-am: install-libLTLIBRARIES + +install-html: install-html-am + +install-info: install-info-am + +install-man: + +install-pdf: install-pdf-am + +install-ps: install-ps-am + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-includeHEADERS uninstall-libLTLIBRARIES \ + uninstall-pkgconfigDATA + +.MAKE: install-am install-strip + +.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \ + clean-libLTLIBRARIES clean-libtool ctags dist-hook distclean \ + distclean-compile distclean-generic distclean-hdr \ + distclean-libtool distclean-tags distdir dvi dvi-am html \ + html-am info info-am install install-am install-data \ + install-data-am install-dvi install-dvi-am install-exec \ + install-exec-am install-html install-html-am \ + install-includeHEADERS install-info install-info-am \ + install-libLTLIBRARIES install-man install-pdf install-pdf-am \ + install-pkgconfigDATA install-ps install-ps-am install-strip \ + installcheck installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-compile \ + mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ + tags uninstall uninstall-am uninstall-includeHEADERS \ + uninstall-libLTLIBRARIES uninstall-pkgconfigDATA + + +@BUILD_PGM_TRUE@../foreign/openpgm/@pgm_basename@/openpgm/pgm/version.c: ../foreign/openpgm/@pgm_basename@/openpgm/pgm/version_generator.py +@BUILD_PGM_TRUE@ python ../foreign/openpgm/@pgm_basename@/openpgm/pgm/version_generator.py > $@ + +@BUILD_PGM_TRUE@../foreign/openpgm/@pgm_basename@/openpgm/pgm/galois_tables.c: ../foreign/openpgm/@pgm_basename@/openpgm/pgm/galois_generator.pl +@BUILD_PGM_TRUE@ perl ../foreign/openpgm/@pgm_basename@/openpgm/pgm/galois_generator.pl > $@ + +dist-hook: + -rm $(distdir)/platform.hpp +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/src/app_thread.cpp b/src/app_thread.cpp new file mode 100644 index 0000000..fbf034c --- /dev/null +++ b/src/app_thread.cpp @@ -0,0 +1,195 @@ +/* + Copyright (c) 2007-2010 iMatix Corporation + + This file is part of 0MQ. + + 0MQ is free software; you can redistribute it and/or modify it under + the terms of the Lesser GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + 0MQ is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + Lesser GNU General Public License for more details. + + You should have received a copy of the Lesser GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <new> +#include <algorithm> + +#include "../include/zmq.h" + +#include "platform.hpp" + +#if defined ZMQ_HAVE_WINDOWS +#include "windows.hpp" +#if defined _MSC_VER +#include <intrin.h> +#endif +#else +#include <unistd.h> +#endif + +#include "app_thread.hpp" +#include "ctx.hpp" +#include "err.hpp" +#include "pipe.hpp" +#include "config.hpp" +#include "socket_base.hpp" +#include "pair.hpp" +#include "pub.hpp" +#include "sub.hpp" +#include "req.hpp" +#include "rep.hpp" +#include "xreq.hpp" +#include "xrep.hpp" +#include "upstream.hpp" +#include "downstream.hpp" + +// If the RDTSC is available we use it to prevent excessive +// polling for commands. The nice thing here is that it will work on any +// system with x86 architecture and gcc or MSVC compiler. +#if (defined __GNUC__ && (defined __i386__ || defined __x86_64__)) ||\ + (defined _MSC_VER && (defined _M_IX86 || defined _M_X64)) +#define ZMQ_DELAY_COMMANDS +#endif + +zmq::app_thread_t::app_thread_t (ctx_t *ctx_, + uint32_t thread_slot_) : + object_t (ctx_, thread_slot_), + last_processing_time (0), + terminated (false) +{ +} + +zmq::app_thread_t::~app_thread_t () +{ + zmq_assert (sockets.empty ()); +} + +void zmq::app_thread_t::stop () +{ + send_stop (); +} + +zmq::signaler_t *zmq::app_thread_t::get_signaler () +{ + return &signaler; +} + +bool zmq::app_thread_t::process_commands (bool block_, bool throttle_) +{ + bool received; + command_t cmd; + if (block_) { + received = signaler.recv (&cmd, true); + zmq_assert (received); + } + else { + +#if defined ZMQ_DELAY_COMMANDS + // Optimised version of command processing - it doesn't have to check + // for incoming commands each time. It does so only if certain time + // elapsed since last command processing. Command delay varies + // depending on CPU speed: It's ~1ms on 3GHz CPU, ~2ms on 1.5GHz CPU + // etc. The optimisation makes sense only on platforms where getting + // a timestamp is a very cheap operation (tens of nanoseconds). + if (throttle_) { + + // Get timestamp counter. +#if defined __GNUC__ + uint32_t low; + uint32_t high; + __asm__ volatile ("rdtsc" : "=a" (low), "=d" (high)); + uint64_t current_time = (uint64_t) high << 32 | low; +#elif defined _MSC_VER + uint64_t current_time = __rdtsc (); +#else +#error +#endif + + // Check whether certain time have elapsed since last command + // processing. + if (current_time - last_processing_time <= max_command_delay) + return !terminated; + last_processing_time = current_time; + } +#endif + + // Check whether there are any commands pending for this thread. + received = signaler.recv (&cmd, false); + } + + // Process all the commands available at the moment. + while (received) { + cmd.destination->process_command (cmd); + received = signaler.recv (&cmd, false); + } + + return !terminated; +} + +zmq::socket_base_t *zmq::app_thread_t::create_socket (int type_) +{ + socket_base_t *s = NULL; + switch (type_) { + case ZMQ_PAIR: + s = new (std::nothrow) pair_t (this); + break; + case ZMQ_PUB: + s = new (std::nothrow) pub_t (this); + break; + case ZMQ_SUB: + s = new (std::nothrow) sub_t (this); + break; + case ZMQ_REQ: + s = new (std::nothrow) req_t (this); + break; + case ZMQ_REP: + s = new (std::nothrow) rep_t (this); + break; + case ZMQ_XREQ: + s = new (std::nothrow) xreq_t (this); + break; + case ZMQ_XREP: + s = new (std::nothrow) xrep_t (this); + break; + case ZMQ_UPSTREAM: + s = new (std::nothrow) upstream_t (this); + break; + case ZMQ_DOWNSTREAM: + s = new (std::nothrow) downstream_t (this); + break; + default: + if (sockets.empty ()) + get_ctx ()->no_sockets (this); + errno = EINVAL; + return NULL; + } + zmq_assert (s); + + sockets.push_back (s); + + return s; +} + +void zmq::app_thread_t::remove_socket (socket_base_t *socket_) +{ + sockets.erase (socket_); + if (sockets.empty ()) + get_ctx ()->no_sockets (this); +} + +void zmq::app_thread_t::process_stop () +{ + terminated = true; +} + +bool zmq::app_thread_t::is_terminated () +{ + return terminated; +} + diff --git a/src/app_thread.hpp b/src/app_thread.hpp new file mode 100644 index 0000000..f0deaab --- /dev/null +++ b/src/app_thread.hpp @@ -0,0 +1,88 @@ +/* + Copyright (c) 2007-2010 iMatix Corporation + + This file is part of 0MQ. + + 0MQ is free software; you can redistribute it and/or modify it under + the terms of the Lesser GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + 0MQ is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + Lesser GNU General Public License for more details. + + You should have received a copy of the Lesser GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef __ZMQ_APP_THREAD_HPP_INCLUDED__ +#define __ZMQ_APP_THREAD_HPP_INCLUDED__ + +#include <vector> + +#include "stdint.hpp" +#include "object.hpp" +#include "yarray.hpp" +#include "signaler.hpp" + +namespace zmq +{ + + class app_thread_t : public object_t + { + public: + + app_thread_t (class ctx_t *ctx_, uint32_t thread_slot_); + + ~app_thread_t (); + + // Interrupt blocking call if the app thread is stuck in one. + // This function is is called from a different thread! + void stop (); + + // Returns signaler associated with this application thread. + signaler_t *get_signaler (); + + // Processes commands sent to this thread (if any). If 'block' is + // set to true, returns only after at least one command was processed. + // If throttle argument is true, commands are processed at most once + // in a predefined time period. The function returns false is the + // associated context was terminated, true otherwise. + bool process_commands (bool block_, bool throttle_); + + // Create a socket of a specified type. + class socket_base_t *create_socket (int type_); + + // Unregister the socket from the app_thread (called by socket itself). + void remove_socket (class socket_base_t *socket_); + + // Returns true is the associated context was already terminated. + bool is_terminated (); + + private: + + // Command handlers. + void process_stop (); + + // All the sockets created from this application thread. + typedef yarray_t <socket_base_t> sockets_t; + sockets_t sockets; + + // App thread's signaler object. + signaler_t signaler; + + // Timestamp of when commands were processed the last time. + uint64_t last_processing_time; + + // If true, 'stop' command was already received. + bool terminated; + + app_thread_t (const app_thread_t&); + void operator = (const app_thread_t&); + }; + +} + +#endif diff --git a/src/atomic_counter.hpp b/src/atomic_counter.hpp new file mode 100644 index 0000000..b446627 --- /dev/null +++ b/src/atomic_counter.hpp @@ -0,0 +1,163 @@ +/* + Copyright (c) 2007-2010 iMatix Corporation + + This file is part of 0MQ. + + 0MQ is free software; you can redistribute it and/or modify it under + the terms of the Lesser GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + 0MQ is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + Lesser GNU General Public License for more details. + + You should have received a copy of the Lesser GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef __ZMQ_ATOMIC_COUNTER_HPP_INCLUDED__ +#define __ZMQ_ATOMIC_COUNTER_HPP_INCLUDED__ + +#include "stdint.hpp" +#include "platform.hpp" + +#if defined ZMQ_FORCE_MUTEXES +#define ZMQ_ATOMIC_COUNTER_MUTEX +#elif (defined __i386__ || defined __x86_64__) && defined __GNUC__ +#define ZMQ_ATOMIC_COUNTER_X86 +#elif defined ZMQ_HAVE_WINDOWS +#define ZMQ_ATOMIC_COUNTER_WINDOWS +#elif (defined ZMQ_HAVE_SOLARIS || defined ZMQ_HAVE_NETBSD) +#define ZMQ_ATOMIC_COUNTER_ATOMIC_H +#else +#define ZMQ_ATOMIC_COUNTER_MUTEX +#endif + +#if defined ZMQ_ATOMIC_COUNTER_MUTEX +#include "mutex.hpp" +#elif defined ZMQ_ATOMIC_COUNTER_WINDOWS +#include "windows.hpp" +#elif defined ZMQ_ATOMIC_COUNTER_ATOMIC_H +#include <atomic.h> +#endif + +namespace zmq +{ + + // This class represents an integer that can be incremented/decremented + // in atomic fashion. + + class atomic_counter_t + { + public: + + typedef uint32_t integer_t; + + inline atomic_counter_t (integer_t value_ = 0) : + value (value_) + { + } + + inline ~atomic_counter_t () + { + } + + // Set counter value (not thread-safe). + inline void set (integer_t value_) + { + value = value_; + } + + // Atomic addition. Returns the old value. + inline integer_t add (integer_t increment_) + { + integer_t old_value; + +#if defined ZMQ_ATOMIC_COUNTER_WINDOWS + old_value = InterlockedExchangeAdd ((LONG*) &value, increment_); +#elif defined ZMQ_ATOMIC_COUNTER_ATOMIC_H + integer_t new_value = atomic_add_32_nv (&value, increment_); + old_value = new_value - increment_; +#elif defined ZMQ_ATOMIC_COUNTER_X86 + __asm__ volatile ( + "lock; xadd %0, %1 \n\t" + : "=r" (old_value), "=m" (value) + : "0" (increment_), "m" (value) + : "cc", "memory"); +#elif defined ZMQ_ATOMIC_COUNTER_MUTEX + sync.lock (); + old_value = value; + value += increment_; + sync.unlock (); +#else +#error atomic_counter is not implemented for this platform +#endif + return old_value; + } + + // Atomic subtraction. Returns false if the counter drops to zero. + inline bool sub (integer_t decrement) + { +#if defined ZMQ_ATOMIC_COUNTER_WINDOWS + LONG delta = - ((LONG) decrement); + integer_t old = InterlockedExchangeAdd ((LONG*) &value, delta); + return old - decrement != 0; +#elif defined ZMQ_ATOMIC_COUNTER_ATOMIC_H + int32_t delta = - ((int32_t) decrement); + integer_t nv = atomic_add_32_nv (&value, delta); + return nv != 0; +#elif defined ZMQ_ATOMIC_COUNTER_X86 + integer_t oldval = -decrement; + volatile integer_t *val = &value; + __asm__ volatile ("lock; xaddl %0,%1" + : "=r" (oldval), "=m" (*val) + : "0" (oldval), "m" (*val) + : "cc", "memory"); + return oldval != decrement; +#elif defined ZMQ_ATOMIC_COUNTER_MUTEX + sync.lock (); + value -= decrement; + bool result = value ? true : false; + sync.unlock (); + return result; +#else +#error atomic_counter is not implemented for this platform +#endif + } + + inline integer_t get () + { + return value; + } + + private: + + volatile integer_t value; +#if defined ZMQ_ATOMIC_COUNTER_MUTEX + mutex_t sync; +#endif + + atomic_counter_t (const atomic_counter_t&); + void operator = (const atomic_counter_t&); + }; + +} + +// Remove macros local to this file. +#if defined ZMQ_ATOMIC_COUNTER_WINDOWS +#undef ZMQ_ATOMIC_COUNTER_WINDOWS +#endif +#if defined ZMQ_ATOMIC_COUNTER_ATOMIC_H +#undef ZMQ_ATOMIC_COUNTER_ATOMIC_H +#endif +#if defined ZMQ_ATOMIC_COUNTER_X86 +#undef ZMQ_ATOMIC_COUNTER_X86 +#endif +#if defined ZMQ_ATOMIC_COUNTER_MUTEX +#undef ZMQ_ATOMIC_COUNTER_MUTEX +#endif + +#endif + diff --git a/src/atomic_ptr.hpp b/src/atomic_ptr.hpp new file mode 100644 index 0000000..54db64f --- /dev/null +++ b/src/atomic_ptr.hpp @@ -0,0 +1,158 @@ +/* + Copyright (c) 2007-2010 iMatix Corporation + + This file is part of 0MQ. + + 0MQ is free software; you can redistribute it and/or modify it under + the terms of the Lesser GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + 0MQ is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + Lesser GNU General Public License for more details. + + You should have received a copy of the Lesser GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef __ZMQ_ATOMIC_PTR_HPP_INCLUDED__ +#define __ZMQ_ATOMIC_PTR_HPP_INCLUDED__ + +#include "platform.hpp" + +#if defined ZMQ_FORCE_MUTEXES +#define ZMQ_ATOMIC_PTR_MUTEX +#elif (defined __i386__ || defined __x86_64__) && defined __GNUC__ +#define ZMQ_ATOMIC_PTR_X86 +#elif defined ZMQ_HAVE_WINDOWS +#define ZMQ_ATOMIC_PTR_WINDOWS +#elif (defined ZMQ_HAVE_SOLARIS || defined ZMQ_HAVE_NETBSD) +#define ZMQ_ATOMIC_PTR_ATOMIC_H +#else +#define ZMQ_ATOMIC_PTR_MUTEX +#endif + +#if defined ZMQ_ATOMIC_PTR_MUTEX +#include "mutex.hpp" +#elif defined ZMQ_ATOMIC_PTR_WINDOWS +#include "windows.hpp" +#elif defined ZMQ_ATOMIC_PTR_ATOMIC_H +#include <atomic.h> +#endif + +namespace zmq +{ + + // This class encapsulates several atomic operations on pointers. + + template <typename T> class atomic_ptr_t + { + public: + + // Initialise atomic pointer + inline atomic_ptr_t () + { + ptr = NULL; + } + + // Destroy atomic pointer + inline ~atomic_ptr_t () + { + } + + // Set value of atomic pointer in a non-threadsafe way + // Use this function only when you are sure that at most one + // thread is accessing the pointer at the moment. + inline void set (T *ptr_) + { + this->ptr = ptr_; + } + + // Perform atomic 'exchange pointers' operation. Pointer is set + // to the 'val' value. Old value is returned. + inline T *xchg (T *val_) + { +#if defined ZMQ_ATOMIC_PTR_WINDOWS + return (T*) InterlockedExchangePointer ((PVOID*) &ptr, val_); +#elif defined ZMQ_ATOMIC_PTR_ATOMIC_H + return (T*) atomic_swap_ptr (&ptr, val_); +#elif defined ZMQ_ATOMIC_PTR_X86 + T *old; + __asm__ volatile ( + "lock; xchg %0, %2" + : "=r" (old), "=m" (ptr) + : "m" (ptr), "0" (val_)); + return old; +#elif defined ZMQ_ATOMIC_PTR_MUTEX + sync.lock (); + T *old = (T*) ptr; + ptr = val_; + sync.unlock (); + return old; +#else +#error atomic_ptr is not implemented for this platform +#endif + } + + // Perform atomic 'compare and swap' operation on the pointer. + // The pointer is compared to 'cmp' argument and if they are + // equal, its value is set to 'val'. Old value of the pointer + // is returned. + inline T *cas (T *cmp_, T *val_) + { +#if defined ZMQ_ATOMIC_PTR_WINDOWS + return (T*) InterlockedCompareExchangePointer ( + (volatile PVOID*) &ptr, val_, cmp_); +#elif defined ZMQ_ATOMIC_PTR_ATOMIC_H + return (T*) atomic_cas_ptr (&ptr, cmp_, val_); +#elif defined ZMQ_ATOMIC_PTR_X86 + T *old; + __asm__ volatile ( + "lock; cmpxchg %2, %3" + : "=a" (old), "=m" (ptr) + : "r" (val_), "m" (ptr), "0" (cmp_) + : "cc"); + return old; +#elif defined ZMQ_ATOMIC_PTR_MUTEX + sync.lock (); + T *old = (T*) ptr; + if (ptr == cmp_) + ptr = val_; + sync.unlock (); + return old; +#else +#error atomic_ptr is not implemented for this platform +#endif + } + + private: + + volatile T *ptr; +#if defined ZMQ_ATOMIC_PTR_MUTEX + mutex_t sync; +#endif + + atomic_ptr_t (const atomic_ptr_t&); + void operator = (const atomic_ptr_t&); + }; + +} + +// Remove macros local to this file. +#if defined ZMQ_ATOMIC_PTR_WINDOWS +#undef ZMQ_ATOMIC_PTR_WINDOWS +#endif +#if defined ZMQ_ATOMIC_PTR_ATOMIC_H +#undef ZMQ_ATOMIC_PTR_ATOMIC_H +#endif +#if defined ZMQ_ATOMIC_PTR_X86 +#undef ZMQ_ATOMIC_PTR_X86 +#endif +#if defined ZMQ_ATOMIC_PTR_MUTEX +#undef ZMQ_ATOMIC_PTR_MUTEX +#endif + +#endif + diff --git a/src/blob.hpp b/src/blob.hpp new file mode 100644 index 0000000..a4fa8cd --- /dev/null +++ b/src/blob.hpp @@ -0,0 +1,33 @@ +/* + Copyright (c) 2007-2010 iMatix Corporation + + This file is part of 0MQ. + + 0MQ is free software; you can redistribute it and/or modify it under + the terms of the Lesser GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + 0MQ is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + Lesser GNU General Public License for more details. + + You should have received a copy of the Lesser GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef __ZMQ_BLOB_HPP_INCLUDED__ +#define __ZMQ_BLOB_HPP_INCLUDED__ + +#include <string> + +namespace zmq +{ + + // Object to hold dynamically allocated opaque binary data. + typedef std::basic_string <unsigned char> blob_t; + +} + +#endif diff --git a/src/command.cpp b/src/command.cpp new file mode 100644 index 0000000..8bf7ea2 --- /dev/null +++ b/src/command.cpp @@ -0,0 +1,38 @@ +/* + Copyright (c) 2007-2010 iMatix Corporation + + This file is part of 0MQ. + + 0MQ is free software; you can redistribute it and/or modify it under + the terms of the Lesser GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + 0MQ is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + Lesser GNU General Public License for more details. + + You should have received a copy of the Lesser GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <stdlib.h> + +#include "command.hpp" + +void zmq::deallocate_command (command_t *cmd_) +{ + switch (cmd_->type) { + case command_t::attach: + if (cmd_->args.attach.peer_identity) + free (cmd_->args.attach.peer_identity); + break; + case command_t::bind: + if (cmd_->args.bind.peer_identity) + free (cmd_->args.bind.peer_identity); + break; + default: + /* noop */; + } +} diff --git a/src/command.hpp b/src/command.hpp new file mode 100644 index 0000000..3d00cd7 --- /dev/null +++ b/src/command.hpp @@ -0,0 +1,127 @@ +/* + Copyright (c) 2007-2010 iMatix Corporation + + This file is part of 0MQ. + + 0MQ is free software; you can redistribute it and/or modify it under + the terms of the Lesser GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + 0MQ is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + Lesser GNU General Public License for more details. + + You should have received a copy of the Lesser GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef __ZMQ_COMMAND_HPP_INCLUDED__ +#define __ZMQ_COMMAND_HPP_INCLUDED__ + +#include "stdint.hpp" + +namespace zmq +{ + + // This structure defines the commands that can be sent between threads. + + struct command_t + { + // Object to process the command. + class object_t *destination; + + enum type_t + { + stop, + plug, + own, + attach, + bind, + revive, + reader_info, + pipe_term, + pipe_term_ack, + term_req, + term, + term_ack + } type; + + union { + + // Sent to I/O thread to let it know that it should + // terminate itself. + struct { + } stop; + + // Sent to I/O object to make it register with its I/O thread. + struct { + } plug; + + // Sent to socket to let it know about the newly created object. + struct { + class owned_t *object; + } own; + + // Attach the engine to the session. + struct { + struct i_engine *engine; + unsigned char peer_identity_size; + unsigned char *peer_identity; + } attach; + + // Sent from session to socket to establish pipe(s) between them. + // Caller have used inc_seqnum beforehand sending the command. + struct { + class reader_t *in_pipe; + class writer_t *out_pipe; + unsigned char peer_identity_size; + unsigned char *peer_identity; + } bind; + + // Sent by pipe writer to inform dormant pipe reader that there + // are messages in the pipe. + struct { + } revive; + + // Sent by pipe reader to inform pipe writer + // about how many messages it has read so far. + // Used to implement the flow control. + struct { + uint64_t msgs_read; + } reader_info; + + // Sent by pipe reader to pipe writer to ask it to terminate + // its end of the pipe. + struct { + } pipe_term; + + // Pipe writer acknowledges pipe_term command. + struct { + } pipe_term_ack; + + // Sent by I/O object ot the socket to request the shutdown of + // the I/O object. + struct { + class owned_t *object; + } term_req; + + // Sent by socket to I/O object to start its shutdown. + struct { + } term; + + // Sent by I/O object to the socket to acknowledge it has + // shut down. + struct { + } term_ack; + + } args; + }; + + // Function to deallocate dynamically allocated components of the command. + void deallocate_command (command_t *cmd_); + +} + +#endif diff --git a/src/config.hpp b/src/config.hpp new file mode 100644 index 0000000..2c0ac2d --- /dev/null +++ b/src/config.hpp @@ -0,0 +1,86 @@ +/* + Copyright (c) 2007-2010 iMatix Corporation + + This file is part of 0MQ. + + 0MQ is free software; you can redistribute it and/or modify it under + the terms of the Lesser GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + 0MQ is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + Lesser GNU General Public License for more details. + + You should have received a copy of the Lesser GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef __ZMQ_CONFIG_HPP_INCLUDED__ +#define __ZMQ_CONFIG_HPP_INCLUDED__ + +namespace zmq +{ + + // Compile-time settings. + + enum + { + // Maximal number of OS threads that can own 0MQ sockets + // at the same time. + max_app_threads = 512, + + // Number of new messages in message pipe needed to trigger new memory + // allocation. Setting this parameter to 256 decreases the impact of + // memory allocation by approximately 99.6% + message_pipe_granularity = 256, + + // Number of signals that can be read by the signaler + // using a single system call. + signal_buffer_size = 8, + + // Determines how often does socket poll for new commands when it + // still has unprocessed messages to handle. Thus, if it is set to 100, + // socket will process 100 inbound messages before doing the poll. + // If there are no unprocessed messages available, poll is done + // immediately. Decreasing the value trades overall latency for more + // real-time behaviour (less latency peaks). + inbound_poll_rate = 100, + + // Maximal batching size for engines with receiving functionality. + // So, if there are 10 messages that fit into the batch size, all of + // them may be read by a single 'recv' system call, thus avoiding + // unnecessary network stack traversals. + in_batch_size = 8192, + + // Maximal batching size for engines with sending functionality. + // So, if there are 10 messages that fit into the batch size, all of + // them may be written by a single 'send' system call, thus avoiding + // unnecessary network stack traversals. + out_batch_size = 8192, + + // Maximal delta between high and low watermark. + max_wm_delta = 1024, + + // Maximum number of events the I/O thread can process in one go. + max_io_events = 256, + + // Maximal wait time for a timer (milliseconds). + max_timer_period = 100, + + // Maximal delay to process command in API thread (in CPU ticks). + // 3,000,000 ticks equals to 1 - 2 milliseconds on current CPUs. + max_command_delay = 3000000, + + // Maximal number of non-accepted connections that can be held by + // TCP listener object. + tcp_connection_backlog = 10, + + // Maximum transport data unit size for PGM (TPDU). + pgm_max_tpdu = 1500 + }; + +} + +#endif diff --git a/src/ctx.cpp b/src/ctx.cpp new file mode 100644 index 0000000..f0e177d --- /dev/null +++ b/src/ctx.cpp @@ -0,0 +1,316 @@ +/* + Copyright (c) 2007-2010 iMatix Corporation + + This file is part of 0MQ. + + 0MQ is free software; you can redistribute it and/or modify it under + the terms of the Lesser GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + 0MQ is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + Lesser GNU General Public License for more details. + + You should have received a copy of the Lesser GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <new> +#include <string.h> + +#include "../include/zmq.h" + +#include "ctx.hpp" +#include "socket_base.hpp" +#include "app_thread.hpp" +#include "io_thread.hpp" +#include "platform.hpp" +#include "err.hpp" +#include "pipe.hpp" + +#if defined ZMQ_HAVE_WINDOWS +#include "windows.h" +#endif + +zmq::ctx_t::ctx_t (uint32_t io_threads_) : + sockets (0), + terminated (false) +{ +#ifdef ZMQ_HAVE_WINDOWS + // Intialise Windows sockets. Note that WSAStartup can be called multiple + // times given that WSACleanup will be called for each WSAStartup. + WORD version_requested = MAKEWORD (2, 2); + WSADATA wsa_data; + int rc = WSAStartup (version_requested, &wsa_data); + zmq_assert (rc == 0); + zmq_assert (LOBYTE (wsa_data.wVersion) == 2 && + HIBYTE (wsa_data.wVersion) == 2); +#endif + + // Initialise the array of signalers. + signalers_count = max_app_threads + io_threads_; + signalers = (signaler_t**) malloc (sizeof (signaler_t*) * signalers_count); + zmq_assert (signalers); + memset (signalers, 0, sizeof (signaler_t*) * signalers_count); + + // Create I/O thread objects and launch them. + for (uint32_t i = 0; i != io_threads_; i++) { + io_thread_t *io_thread = new (std::nothrow) io_thread_t (this, i); + zmq_assert (io_thread); + io_threads.push_back (io_thread); + signalers [i] = io_thread->get_signaler (); + io_thread->start (); + } +} + +int zmq::ctx_t::term () +{ + // First send stop command to application threads so that any + // blocking calls are interrupted. + for (app_threads_t::size_type i = 0; i != app_threads.size (); i++) + app_threads [i].app_thread->stop (); + + // Then mark context as terminated. + term_sync.lock (); + zmq_assert (!terminated); + terminated = true; + bool destroy = (sockets == 0); + term_sync.unlock (); + + // If there are no sockets open, destroy the context immediately. + if (destroy) + delete this; + + return 0; +} + +zmq::ctx_t::~ctx_t () +{ + // Ask I/O threads to terminate. If stop signal wasn't sent to I/O + // thread subsequent invocation of destructor would hang-up. + for (io_threads_t::size_type i = 0; i != io_threads.size (); i++) + io_threads [i]->stop (); + + // Wait till I/O threads actually terminate. + for (io_threads_t::size_type i = 0; i != io_threads.size (); i++) + delete io_threads [i]; + + // Close all application theads, sockets, io_objects etc. + for (app_threads_t::size_type i = 0; i != app_threads.size (); i++) + delete app_threads [i].app_thread; + + // Deallocate all the orphaned pipes. + while (!pipes.empty ()) + delete *pipes.begin (); + + // Deallocate the array of pointers to signalers. No special work is + // needed as signalers themselves were deallocated with their + // corresponding (app_/io_) thread objects. + free (signalers); + +#ifdef ZMQ_HAVE_WINDOWS + // On Windows, uninitialise socket layer. + int rc = WSACleanup (); + wsa_assert (rc != SOCKET_ERROR); +#endif +} + +zmq::socket_base_t *zmq::ctx_t::create_socket (int type_) +{ + app_threads_sync.lock (); + + // Find whether the calling thread has app_thread_t object associated + // already. At the same time find an unused app_thread_t so that it can + // be used if there's no associated object for the calling thread. + // Check whether thread ID is already assigned. If so, return it. + app_threads_t::size_type unused = app_threads.size (); + app_threads_t::size_type current; + for (current = 0; current != app_threads.size (); current++) { + if (app_threads [current].associated && + thread_t::equal (thread_t::id (), app_threads [current].tid)) + break; + if (!app_threads [current].associated) + unused = current; + } + + // If no app_thread_t is associated with the calling thread, + // associate it with one of the unused app_thread_t objects. + if (current == app_threads.size ()) { + + // If all the existing app_threads are already used, create one more. + if (unused == app_threads.size ()) { + + // If max_app_threads limit was reached, return error. + if (app_threads.size () == max_app_threads) { + app_threads_sync.unlock (); + errno = EMTHREAD; + return NULL; + } + + // Create the new application thread proxy object. + app_thread_info_t info; + info.associated = false; + info.app_thread = new (std::nothrow) app_thread_t (this, + io_threads.size () + app_threads.size ()); + zmq_assert (info.app_thread); + signalers [io_threads.size () + app_threads.size ()] = + info.app_thread->get_signaler (); + app_threads.push_back (info); + } + + // Incidentally, this works both when there is an unused app_thread + // and when a new one is created. + current = unused; + + // Associate the selected app_thread with the OS thread. + app_threads [current].associated = true; + app_threads [current].tid = thread_t::id (); + } + + app_thread_t *thread = app_threads [current].app_thread; + app_threads_sync.unlock (); + + socket_base_t *s = thread->create_socket (type_); + if (!s) + return NULL; + + term_sync.lock (); + sockets++; + term_sync.unlock (); + + return s; +} + +void zmq::ctx_t::destroy_socket () +{ + // If zmq_term was already called and there are no more sockets, + // terminate the whole 0MQ infrastructure. + term_sync.lock (); + zmq_assert (sockets > 0); + sockets--; + bool destroy = (sockets == 0 && terminated); + term_sync.unlock (); + + if (destroy) + delete this; +} + +void zmq::ctx_t::no_sockets (app_thread_t *thread_) +{ + app_threads_sync.lock (); + app_threads_t::size_type i; + for (i = 0; i != app_threads.size (); i++) + if (app_threads [i].app_thread == thread_) { + app_threads [i].associated = false; + break; + } + zmq_assert (i != app_threads.size ()); + app_threads_sync.unlock (); +} + +void zmq::ctx_t::send_command (uint32_t destination_, + const command_t &command_) +{ + signalers [destination_]->send (command_); +} + +bool zmq::ctx_t::recv_command (uint32_t thread_slot_, + command_t *command_, bool block_) +{ + return signalers [thread_slot_]->recv (command_, block_); +} + +zmq::io_thread_t *zmq::ctx_t::choose_io_thread (uint64_t affinity_) +{ + // Find the I/O thread with minimum load. + zmq_assert (io_threads.size () > 0); + int min_load = -1; + io_threads_t::size_type result = 0; + for (io_threads_t::size_type i = 0; i != io_threads.size (); i++) { + if (!affinity_ || (affinity_ & (uint64_t (1) << i))) { + int load = io_threads [i]->get_load (); + if (min_load == -1 || load < min_load) { + min_load = load; + result = i; + } + } + } + zmq_assert (min_load != -1); + return io_threads [result]; +} + +void zmq::ctx_t::register_pipe (class pipe_t *pipe_) +{ + pipes_sync.lock (); + bool inserted = pipes.insert (pipe_).second; + zmq_assert (inserted); + pipes_sync.unlock (); +} + +void zmq::ctx_t::unregister_pipe (class pipe_t *pipe_) +{ + pipes_sync.lock (); + pipes_t::size_type erased = pipes.erase (pipe_); + zmq_assert (erased == 1); + pipes_sync.unlock (); +} + +int zmq::ctx_t::register_endpoint (const char *addr_, + socket_base_t *socket_) +{ + endpoints_sync.lock (); + + bool inserted = endpoints.insert (std::make_pair (std::string (addr_), + socket_)).second; + if (!inserted) { + errno = EADDRINUSE; + endpoints_sync.unlock (); + return -1; + } + + endpoints_sync.unlock (); + return 0; +} + +void zmq::ctx_t::unregister_endpoints (socket_base_t *socket_) +{ + endpoints_sync.lock (); + + endpoints_t::iterator it = endpoints.begin (); + while (it != endpoints.end ()) { + if (it->second == socket_) { + endpoints_t::iterator to_erase = it; + it++; + endpoints.erase (to_erase); + continue; + } + it++; + } + + endpoints_sync.unlock (); +} + +zmq::socket_base_t *zmq::ctx_t::find_endpoint (const char *addr_) +{ + endpoints_sync.lock (); + + endpoints_t::iterator it = endpoints.find (addr_); + if (it == endpoints.end ()) { + endpoints_sync.unlock (); + errno = ECONNREFUSED; + return NULL; + } + socket_base_t *endpoint = it->second; + + // Increment the command sequence number of the peer so that it won't + // get deallocated until "bind" command is issued by the caller. + // The subsequent 'bind' has to be called with inc_seqnum parameter + // set to false, so that the seqnum isn't incremented twice. + endpoint->inc_seqnum (); + + endpoints_sync.unlock (); + return endpoint; +} + diff --git a/src/ctx.hpp b/src/ctx.hpp new file mode 100644 index 0000000..c96a923 --- /dev/null +++ b/src/ctx.hpp @@ -0,0 +1,156 @@ +/* + Copyright (c) 2007-2010 iMatix Corporation + + This file is part of 0MQ. + + 0MQ is free software; you can redistribute it and/or modify it under + the terms of the Lesser GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + 0MQ is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + Lesser GNU General Public License for more details. + + You should have received a copy of the Lesser GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef __ZMQ_CTX_HPP_INCLUDED__ +#define __ZMQ_CTX_HPP_INCLUDED__ + +#include <vector> +#include <set> +#include <map> +#include <string> + +#include "signaler.hpp" +#include "ypipe.hpp" +#include "config.hpp" +#include "mutex.hpp" +#include "stdint.hpp" +#include "thread.hpp" + +namespace zmq +{ + + // Context object encapsulates all the global state associated with + // the library. + + class ctx_t + { + public: + + // Create the context object. The argument specifies the size + // of I/O thread pool to create. + ctx_t (uint32_t io_threads_); + + // This function is called when user invokes zmq_term. If there are + // no more sockets open it'll cause all the infrastructure to be shut + // down. If there are open sockets still, the deallocation happens + // after the last one is closed. + int term (); + + // Create a socket. + class socket_base_t *create_socket (int type_); + + // Destroy a socket. + void destroy_socket (); + + // Called by app_thread_t when it has no more sockets. The function + // should disassociate the object from the current OS thread. + void no_sockets (class app_thread_t *thread_); + + // Send command to the destination thread. + void send_command (uint32_t destination_, const command_t &command_); + + // Receive command from another thread. + bool recv_command (uint32_t thread_slot_, command_t *command_, + bool block_); + + // Returns the I/O thread that is the least busy at the moment. + // Taskset specifies which I/O threads are eligible (0 = all). + class io_thread_t *choose_io_thread (uint64_t taskset_); + + // All pipes are registered with the context so that even the + // orphaned pipes can be deallocated on the terminal shutdown. + void register_pipe (class pipe_t *pipe_); + void unregister_pipe (class pipe_t *pipe_); + + // Management of inproc endpoints. + int register_endpoint (const char *addr_, class socket_base_t *socket_); + void unregister_endpoints (class socket_base_t *socket_); + class socket_base_t *find_endpoint (const char *addr_); + + private: + + ~ctx_t (); + + struct app_thread_info_t + { + // If false, 0MQ application thread is free, there's no associated + // OS thread. + bool associated; + + // ID of the associated OS thread. If 'associated' is false, + // this field contains bogus data. + thread_t::id_t tid; + + // Pointer to the 0MQ application thread object. + class app_thread_t *app_thread; + }; + + // Application threads. + typedef std::vector <app_thread_info_t> app_threads_t; + app_threads_t app_threads; + + // Synchronisation of accesses to shared application thread data. + mutex_t app_threads_sync; + + // I/O threads. + typedef std::vector <class io_thread_t*> io_threads_t; + io_threads_t io_threads; + + // Array of pointers to signalers for both application and I/O threads. + int signalers_count; + signaler_t **signalers; + + // As pipes may reside in orphaned state in particular moments + // of the pipe shutdown process, i.e. neither pipe reader nor + // pipe writer hold reference to the pipe, we have to hold references + // to all pipes in context so that we can deallocate them + // during terminal shutdown even though it conincides with the + // pipe being in the orphaned state. + typedef std::set <class pipe_t*> pipes_t; + pipes_t pipes; + + // Synchronisation of access to the pipes repository. + mutex_t pipes_sync; + + // Number of sockets alive. + int sockets; + + // If true, zmq_term was already called. When last socket is closed + // the whole 0MQ infrastructure should be deallocated. + bool terminated; + + // Synchronisation of access to the termination data (socket count + // and 'terminated' flag). + mutex_t term_sync; + + // List of inproc endpoints within this context. + typedef std::map <std::string, class socket_base_t*> endpoints_t; + endpoints_t endpoints; + + // Synchronisation of access to the list of inproc endpoints. + mutex_t endpoints_sync; + + ctx_t (const ctx_t&); + void operator = (const ctx_t&); + }; + +} + +#endif + diff --git a/src/decoder.hpp b/src/decoder.hpp new file mode 100644 index 0000000..1662bda --- /dev/null +++ b/src/decoder.hpp @@ -0,0 +1,156 @@ +/* + Copyright (c) 2007-2010 iMatix Corporation + + This file is part of 0MQ. + + 0MQ is free software; you can redistribute it and/or modify it under + the terms of the Lesser GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + 0MQ is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + Lesser GNU General Public License for more details. + + You should have received a copy of the Lesser GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef __ZMQ_DECODER_HPP_INCLUDED__ +#define __ZMQ_DECODER_HPP_INCLUDED__ + +#include <stddef.h> +#include <string.h> +#include <stdlib.h> +#include <algorithm> + +#include "err.hpp" + +namespace zmq +{ + + // Helper base class for decoders that know the amount of data to read + // in advance at any moment. Knowing the amount in advance is a property + // of the protocol used. Both AMQP and backend protocol are based on + // size-prefixed paradigm, therefore they are using decoder_t to parse + // the messages. On the other hand, XML-based transports (like XMPP or + // SOAP) don't allow for knowing the size of data to read in advance and + // should use different decoding algorithms. + // + // Decoder implements the state machine that parses the incoming buffer. + // Derived class should implement individual state machine actions. + + template <typename T> class decoder_t + { + public: + + inline decoder_t (size_t bufsize_) : + read_pos (NULL), + to_read (0), + next (NULL), + bufsize (bufsize_) + { + buf = (unsigned char*) malloc (bufsize_); + zmq_assert (buf); + } + + inline ~decoder_t () + { + free (buf); + } + + // Returns a buffer to be filled with binary data. + inline void get_buffer (unsigned char **data_, size_t *size_) + { + // If we are expected to read large message, we'll opt for zero- + // copy, i.e. we'll ask caller to fill the data directly to the + // message. Note that subsequent read(s) are non-blocking, thus + // each single read reads at most SO_RCVBUF bytes at once not + // depending on how large is the chunk returned from here. + // As a consequence, large messages being received won't block + // other engines running in the same I/O thread for excessive + // amounts of time. + if (to_read >= bufsize) { + *data_ = read_pos; + *size_ = to_read; + return; + } + + *data_ = buf; + *size_ = bufsize; + } + + // Processes the data in the buffer previously allocated using + // get_buffer function. size_ argument specifies nemuber of bytes + // actually filled into the buffer. Function returns number of + // bytes actually processed. + inline size_t process_buffer (unsigned char *data_, size_t size_) + { + // In case of zero-copy simply adjust the pointers, no copying + // is required. Also, run the state machine in case all the data + // were processed. + if (data_ == read_pos) { + read_pos += size_; + to_read -= size_; + + while (!to_read) + if (!(static_cast <T*> (this)->*next) ()) + return size_; + return size_; + } + + size_t pos = 0; + while (true) { + + // Try to get more space in the message to fill in. + // If none is available, return. + while (!to_read) + if (!(static_cast <T*> (this)->*next) ()) + return pos; + + // If there are no more data in the buffer, return. + if (pos == size_) + return pos; + + // Copy the data from buffer to the message. + size_t to_copy = std::min (to_read, size_ - pos); + memcpy (read_pos, data_ + pos, to_copy); + read_pos += to_copy; + pos += to_copy; + to_read -= to_copy; + } + } + + protected: + + // Prototype of state machine action. Action should return false if + // it is unable to push the data to the system. + typedef bool (T::*step_t) (); + + // This function should be called from derived class to read data + // from the buffer and schedule next state machine action. + inline void next_step (void *read_pos_, size_t to_read_, + step_t next_) + { + read_pos = (unsigned char*) read_pos_; + to_read = to_read_; + next = next_; + } + + private: + + unsigned char *read_pos; + size_t to_read; + step_t next; + + size_t bufsize; + unsigned char *buf; + + decoder_t (const decoder_t&); + void operator = (const decoder_t&); + }; + +} + +#endif diff --git a/src/devpoll.cpp b/src/devpoll.cpp new file mode 100644 index 0000000..003f465 --- /dev/null +++ b/src/devpoll.cpp @@ -0,0 +1,222 @@ +/* + Copyright (c) 2007-2010 iMatix Corporation + + This file is part of 0MQ. + + 0MQ is free software; you can redistribute it and/or modify it under + the terms of the Lesser GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + 0MQ is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + Lesser GNU General Public License for more details. + + You should have received a copy of the Lesser GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "platform.hpp" + +#if defined ZMQ_HAVE_SOLARIS || defined ZMQ_HAVE_HPUX + +#include <sys/devpoll.h> +#include <sys/time.h> +#include <sys/resource.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/ioctl.h> +#include <fcntl.h> +#include <unistd.h> +#include <limits.h> +#include <algorithm> + +#include "devpoll.hpp" +#include "err.hpp" +#include "config.hpp" +#include "i_poll_events.hpp" + +zmq::devpoll_t::devpoll_t () : + stopping (false) +{ + // Get limit on open files + struct rlimit rl; + int rc = getrlimit (RLIMIT_NOFILE, &rl); + errno_assert (rc != -1); + fd_table.resize (rl.rlim_cur); + + for (rlim_t i = 0; i < rl.rlim_cur; i ++) + fd_table [i].valid = false; + + devpoll_fd = open ("/dev/poll", O_RDWR); + errno_assert (devpoll_fd != -1); +} + +zmq::devpoll_t::~devpoll_t () +{ + worker.stop (); + + // Make sure there are no fds registered on shutdown. + zmq_assert (load.get () == 0); + + close (devpoll_fd); +} + +void zmq::devpoll_t::devpoll_ctl (fd_t fd_, short events_) +{ + struct pollfd pfd = {fd_, events_, 0}; + ssize_t rc = write (devpoll_fd, &pfd, sizeof pfd); + zmq_assert (rc == sizeof pfd); +} + +zmq::devpoll_t::handle_t zmq::devpoll_t::add_fd (fd_t fd_, + i_poll_events *reactor_) +{ + assert (!fd_table [fd_].valid); + + fd_table [fd_].events = 0; + fd_table [fd_].reactor = reactor_; + fd_table [fd_].valid = true; + fd_table [fd_].accepted = false; + + devpoll_ctl (fd_, 0); + pending_list.push_back (fd_); + + // Increase the load metric of the thread. + load.add (1); + + return fd_; +} + +void zmq::devpoll_t::rm_fd (handle_t handle_) +{ + assert (fd_table [handle_].valid); + + devpoll_ctl (handle_, POLLREMOVE); + fd_table [handle_].valid = false; + + // Decrease the load metric of the thread. + load.sub (1); +} + +void zmq::devpoll_t::set_pollin (handle_t handle_) +{ + devpoll_ctl (handle_, POLLREMOVE); + fd_table [handle_].events |= POLLIN; + devpoll_ctl (handle_, fd_table [handle_].events); +} + +void zmq::devpoll_t::reset_pollin (handle_t handle_) +{ + devpoll_ctl (handle_, POLLREMOVE); + fd_table [handle_].events &= ~((short) POLLIN); + devpoll_ctl (handle_, fd_table [handle_].events); +} + +void zmq::devpoll_t::set_pollout (handle_t handle_) +{ + devpoll_ctl (handle_, POLLREMOVE); + fd_table [handle_].events |= POLLOUT; + devpoll_ctl (handle_, fd_table [handle_].events); +} + +void zmq::devpoll_t::reset_pollout (handle_t handle_) +{ + devpoll_ctl (handle_, POLLREMOVE); + fd_table [handle_].events &= ~((short) POLLOUT); + devpoll_ctl (handle_, fd_table [handle_].events); +} + +void zmq::devpoll_t::add_timer (i_poll_events *events_) +{ + timers.push_back (events_); +} + +void zmq::devpoll_t::cancel_timer (i_poll_events *events_) +{ + timers_t::iterator it = std::find (timers.begin (), timers.end (), events_); + if (it != timers.end ()) + timers.erase (it); +} + +int zmq::devpoll_t::get_load () +{ + return load.get (); +} + +void zmq::devpoll_t::start () +{ + worker.start (worker_routine, this); +} + +void zmq::devpoll_t::stop () +{ + stopping = true; +} + +void zmq::devpoll_t::loop () +{ + // According to the poll(7d) man page, we can retrieve + // no more then (OPEN_MAX - 1) events. + int nfds = std::min ((int) max_io_events, OPEN_MAX - 1); + + while (!stopping) { + + struct pollfd ev_buf [max_io_events]; + struct dvpoll poll_req; + + for (pending_list_t::size_type i = 0; i < pending_list.size (); i ++) + fd_table [pending_list [i]].accepted = true; + pending_list.clear (); + + poll_req.dp_fds = &ev_buf [0]; + poll_req.dp_nfds = nfds; + poll_req.dp_timeout = timers.empty () ? -1 : max_timer_period; + + // Wait for events. + int n = ioctl (devpoll_fd, DP_POLL, &poll_req); + if (n == -1 && errno == EINTR) + continue; + errno_assert (n != -1); + + // Handle timer. + if (!n) { + + // Use local list of timers as timer handlers may fill new timers + // into the original array. + timers_t t; + std::swap (timers, t); + + // Trigger all the timers. + for (timers_t::iterator it = t.begin (); it != t.end (); it ++) + (*it)->timer_event (); + + continue; + } + + for (int i = 0; i < n; i ++) { + + fd_entry_t *fd_ptr = &fd_table [ev_buf [i].fd]; + if (!fd_ptr->valid || !fd_ptr->accepted) + continue; + if (ev_buf [i].revents & (POLLERR | POLLHUP)) + fd_ptr->reactor->in_event (); + if (!fd_ptr->valid || !fd_ptr->accepted) + continue; + if (ev_buf [i].revents & POLLOUT) + fd_ptr->reactor->out_event (); + if (!fd_ptr->valid || !fd_ptr->accepted) + continue; + if (ev_buf [i].revents & POLLIN) + fd_ptr->reactor->in_event (); + } + } +} + +void zmq::devpoll_t::worker_routine (void *arg_) +{ + ((devpoll_t*) arg_)->loop (); +} + +#endif diff --git a/src/devpoll.hpp b/src/devpoll.hpp new file mode 100644 index 0000000..019d268 --- /dev/null +++ b/src/devpoll.hpp @@ -0,0 +1,110 @@ +/* + Copyright (c) 2007-2010 iMatix Corporation + + This file is part of 0MQ. + + 0MQ is free software; you can redistribute it and/or modify it under + the terms of the Lesser GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + 0MQ is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + Lesser GNU General Public License for more details. + + You should have received a copy of the Lesser GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef __ZMQ_DEVPOLL_HPP_INCLUDED__ +#define __ZMQ_DEVPOLL_HPP_INCLUDED__ + +#include "platform.hpp" + +#if defined ZMQ_HAVE_SOLARIS || ZMQ_HAVE_HPUX + +#include <vector> + +#include "fd.hpp" +#include "thread.hpp" +#include "atomic_counter.hpp" + +namespace zmq +{ + + // Implements socket polling mechanism using the Solaris-specific + // "/dev/poll" interface. + + class devpoll_t + { + public: + + typedef fd_t handle_t; + + devpoll_t (); + ~devpoll_t (); + + // "poller" concept. + handle_t add_fd (fd_t fd_, struct i_poll_events *events_); + void rm_fd (handle_t handle_); + void set_pollin (handle_t handle_); + void reset_pollin (handle_t handle_); + void set_pollout (handle_t handle_); + void reset_pollout (handle_t handle_); + void add_timer (struct i_poll_events *events_); + void cancel_timer (struct i_poll_events *events_); + int get_load (); + void start (); + void stop (); + + private: + + // Main worker thread routine. + static void worker_routine (void *arg_); + + // Main event loop. + void loop (); + + // File descriptor referring to "/dev/poll" pseudo-device. + fd_t devpoll_fd; + + struct fd_entry_t + { + short events; + struct i_poll_events *reactor; + bool valid; + bool accepted; + }; + + std::vector <fd_entry_t> fd_table; + + typedef std::vector <fd_t> pending_list_t; + pending_list_t pending_list; + + // Pollset manipulation function. + void devpoll_ctl (fd_t fd_, short events_); + + // List of all the engines waiting for the timer event. + typedef std::vector <struct i_poll_events*> timers_t; + timers_t timers; + + // If true, thread is in the process of shutting down. + bool stopping; + + // Handle of the physical thread doing the I/O work. + thread_t worker; + + // Load of the poller. Currently number of file descriptors + // registered with the poller. + atomic_counter_t load; + + devpoll_t (const devpoll_t&); + void operator = (const devpoll_t&); + }; + +} + +#endif + +#endif diff --git a/src/downstream.cpp b/src/downstream.cpp new file mode 100644 index 0000000..4074a9e --- /dev/null +++ b/src/downstream.cpp @@ -0,0 +1,101 @@ +/* + Copyright (c) 2007-2010 iMatix Corporation + + This file is part of 0MQ. + + 0MQ is free software; you can redistribute it and/or modify it under + the terms of the Lesser GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + 0MQ is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + Lesser GNU General Public License for more details. + + You should have received a copy of the Lesser GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "../include/zmq.h" + +#include "downstream.hpp" +#include "err.hpp" +#include "pipe.hpp" + +zmq::downstream_t::downstream_t (class app_thread_t *parent_) : + socket_base_t (parent_) +{ + options.requires_in = false; + options.requires_out = true; +} + +zmq::downstream_t::~downstream_t () +{ +} + +void zmq::downstream_t::xattach_pipes (class reader_t *inpipe_, + class writer_t *outpipe_, const blob_t &peer_identity_) +{ + zmq_assert (!inpipe_ && outpipe_); + lb.attach (outpipe_); +} + +void zmq::downstream_t::xdetach_inpipe (class reader_t *pipe_) +{ + // There are no inpipes, so this function shouldn't be called at all. + zmq_assert (false); +} + +void zmq::downstream_t::xdetach_outpipe (class writer_t *pipe_) +{ + zmq_assert (pipe_); + lb.detach (pipe_); +} + +void zmq::downstream_t::xkill (class reader_t *pipe_) +{ + // There are no inpipes, so this function shouldn't be called at all. + zmq_assert (false); +} + +void zmq::downstream_t::xrevive (class reader_t *pipe_) +{ + // There are no inpipes, so this function shouldn't be called at all. + zmq_assert (false); +} + +void zmq::downstream_t::xrevive (class writer_t *pipe_) +{ + lb.revive (pipe_); +} + +int zmq::downstream_t::xsetsockopt (int option_, const void *optval_, + size_t optvallen_) +{ + // No special option for this socket type. + errno = EINVAL; + return -1; +} + +int zmq::downstream_t::xsend (zmq_msg_t *msg_, int flags_) +{ + return lb.send (msg_, flags_); +} + +int zmq::downstream_t::xrecv (zmq_msg_t *msg_, int flags_) +{ + errno = ENOTSUP; + return -1; +} + +bool zmq::downstream_t::xhas_in () +{ + return false; +} + +bool zmq::downstream_t::xhas_out () +{ + return lb.has_out (); +} + diff --git a/src/downstream.hpp b/src/downstream.hpp new file mode 100644 index 0000000..1306743 --- /dev/null +++ b/src/downstream.hpp @@ -0,0 +1,61 @@ +/* + Copyright (c) 2007-2010 iMatix Corporation + + This file is part of 0MQ. + + 0MQ is free software; you can redistribute it and/or modify it under + the terms of the Lesser GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + 0MQ is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + Lesser GNU General Public License for more details. + + You should have received a copy of the Lesser GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef __ZMQ_DOWNSTREAM_HPP_INCLUDED__ +#define __ZMQ_DOWNSTREAM_HPP_INCLUDED__ + +#include "socket_base.hpp" +#include "lb.hpp" + +namespace zmq +{ + + class downstream_t : public socket_base_t + { + public: + + downstream_t (class app_thread_t *parent_); + ~downstream_t (); + + // Overloads of functions from socket_base_t. + void xattach_pipes (class reader_t *inpipe_, class writer_t *outpipe_, + const blob_t &peer_identity_); + void xdetach_inpipe (class reader_t *pipe_); + void xdetach_outpipe (class writer_t *pipe_); + void xkill (class reader_t *pipe_); + void xrevive (class reader_t *pipe_); + void xrevive (class writer_t *pipe_); + int xsetsockopt (int option_, const void *optval_, size_t optvallen_); + int xsend (zmq_msg_t *msg_, int flags_); + int xrecv (zmq_msg_t *msg_, int flags_); + bool xhas_in (); + bool xhas_out (); + + private: + + // Load balancer managing the outbound pipes. + lb_t lb; + + downstream_t (const downstream_t&); + void operator = (const downstream_t&); + }; + +} + +#endif diff --git a/src/encoder.hpp b/src/encoder.hpp new file mode 100644 index 0000000..10fe912 --- /dev/null +++ b/src/encoder.hpp @@ -0,0 +1,160 @@ +/* + Copyright (c) 2007-2010 iMatix Corporation + + This file is part of 0MQ. + + 0MQ is free software; you can redistribute it and/or modify it under + the terms of the Lesser GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + 0MQ is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + Lesser GNU General Public License for more details. + + You should have received a copy of the Lesser GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef __ZMQ_ENCODER_HPP_INCLUDED__ +#define __ZMQ_ENCODER_HPP_INCLUDED__ + +#include "platform.hpp" +#if defined ZMQ_HAVE_WINDOWS +#include "windows.hpp" +#endif + +#include <stddef.h> +#include <string.h> +#include <stdlib.h> +#include <algorithm> + +#include "err.hpp" + +namespace zmq +{ + + // Helper base class for encoders. It implements the state machine that + // fills the outgoing buffer. Derived classes should implement individual + // state machine actions. + + template <typename T> class encoder_t + { + public: + + inline encoder_t (size_t bufsize_) : + bufsize (bufsize_) + { + buf = (unsigned char*) malloc (bufsize_); + zmq_assert (buf); + } + + inline ~encoder_t () + { + free (buf); + } + + // The function returns a batch of binary data. The data + // are filled to a supplied buffer. If no buffer is supplied (data_ + // points to NULL) decoder object will provide buffer of its own. + // If offset is not NULL, it is filled by offset of the first message + // in the batch.If there's no beginning of a message in the batch, + // offset is set to -1. + inline void get_data (unsigned char **data_, size_t *size_, + int *offset_ = NULL) + { + unsigned char *buffer = !*data_ ? buf : *data_; + size_t buffersize = !*data_ ? bufsize : *size_; + + size_t pos = 0; + if (offset_) + *offset_ = -1; + + while (true) { + + // If there are no more data to return, run the state machine. + // If there are still no data, return what we already have + // in the buffer. + if (!to_write) { + if (!(static_cast <T*> (this)->*next) ()) { + *data_ = buffer; + *size_ = pos; + return; + } + + // If beginning of the message was processed, adjust the + // first-message-offset. + if (beginning) { + if (offset_ && *offset_ == -1) + *offset_ = pos; + beginning = false; + } + } + + // If there are no data in the buffer yet and we are able to + // fill whole buffer in a single go, let's use zero-copy. + // There's no disadvantage to it as we cannot stuck multiple + // messages into the buffer anyway. Note that subsequent + // write(s) are non-blocking, thus each single write writes + // at most SO_SNDBUF bytes at once not depending on how large + // is the chunk returned from here. + // As a consequence, large messages being sent won't block + // other engines running in the same I/O thread for excessive + // amounts of time. + if (!pos && !*data_ && to_write >= buffersize) { + *data_ = write_pos; + *size_ = to_write; + write_pos = NULL; + to_write = 0; + return; + } + + // Copy data to the buffer. If the buffer is full, return. + size_t to_copy = std::min (to_write, buffersize - pos); + memcpy (buffer + pos, write_pos, to_copy); + pos += to_copy; + write_pos += to_copy; + to_write -= to_copy; + if (pos == buffersize) { + *data_ = buffer; + *size_ = pos; + return; + } + } + } + + protected: + + // Prototype of state machine action. + typedef bool (T::*step_t) (); + + // This function should be called from derived class to write the data + // to the buffer and schedule next state machine action. Set beginning + // to true when you are writing first byte of a message. + inline void next_step (void *write_pos_, size_t to_write_, + step_t next_, bool beginning_) + { + write_pos = (unsigned char*) write_pos_; + to_write = to_write_; + next = next_; + beginning = beginning_; + } + + private: + + unsigned char *write_pos; + size_t to_write; + step_t next; + bool beginning; + + size_t bufsize; + unsigned char *buf; + + encoder_t (const encoder_t&); + void operator = (const encoder_t&); + }; + +} + +#endif diff --git a/src/epoll.cpp b/src/epoll.cpp new file mode 100644 index 0000000..e22eb8c --- /dev/null +++ b/src/epoll.cpp @@ -0,0 +1,213 @@ +/* + Copyright (c) 2007-2010 iMatix Corporation + + This file is part of 0MQ. + + 0MQ is free software; you can redistribute it and/or modify it under + the terms of the Lesser GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + 0MQ is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + Lesser GNU General Public License for more details. + + You should have received a copy of the Lesser GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "platform.hpp" + +#ifdef ZMQ_HAVE_LINUX + +#include <sys/epoll.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <algorithm> +#include <new> + +#include "epoll.hpp" +#include "err.hpp" +#include "config.hpp" +#include "i_poll_events.hpp" + +zmq::epoll_t::epoll_t () : + stopping (false) +{ + epoll_fd = epoll_create (1); + errno_assert (epoll_fd != -1); +} + +zmq::epoll_t::~epoll_t () +{ + // Wait till the worker thread exits. + worker.stop (); + + // Make sure there are no fds registered on shutdown. + zmq_assert (load.get () == 0); + + close (epoll_fd); + for (retired_t::iterator it = retired.begin (); it != retired.end (); it ++) + delete *it; +} + +zmq::epoll_t::handle_t zmq::epoll_t::add_fd (fd_t fd_, i_poll_events *events_) +{ + poll_entry_t *pe = new (std::nothrow) poll_entry_t; + zmq_assert (pe != NULL); + + // The memset is not actually needed. It's here to prevent debugging + // tools to complain about using uninitialised memory. + memset (pe, 0, sizeof (poll_entry_t)); + + pe->fd = fd_; + pe->ev.events = 0; + pe->ev.data.ptr = pe; + pe->events = events_; + + int rc = epoll_ctl (epoll_fd, EPOLL_CTL_ADD, fd_, &pe->ev); + errno_assert (rc != -1); + + // Increase the load metric of the thread. + load.add (1); + + return pe; +} + +void zmq::epoll_t::rm_fd (handle_t handle_) +{ + poll_entry_t *pe = (poll_entry_t*) handle_; + int rc = epoll_ctl (epoll_fd, EPOLL_CTL_DEL, pe->fd, &pe->ev); + errno_assert (rc != -1); + pe->fd = retired_fd; + retired.push_back (pe); + + // Decrease the load metric of the thread. + load.sub (1); +} + +void zmq::epoll_t::set_pollin (handle_t handle_) +{ + poll_entry_t *pe = (poll_entry_t*) handle_; + pe->ev.events |= EPOLLIN; + int rc = epoll_ctl (epoll_fd, EPOLL_CTL_MOD, pe->fd, &pe->ev); + errno_assert (rc != -1); +} + +void zmq::epoll_t::reset_pollin (handle_t handle_) +{ + poll_entry_t *pe = (poll_entry_t*) handle_; + pe->ev.events &= ~((short) EPOLLIN); + int rc = epoll_ctl (epoll_fd, EPOLL_CTL_MOD, pe->fd, &pe->ev); + errno_assert (rc != -1); +} + +void zmq::epoll_t::set_pollout (handle_t handle_) +{ + poll_entry_t *pe = (poll_entry_t*) handle_; + pe->ev.events |= EPOLLOUT; + int rc = epoll_ctl (epoll_fd, EPOLL_CTL_MOD, pe->fd, &pe->ev); + errno_assert (rc != -1); +} + +void zmq::epoll_t::reset_pollout (handle_t handle_) +{ + poll_entry_t *pe = (poll_entry_t*) handle_; + pe->ev.events &= ~((short) EPOLLOUT); + int rc = epoll_ctl (epoll_fd, EPOLL_CTL_MOD, pe->fd, &pe->ev); + errno_assert (rc != -1); +} + +void zmq::epoll_t::add_timer (i_poll_events *events_) +{ + timers.push_back (events_); +} + +void zmq::epoll_t::cancel_timer (i_poll_events *events_) +{ + timers_t::iterator it = std::find (timers.begin (), timers.end (), events_); + if (it == timers.end ()) + return; + timers.erase (it); +} + +int zmq::epoll_t::get_load () +{ + return load.get (); +} + +void zmq::epoll_t::start () +{ + worker.start (worker_routine, this); +} + +void zmq::epoll_t::stop () +{ + stopping = true; +} + +void zmq::epoll_t::loop () +{ + epoll_event ev_buf [max_io_events]; + + while (!stopping) { + + // Wait for events. + int n; + while (true) { + n = epoll_wait (epoll_fd, &ev_buf [0], max_io_events, + timers.empty () ? -1 : max_timer_period); + if (!(n == -1 && errno == EINTR)) { + errno_assert (n != -1); + break; + } + } + + // Handle timer. + if (!n) { + + // Use local list of timers as timer handlers may fill new timers + // into the original array. + timers_t t; + std::swap (timers, t); + + // Trigger all the timers. + for (timers_t::iterator it = t.begin (); it != t.end (); it ++) + (*it)->timer_event (); + + continue; + } + + for (int i = 0; i < n; i ++) { + poll_entry_t *pe = ((poll_entry_t*) ev_buf [i].data.ptr); + + if (pe->fd == retired_fd) + continue; + if (ev_buf [i].events & (EPOLLERR | EPOLLHUP)) + pe->events->in_event (); + if (pe->fd == retired_fd) + continue; + if (ev_buf [i].events & EPOLLOUT) + pe->events->out_event (); + if (pe->fd == retired_fd) + continue; + if (ev_buf [i].events & EPOLLIN) + pe->events->in_event (); + } + + // Destroy retired event sources. + for (retired_t::iterator it = retired.begin (); it != retired.end (); + it ++) + delete *it; + retired.clear (); + } +} + +void zmq::epoll_t::worker_routine (void *arg_) +{ + ((epoll_t*) arg_)->loop (); +} + +#endif diff --git a/src/epoll.hpp b/src/epoll.hpp new file mode 100644 index 0000000..38175cb --- /dev/null +++ b/src/epoll.hpp @@ -0,0 +1,106 @@ +/* + Copyright (c) 2007-2010 iMatix Corporation + + This file is part of 0MQ. + + 0MQ is free software; you can redistribute it and/or modify it under + the terms of the Lesser GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + 0MQ is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + Lesser GNU General Public License for more details. + + You should have received a copy of the Lesser GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef __ZMQ_EPOLL_HPP_INCLUDED__ +#define __ZMQ_EPOLL_HPP_INCLUDED__ + +#include "platform.hpp" + +#ifdef ZMQ_HAVE_LINUX + +#include <vector> +#include <sys/epoll.h> + +#include "fd.hpp" +#include "thread.hpp" +#include "atomic_counter.hpp" + +namespace zmq +{ + + // This class implements socket polling mechanism using the Linux-specific + // epoll mechanism. + + class epoll_t + { + public: + + typedef void* handle_t; + + epoll_t (); + ~epoll_t (); + + // "poller" concept. + handle_t add_fd (fd_t fd_, struct i_poll_events *events_); + void rm_fd (handle_t handle_); + void set_pollin (handle_t handle_); + void reset_pollin (handle_t handle_); + void set_pollout (handle_t handle_); + void reset_pollout (handle_t handle_); + void add_timer (struct i_poll_events *events_); + void cancel_timer (struct i_poll_events *events_); + int get_load (); + void start (); + void stop (); + + private: + + // Main worker thread routine. + static void worker_routine (void *arg_); + + // Main event loop. + void loop (); + + // Main epoll file descriptor + fd_t epoll_fd; + + struct poll_entry_t + { + fd_t fd; + epoll_event ev; + struct i_poll_events *events; + }; + + // List of retired event sources. + typedef std::vector <poll_entry_t*> retired_t; + retired_t retired; + + // List of all the engines waiting for the timer event. + typedef std::vector <struct i_poll_events*> timers_t; + timers_t timers; + + // If true, thread is in the process of shutting down. + bool stopping; + + // Handle of the physical thread doing the I/O work. + thread_t worker; + + // Load of the poller. Currently number of file descriptors + // registered with the poller. + atomic_counter_t load; + + epoll_t (const epoll_t&); + void operator = (const epoll_t&); + }; + +} + +#endif + +#endif diff --git a/src/err.cpp b/src/err.cpp new file mode 100644 index 0000000..17a9689 --- /dev/null +++ b/src/err.cpp @@ -0,0 +1,190 @@ +/* + Copyright (c) 2007-2010 iMatix Corporation + + This file is part of 0MQ. + + 0MQ is free software; you can redistribute it and/or modify it under + the terms of the Lesser GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + 0MQ is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + Lesser GNU General Public License for more details. + + You should have received a copy of the Lesser GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "../include/zmq.h" + +#include "err.hpp" +#include "platform.hpp" + +#ifdef ZMQ_HAVE_WINDOWS + +const char *zmq::wsa_error() +{ + int errcode = WSAGetLastError (); + // TODO: This is not a generic way to handle this... + if (errcode == WSAEWOULDBLOCK) + return NULL; + + // TODO: It seems that list of Windows socket errors is longer than this. + // Investigate whether there's a way to convert it into the string + // automatically (wsaError->HRESULT->string?). + return + (errcode == WSABASEERR) ? + "No Error" : + (errcode == WSAEINTR) ? + "Interrupted system call" : + (errcode == WSAEBADF) ? + "Bad file number" : + (errcode == WSAEACCES) ? + "Permission denied" : + (errcode == WSAEFAULT) ? + "Bad address" : + (errcode == WSAEINVAL) ? + "Invalid argument" : + (errcode == WSAEMFILE) ? + "Too many open files" : + (errcode == WSAEWOULDBLOCK) ? + "Operation would block" : + (errcode == WSAEINPROGRESS) ? + "Operation now in progress" : + (errcode == WSAEALREADY) ? + "Operation already in progress" : + (errcode == WSAENOTSOCK) ? + "Socket operation on non-socket" : + (errcode == WSAEDESTADDRREQ) ? + "Destination address required" : + (errcode == WSAEMSGSIZE) ? + "Message too long" : + (errcode == WSAEPROTOTYPE) ? + "Protocol wrong type for socket" : + (errcode == WSAENOPROTOOPT) ? + "Bad protocol option" : + (errcode == WSAEPROTONOSUPPORT) ? + "Protocol not supported" : + (errcode == WSAESOCKTNOSUPPORT) ? + "Socket type not supported" : + (errcode == WSAEOPNOTSUPP) ? + "Operation not supported on socket" : + (errcode == WSAEPFNOSUPPORT) ? + "Protocol family not supported" : + (errcode == WSAEAFNOSUPPORT) ? + "Address family not supported by protocol family" : + (errcode == WSAEADDRINUSE) ? + "Address already in use" : + (errcode == WSAEADDRNOTAVAIL) ? + "Can't assign requested address" : + (errcode == WSAENETDOWN) ? + "Network is down" : + (errcode == WSAENETUNREACH) ? + "Network is unreachable" : + (errcode == WSAENETRESET) ? + "Net dropped connection or reset" : + (errcode == WSAECONNABORTED) ? + "Software caused connection abort" : + (errcode == WSAECONNRESET) ? + "Connection reset by peer" : + (errcode == WSAENOBUFS) ? + "No buffer space available" : + (errcode == WSAEISCONN) ? + "Socket is already connected" : + (errcode == WSAENOTCONN) ? + "Socket is not connected" : + (errcode == WSAESHUTDOWN) ? + "Can't send after socket shutdown" : + (errcode == WSAETOOMANYREFS) ? + "Too many references can't splice" : + (errcode == WSAETIMEDOUT) ? + "Connection timed out" : + (errcode == WSAECONNREFUSED) ? + "Connection refused" : + (errcode == WSAELOOP) ? + "Too many levels of symbolic links" : + (errcode == WSAENAMETOOLONG) ? + "File name too long" : + (errcode == WSAEHOSTDOWN) ? + "Host is down" : + (errcode == WSAEHOSTUNREACH) ? + "No Route to Host" : + (errcode == WSAENOTEMPTY) ? + "Directory not empty" : + (errcode == WSAEPROCLIM) ? + "Too many processes" : + (errcode == WSAEUSERS) ? + "Too many users" : + (errcode == WSAEDQUOT) ? + "Disc Quota Exceeded" : + (errcode == WSAESTALE) ? + "Stale NFS file handle" : + (errcode == WSAEREMOTE) ? + "Too many levels of remote in path" : + (errcode == WSASYSNOTREADY) ? + "Network SubSystem is unavailable" : + (errcode == WSAVERNOTSUPPORTED) ? + "WINSOCK DLL Version out of range" : + (errcode == WSANOTINITIALISED) ? + "Successful WSASTARTUP not yet performed" : + (errcode == WSAHOST_NOT_FOUND) ? + "Host not found" : + (errcode == WSATRY_AGAIN) ? + "Non-Authoritative Host not found" : + (errcode == WSANO_RECOVERY) ? + "Non-Recoverable errors: FORMERR REFUSED NOTIMP" : + (errcode == WSANO_DATA) ? + "Valid name no data record of requested" : + "error not defined"; +} +void zmq::win_error (char *buffer_, size_t buffer_size_) +{ + DWORD errcode = GetLastError (); + DWORD rc = FormatMessageA (FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, NULL, errcode, MAKELANGID(LANG_NEUTRAL, + SUBLANG_DEFAULT), buffer_, buffer_size_, NULL ); + zmq_assert (rc); +} + +void zmq::wsa_error_to_errno () +{ + int errcode = WSAGetLastError (); + switch (errcode) { + case WSAEINPROGRESS: + errno = EAGAIN; + return; + case WSAEBADF: + errno = EBADF; + return; + case WSAEINVAL: + errno = EINVAL; + return; + case WSAEMFILE: + errno = EMFILE; + return; + case WSAEFAULT: + errno = EFAULT; + return; + case WSAEPROTONOSUPPORT: + errno = EPROTONOSUPPORT; + return; + case WSAENOBUFS: + errno = ENOBUFS; + return; + case WSAENETDOWN: + errno = ENETDOWN; + return; + case WSAEADDRINUSE: + errno = EADDRINUSE; + return; + case WSAEADDRNOTAVAIL: + errno = EADDRNOTAVAIL; + return; + default: + wsa_assert (false); + } +} + +#endif diff --git a/src/err.hpp b/src/err.hpp new file mode 100644 index 0000000..2b76569 --- /dev/null +++ b/src/err.hpp @@ -0,0 +1,123 @@ +/* + Copyright (c) 2007-2010 iMatix Corporation + + This file is part of 0MQ. + + 0MQ is free software; you can redistribute it and/or modify it under + the terms of the Lesser GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + 0MQ is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + Lesser GNU General Public License for more details. + + You should have received a copy of the Lesser GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef __ZMQ_ERR_HPP_INCLUDED__ +#define __ZMQ_ERR_HPP_INCLUDED__ + +#include <assert.h> +#include <errno.h> +#include <string.h> +#include <stdlib.h> +#include <stdio.h> + +#include "platform.hpp" +#include "likely.hpp" + +#ifdef ZMQ_HAVE_WINDOWS +#include "windows.hpp" +#else +#include <netdb.h> +#endif + +#ifdef ZMQ_HAVE_WINDOWS + +namespace zmq +{ + + const char *wsa_error (); + void win_error (char *buffer_, size_t buffer_size_); + void wsa_error_to_errno (); + +} + +// Provides convenient way to check WSA-style errors on Windows. +#define wsa_assert(x) \ + do {\ + if (unlikely (!(x))) {\ + const char *errstr = zmq::wsa_error ();\ + if (errstr != NULL) {\ + fprintf (stderr, "Assertion failed: %s (%s:%d)\n", errstr, \ + __FILE__, __LINE__);\ + abort ();\ + }\ + }\ + } while (false) + +// Provides convenient way to check GetLastError-style errors on Windows. +#define win_assert(x) \ + do {\ + if (unlikely (!(x))) {\ + char errstr [256];\ + zmq::win_error (errstr, 256);\ + fprintf (stderr, "Assertion failed: %s (%s:%d)\n", errstr, \ + __FILE__, __LINE__);\ + abort ();\ + }\ + } while (false) + +#endif + +// This macro works in exactly the same way as the normal assert. It is used +// in its stead because standard assert on Win32 in broken - it prints nothing +// when used within the scope of JNI library. +#define zmq_assert(x) \ + do {\ + if (unlikely (!(x))) {\ + fprintf (stderr, "Assertion failed: %s (%s:%d)\n", #x, \ + __FILE__, __LINE__);\ + abort ();\ + }\ + } while (false) + +// Provides convenient way to check for errno-style errors. +#define errno_assert(x) \ + do {\ + if (unlikely (!(x))) {\ + perror (NULL);\ + fprintf (stderr, "%s (%s:%d)\n", #x, __FILE__, __LINE__);\ + abort ();\ + }\ + } while (false) + +// Provides convenient way to check for POSIX errors. +#define posix_assert(x) \ + do {\ + if (unlikely (x)) {\ + fprintf (stderr, "%s (%s:%d)\n", strerror (x), __FILE__, __LINE__);\ + abort ();\ + }\ + } while (false) + +// Provides convenient way to check for errors from getaddrinfo. +#define gai_assert(x) \ + do {\ + if (unlikely (x)) {\ + const char *errstr = gai_strerror (x);\ + fprintf (stderr, "%s (%s:%d)\n", errstr, __FILE__, __LINE__);\ + abort ();\ + }\ + } while (false) + +#endif + +#define zmq_not_implemented() \ + do {\ + fprintf (stderr, "Hic sunt leones (%s:%d)\n", __FILE__, __LINE__);\ + abort ();\ + } while (false) diff --git a/src/fd.hpp b/src/fd.hpp new file mode 100644 index 0000000..6ad8252 --- /dev/null +++ b/src/fd.hpp @@ -0,0 +1,44 @@ +/* + Copyright (c) 2007-2010 iMatix Corporation + + This file is part of 0MQ. + + 0MQ is free software; you can redistribute it and/or modify it under + the terms of the Lesser GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + 0MQ is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + Lesser GNU General Public License for more details. + + You should have received a copy of the Lesser GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef __ZMQ_FD_HPP_INCLUDED__ +#define __ZMQ_FD_HPP_INCLUDED__ + +#include "platform.hpp" + +#ifdef ZMQ_HAVE_WINDOWS +#include "windows.hpp" +#endif + +namespace zmq +{ +#ifdef ZMQ_HAVE_WINDOWS +#if defined _MSC_VER &&_MSC_VER <= 1400 + typedef UINT_PTR fd_t; + enum {retired_fd = (fd_t)(~0)}; +#else + typedef SOCKET fd_t; + enum {retired_fd = INVALID_SOCKET}; +#endif +#else + typedef int fd_t; + enum {retired_fd = -1}; +#endif +} +#endif diff --git a/src/forwarder.cpp b/src/forwarder.cpp new file mode 100644 index 0000000..5aab8f2 --- /dev/null +++ b/src/forwarder.cpp @@ -0,0 +1,38 @@ +/* + Copyright (c) 2007-2010 iMatix Corporation + + This file is part of 0MQ. + + 0MQ is free software; you can redistribute it and/or modify it under + the terms of the Lesser GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + 0MQ is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + Lesser GNU General Public License for more details. + + You should have received a copy of the Lesser GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "../include/zmq.h" + +#include "forwarder.hpp" +#include "socket_base.hpp" +#include "err.hpp" + +int zmq::forwarder (socket_base_t *insocket_, socket_base_t *outsocket_) +{ + zmq_msg_t msg; + int rc = zmq_msg_init (&msg); + errno_assert (rc == 0); + + while (true) { + insocket_->recv (&msg, 0); + outsocket_->send (&msg, 0); + } + + return 0; +} diff --git a/src/forwarder.hpp b/src/forwarder.hpp new file mode 100644 index 0000000..68827a4 --- /dev/null +++ b/src/forwarder.hpp @@ -0,0 +1,31 @@ +/* + Copyright (c) 2007-2010 iMatix Corporation + + This file is part of 0MQ. + + 0MQ is free software; you can redistribute it and/or modify it under + the terms of the Lesser GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + 0MQ is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + Lesser GNU General Public License for more details. + + You should have received a copy of the Lesser GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef __ZMQ_FORWARDER_HPP_INCLUDED__ +#define __ZMQ_FORWARDER_HPP_INCLUDED__ + +namespace zmq +{ + + int forwarder (class socket_base_t *insocket_, + class socket_base_t *outsocket_); + +} + +#endif diff --git a/src/fq.cpp b/src/fq.cpp new file mode 100644 index 0000000..9028853 --- /dev/null +++ b/src/fq.cpp @@ -0,0 +1,130 @@ +/* + Copyright (c) 2007-2010 iMatix Corporation + + This file is part of 0MQ. + + 0MQ is free software; you can redistribute it and/or modify it under + the terms of the Lesser GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + 0MQ is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + Lesser GNU General Public License for more details. + + You should have received a copy of the Lesser GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "../include/zmq.h" + +#include "fq.hpp" +#include "pipe.hpp" +#include "err.hpp" + +zmq::fq_t::fq_t () : + active (0), + current (0), + more (false) +{ +} + +zmq::fq_t::~fq_t () +{ + for (pipes_t::size_type i = 0; i != pipes.size (); i++) + pipes [i]->term (); +} + +void zmq::fq_t::attach (reader_t *pipe_) +{ + pipes.push_back (pipe_); + pipes.swap (active, pipes.size () - 1); + active++; +} + +void zmq::fq_t::detach (reader_t *pipe_) +{ + zmq_assert (!more || pipes [current] != pipe_); + + // Remove the pipe from the list; adjust number of active pipes + // accordingly. + if (pipes.index (pipe_) < active) { + active--; + if (current == active) + current = 0; + } + pipes.erase (pipe_); +} + +void zmq::fq_t::kill (reader_t *pipe_) +{ + // Move the pipe to the list of inactive pipes. + active--; + if (current == active) + current = 0; + pipes.swap (pipes.index (pipe_), active); +} + +void zmq::fq_t::revive (reader_t *pipe_) +{ + // Move the pipe to the list of active pipes. + pipes.swap (pipes.index (pipe_), active); + active++; +} + +int zmq::fq_t::recv (zmq_msg_t *msg_, int flags_) +{ + // Deallocate old content of the message. + zmq_msg_close (msg_); + + // Round-robin over the pipes to get the next message. + for (int count = active; count != 0; count--) { + + // Try to fetch new message. If we've already read part of the message + // subsequent part should be immediately available. + bool fetched = pipes [current]->read (msg_); + zmq_assert (!(more && !fetched)); + + // Note that when message is not fetched, current pipe is killed and + // replaced by another active pipe. Thus we don't have to increase + // the 'current' pointer. + if (fetched) { + more = msg_->flags & ZMQ_MSG_MORE; + if (!more) { + current++; + if (current >= active) + current = 0; + } + return 0; + } + } + + // No message is available. Initialise the output parameter + // to be a 0-byte message. + zmq_msg_init (msg_); + errno = EAGAIN; + return -1; +} + +bool zmq::fq_t::has_in () +{ + // There are subsequent parts of the partly-read message available. + if (more) + return true; + + // Note that messing with current doesn't break the fairness of fair + // queueing algorithm. If there are no messages available current will + // get back to its original value. Otherwise it'll point to the first + // pipe holding messages, skipping only pipes with no messages available. + for (int count = active; count != 0; count--) { + if (pipes [current]->check_read ()) + return true; + current++; + if (current >= active) + current = 0; + } + + return false; +} + diff --git a/src/fq.hpp b/src/fq.hpp new file mode 100644 index 0000000..5c699ee --- /dev/null +++ b/src/fq.hpp @@ -0,0 +1,68 @@ +/* + Copyright (c) 2007-2010 iMatix Corporation + + This file is part of 0MQ. + + 0MQ is free software; you can redistribute it and/or modify it under + the terms of the Lesser GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + 0MQ is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + Lesser GNU General Public License for more details. + + You should have received a copy of the Lesser GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef __ZMQ_FQ_HPP_INCLUDED__ +#define __ZMQ_FQ_HPP_INCLUDED__ + +#include "yarray.hpp" + +namespace zmq +{ + + // Class manages a set of inbound pipes. On receive it performs fair + // queueing (RFC970) so that senders gone berserk won't cause denial of + // service for decent senders. + class fq_t + { + public: + + fq_t (); + ~fq_t (); + + void attach (class reader_t *pipe_); + void detach (class reader_t *pipe_); + void kill (class reader_t *pipe_); + void revive (class reader_t *pipe_); + int recv (zmq_msg_t *msg_, int flags_); + bool has_in (); + + private: + + // Inbound pipes. + typedef yarray_t <class reader_t> pipes_t; + pipes_t pipes; + + // Number of active pipes. All the active pipes are located at the + // beginning of the pipes array. + pipes_t::size_type active; + + // Index of the next bound pipe to read a message from. + pipes_t::size_type current; + + // If true, part of a multipart message was already received, but + // there are following parts still waiting in the current pipe. + bool more; + + fq_t (const fq_t&); + void operator = (const fq_t&); + }; + +} + +#endif diff --git a/src/i_endpoint.hpp b/src/i_endpoint.hpp new file mode 100644 index 0000000..0d14224 --- /dev/null +++ b/src/i_endpoint.hpp @@ -0,0 +1,43 @@ +/* + Copyright (c) 2007-2010 iMatix Corporation + + This file is part of 0MQ. + + 0MQ is free software; you can redistribute it and/or modify it under + the terms of the Lesser GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + 0MQ is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + Lesser GNU General Public License for more details. + + You should have received a copy of the Lesser GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef __ZMQ_I_ENDPOINT_HPP_INCLUDED__ +#define __ZMQ_I_ENDPOINT_HPP_INCLUDED__ + +#include "blob.hpp" + +namespace zmq +{ + + struct i_endpoint + { + virtual ~i_endpoint () {} + + virtual void attach_pipes (class reader_t *inpipe_, + class writer_t *outpipe_, const blob_t &peer_identity_) = 0; + virtual void detach_inpipe (class reader_t *pipe_) = 0; + virtual void detach_outpipe (class writer_t *pipe_) = 0; + virtual void kill (class reader_t *pipe_) = 0; + virtual void revive (class reader_t *pipe_) = 0; + virtual void revive (class writer_t *pipe_) = 0; + }; + +} + +#endif diff --git a/src/i_engine.hpp b/src/i_engine.hpp new file mode 100644 index 0000000..ea6b850 --- /dev/null +++ b/src/i_engine.hpp @@ -0,0 +1,49 @@ +/* + Copyright (c) 2007-2010 iMatix Corporation + + This file is part of 0MQ. + + 0MQ is free software; you can redistribute it and/or modify it under + the terms of the Lesser GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + 0MQ is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + Lesser GNU General Public License for more details. + + You should have received a copy of the Lesser GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef __ZMQ_I_ENGINE_HPP_INCLUDED__ +#define __ZMQ_I_ENGINE_HPP_INCLUDED__ + +#include <stddef.h> + +namespace zmq +{ + + struct i_engine + { + virtual ~i_engine () {} + + // Plug the engine to the session. + virtual void plug (struct i_inout *inout_) = 0; + + // Unplug the engine from the session. + virtual void unplug () = 0; + + // This method is called by the session to signalise that there + // are messages to send available. + virtual void revive () = 0; + + // This method is called by the session to signalise that more + // messages can be written to the pipe. + virtual void resume_input () = 0; + }; + +} + +#endif diff --git a/src/i_inout.hpp b/src/i_inout.hpp new file mode 100644 index 0000000..21d1838 --- /dev/null +++ b/src/i_inout.hpp @@ -0,0 +1,60 @@ +/* + Copyright (c) 2007-2010 iMatix Corporation + + This file is part of 0MQ. + + 0MQ is free software; you can redistribute it and/or modify it under + the terms of the Lesser GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + 0MQ is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + Lesser GNU General Public License for more details. + + You should have received a copy of the Lesser GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef __ZMQ_I_INOUT_HPP_INCLUDED__ +#define __ZMQ_I_INOUT_HPP_INCLUDED__ + +#include "../include/zmq.h" + +#include "stdint.hpp" + +namespace zmq +{ + + struct i_inout + { + virtual ~i_inout () {} + + // Engine asks to get a message to send to the network. + virtual bool read (::zmq_msg_t *msg_) = 0; + + // Engine sends the incoming message further on downstream. + virtual bool write (::zmq_msg_t *msg_) = 0; + + // Flush all the previously written messages downstream. + virtual void flush () = 0; + + // Drop all the references to the engine. The parameter is the object + // to use to reconnect. If reconnection is not required, the argument + // is set to NULL. + virtual void detach (class owned_t *reconnecter_) = 0; + + // Returns least loaded I/O thread. + virtual class io_thread_t *get_io_thread () = 0; + + // Return pointer to the owning socket. + virtual class socket_base_t *get_owner () = 0; + + // Return ordinal number of the session. + virtual uint64_t get_ordinal () = 0; + }; + +} + +#endif diff --git a/src/i_poll_events.hpp b/src/i_poll_events.hpp new file mode 100644 index 0000000..8b85f7a --- /dev/null +++ b/src/i_poll_events.hpp @@ -0,0 +1,45 @@ +/* +Copyright (c) 2007-2010 iMatix Corporation + +This file is part of 0MQ. + +0MQ is free software; you can redistribute it and/or modify it under +the terms of the Lesser GNU General Public License as published by +the Free Software Foundation; either version 3 of the License, or +(at your option) any later version. + +0MQ is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +Lesser GNU General Public License for more details. + +You should have received a copy of the Lesser GNU General Public License +along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef __ZMQ_I_POLL_EVENTS_HPP_INCLUDED__ +#define __ZMQ_I_POLL_EVENTS_HPP_INCLUDED__ + +namespace zmq +{ + + // Virtual interface to be exposed by object that want to be notified + // about events on file descriptors. + + struct i_poll_events + { + virtual ~i_poll_events () {}; + + // Called by I/O thread when file descriptor is ready for reading. + virtual void in_event () = 0; + + // Called by I/O thread when file descriptor is ready for writing. + virtual void out_event () = 0; + + // Called when timer expires. + virtual void timer_event () = 0; + }; + +} + +#endif diff --git a/src/io_object.cpp b/src/io_object.cpp new file mode 100644 index 0000000..086f173 --- /dev/null +++ b/src/io_object.cpp @@ -0,0 +1,92 @@ +/* + Copyright (c) 2007-2010 iMatix Corporation + + This file is part of 0MQ. + + 0MQ is free software; you can redistribute it and/or modify it under + the terms of the Lesser GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + 0MQ is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + Lesser GNU General Public License for more details. + + You should have received a copy of the Lesser GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "io_object.hpp" +#include "io_thread.hpp" +#include "err.hpp" + +zmq::io_object_t::io_object_t (io_thread_t *io_thread_) +{ + // Retrieve the poller from the thread we are running in. + poller = io_thread_->get_poller (); +} + +zmq::io_object_t::~io_object_t () +{ +} + +void zmq::io_object_t::set_io_thread (io_thread_t *io_thread_) +{ + poller = io_thread_->get_poller (); +} + +zmq::io_object_t::handle_t zmq::io_object_t::add_fd (fd_t fd_) +{ + return poller->add_fd (fd_, this); +} + +void zmq::io_object_t::rm_fd (handle_t handle_) +{ + poller->rm_fd (handle_); +} + +void zmq::io_object_t::set_pollin (handle_t handle_) +{ + poller->set_pollin (handle_); +} + +void zmq::io_object_t::reset_pollin (handle_t handle_) +{ + poller->reset_pollin (handle_); +} + +void zmq::io_object_t::set_pollout (handle_t handle_) +{ + poller->set_pollout (handle_); +} + +void zmq::io_object_t::reset_pollout (handle_t handle_) +{ + poller->reset_pollout (handle_); +} + +void zmq::io_object_t::add_timer () +{ + poller->add_timer (this); +} + +void zmq::io_object_t::cancel_timer () +{ + poller->cancel_timer (this); +} + +void zmq::io_object_t::in_event () +{ + zmq_assert (false); +} + +void zmq::io_object_t::out_event () +{ + zmq_assert (false); +} + +void zmq::io_object_t::timer_event () +{ + zmq_assert (false); +} diff --git a/src/io_object.hpp b/src/io_object.hpp new file mode 100644 index 0000000..655e7f5 --- /dev/null +++ b/src/io_object.hpp @@ -0,0 +1,77 @@ +/* + Copyright (c) 2007-2010 iMatix Corporation + + This file is part of 0MQ. + + 0MQ is free software; you can redistribute it and/or modify it under + the terms of the Lesser GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + 0MQ is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + Lesser GNU General Public License for more details. + + You should have received a copy of the Lesser GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef __ZMQ_IO_OBJECT_HPP_INCLUDED__ +#define __ZMQ_IO_OBJECT_HPP_INCLUDED__ + +#include <stddef.h> + +#include "stdint.hpp" +#include "poller.hpp" +#include "i_poll_events.hpp" + +namespace zmq +{ + + // Simple base class for objects that live in I/O threads. + // It makes communication with the poller object easier and + // makes defining unneeded event handlers unnecessary. + + class io_object_t : public i_poll_events + { + public: + + io_object_t (class io_thread_t *io_thread_ = NULL); + ~io_object_t (); + + protected: + + typedef poller_t::handle_t handle_t; + + // Derived class can init/swap the underlying I/O thread. + // Caution: Remove all the file descriptors from the old I/O thread + // before swapping to the new one! + void set_io_thread (class io_thread_t *io_thread_); + + // Methods to access underlying poller object. + handle_t add_fd (fd_t fd_); + void rm_fd (handle_t handle_); + void set_pollin (handle_t handle_); + void reset_pollin (handle_t handle_); + void set_pollout (handle_t handle_); + void reset_pollout (handle_t handle_); + void add_timer (); + void cancel_timer (); + + // i_poll_events interface implementation. + void in_event (); + void out_event (); + void timer_event (); + + private: + + poller_t *poller; + + io_object_t (const io_object_t&); + void operator = (const io_object_t&); + }; + +} + +#endif diff --git a/src/io_thread.cpp b/src/io_thread.cpp new file mode 100644 index 0000000..fac6961 --- /dev/null +++ b/src/io_thread.cpp @@ -0,0 +1,105 @@ +/* + Copyright (c) 2007-2010 iMatix Corporation + + This file is part of 0MQ. + + 0MQ is free software; you can redistribute it and/or modify it under + the terms of the Lesser GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + 0MQ is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + Lesser GNU General Public License for more details. + + You should have received a copy of the Lesser GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <new> + +#include "../include/zmq.h" + +#include "io_thread.hpp" +#include "platform.hpp" +#include "err.hpp" +#include "ctx.hpp" + +zmq::io_thread_t::io_thread_t (ctx_t *ctx_, + uint32_t thread_slot_) : + object_t (ctx_, thread_slot_) +{ + poller = new (std::nothrow) poller_t; + zmq_assert (poller); + + signaler_handle = poller->add_fd (signaler.get_fd (), this); + poller->set_pollin (signaler_handle); +} + +zmq::io_thread_t::~io_thread_t () +{ + delete poller; +} + +void zmq::io_thread_t::start () +{ + // Start the underlying I/O thread. + poller->start (); +} + +void zmq::io_thread_t::stop () +{ + send_stop (); +} + +zmq::signaler_t *zmq::io_thread_t::get_signaler () +{ + return &signaler; +} + +int zmq::io_thread_t::get_load () +{ + return poller->get_load (); +} + +void zmq::io_thread_t::in_event () +{ + // TODO: Do we want to limit number of commands I/O thread can + // process in a single go? + + while (true) { + + // Get the next command. If there is none, exit. + command_t cmd; + if (!signaler.recv (&cmd, false)) + break; + + // Process the command. + cmd.destination->process_command (cmd); + } +} + +void zmq::io_thread_t::out_event () +{ + // We are never polling for POLLOUT here. This function is never called. + zmq_assert (false); +} + +void zmq::io_thread_t::timer_event () +{ + // No timers here. This function is never called. + zmq_assert (false); +} + +zmq::poller_t *zmq::io_thread_t::get_poller () +{ + zmq_assert (poller); + return poller; +} + +void zmq::io_thread_t::process_stop () +{ + poller->rm_fd (signaler_handle); + poller->stop (); +} diff --git a/src/io_thread.hpp b/src/io_thread.hpp new file mode 100644 index 0000000..3d832c0 --- /dev/null +++ b/src/io_thread.hpp @@ -0,0 +1,85 @@ +/* + Copyright (c) 2007-2010 iMatix Corporation + + This file is part of 0MQ. + + 0MQ is free software; you can redistribute it and/or modify it under + the terms of the Lesser GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + 0MQ is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + Lesser GNU General Public License for more details. + + You should have received a copy of the Lesser GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef __ZMQ_IO_THREAD_HPP_INCLUDED__ +#define __ZMQ_IO_THREAD_HPP_INCLUDED__ + +#include <vector> + +#include "stdint.hpp" +#include "object.hpp" +#include "poller.hpp" +#include "i_poll_events.hpp" +#include "signaler.hpp" + +namespace zmq +{ + + // Generic part of the I/O thread. Polling-mechanism-specific features + // are implemented in separate "polling objects". + + class io_thread_t : public object_t, public i_poll_events + { + public: + + io_thread_t (class ctx_t *ctx_, uint32_t thread_slot_); + + // Clean-up. If the thread was started, it's neccessary to call 'stop' + // before invoking destructor. Otherwise the destructor would hang up. + ~io_thread_t (); + + // Launch the physical thread. + void start (); + + // Ask underlying thread to stop. + void stop (); + + // Returns signaler associated with this I/O thread. + signaler_t *get_signaler (); + + // i_poll_events implementation. + void in_event (); + void out_event (); + void timer_event (); + + // Used by io_objects to retrieve the assciated poller object. + poller_t *get_poller (); + + // Command handlers. + void process_stop (); + + // Returns load experienced by the I/O thread. + int get_load (); + + private: + + // Poll thread gets notifications about incoming commands using + // this signaler. + signaler_t signaler; + + // Handle associated with signaler's file descriptor. + poller_t::handle_t signaler_handle; + + // I/O multiplexing is performed using a poller object. + poller_t *poller; + }; + +} + +#endif diff --git a/src/ip.cpp b/src/ip.cpp new file mode 100644 index 0000000..79d90da --- /dev/null +++ b/src/ip.cpp @@ -0,0 +1,335 @@ +/* + Copyright (c) 2007-2010 iMatix Corporation + + This file is part of 0MQ. + + 0MQ is free software; you can redistribute it and/or modify it under + the terms of the Lesser GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + 0MQ is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + Lesser GNU General Public License for more details. + + You should have received a copy of the Lesser GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <stdlib.h> +#include <string.h> +#include <stdlib.h> +#include <string> + +#include "ip.hpp" +#include "platform.hpp" +#include "err.hpp" +#include "stdint.hpp" + +#if defined ZMQ_HAVE_SOLARIS + +#include <sys/sockio.h> +#include <net/if.h> +#include <unistd.h> + +// On Solaris platform, network interface name can be queried by ioctl. +static int resolve_nic_name (in_addr* addr_, char const *interface_) +{ + // Create a socket. + int fd = socket (AF_INET, SOCK_DGRAM, 0); + zmq_assert (fd != -1); + + // Retrieve number of interfaces. + lifnum ifn; + ifn.lifn_family = AF_UNSPEC; + ifn.lifn_flags = 0; + int rc = ioctl (fd, SIOCGLIFNUM, (char*) &ifn); + zmq_assert (rc != -1); + + // Allocate memory to get interface names. + size_t ifr_size = sizeof (struct lifreq) * ifn.lifn_count; + char *ifr = (char*) malloc (ifr_size); + errno_assert (ifr); + + // Retrieve interface names. + lifconf ifc; + ifc.lifc_family = AF_UNSPEC; + ifc.lifc_flags = 0; + ifc.lifc_len = ifr_size; + ifc.lifc_buf = ifr; + rc = ioctl (fd, SIOCGLIFCONF, (char*) &ifc); + zmq_assert (rc != -1); + + // Find the interface with the specified name and AF_INET family. + bool found = false; + lifreq *ifrp = ifc.lifc_req; + for (int n = 0; n < (int) (ifc.lifc_len / sizeof (lifreq)); + n ++, ifrp ++) { + if (!strcmp (interface_, ifrp->lifr_name)) { + rc = ioctl (fd, SIOCGLIFADDR, (char*) ifrp); + zmq_assert (rc != -1); + if (ifrp->lifr_addr.ss_family == AF_INET) { + *addr_ = ((sockaddr_in*) &ifrp->lifr_addr)->sin_addr; + found = true; + break; + } + } + } + + // Clean-up. + free (ifr); + close (fd); + + if (!found) { + errno = ENODEV; + return -1; + } + + return 0; +} + +#elif defined ZMQ_HAVE_AIX || ZMQ_HAVE_HPUX + +#include <sys/types.h> +#include <unistd.h> +#include <sys/ioctl.h> +#include <net/if.h> + +static int resolve_nic_name (in_addr* addr_, char const *interface_) +{ + // Create a socket. + int sd = socket (AF_INET, SOCK_DGRAM, 0); + zmq_assert (sd != -1); + + struct ifreq ifr; + + // Copy interface name for ioctl get. + strncpy (ifr.ifr_name, interface_, sizeof (ifr.ifr_name)); + + // Fetch interface address. + int rc = ioctl (sd, SIOCGIFADDR, (caddr_t) &ifr, sizeof (struct ifreq)); + + // Clean up. + close (sd); + + if (rc == -1) { + errno = ENODEV; + return -1; + } + + struct sockaddr *sa = (struct sockaddr *) &ifr.ifr_addr; + *addr_ = ((sockaddr_in*)sa)->sin_addr; + return 0; +} + +#elif ((defined ZMQ_HAVE_LINUX || defined ZMQ_HAVE_FREEBSD ||\ + defined ZMQ_HAVE_OSX || defined ZMQ_HAVE_OPENBSD ||\ + defined ZMQ_HAVE_QNXNTO || defined ZMQ_HAVE_NETBSD)\ + && defined ZMQ_HAVE_IFADDRS) + +#include <ifaddrs.h> + +// On these platforms, network interface name can be queried +// using getifaddrs function. +static int resolve_nic_name (in_addr* addr_, char const *interface_) +{ + // Get the addresses. + ifaddrs* ifa = NULL; + int rc = getifaddrs (&ifa); + zmq_assert (rc == 0); + zmq_assert (ifa != NULL); + + // Find the corresponding network interface. + bool found = false; + for (ifaddrs *ifp = ifa; ifp != NULL ;ifp = ifp->ifa_next) + if (ifp->ifa_addr && ifp->ifa_addr->sa_family == AF_INET + && !strcmp (interface_, ifp->ifa_name)) + { + *addr_ = ((sockaddr_in*) ifp->ifa_addr)->sin_addr; + found = true; + break; + } + + // Clean-up; + freeifaddrs (ifa); + + if (!found) { + errno = ENODEV; + return -1; + } + + return 0; +} + +#else + +// On other platforms we assume there are no sane interface names. +// This is true especially of Windows. +static int resolve_nic_name (in_addr* addr_, char const *interface_) +{ + errno = ENODEV; + return -1; +} + +#endif + +int zmq::resolve_ip_interface (sockaddr_storage* addr_, socklen_t *addr_len_, + char const *interface_) +{ + // Find the ':' at end that separates NIC name from service. + const char *delimiter = strrchr (interface_, ':'); + if (!delimiter) { + errno = EINVAL; + return -1; + } + + // Separate the name/port. + std::string iface (interface_, delimiter - interface_); + std::string service (delimiter + 1); + + // Initialize the output parameter. + memset (addr_, 0, sizeof (*addr_)); + + // Initialise IPv4-format family/port. + sockaddr_in ip4_addr; + memset (&ip4_addr, 0, sizeof (ip4_addr)); + ip4_addr.sin_family = AF_INET; + ip4_addr.sin_port = htons ((uint16_t) atoi (service.c_str())); + + // Initialize temporary output pointers with ip4_addr + sockaddr *out_addr = (sockaddr *) &ip4_addr; + size_t out_addrlen = sizeof (ip4_addr); + + // 0 is not a valid port. + if (!ip4_addr.sin_port) { + errno = EINVAL; + return -1; + } + + // * resolves to INADDR_ANY. + if (iface.compare("*") == 0) { + ip4_addr.sin_addr.s_addr = htonl (INADDR_ANY); + zmq_assert (out_addrlen <= sizeof (*addr_)); + memcpy (addr_, out_addr, out_addrlen); + *addr_len_ = out_addrlen; + return 0; + } + + // Try to resolve the string as a NIC name. + int rc = resolve_nic_name (&ip4_addr.sin_addr, iface.c_str()); + if (rc != 0 && errno != ENODEV) + return rc; + if (rc == 0) { + zmq_assert (out_addrlen <= sizeof (*addr_)); + memcpy (addr_, out_addr, out_addrlen); + *addr_len_ = out_addrlen; + return 0; + } + + // There's no such interface name. Assume literal address. + addrinfo *res = NULL; + + // Set up the query. + addrinfo req; + memset (&req, 0, sizeof (req)); + + // We only support IPv4 addresses for now. + req.ai_family = AF_INET; + + // Arbitrary, not used in the output, but avoids duplicate results. + req.ai_socktype = SOCK_STREAM; + + // Restrict hostname/service to literals to avoid any DNS lookups or + // service-name irregularity due to indeterminate socktype. + req.ai_flags = AI_PASSIVE | AI_NUMERICHOST | AI_NUMERICSERV; + + // Resolve the literal address. Some of the error info is lost in case + // of error, however, there's no way to report EAI errors via errno. + rc = getaddrinfo (iface.c_str(), service.c_str(), &req, &res); + if (rc) { + errno = ENODEV; + return -1; + } + + // Use the first result. + zmq_assert ((size_t) (res->ai_addrlen) <= sizeof (*addr_)); + memcpy (addr_, res->ai_addr, res->ai_addrlen); + *addr_len_ = res->ai_addrlen; + + // Cleanup getaddrinfo after copying the possibly referenced result. + if (res) + freeaddrinfo (res); + + return 0; +} + +int zmq::resolve_ip_hostname (sockaddr_storage *addr_, socklen_t *addr_len_, + const char *hostname_) +{ + // Find the ':' that separates hostname name from service. + const char *delimiter = strchr (hostname_, ':'); + if (!delimiter) { + errno = EINVAL; + return -1; + } + + // Separate the hostname and service. + std::string hostname (hostname_, delimiter - hostname_); + std::string service (delimiter + 1); + + // Set up the query. + addrinfo req; + memset (&req, 0, sizeof (req)); + + // We only support IPv4 addresses for now. + req.ai_family = AF_INET; + + // Need to choose one to avoid duplicate results from getaddrinfo() - this + // doesn't really matter, since it's not included in the addr-output. + req.ai_socktype = SOCK_STREAM; + + // Avoid named services due to unclear socktype, and don't pick IPv4 + // addresses if we don't have a local IPv4 address configured. + // If this is failing for you on a host with only IPv6 connectivity, + // please contribute proper IPv6 support for all functions in this file. + req.ai_flags = AI_NUMERICSERV | AI_ADDRCONFIG; + + // Resolve host name. Some of the error info is lost in case of error, + // however, there's no way to report EAI errors via errno. + addrinfo *res; + int rc = getaddrinfo (hostname.c_str (), service.c_str (), &req, &res); + if (rc) { + errno = EINVAL; + return -1; + } + + // Copy first result to output addr with hostname and service. + zmq_assert ((size_t) (res->ai_addrlen) <= sizeof (*addr_)); + memcpy (addr_, res->ai_addr, res->ai_addrlen); + *addr_len_ = res->ai_addrlen; + + freeaddrinfo (res); + + return 0; +} + +#if !defined ZMQ_HAVE_WINDOWS && !defined ZMQ_HAVE_OPENVMS + +int zmq::resolve_local_path (sockaddr_storage *addr_, socklen_t *addr_len_, + const char *path_) +{ + sockaddr_un *un = (sockaddr_un*) addr_; + if (strlen (path_) >= sizeof (un->sun_path)) + { + errno = ENAMETOOLONG; + return -1; + } + strcpy (un->sun_path, path_); + un->sun_family = AF_UNIX; + *addr_len_ = sizeof (sockaddr_un); + return 0; +} + +#endif + diff --git a/src/ip.hpp b/src/ip.hpp new file mode 100644 index 0000000..d963df2 --- /dev/null +++ b/src/ip.hpp @@ -0,0 +1,69 @@ +/* + Copyright (c) 2007-2010 iMatix Corporation + + This file is part of 0MQ. + + 0MQ is free software; you can redistribute it and/or modify it under + the terms of the Lesser GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + 0MQ is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + Lesser GNU General Public License for more details. + + You should have received a copy of the Lesser GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef __ZMQ_IP_HPP_INCLUDED__ +#define __ZMQ_IP_HPP_INCLUDED__ + +#include "platform.hpp" + +#ifdef ZMQ_HAVE_WINDOWS +#include "windows.hpp" +#else +#include <sys/types.h> +#include <sys/socket.h> +#include <arpa/inet.h> +#include <netinet/in.h> +#include <netdb.h> +#endif + +#if !defined ZMQ_HAVE_WINDOWS && !defined ZMQ_HAVE_OPENVMS +#include <sys/un.h> +#endif + +// Some platforms (notably Darwin/OSX and NetBSD) do not define all AI_ +// flags for getaddrinfo(). This can be worked around safely by defining +// these to 0. +#ifndef AI_ADDRCONFIG +#define AI_ADDRCONFIG 0 +#endif +#ifndef AI_NUMERICSERV +#define AI_NUMERICSERV 0 +#endif + +namespace zmq +{ + + // Resolves network interface name in <nic-name>:<port> format. Symbol "*" + // (asterisk) resolves to INADDR_ANY (all network interfaces). + int resolve_ip_interface (sockaddr_storage *addr_, socklen_t *addr_len_, + char const *interface_); + + // This function resolves a string in <hostname>:<port-number> format. + // Hostname can be either the name of the host or its IP address. + int resolve_ip_hostname (sockaddr_storage *addr_, socklen_t *addr_len_, + const char *hostname_); + +#if !defined ZMQ_HAVE_WINDOWS && !defined ZMQ_HAVE_OPENVMS + // This function sets up address for UNIX domain transport. + int resolve_local_path (sockaddr_storage *addr_, socklen_t *addr_len_, + const char* pathname_); +#endif +} + +#endif diff --git a/src/kqueue.cpp b/src/kqueue.cpp new file mode 100644 index 0000000..e1fe2fa --- /dev/null +++ b/src/kqueue.cpp @@ -0,0 +1,226 @@ +/* + Copyright (c) 2007-2010 iMatix Corporation + + This file is part of 0MQ. + + 0MQ is free software; you can redistribute it and/or modify it under + the terms of the Lesser GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + 0MQ is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + Lesser GNU General Public License for more details. + + You should have received a copy of the Lesser GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "platform.hpp" + +#if defined ZMQ_HAVE_FREEBSD || defined ZMQ_HAVE_OPENBSD ||\ + defined ZMQ_HAVE_OSX || defined ZMQ_HAVE_NETBSD + +#include <sys/time.h> +#include <sys/types.h> +#include <sys/event.h> +#include <stdlib.h> +#include <unistd.h> +#include <algorithm> +#include <new> + +#include "kqueue.hpp" +#include "err.hpp" +#include "config.hpp" +#include "i_poll_events.hpp" + +// NetBSD defines (struct kevent).udata as intptr_t, everyone else +// as void *. +#if defined ZMQ_HAVE_NETBSD +#define kevent_udata_t intptr_t +#else +#define kevent_udata_t void * +#endif + +zmq::kqueue_t::kqueue_t () : + stopping (false) +{ + // Create event queue + kqueue_fd = kqueue (); + errno_assert (kqueue_fd != -1); +} + +zmq::kqueue_t::~kqueue_t () +{ + worker.stop (); + + // Make sure there are no fds registered on shutdown. + zmq_assert (load.get () == 0); + + close (kqueue_fd); +} + +void zmq::kqueue_t::kevent_add (fd_t fd_, short filter_, void *udata_) +{ + struct kevent ev; + + EV_SET (&ev, fd_, filter_, EV_ADD, 0, 0, (kevent_udata_t)udata_); + int rc = kevent (kqueue_fd, &ev, 1, NULL, 0, NULL); + errno_assert (rc != -1); +} + +void zmq::kqueue_t::kevent_delete (fd_t fd_, short filter_) +{ + struct kevent ev; + + EV_SET (&ev, fd_, filter_, EV_DELETE, 0, 0, (kevent_udata_t)NULL); + int rc = kevent (kqueue_fd, &ev, 1, NULL, 0, NULL); + errno_assert (rc != -1); +} + +zmq::kqueue_t::handle_t zmq::kqueue_t::add_fd (fd_t fd_, + i_poll_events *reactor_) +{ + poll_entry_t *pe = new (std::nothrow) poll_entry_t; + zmq_assert (pe != NULL); + + pe->fd = fd_; + pe->flag_pollin = 0; + pe->flag_pollout = 0; + pe->reactor = reactor_; + + return pe; +} + +void zmq::kqueue_t::rm_fd (handle_t handle_) +{ + poll_entry_t *pe = (poll_entry_t*) handle_; + if (pe->flag_pollin) + kevent_delete (pe->fd, EVFILT_READ); + if (pe->flag_pollout) + kevent_delete (pe->fd, EVFILT_WRITE); + pe->fd = retired_fd; + retired.push_back (pe); +} + +void zmq::kqueue_t::set_pollin (handle_t handle_) +{ + poll_entry_t *pe = (poll_entry_t*) handle_; + pe->flag_pollin = true; + kevent_add (pe->fd, EVFILT_READ, pe); +} + +void zmq::kqueue_t::reset_pollin (handle_t handle_) +{ + poll_entry_t *pe = (poll_entry_t*) handle_; + pe->flag_pollin = false; + kevent_delete (pe->fd, EVFILT_READ); +} + +void zmq::kqueue_t::set_pollout (handle_t handle_) +{ + poll_entry_t *pe = (poll_entry_t*) handle_; + pe->flag_pollout = true; + kevent_add (pe->fd, EVFILT_WRITE, pe); +} + +void zmq::kqueue_t::reset_pollout (handle_t handle_) +{ + poll_entry_t *pe = (poll_entry_t*) handle_; + pe->flag_pollout = false; + kevent_delete (pe->fd, EVFILT_WRITE); +} + +void zmq::kqueue_t::add_timer (i_poll_events *events_) +{ + timers.push_back (events_); +} + +void zmq::kqueue_t::cancel_timer (i_poll_events *events_) +{ + timers_t::iterator it = std::find (timers.begin (), timers.end (), events_); + if (it != timers.end ()) + timers.erase (it); +} + +int zmq::kqueue_t::get_load () +{ + return load.get (); +} + +void zmq::kqueue_t::start () +{ + worker.start (worker_routine, this); +} + +void zmq::kqueue_t::stop () +{ + stopping = true; +} + +void zmq::kqueue_t::loop () +{ + while (!stopping) { + + struct kevent ev_buf [max_io_events]; + + // Compute time interval to wait. + timespec timeout = {max_timer_period / 1000, + (max_timer_period % 1000) * 1000000}; + + // Wait for events. + int n = kevent (kqueue_fd, NULL, 0, + &ev_buf [0], max_io_events, timers.empty () ? NULL : &timeout); + if (n == -1 && errno == EINTR) + continue; + errno_assert (n != -1); + + // Handle timer. + if (!n) { + + // Use local list of timers as timer handlers may fill new timers + // into the original array. + timers_t t; + std::swap (timers, t); + + // Trigger all the timers. + for (timers_t::iterator it = t.begin (); it != t.end (); it ++) + (*it)->timer_event (); + + continue; + } + + for (int i = 0; i < n; i ++) { + poll_entry_t *pe = (poll_entry_t*) ev_buf [i].udata; + + if (pe->fd == retired_fd) + continue; + if (ev_buf [i].flags & EV_EOF) + pe->reactor->in_event (); + if (pe->fd == retired_fd) + continue; + if (ev_buf [i].filter == EVFILT_WRITE) + pe->reactor->out_event (); + if (pe->fd == retired_fd) + continue; + if (ev_buf [i].filter == EVFILT_READ) + pe->reactor->in_event (); + } + + // Destroy retired event sources. + for (retired_t::iterator it = retired.begin (); it != retired.end (); + it ++) + delete *it; + retired.clear (); + } +} + +void zmq::kqueue_t::worker_routine (void *arg_) +{ + ((kqueue_t*) arg_)->loop (); +} + +// Don't pollute namespace with defines local to this file +#undef kevent_udata_t +#endif diff --git a/src/kqueue.hpp b/src/kqueue.hpp new file mode 100644 index 0000000..ac28a7d --- /dev/null +++ b/src/kqueue.hpp @@ -0,0 +1,113 @@ +/* + Copyright (c) 2007-2010 iMatix Corporation + + This file is part of 0MQ. + + 0MQ is free software; you can redistribute it and/or modify it under + the terms of the Lesser GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + 0MQ is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + Lesser GNU General Public License for more details. + + You should have received a copy of the Lesser GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef __ZMQ_KQUEUE_HPP_INCLUDED__ +#define __ZMQ_KQUEUE_HPP_INCLUDED__ + +#include "platform.hpp" + +#if defined ZMQ_HAVE_FREEBSD || defined ZMQ_HAVE_OPENBSD ||\ + defined ZMQ_HAVE_OSX || defined ZMQ_HAVE_NETBSD + +#include <vector> + +#include "fd.hpp" +#include "thread.hpp" +#include "atomic_counter.hpp" + +namespace zmq +{ + + // Implements socket polling mechanism using the BSD-specific + // kqueue interface. + + class kqueue_t + { + public: + + typedef void* handle_t; + + kqueue_t (); + ~kqueue_t (); + + // "poller" concept. + handle_t add_fd (fd_t fd_, struct i_poll_events *events_); + void rm_fd (handle_t handle_); + void set_pollin (handle_t handle_); + void reset_pollin (handle_t handle_); + void set_pollout (handle_t handle_); + void reset_pollout (handle_t handle_); + void add_timer (struct i_poll_events *events_); + void cancel_timer (struct i_poll_events *events_); + int get_load (); + void start (); + void stop (); + + private: + + // Main worker thread routine. + static void worker_routine (void *arg_); + + // Main event loop. + void loop (); + + // File descriptor referring to the kernel event queue. + fd_t kqueue_fd; + + // Adds the event to the kqueue. + void kevent_add (fd_t fd_, short filter_, void *udata_); + + // Deletes the event from the kqueue. + void kevent_delete (fd_t fd_, short filter_); + + struct poll_entry_t + { + fd_t fd; + bool flag_pollin; + bool flag_pollout; + i_poll_events *reactor; + }; + + // List of retired event sources. + typedef std::vector <poll_entry_t*> retired_t; + retired_t retired; + + // List of all the engines waiting for the timer event. + typedef std::vector <struct i_poll_events*> timers_t; + timers_t timers; + + // If true, thread is in the process of shutting down. + bool stopping; + + // Handle of the physical thread doing the I/O work. + thread_t worker; + + // Load of the poller. Currently number of file descriptors + // registered with the poller. + atomic_counter_t load; + + kqueue_t (const kqueue_t&); + void operator = (const kqueue_t&); + }; + +} + +#endif + +#endif diff --git a/src/lb.cpp b/src/lb.cpp new file mode 100644 index 0000000..ca93ba2 --- /dev/null +++ b/src/lb.cpp @@ -0,0 +1,123 @@ +/* + Copyright (c) 2007-2010 iMatix Corporation + + This file is part of 0MQ. + + 0MQ is free software; you can redistribute it and/or modify it under + the terms of the Lesser GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + 0MQ is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + Lesser GNU General Public License for more details. + + You should have received a copy of the Lesser GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "../include/zmq.h" + +#include "lb.hpp" +#include "pipe.hpp" +#include "err.hpp" + +zmq::lb_t::lb_t () : + active (0), + current (0), + more (false) +{ +} + +zmq::lb_t::~lb_t () +{ + for (pipes_t::size_type i = 0; i != pipes.size (); i++) + pipes [i]->term (); +} + +void zmq::lb_t::attach (writer_t *pipe_) +{ + pipes.push_back (pipe_); + pipes.swap (active, pipes.size () - 1); + active++; +} + +void zmq::lb_t::detach (writer_t *pipe_) +{ + zmq_assert (!more || pipes [current] != pipe_); + + // Remove the pipe from the list; adjust number of active pipes + // accordingly. + if (pipes.index (pipe_) < active) { + active--; + if (current == active) + current = 0; + } + pipes.erase (pipe_); +} + +void zmq::lb_t::revive (writer_t *pipe_) +{ + // Move the pipe to the list of active pipes. + pipes.swap (pipes.index (pipe_), active); + active++; +} + +int zmq::lb_t::send (zmq_msg_t *msg_, int flags_) +{ + while (active > 0) { + if (pipes [current]->write (msg_)) { + more = msg_->flags & ZMQ_MSG_MORE; + break; + } + + zmq_assert (!more); + active--; + if (current < active) + pipes.swap (current, active); + else + current = 0; + } + + // If there are no pipes we cannot send the message. + if (active == 0) { + errno = EAGAIN; + return -1; + } + + // If it's final part of the message we can fluch it downstream and + // continue round-robinning (load balance). + if (!more) { + pipes [current]->flush (); + current = (current + 1) % active; + } + + // Detach the message from the data buffer. + int rc = zmq_msg_init (msg_); + zmq_assert (rc == 0); + + return 0; +} + +bool zmq::lb_t::has_out () +{ + // If one part of the message was already written we can definitely + // write the rest of the message. + if (more) + return true; + + while (active > 0) { + if (pipes [current]->check_write ()) + return true; + + active--; + if (current < active) + pipes.swap (current, active); + else + current = 0; + } + + return false; +} + diff --git a/src/lb.hpp b/src/lb.hpp new file mode 100644 index 0000000..526a727 --- /dev/null +++ b/src/lb.hpp @@ -0,0 +1,65 @@ +/* + Copyright (c) 2007-2010 iMatix Corporation + + This file is part of 0MQ. + + 0MQ is free software; you can redistribute it and/or modify it under + the terms of the Lesser GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + 0MQ is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + Lesser GNU General Public License for more details. + + You should have received a copy of the Lesser GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef __ZMQ_LB_HPP_INCLUDED__ +#define __ZMQ_LB_HPP_INCLUDED__ + +#include "yarray.hpp" + +namespace zmq +{ + + // Class manages a set of outbound pipes. On send it load balances + // messages fairly among the pipes. + class lb_t + { + public: + + lb_t (); + ~lb_t (); + + void attach (class writer_t *pipe_); + void detach (class writer_t *pipe_); + void revive (class writer_t *pipe_); + int send (zmq_msg_t *msg_, int flags_); + bool has_out (); + + private: + + // List of outbound pipes. + typedef yarray_t <class writer_t> pipes_t; + pipes_t pipes; + + // Number of active pipes. All the active pipes are located at the + // beginning of the pipes array. + pipes_t::size_type active; + + // Points to the last pipe that the most recent message was sent to. + pipes_t::size_type current; + + // True if last we are in the middle of a multipart message. + bool more; + + lb_t (const lb_t&); + void operator = (const lb_t&); + }; + +} + +#endif diff --git a/src/libzmq.pc.in b/src/libzmq.pc.in new file mode 100644 index 0000000..ba155a3 --- /dev/null +++ b/src/libzmq.pc.in @@ -0,0 +1,10 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: libzmq +Description: 0MQ c++ library +Version: @VERSION@ +Libs: -L${libdir} -lzmq +Cflags: -I${includedir} diff --git a/src/likely.hpp b/src/likely.hpp new file mode 100644 index 0000000..b7a8171 --- /dev/null +++ b/src/likely.hpp @@ -0,0 +1,32 @@ +/* + Copyright (c) 2007-2010 iMatix Corporation + + This file is part of 0MQ. + + 0MQ is free software; you can redistribute it and/or modify it under + the terms of the Lesser GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + 0MQ is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + Lesser GNU General Public License for more details. + + You should have received a copy of the Lesser GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef __ZMQ_LIKELY_HPP_INCLUDED__ +#define __ZMQ_LIKELY_HPP_INCLUDED__ + +#if defined __GNUC__ +#define likely(x) __builtin_expect ((x), 1) +#define unlikely(x) __builtin_expect ((x), 0) +#else +#define likely(x) (x) +#define unlikely(x) (x) +#endif + + +#endif diff --git a/src/msg_content.hpp b/src/msg_content.hpp new file mode 100644 index 0000000..d409d45 --- /dev/null +++ b/src/msg_content.hpp @@ -0,0 +1,51 @@ +/* + Copyright (c) 2007-2010 iMatix Corporation + + This file is part of 0MQ. + + 0MQ is free software; you can redistribute it and/or modify it under + the terms of the Lesser GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + 0MQ is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + Lesser GNU General Public License for more details. + + You should have received a copy of the Lesser GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef __ZMQ_MSG_HPP_INCLUDE__ +#define __ZMQ_MSG_HPP_INCLUDE__ + +#include <stddef.h> + +#include "../include/zmq.h" + +#include "atomic_counter.hpp" + +namespace zmq +{ + + // Shared message buffer. Message data are either allocated in one + // continuous block along with this structure - thus avoiding one + // malloc/free pair or they are stored in used-supplied memory. + // In the latter case, ffn member stores pointer to the function to be + // used to deallocate the data. If the buffer is actually shared (there + // are at least 2 references to it) refcount member contains number of + // references. + + struct msg_content_t + { + void *data; + size_t size; + zmq_free_fn *ffn; + void *hint; + zmq::atomic_counter_t refcnt; + }; + +} + +#endif diff --git a/src/mutex.hpp b/src/mutex.hpp new file mode 100644 index 0000000..3306d2e --- /dev/null +++ b/src/mutex.hpp @@ -0,0 +1,120 @@ +/* + Copyright (c) 2007-2010 iMatix Corporation + + This file is part of 0MQ. + + 0MQ is free software; you can redistribute it and/or modify it under + the terms of the Lesser GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + 0MQ is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + Lesser GNU General Public License for more details. + + You should have received a copy of the Lesser GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef __ZMQ_MUTEX_HPP_INCLUDED__ +#define __ZMQ_MUTEX_HPP_INCLUDED__ + +#include "platform.hpp" +#include "err.hpp" + +// Mutex class encapsulates OS mutex in a platform-independent way. + +#ifdef ZMQ_HAVE_WINDOWS + +#include "windows.hpp" + +namespace zmq +{ + + class mutex_t + { + public: + inline mutex_t () + { + InitializeCriticalSection (&cs); + } + + inline ~mutex_t () + { + DeleteCriticalSection (&cs); + } + + inline void lock () + { + EnterCriticalSection (&cs); + } + + inline void unlock () + { + LeaveCriticalSection (&cs); + } + + private: + + CRITICAL_SECTION cs; + + // Disable copy construction and assignment. + mutex_t (const mutex_t&); + void operator = (const mutex_t&); + }; + +} + +#else + +#include <pthread.h> + +namespace zmq +{ + + class mutex_t + { + public: + inline mutex_t () + { + int rc = pthread_mutex_init (&mutex, NULL); + if (rc) + posix_assert (rc); + } + + inline ~mutex_t () + { + int rc = pthread_mutex_destroy (&mutex); + if (rc) + posix_assert (rc); + } + + inline void lock () + { + int rc = pthread_mutex_lock (&mutex); + if (rc) + posix_assert (rc); + } + + inline void unlock () + { + int rc = pthread_mutex_unlock (&mutex); + if (rc) + posix_assert (rc); + } + + private: + + pthread_mutex_t mutex; + + // Disable copy construction and assignment. + mutex_t (const mutex_t&); + void operator = (const mutex_t&); + }; + +} + +#endif + +#endif diff --git a/src/object.cpp b/src/object.cpp new file mode 100644 index 0000000..324450f --- /dev/null +++ b/src/object.cpp @@ -0,0 +1,374 @@ +/* + Copyright (c) 2007-2010 iMatix Corporation + + This file is part of 0MQ. + + 0MQ is free software; you can redistribute it and/or modify it under + the terms of the Lesser GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + 0MQ is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + Lesser GNU General Public License for more details. + + You should have received a copy of the Lesser GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <string.h> + +#include "object.hpp" +#include "ctx.hpp" +#include "err.hpp" +#include "pipe.hpp" +#include "io_thread.hpp" +#include "owned.hpp" +#include "session.hpp" +#include "socket_base.hpp" + +zmq::object_t::object_t (ctx_t *ctx_, uint32_t thread_slot_) : + ctx (ctx_), + thread_slot (thread_slot_) +{ +} + +zmq::object_t::object_t (object_t *parent_) : + ctx (parent_->ctx), + thread_slot (parent_->thread_slot) +{ +} + +zmq::object_t::~object_t () +{ +} + +uint32_t zmq::object_t::get_thread_slot () +{ + return thread_slot; +} + +zmq::ctx_t *zmq::object_t::get_ctx () +{ + return ctx; +} + +void zmq::object_t::process_command (command_t &cmd_) +{ + switch (cmd_.type) { + + case command_t::revive: + process_revive (); + break; + + case command_t::stop: + process_stop (); + break; + + case command_t::plug: + process_plug (); + process_seqnum (); + return; + + case command_t::own: + process_own (cmd_.args.own.object); + process_seqnum (); + break; + + case command_t::attach: + process_attach (cmd_.args.attach.engine, + blob_t (cmd_.args.attach.peer_identity, + cmd_.args.attach.peer_identity_size)); + process_seqnum (); + break; + + case command_t::bind: + process_bind (cmd_.args.bind.in_pipe, cmd_.args.bind.out_pipe, + blob_t (cmd_.args.bind.peer_identity, + cmd_.args.bind.peer_identity_size)); + process_seqnum (); + break; + + case command_t::reader_info: + process_reader_info (cmd_.args.reader_info.msgs_read); + break; + + case command_t::pipe_term: + process_pipe_term (); + return; + + case command_t::pipe_term_ack: + process_pipe_term_ack (); + break; + + case command_t::term_req: + process_term_req (cmd_.args.term_req.object); + break; + + case command_t::term: + process_term (); + break; + + case command_t::term_ack: + process_term_ack (); + break; + + default: + zmq_assert (false); + } + + // The assumption here is that each command is processed once only, + // so deallocating it after processing is all right. + deallocate_command (&cmd_); +} + +void zmq::object_t::register_pipe (class pipe_t *pipe_) +{ + ctx->register_pipe (pipe_); +} + +void zmq::object_t::unregister_pipe (class pipe_t *pipe_) +{ + ctx->unregister_pipe (pipe_); +} + +int zmq::object_t::register_endpoint (const char *addr_, socket_base_t *socket_) +{ + return ctx->register_endpoint (addr_, socket_); +} + +void zmq::object_t::unregister_endpoints (socket_base_t *socket_) +{ + return ctx->unregister_endpoints (socket_); +} + +zmq::socket_base_t *zmq::object_t::find_endpoint (const char *addr_) +{ + return ctx->find_endpoint (addr_); +} + +zmq::io_thread_t *zmq::object_t::choose_io_thread (uint64_t taskset_) +{ + return ctx->choose_io_thread (taskset_); +} + +void zmq::object_t::send_stop () +{ + // 'stop' command goes always from administrative thread to + // the current object. + command_t cmd; + cmd.destination = this; + cmd.type = command_t::stop; + ctx->send_command (thread_slot, cmd); +} + +void zmq::object_t::send_plug (owned_t *destination_, bool inc_seqnum_) +{ + if (inc_seqnum_) + destination_->inc_seqnum (); + + command_t cmd; + cmd.destination = destination_; + cmd.type = command_t::plug; + send_command (cmd); +} + +void zmq::object_t::send_own (socket_base_t *destination_, owned_t *object_) +{ + destination_->inc_seqnum (); + command_t cmd; + cmd.destination = destination_; + cmd.type = command_t::own; + cmd.args.own.object = object_; + send_command (cmd); +} + +void zmq::object_t::send_attach (session_t *destination_, i_engine *engine_, + const blob_t &peer_identity_, bool inc_seqnum_) +{ + if (inc_seqnum_) + destination_->inc_seqnum (); + + command_t cmd; + cmd.destination = destination_; + cmd.type = command_t::attach; + cmd.args.attach.engine = engine_; + if (peer_identity_.empty ()) { + cmd.args.attach.peer_identity_size = 0; + cmd.args.attach.peer_identity = NULL; + } + else { + zmq_assert (peer_identity_.size () <= 0xff); + cmd.args.attach.peer_identity_size = + (unsigned char) peer_identity_.size (); + cmd.args.attach.peer_identity = + (unsigned char*) malloc (peer_identity_.size ()); + zmq_assert (cmd.args.attach.peer_identity_size); + memcpy (cmd.args.attach.peer_identity, peer_identity_.data (), + peer_identity_.size ()); + } + send_command (cmd); +} + +void zmq::object_t::send_bind (socket_base_t *destination_, + reader_t *in_pipe_, writer_t *out_pipe_, const blob_t &peer_identity_, + bool inc_seqnum_) +{ + if (inc_seqnum_) + destination_->inc_seqnum (); + + command_t cmd; + cmd.destination = destination_; + cmd.type = command_t::bind; + cmd.args.bind.in_pipe = in_pipe_; + cmd.args.bind.out_pipe = out_pipe_; + if (peer_identity_.empty ()) { + cmd.args.bind.peer_identity_size = 0; + cmd.args.bind.peer_identity = NULL; + } + else { + zmq_assert (peer_identity_.size () <= 0xff); + cmd.args.bind.peer_identity_size = + (unsigned char) peer_identity_.size (); + cmd.args.bind.peer_identity = + (unsigned char*) malloc (peer_identity_.size ()); + zmq_assert (cmd.args.bind.peer_identity_size); + memcpy (cmd.args.bind.peer_identity, peer_identity_.data (), + peer_identity_.size ()); + } + send_command (cmd); +} + +void zmq::object_t::send_revive (object_t *destination_) +{ + command_t cmd; + cmd.destination = destination_; + cmd.type = command_t::revive; + send_command (cmd); +} + +void zmq::object_t::send_reader_info (writer_t *destination_, + uint64_t msgs_read_) +{ + command_t cmd; + cmd.destination = destination_; + cmd.type = command_t::reader_info; + cmd.args.reader_info.msgs_read = msgs_read_; + send_command (cmd); +} + +void zmq::object_t::send_pipe_term (writer_t *destination_) +{ + command_t cmd; + cmd.destination = destination_; + cmd.type = command_t::pipe_term; + send_command (cmd); +} + +void zmq::object_t::send_pipe_term_ack (reader_t *destination_) +{ + command_t cmd; + cmd.destination = destination_; + cmd.type = command_t::pipe_term_ack; + send_command (cmd); +} + +void zmq::object_t::send_term_req (socket_base_t *destination_, + owned_t *object_) +{ + command_t cmd; + cmd.destination = destination_; + cmd.type = command_t::term_req; + cmd.args.term_req.object = object_; + send_command (cmd); +} + +void zmq::object_t::send_term (owned_t *destination_) +{ + command_t cmd; + cmd.destination = destination_; + cmd.type = command_t::term; + send_command (cmd); +} + +void zmq::object_t::send_term_ack (socket_base_t *destination_) +{ + command_t cmd; + cmd.destination = destination_; + cmd.type = command_t::term_ack; + send_command (cmd); +} + +void zmq::object_t::process_stop () +{ + zmq_assert (false); +} + +void zmq::object_t::process_plug () +{ + zmq_assert (false); +} + +void zmq::object_t::process_own (owned_t *object_) +{ + zmq_assert (false); +} + +void zmq::object_t::process_attach (i_engine *engine_, + const blob_t &peer_identity_) +{ + zmq_assert (false); +} + +void zmq::object_t::process_bind (reader_t *in_pipe_, writer_t *out_pipe_, + const blob_t &peer_identity_) +{ + zmq_assert (false); +} + +void zmq::object_t::process_revive () +{ + zmq_assert (false); +} + +void zmq::object_t::process_reader_info (uint64_t msgs_read_) +{ + zmq_assert (false); +} + +void zmq::object_t::process_pipe_term () +{ + zmq_assert (false); +} + +void zmq::object_t::process_pipe_term_ack () +{ + zmq_assert (false); +} + +void zmq::object_t::process_term_req (owned_t *object_) +{ + zmq_assert (false); +} + +void zmq::object_t::process_term () +{ + zmq_assert (false); +} + +void zmq::object_t::process_term_ack () +{ + zmq_assert (false); +} + +void zmq::object_t::process_seqnum () +{ + zmq_assert (false); +} + +void zmq::object_t::send_command (command_t &cmd_) +{ + ctx->send_command (cmd_.destination->get_thread_slot (), cmd_); +} + diff --git a/src/object.hpp b/src/object.hpp new file mode 100644 index 0000000..a38b0a6 --- /dev/null +++ b/src/object.hpp @@ -0,0 +1,118 @@ +/* + Copyright (c) 2007-2010 iMatix Corporation + + This file is part of 0MQ. + + 0MQ is free software; you can redistribute it and/or modify it under + the terms of the Lesser GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + 0MQ is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + Lesser GNU General Public License for more details. + + You should have received a copy of the Lesser GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef __ZMQ_OBJECT_HPP_INCLUDED__ +#define __ZMQ_OBJECT_HPP_INCLUDED__ + +#include "stdint.hpp" +#include "blob.hpp" + +namespace zmq +{ + // Base class for all objects that participate in inter-thread + // communication. + + class object_t + { + public: + + object_t (class ctx_t *ctx_, uint32_t thread_slot_); + object_t (object_t *parent_); + virtual ~object_t (); + + uint32_t get_thread_slot (); + ctx_t *get_ctx (); + void process_command (struct command_t &cmd_); + + // Allow pipe to access corresponding context functions. + void register_pipe (class pipe_t *pipe_); + void unregister_pipe (class pipe_t *pipe_); + + protected: + + // Using following function, socket is able to access global + // repository of inproc endpoints. + int register_endpoint (const char *addr_, class socket_base_t *socket_); + void unregister_endpoints (class socket_base_t *socket_); + class socket_base_t *find_endpoint (const char *addr_); + + // Chooses least loaded I/O thread. + class io_thread_t *choose_io_thread (uint64_t taskset_); + + // Derived object can use these functions to send commands + // to other objects. + void send_stop (); + void send_plug (class owned_t *destination_, bool inc_seqnum_ = true); + void send_own (class socket_base_t *destination_, + class owned_t *object_); + void send_attach (class session_t *destination_, + struct i_engine *engine_, const blob_t &peer_identity_, + bool inc_seqnum_ = true); + void send_bind (class socket_base_t *destination_, + class reader_t *in_pipe_, class writer_t *out_pipe_, + const blob_t &peer_identity_, bool inc_seqnum_ = true); + void send_revive (class object_t *destination_); + void send_reader_info (class writer_t *destination_, + uint64_t msgs_read_); + void send_pipe_term (class writer_t *destination_); + void send_pipe_term_ack (class reader_t *destination_); + void send_term_req (class socket_base_t *destination_, + class owned_t *object_); + void send_term (class owned_t *destination_); + void send_term_ack (class socket_base_t *destination_); + + // These handlers can be overloaded by the derived objects. They are + // called when command arrives from another thread. + virtual void process_stop (); + virtual void process_plug (); + virtual void process_own (class owned_t *object_); + virtual void process_attach (struct i_engine *engine_, + const blob_t &peer_identity_); + virtual void process_bind (class reader_t *in_pipe_, + class writer_t *out_pipe_, const blob_t &peer_identity_); + virtual void process_revive (); + virtual void process_reader_info (uint64_t msgs_read_); + virtual void process_pipe_term (); + virtual void process_pipe_term_ack (); + virtual void process_term_req (class owned_t *object_); + virtual void process_term (); + virtual void process_term_ack (); + + // Special handler called after a command that requires a seqnum + // was processed. The implementation should catch up with its counter + // of processed commands here. + virtual void process_seqnum (); + + private: + + // Context provides access to the global state. + class ctx_t *ctx; + + // Slot ID of the thread the object belongs to. + uint32_t thread_slot; + + void send_command (command_t &cmd_); + + object_t (const object_t&); + void operator = (const object_t&); + }; + +} + +#endif diff --git a/src/options.cpp b/src/options.cpp new file mode 100644 index 0000000..f6b24d6 --- /dev/null +++ b/src/options.cpp @@ -0,0 +1,225 @@ +/* + Copyright (c) 2007-2010 iMatix Corporation + + This file is part of 0MQ. + + 0MQ is free software; you can redistribute it and/or modify it under + the terms of the Lesser GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + 0MQ is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + Lesser GNU General Public License for more details. + + You should have received a copy of the Lesser GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <string.h> + +#include "../include/zmq.h" + +#include "options.hpp" +#include "err.hpp" + +zmq::options_t::options_t () : + hwm (0), + swap (0), + affinity (0), + rate (100), + recovery_ivl (10), + use_multicast_loop (true), + sndbuf (0), + rcvbuf (0), + requires_in (false), + requires_out (false), + immediate_connect (true) +{ +} + +int zmq::options_t::setsockopt (int option_, const void *optval_, + size_t optvallen_) +{ + switch (option_) { + + case ZMQ_HWM: + if (optvallen_ != sizeof (uint64_t)) { + errno = EINVAL; + return -1; + } + hwm = *((uint64_t*) optval_); + return 0; + + case ZMQ_SWAP: + if (optvallen_ != sizeof (int64_t)) { + errno = EINVAL; + return -1; + } + swap = *((int64_t*) optval_); + return 0; + + case ZMQ_AFFINITY: + if (optvallen_ != sizeof (uint64_t)) { + errno = EINVAL; + return -1; + } + affinity = *((uint64_t*) optval_); + return 0; + + case ZMQ_IDENTITY: + + // Empty identity is invalid as well as identity longer than + // 255 bytes. Identity starting with binary zero is invalid + // as these are used for auto-generated identities. + if (optvallen_ < 1 || optvallen_ > 255 || + *((const unsigned char*) optval_) == 0) { + errno = EINVAL; + return -1; + } + identity.assign ((const unsigned char*) optval_, optvallen_); + return 0; + + case ZMQ_RATE: + if (optvallen_ != sizeof (int64_t)) { + errno = EINVAL; + return -1; + } + rate = (uint32_t) *((int64_t*) optval_); + return 0; + + case ZMQ_RECOVERY_IVL: + if (optvallen_ != sizeof (int64_t)) { + errno = EINVAL; + return -1; + } + recovery_ivl = (uint32_t) *((int64_t*) optval_); + return 0; + + case ZMQ_MCAST_LOOP: + if (optvallen_ != sizeof (int64_t)) { + errno = EINVAL; + return -1; + } + if ((int64_t) *((int64_t*) optval_) == 0) + use_multicast_loop = false; + else if ((int64_t) *((int64_t*) optval_) == 1) + use_multicast_loop = true; + else { + errno = EINVAL; + return -1; + } + return 0; + + case ZMQ_SNDBUF: + if (optvallen_ != sizeof (uint64_t)) { + errno = EINVAL; + return -1; + } + sndbuf = *((uint64_t*) optval_); + return 0; + + case ZMQ_RCVBUF: + if (optvallen_ != sizeof (uint64_t)) { + errno = EINVAL; + return -1; + } + rcvbuf = *((uint64_t*) optval_); + return 0; + } + + errno = EINVAL; + return -1; +} + +int zmq::options_t::getsockopt (int option_, void *optval_, size_t *optvallen_) +{ + switch (option_) { + + case ZMQ_HWM: + if (*optvallen_ < sizeof (uint64_t)) { + errno = EINVAL; + return -1; + } + *((uint64_t*) optval_) = hwm; + *optvallen_ = sizeof (uint64_t); + return 0; + + case ZMQ_SWAP: + if (*optvallen_ < sizeof (int64_t)) { + errno = EINVAL; + return -1; + } + *((int64_t*) optval_) = swap; + *optvallen_ = sizeof (int64_t); + return 0; + + case ZMQ_AFFINITY: + if (*optvallen_ < sizeof (uint64_t)) { + errno = EINVAL; + return -1; + } + *((uint64_t*) optval_) = affinity; + *optvallen_ = sizeof (uint64_t); + return 0; + + case ZMQ_IDENTITY: + if (*optvallen_ < identity.size ()) { + errno = EINVAL; + return -1; + } + memcpy (optval_, identity.data (), identity.size ()); + *optvallen_ = identity.size (); + return 0; + + + case ZMQ_RATE: + if (*optvallen_ < sizeof (int64_t)) { + errno = EINVAL; + return -1; + } + *((int64_t*) optval_) = rate; + *optvallen_ = sizeof (int64_t); + return 0; + + case ZMQ_RECOVERY_IVL: + if (*optvallen_ < sizeof (int64_t)) { + errno = EINVAL; + return -1; + } + *((int64_t*) optval_) = recovery_ivl; + *optvallen_ = sizeof (int64_t); + return 0; + + case ZMQ_MCAST_LOOP: + if (*optvallen_ < sizeof (int64_t)) { + errno = EINVAL; + return -1; + } + *((int64_t*) optval_) = use_multicast_loop ? 1 : 0; + *optvallen_ = sizeof (int64_t); + return 0; + + case ZMQ_SNDBUF: + if (*optvallen_ < sizeof (uint64_t)) { + errno = EINVAL; + return -1; + } + *((uint64_t*) optval_) = sndbuf; + *optvallen_ = sizeof (uint64_t); + return 0; + + case ZMQ_RCVBUF: + if (*optvallen_ < sizeof (uint64_t)) { + errno = EINVAL; + return -1; + } + *((uint64_t*) optval_) = rcvbuf; + *optvallen_ = sizeof (uint64_t); + return 0; + } + + errno = EINVAL; + return -1; +} diff --git a/src/options.hpp b/src/options.hpp new file mode 100644 index 0000000..908b166 --- /dev/null +++ b/src/options.hpp @@ -0,0 +1,68 @@ +/* + Copyright (c) 2007-2010 iMatix Corporation + + This file is part of 0MQ. + + 0MQ is free software; you can redistribute it and/or modify it under + the terms of the Lesser GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + 0MQ is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + Lesser GNU General Public License for more details. + + You should have received a copy of the Lesser GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef __ZMQ_OPTIONS_HPP_INCLUDED__ +#define __ZMQ_OPTIONS_HPP_INCLUDED__ + +#include "stddef.h" +#include "stdint.hpp" +#include "blob.hpp" + +namespace zmq +{ + + struct options_t + { + options_t (); + + int setsockopt (int option_, const void *optval_, size_t optvallen_); + int getsockopt (int option_, void *optval_, size_t *optvallen_); + + uint64_t hwm; + int64_t swap; + uint64_t affinity; + blob_t identity; + + // Maximum tranfer rate [kb/s]. Default 100kb/s. + uint32_t rate; + + // Reliability time interval [s]. Default 10s. + uint32_t recovery_ivl; + + // Enable multicast loopback. Default disabled (false). + bool use_multicast_loop; + + uint64_t sndbuf; + uint64_t rcvbuf; + + // These options are never set by the user directly. Instead they are + // provided by the specific socket type. + bool requires_in; + bool requires_out; + + // If true, when connecting, pipes are created immediately without + // waiting for the connection to be established. That way the socket + // is not aware of the peer's identity, however, it is able to send + // messages straight away. + bool immediate_connect; + }; + +} + +#endif diff --git a/src/owned.cpp b/src/owned.cpp new file mode 100644 index 0000000..d6be444 --- /dev/null +++ b/src/owned.cpp @@ -0,0 +1,71 @@ +/* + Copyright (c) 2007-2010 iMatix Corporation + + This file is part of 0MQ. + + 0MQ is free software; you can redistribute it and/or modify it under + the terms of the Lesser GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + 0MQ is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + Lesser GNU General Public License for more details. + + You should have received a copy of the Lesser GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "owned.hpp" +#include "err.hpp" + +zmq::owned_t::owned_t (object_t *parent_, socket_base_t *owner_) : + object_t (parent_), + owner (owner_), + sent_seqnum (0), + processed_seqnum (0), + shutting_down (false) +{ +} + +zmq::owned_t::~owned_t () +{ +} + +void zmq::owned_t::inc_seqnum () +{ + // NB: This function may be called from a different thread! + sent_seqnum.add (1); +} + +void zmq::owned_t::term () +{ + send_term_req (owner, this); +} + +void zmq::owned_t::process_term () +{ + zmq_assert (!shutting_down); + shutting_down = true; + finalise (); +} + +void zmq::owned_t::process_seqnum () +{ + // Catch up with counter of processed commands. + processed_seqnum++; + finalise (); +} + +void zmq::owned_t::finalise () +{ + // If termination request was already received and there are no more + // commands to wait for, terminate the object. + if (shutting_down && processed_seqnum == sent_seqnum.get ()) { + process_unplug (); + send_term_ack (owner); + delete this; + } +} + diff --git a/src/owned.hpp b/src/owned.hpp new file mode 100644 index 0000000..91189a1 --- /dev/null +++ b/src/owned.hpp @@ -0,0 +1,89 @@ +/* + Copyright (c) 2007-2010 iMatix Corporation + + This file is part of 0MQ. + + 0MQ is free software; you can redistribute it and/or modify it under + the terms of the Lesser GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + 0MQ is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + Lesser GNU General Public License for more details. + + You should have received a copy of the Lesser GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef __ZMQ_OWNED_HPP_INCLUDED__ +#define __ZMQ_OWNED_HPP_INCLUDED__ + +#include "socket_base.hpp" +#include "atomic_counter.hpp" +#include "stdint.hpp" + +namespace zmq +{ + + // Base class for objects owned by individual sockets. Handles + // initialisation and destruction of such objects. + + class owned_t : public object_t + { + public: + + // The object will live in parent's thread, however, its lifetime + // will be managed by its owner socket. + owned_t (object_t *parent_, socket_base_t *owner_); + + // When another owned object wants to send command to this object + // it calls this function to let it know it should not shut down + // before the command is delivered. + void inc_seqnum (); + + protected: + + // Ask owner socket to terminate this object. + void term (); + + // Derived object destroys owned_t. No point in allowing others to + // invoke the destructor. At the same time, it has to be virtual so + // that generic owned_t deallocation mechanism destroys specific type + // of the owned object correctly. + virtual ~owned_t (); + + // io_object_t defines a new handler used to disconnect the object + // from the poller object. Implement the handlen in the derived + // classes to ensure sane cleanup. + virtual void process_unplug () = 0; + + // Socket owning this object. When the socket is being closed it's + // responsible for shutting down this object. + socket_base_t *owner; + + private: + + // Handlers for incoming commands. + void process_term (); + void process_seqnum (); + + void finalise (); + + // Sequence number of the last command sent to this object. + atomic_counter_t sent_seqnum; + + // Sequence number of the last command processed by this object. + uint64_t processed_seqnum; + + // If true, the object is already shutting down. + bool shutting_down; + + owned_t (const owned_t&); + void operator = (const owned_t&); + }; + +} + +#endif diff --git a/src/pair.cpp b/src/pair.cpp new file mode 100644 index 0000000..31524de --- /dev/null +++ b/src/pair.cpp @@ -0,0 +1,139 @@ +/* + Copyright (c) 2007-2010 iMatix Corporation + + This file is part of 0MQ. + + 0MQ is free software; you can redistribute it and/or modify it under + the terms of the Lesser GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + 0MQ is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + Lesser GNU General Public License for more details. + + You should have received a copy of the Lesser GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "../include/zmq.h" + +#include "pair.hpp" +#include "err.hpp" +#include "pipe.hpp" + +zmq::pair_t::pair_t (class app_thread_t *parent_) : + socket_base_t (parent_), + inpipe (NULL), + outpipe (NULL), + alive (true) +{ + options.requires_in = true; + options.requires_out = true; +} + +zmq::pair_t::~pair_t () +{ + if (inpipe) + inpipe->term (); + if (outpipe) + outpipe->term (); +} + +void zmq::pair_t::xattach_pipes (class reader_t *inpipe_, + class writer_t *outpipe_, const blob_t &peer_identity_) +{ + zmq_assert (!inpipe && !outpipe); + inpipe = inpipe_; + outpipe = outpipe_; + outpipe_alive = true; +} + +void zmq::pair_t::xdetach_inpipe (class reader_t *pipe_) +{ + zmq_assert (pipe_ == inpipe); + inpipe = NULL; +} + +void zmq::pair_t::xdetach_outpipe (class writer_t *pipe_) +{ + zmq_assert (pipe_ == outpipe); + outpipe = NULL; +} + +void zmq::pair_t::xkill (class reader_t *pipe_) +{ + zmq_assert (alive); + alive = false; +} + +void zmq::pair_t::xrevive (class reader_t *pipe_) +{ + zmq_assert (!alive); + alive = true; +} + +void zmq::pair_t::xrevive (class writer_t *pipe_) +{ + zmq_assert (!outpipe_alive); + outpipe_alive = true; +} + +int zmq::pair_t::xsetsockopt (int option_, const void *optval_, + size_t optvallen_) +{ + errno = EINVAL; + return -1; +} + +int zmq::pair_t::xsend (zmq_msg_t *msg_, int flags_) +{ + if (outpipe == NULL || !outpipe_alive) { + errno = EAGAIN; + return -1; + } + + if (!outpipe->write (msg_)) { + outpipe_alive = false; + errno = EAGAIN; + return -1; + } + + outpipe->flush (); + + // Detach the original message from the data buffer. + int rc = zmq_msg_init (msg_); + zmq_assert (rc == 0); + + return 0; +} + +int zmq::pair_t::xrecv (zmq_msg_t *msg_, int flags_) +{ + // Deallocate old content of the message. + zmq_msg_close (msg_); + + if (!alive || !inpipe || !inpipe->read (msg_)) { + errno = EAGAIN; + return -1; + } + return 0; +} + +bool zmq::pair_t::xhas_in () +{ + if (alive && inpipe && inpipe->check_read ()) + return true; + return false; +} + +bool zmq::pair_t::xhas_out () +{ + if (outpipe == NULL || !outpipe_alive) + return false; + + outpipe_alive = outpipe->check_write (); + return outpipe_alive; +} + diff --git a/src/pair.hpp b/src/pair.hpp new file mode 100644 index 0000000..aea249f --- /dev/null +++ b/src/pair.hpp @@ -0,0 +1,63 @@ +/* + Copyright (c) 2007-2010 iMatix Corporation + + This file is part of 0MQ. + + 0MQ is free software; you can redistribute it and/or modify it under + the terms of the Lesser GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + 0MQ is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + Lesser GNU General Public License for more details. + + You should have received a copy of the Lesser GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef __ZMQ_PAIR_HPP_INCLUDED__ +#define __ZMQ_PAIR_HPP_INCLUDED__ + +#include "socket_base.hpp" + +namespace zmq +{ + + class pair_t : public socket_base_t + { + public: + + pair_t (class app_thread_t *parent_); + ~pair_t (); + + // Overloads of functions from socket_base_t. + void xattach_pipes (class reader_t *inpipe_, class writer_t *outpipe_, + const blob_t &peer_identity_); + void xdetach_inpipe (class reader_t *pipe_); + void xdetach_outpipe (class writer_t *pipe_); + void xkill (class reader_t *pipe_); + void xrevive (class reader_t *pipe_); + void xrevive (class writer_t *pipe_); + int xsetsockopt (int option_, const void *optval_, size_t optvallen_); + int xsend (zmq_msg_t *msg_, int flags_); + int xrecv (zmq_msg_t *msg_, int flags_); + bool xhas_in (); + bool xhas_out (); + + private: + + class reader_t *inpipe; + class writer_t *outpipe; + + bool alive; + bool outpipe_alive; + + pair_t (const pair_t&); + void operator = (const pair_t&); + }; + +} + +#endif diff --git a/src/pgm_receiver.cpp b/src/pgm_receiver.cpp new file mode 100644 index 0000000..048c529 --- /dev/null +++ b/src/pgm_receiver.cpp @@ -0,0 +1,217 @@ +/* + Copyright (c) 2007-2010 iMatix Corporation + + This file is part of 0MQ. + + 0MQ is free software; you can redistribute it and/or modify it under + the terms of the Lesser GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + 0MQ is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + Lesser GNU General Public License for more details. + + You should have received a copy of the Lesser GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "platform.hpp" + +#if defined ZMQ_HAVE_OPENPGM + +#include <new> + +#ifdef ZMQ_HAVE_WINDOWS +#include "windows.hpp" +#endif + +#include "pgm_receiver.hpp" +#include "err.hpp" +#include "stdint.hpp" +#include "wire.hpp" +#include "i_inout.hpp" + +zmq::pgm_receiver_t::pgm_receiver_t (class io_thread_t *parent_, + const options_t &options_) : + io_object_t (parent_), + pgm_socket (true, options_), + options (options_), + inout (NULL), + mru_decoder (NULL), + pending_bytes (0) +{ +} + +zmq::pgm_receiver_t::~pgm_receiver_t () +{ + // Destructor should not be called before unplug. + zmq_assert (peers.empty ()); +} + +int zmq::pgm_receiver_t::init (bool udp_encapsulation_, const char *network_) +{ + return pgm_socket.init (udp_encapsulation_, network_); +} + +void zmq::pgm_receiver_t::plug (i_inout *inout_) +{ + // Retrieve PGM fds and start polling. + int socket_fd; + int waiting_pipe_fd; + pgm_socket.get_receiver_fds (&socket_fd, &waiting_pipe_fd); + socket_handle = add_fd (socket_fd); + pipe_handle = add_fd (waiting_pipe_fd); + set_pollin (pipe_handle); + set_pollin (socket_handle); + + inout = inout_; +} + +void zmq::pgm_receiver_t::unplug () +{ + // Delete decoders. + for (peers_t::iterator it = peers.begin (); it != peers.end (); it++) { + if (it->second.decoder != NULL) + delete it->second.decoder; + } + peers.clear (); + + mru_decoder = NULL; + pending_bytes = 0; + + // Stop polling. + rm_fd (socket_handle); + rm_fd (pipe_handle); + + inout = NULL; +} + +void zmq::pgm_receiver_t::revive () +{ + zmq_assert (false); +} + +void zmq::pgm_receiver_t::resume_input () +{ + // It is possible that the most recently used decoder + // processed the whole buffer but failed to write + // the last message into the pipe. + if (pending_bytes == 0) { + if (mru_decoder != NULL) + mru_decoder->process_buffer (NULL, 0); + return; + } + + zmq_assert (mru_decoder != NULL); + zmq_assert (pending_ptr != NULL); + + // Ask the decoder to process remaining data. + size_t n = mru_decoder->process_buffer (pending_ptr, pending_bytes); + pending_bytes -= n; + + if (pending_bytes > 0) + return; + + // Resume polling. + set_pollin (pipe_handle); + set_pollin (socket_handle); + + in_event (); +} + +void zmq::pgm_receiver_t::in_event () +{ + // Read data from the underlying pgm_socket. + unsigned char *data = NULL; + const pgm_tsi_t *tsi = NULL; + + zmq_assert (pending_bytes == 0); + + // TODO: This loop can effectively block other engines in the same I/O + // thread in the case of high load. + while (true) { + + // Get new batch of data. + ssize_t received = pgm_socket.receive ((void**) &data, &tsi); + + // No data to process. This may happen if the packet received is + // neither ODATA nor ODATA. + if (received == 0) + break; + + // Find the peer based on its TSI. + peers_t::iterator it = peers.find (*tsi); + + // Data loss. Delete decoder and mark the peer as disjoint. + if (received == -1) { + if (it != peers.end ()) { + it->second.joined = false; + if (it->second.decoder == mru_decoder) + mru_decoder = NULL; + if (it->second.decoder != NULL) { + delete it->second.decoder; + it->second.decoder = NULL; + } + } + break; + } + + // New peer. Add it to the list of know but unjoint peers. + if (it == peers.end ()) { + peer_info_t peer_info = {false, NULL}; + it = peers.insert (std::make_pair (*tsi, peer_info)).first; + } + + // Read the offset of the fist message in the current packet. + zmq_assert ((size_t) received >= sizeof (uint16_t)); + uint16_t offset = get_uint16 (data); + data += sizeof (uint16_t); + received -= sizeof (uint16_t); + + // Join the stream if needed. + if (!it->second.joined) { + + // There is no beginning of the message in current packet. + // Ignore the data. + if (offset == 0xffff) + continue; + + zmq_assert (offset <= received); + zmq_assert (it->second.decoder == NULL); + + // We have to move data to the begining of the first message. + data += offset; + received -= offset; + + // Mark the stream as joined. + it->second.joined = true; + + // Create and connect decoder for the peer. + it->second.decoder = new (std::nothrow) zmq_decoder_t (0); + it->second.decoder->set_inout (inout); + } + + mru_decoder = it->second.decoder; + + // Push all the data to the decoder. + ssize_t processed = it->second.decoder->process_buffer (data, received); + if (processed < received) { + // Save some state so we can resume the decoding process later. + pending_bytes = received - processed; + pending_ptr = data + processed; + // Stop polling. + reset_pollin (pipe_handle); + reset_pollin (socket_handle); + + break; + } + } + + // Flush any messages decoder may have produced. + inout->flush (); +} + +#endif + diff --git a/src/pgm_receiver.hpp b/src/pgm_receiver.hpp new file mode 100644 index 0000000..1b367bf --- /dev/null +++ b/src/pgm_receiver.hpp @@ -0,0 +1,122 @@ +/* + Copyright (c) 2007-2010 iMatix Corporation + + This file is part of 0MQ. + + 0MQ is free software; you can redistribute it and/or modify it under + the terms of the Lesser GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + 0MQ is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + Lesser GNU General Public License for more details. + + You should have received a copy of the Lesser GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef __ZMQ_PGM_RECEIVER_HPP_INCLUDED__ +#define __ZMQ_PGM_RECEIVER_HPP_INCLUDED__ + +#include "platform.hpp" + +#if defined ZMQ_HAVE_OPENPGM + +#ifdef ZMQ_HAVE_WINDOWS +#include "windows.hpp" +#endif + +#include <map> +#include <algorithm> + +#include "io_object.hpp" +#include "i_engine.hpp" +#include "options.hpp" +#include "zmq_decoder.hpp" +#include "pgm_socket.hpp" + +namespace zmq +{ + + class pgm_receiver_t : public io_object_t, public i_engine + { + + public: + + pgm_receiver_t (class io_thread_t *parent_, const options_t &options_); + ~pgm_receiver_t (); + + int init (bool udp_encapsulation_, const char *network_); + + // i_engine interface implementation. + void plug (struct i_inout *inout_); + void unplug (); + void revive (); + void resume_input (); + + // i_poll_events interface implementation. + void in_event (); + + private: + + // If joined is true we are already getting messages from the peer. + // It it's false, we are getting data but still we haven't seen + // beginning of a message. + struct peer_info_t + { + bool joined; + zmq_decoder_t *decoder; + }; + + struct tsi_comp + { + inline bool operator () (const pgm_tsi_t <si, + const pgm_tsi_t &rtsi) const + { + if (ltsi.sport < rtsi.sport) + return true; + + return (std::lexicographical_compare (ltsi.gsi.identifier, + ltsi.gsi.identifier + 6, + rtsi.gsi.identifier, rtsi.gsi.identifier + 6)); + } + }; + + typedef std::map <pgm_tsi_t, peer_info_t, tsi_comp> peers_t; + peers_t peers; + + // PGM socket. + pgm_socket_t pgm_socket; + + // Socket options. + options_t options; + + // Parent session. + i_inout *inout; + + // Most recently used decoder. + zmq_decoder_t *mru_decoder; + + // Number of bytes not consumed by the decoder due to pipe overflow. + size_t pending_bytes; + + // Pointer to data still waiting to be processed by the decoder. + unsigned char *pending_ptr; + + // Poll handle associated with PGM socket. + handle_t socket_handle; + + // Poll handle associated with engine PGM waiting pipe. + handle_t pipe_handle; + + pgm_receiver_t (const pgm_receiver_t&); + void operator = (const pgm_receiver_t&); + }; + +} + +#endif + +#endif diff --git a/src/pgm_sender.cpp b/src/pgm_sender.cpp new file mode 100644 index 0000000..9aeb7a9 --- /dev/null +++ b/src/pgm_sender.cpp @@ -0,0 +1,160 @@ +/* + Copyright (c) 2007-2010 iMatix Corporation + + This file is part of 0MQ. + + 0MQ is free software; you can redistribute it and/or modify it under + the terms of the Lesser GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + 0MQ is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + Lesser GNU General Public License for more details. + + You should have received a copy of the Lesser GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "platform.hpp" + +#if defined ZMQ_HAVE_OPENPGM + +#ifdef ZMQ_HAVE_WINDOWS +#include "windows.hpp" +#endif + +#include <stdlib.h> + +#include "io_thread.hpp" +#include "pgm_sender.hpp" +#include "err.hpp" +#include "wire.hpp" +#include "stdint.hpp" + +zmq::pgm_sender_t::pgm_sender_t (io_thread_t *parent_, + const options_t &options_) : + io_object_t (parent_), + encoder (0), + pgm_socket (false, options_), + options (options_), + out_buffer (NULL), + out_buffer_size (0), + write_size (0) +{ +} + +int zmq::pgm_sender_t::init (bool udp_encapsulation_, const char *network_) +{ + int rc = pgm_socket.init (udp_encapsulation_, network_); + if (rc != 0) + return rc; + + out_buffer_size = pgm_socket.get_max_tsdu_size (); + out_buffer = (unsigned char*) malloc (out_buffer_size); + zmq_assert (out_buffer); + + return rc; +} + +void zmq::pgm_sender_t::plug (i_inout *inout_) +{ + // Alocate 2 fds for PGM socket. + int downlink_socket_fd = 0; + int uplink_socket_fd = 0; + int rdata_notify_fd = 0; + int pending_notify_fd = 0; + + encoder.set_inout (inout_); + + // Fill fds from PGM transport and add them to the poller. + pgm_socket.get_sender_fds (&downlink_socket_fd, &uplink_socket_fd, + &rdata_notify_fd, &pending_notify_fd); + + handle = add_fd (downlink_socket_fd); + uplink_handle = add_fd (uplink_socket_fd); + rdata_notify_handle = add_fd (rdata_notify_fd); + pending_notify_handle = add_fd (pending_notify_fd); + + // Set POLLIN. We wont never want to stop polling for uplink = we never + // want to stop porocess NAKs. + set_pollin (uplink_handle); + set_pollin (rdata_notify_handle); + set_pollin (pending_notify_handle); + + // Set POLLOUT for downlink_socket_handle. + set_pollout (handle); +} + +void zmq::pgm_sender_t::unplug () +{ + rm_fd (handle); + rm_fd (uplink_handle); + rm_fd (rdata_notify_handle); + rm_fd (pending_notify_handle); + encoder.set_inout (NULL); +} + +void zmq::pgm_sender_t::revive () +{ + set_pollout (handle); + out_event (); +} + +void zmq::pgm_sender_t::resume_input () +{ + zmq_assert (false); +} + +zmq::pgm_sender_t::~pgm_sender_t () +{ + if (out_buffer) { + free (out_buffer); + out_buffer = NULL; + } +} + +void zmq::pgm_sender_t::in_event () +{ + // In event on sender side means NAK or SPMR receiving from some peer. + pgm_socket.process_upstream (); +} + +void zmq::pgm_sender_t::out_event () +{ + // POLLOUT event from send socket. If write buffer is empty, + // try to read new data from the encoder. + if (write_size == 0) { + + // First two bytes (sizeof uint16_t) are used to store message + // offset in following steps. Note that by passing our buffer to + // the get data function we prevent it from returning its own buffer. + unsigned char *bf = out_buffer + sizeof (uint16_t); + size_t bfsz = out_buffer_size - sizeof (uint16_t); + int offset = -1; + encoder.get_data (&bf, &bfsz, &offset); + + // If there are no data to write stop polling for output. + if (!bfsz) { + reset_pollout (handle); + return; + } + + // Put offset information in the buffer. + write_size = bfsz + sizeof (uint16_t); + put_uint16 (out_buffer, offset == -1 ? 0xffff : (uint16_t) offset); + } + + // Send the data. + size_t nbytes = pgm_socket.send (out_buffer, write_size); + + // We can write either all data or 0 which means rate limit reached. + if (nbytes == write_size) + write_size = 0; + else + zmq_assert (nbytes == 0); +} + +#endif + diff --git a/src/pgm_sender.hpp b/src/pgm_sender.hpp new file mode 100644 index 0000000..23a53bc --- /dev/null +++ b/src/pgm_sender.hpp @@ -0,0 +1,95 @@ +/* + Copyright (c) 2007-2010 iMatix Corporation + + This file is part of 0MQ. + + 0MQ is free software; you can redistribute it and/or modify it under + the terms of the Lesser GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + 0MQ is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + Lesser GNU General Public License for more details. + + You should have received a copy of the Lesser GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef __ZMQ_PGM_SENDER_HPP_INCLUDED__ +#define __ZMQ_PGM_SENDER_HPP_INCLUDED__ + +#include "platform.hpp" + +#if defined ZMQ_HAVE_OPENPGM + +#ifdef ZMQ_HAVE_WINDOWS +#include "windows.hpp" +#endif + +#include "stdint.hpp" +#include "io_object.hpp" +#include "i_engine.hpp" +#include "options.hpp" +#include "pgm_socket.hpp" +#include "zmq_encoder.hpp" + +namespace zmq +{ + + class pgm_sender_t : public io_object_t, public i_engine + { + + public: + + pgm_sender_t (class io_thread_t *parent_, const options_t &options_); + ~pgm_sender_t (); + + int init (bool udp_encapsulation_, const char *network_); + + // i_engine interface implementation. + void plug (struct i_inout *inout_); + void unplug (); + void revive (); + void resume_input (); + + // i_poll_events interface implementation. + void in_event (); + void out_event (); + + private: + + // Message encoder. + zmq_encoder_t encoder; + + // PGM socket. + pgm_socket_t pgm_socket; + + // Socket options. + options_t options; + + // Poll handle associated with PGM socket. + handle_t handle; + handle_t uplink_handle; + handle_t rdata_notify_handle; + handle_t pending_notify_handle; + + // Output buffer from pgm_socket. + unsigned char *out_buffer; + + // Output buffer size. + size_t out_buffer_size; + + // Number of bytes in the buffer to be written to the socket. + // If zero, there are no data to be sent. + size_t write_size; + + pgm_sender_t (const pgm_sender_t&); + void operator = (const pgm_sender_t&); + }; + +} +#endif + +#endif diff --git a/src/pgm_socket.cpp b/src/pgm_socket.cpp new file mode 100644 index 0000000..5a952a7 --- /dev/null +++ b/src/pgm_socket.cpp @@ -0,0 +1,538 @@ +/* + Copyright (c) 2007-2010 iMatix Corporation + + This file is part of 0MQ. + + 0MQ is free software; you can redistribute it and/or modify it under + the terms of the Lesser GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + 0MQ is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + Lesser GNU General Public License for more details. + + You should have received a copy of the Lesser GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "platform.hpp" + +#ifdef ZMQ_HAVE_OPENPGM + +#ifdef ZMQ_HAVE_WINDOWS +#include "windows.hpp" +#endif + +#ifdef ZMQ_HAVE_LINUX +#include <poll.h> +#endif + +#include <stdlib.h> +#include <string.h> +#include <string> + +#include "options.hpp" +#include "pgm_socket.hpp" +#include "config.hpp" +#include "err.hpp" +#include "uuid.hpp" +#include "stdint.hpp" + +zmq::pgm_socket_t::pgm_socket_t (bool receiver_, const options_t &options_) : + transport (NULL), + options (options_), + receiver (receiver_), + pgm_msgv (NULL), + pgm_msgv_len (0), + nbytes_rec (0), + nbytes_processed (0), + pgm_msgv_processed (0) +{ +} + +int zmq::pgm_socket_t::init (bool udp_encapsulation_, const char *network_) +{ + // Can not open transport before destroying old one. + zmq_assert (transport == NULL); + + // Parse port number. + const char *port_delim = strchr (network_, ':'); + if (!port_delim) { + errno = EINVAL; + return -1; + } + + uint16_t port_number = atoi (port_delim + 1); + + char network [256]; + if (port_delim - network_ >= (int) sizeof (network) - 1) { + errno = EINVAL; + return -1; + } + memset (network, '\0', sizeof (network)); + memcpy (network, network_, port_delim - network_); + + // Zero counter used in msgrecv. + nbytes_rec = 0; + nbytes_processed = 0; + pgm_msgv_processed = 0; + + int rc; + GError *pgm_error = NULL; + + // PGM transport GSI. + pgm_gsi_t gsi; + + std::string gsi_base; + + if (options.identity.size () > 0) { + + // Create gsi from identity. + // TODO: We assume that identity is standard C string here. + // What if it contains binary zeroes? + gsi_base.assign ((const char*) options.identity.data (), + options.identity.size ()); + } else { + + // Generate random gsi. + gsi_base = uuid_t ().to_string (); + } + + rc = pgm_gsi_create_from_string (&gsi, gsi_base.c_str (), -1); + if (rc != TRUE) { + errno = EINVAL; + return -1; + } + + struct pgm_transport_info_t *res = NULL; + struct pgm_transport_info_t hint; + memset (&hint, 0, sizeof (hint)); + hint.ti_family = AF_INET; + + if (!pgm_if_get_transport_info (network, &hint, &res, &pgm_error)) { + if (pgm_error->domain == PGM_IF_ERROR && ( + pgm_error->code == PGM_IF_ERROR_INVAL || + pgm_error->code == PGM_IF_ERROR_XDEV || + pgm_error->code == PGM_IF_ERROR_NODEV || + pgm_error->code == PGM_IF_ERROR_NOTUNIQ || + pgm_error->code == PGM_IF_ERROR_ADDRFAMILY || + pgm_error->code == PGM_IF_ERROR_FAMILY || + pgm_error->code == PGM_IF_ERROR_NODATA || + pgm_error->code == PGM_IF_ERROR_NONAME || + pgm_error->code == PGM_IF_ERROR_SERVICE)) { + g_error_free (pgm_error); + errno = EINVAL; + return -1; + } + + zmq_assert (false); + } + + res->ti_gsi = gsi; + res->ti_dport = port_number; + + // If we are using UDP encapsulation update gsr or res. + if (udp_encapsulation_) { + res->ti_udp_encap_ucast_port = port_number; + res->ti_udp_encap_mcast_port = port_number; + } + + if (!pgm_transport_create (&transport, res, &pgm_error)) { + if (pgm_error->domain == PGM_TRANSPORT_ERROR && ( + pgm_error->code == PGM_TRANSPORT_ERROR_INVAL || + pgm_error->code == PGM_TRANSPORT_ERROR_PERM || + pgm_error->code == PGM_TRANSPORT_ERROR_NODEV)) { + pgm_if_free_transport_info (res); + g_error_free (pgm_error); + errno = EINVAL; + return -1; + } + + zmq_assert (false); + } + + pgm_if_free_transport_info (res); + + // Common parameters for receiver and sender. + + // Set maximum transport protocol data unit size (TPDU). + rc = pgm_transport_set_max_tpdu (transport, pgm_max_tpdu); + if (rc != TRUE) { + errno = EINVAL; + return -1; + } + + // Set maximum number of network hops to cross. + rc = pgm_transport_set_hops (transport, 16); + if (rc != TRUE) { + errno = EINVAL; + return -1; + } + + // Set nonblocking send/recv sockets. + if (!pgm_transport_set_nonblocking (transport, true)) { + errno = EINVAL; + return -1; + } + + if (receiver) { + + // Receiver transport. + + // Note that NAKs are still generated by the transport. + rc = pgm_transport_set_recv_only (transport, true, false); + zmq_assert (rc == TRUE); + + if (options.rcvbuf) { + rc = pgm_transport_set_rcvbuf (transport, (int) options.rcvbuf); + if (rc != TRUE) + return -1; + } + + // Set NAK transmit back-off interval [us]. + rc = pgm_transport_set_nak_bo_ivl (transport, 50 * 1000); + zmq_assert (rc == TRUE); + + // Set timeout before repeating NAK [us]. + rc = pgm_transport_set_nak_rpt_ivl (transport, 200 * 1000); + zmq_assert (rc == TRUE); + + // Set timeout for receiving RDATA. + rc = pgm_transport_set_nak_rdata_ivl (transport, 200 * 1000); + zmq_assert (rc == TRUE); + + // Set retries for NAK without NCF/DATA (NAK_DATA_RETRIES). + rc = pgm_transport_set_nak_data_retries (transport, 5); + zmq_assert (rc == TRUE); + + // Set retries for NCF after NAK (NAK_NCF_RETRIES). + rc = pgm_transport_set_nak_ncf_retries (transport, 2); + zmq_assert (rc == TRUE); + + // Set timeout for removing a dead peer [us]. + rc = pgm_transport_set_peer_expiry (transport, 5 * 8192 * 1000); + zmq_assert (rc == TRUE); + + // Set expiration time of SPM Requests [us]. + rc = pgm_transport_set_spmr_expiry (transport, 25 * 1000); + zmq_assert (rc == TRUE); + + // Set the size of the receive window. + // Data rate is in [B/s]. options.rate is in [kb/s]. + if (options.rate <= 0) { + errno = EINVAL; + return -1; + } + rc = pgm_transport_set_rxw_max_rte (transport, + options.rate * 1000 / 8); + if (rc != TRUE) { + errno = EINVAL; + return -1; + } + + // Recovery interval [s]. + if (options.recovery_ivl <= 0) { + errno = EINVAL; + return -1; + } + rc = pgm_transport_set_rxw_secs (transport, options.recovery_ivl); + if (rc != TRUE) { + errno = EINVAL; + return -1; + } + + } else { + + // Sender transport. + + // Waiting pipe won't be read. + rc = pgm_transport_set_send_only (transport, TRUE); + zmq_assert (rc == TRUE); + + if (options.sndbuf) { + rc = pgm_transport_set_sndbuf (transport, (int) options.sndbuf); + if (rc != TRUE) + return -1; + } + + // Set the size of the send window. + // Data rate is in [B/s] options.rate is in [kb/s]. + if (options.rate <= 0) { + errno = EINVAL; + return -1; + } + rc = pgm_transport_set_txw_max_rte (transport, + options.rate * 1000 / 8); + if (rc != TRUE) { + errno = EINVAL; + return -1; + } + + // Recovery interval [s]. + if (options.recovery_ivl <= 0) { + errno = EINVAL; + return -1; + } + rc = pgm_transport_set_txw_secs (transport, options.recovery_ivl); + if (rc != TRUE) { + errno = EINVAL; + return -1; + } + + // Set interval of background SPM packets [us]. + rc = pgm_transport_set_ambient_spm (transport, 8192 * 1000); + zmq_assert (rc == TRUE); + + // Set intervals of data flushing SPM packets [us]. + guint spm_heartbeat[] = {4 * 1000, 4 * 1000, 8 * 1000, 16 * 1000, + 32 * 1000, 64 * 1000, 128 * 1000, 256 * 1000, 512 * 1000, + 1024 * 1000, 2048 * 1000, 4096 * 1000, 8192 * 1000}; + rc = pgm_transport_set_heartbeat_spm (transport, spm_heartbeat, + G_N_ELEMENTS(spm_heartbeat)); + zmq_assert (rc == TRUE); + } + + // Enable multicast loopback. + if (options.use_multicast_loop) { + rc = pgm_transport_set_multicast_loop (transport, true); + zmq_assert (rc == TRUE); + } + + // Bind a transport to the specified network devices. + if (!pgm_transport_bind (transport, &pgm_error)) { + if (pgm_error->domain == PGM_IF_ERROR && ( + pgm_error->code == PGM_IF_ERROR_INVAL || + pgm_error->code == PGM_IF_ERROR_XDEV || + pgm_error->code == PGM_IF_ERROR_NODEV || + pgm_error->code == PGM_IF_ERROR_NOTUNIQ || + pgm_error->code == PGM_IF_ERROR_ADDRFAMILY || + pgm_error->code == PGM_IF_ERROR_FAMILY || + pgm_error->code == PGM_IF_ERROR_NODATA || + pgm_error->code == PGM_IF_ERROR_NONAME || + pgm_error->code == PGM_IF_ERROR_SERVICE)) { + g_error_free (pgm_error); + errno = EINVAL; + return -1; + } + if (pgm_error->domain == PGM_TRANSPORT_ERROR && ( + pgm_error->code == PGM_TRANSPORT_ERROR_FAILED)) { + g_error_free (pgm_error); + errno = EINVAL; + return -1; + } + + zmq_assert (false); + } + + // For receiver transport preallocate pgm_msgv array. + // TODO: ? + if (receiver) { + zmq_assert (in_batch_size > 0); + size_t max_tsdu_size = get_max_tsdu_size (); + pgm_msgv_len = (int) in_batch_size / max_tsdu_size; + if ((int) in_batch_size % max_tsdu_size) + pgm_msgv_len++; + zmq_assert (pgm_msgv_len); + + pgm_msgv = (pgm_msgv_t*) malloc (sizeof (pgm_msgv_t) * pgm_msgv_len); + } + + return 0; +} + +zmq::pgm_socket_t::~pgm_socket_t () +{ + if (pgm_msgv) + free (pgm_msgv); + if (transport) + pgm_transport_destroy (transport, TRUE); +} + +// Get receiver fds. recv_fd is from transport->recv_sock +// waiting_pipe_fd is from transport->waiting_pipe [0] +void zmq::pgm_socket_t::get_receiver_fds (int *receive_fd_, + int *waiting_pipe_fd_) +{ + zmq_assert (receive_fd_); + zmq_assert (waiting_pipe_fd_); + + // recv_sock2 should not be used - check it. + zmq_assert (transport->recv_sock2 == -1); + + // Check if transport can receive data and can not send. + zmq_assert (transport->can_recv_data); + zmq_assert (!transport->can_send_data); + + // Take FDs directly from transport. + *receive_fd_ = pgm_transport_get_recv_fd (transport); + *waiting_pipe_fd_ = pgm_transport_get_pending_fd (transport); +} + +// Get fds and store them into user allocated memory. +// sender_fd is from pgm_transport->send_sock. +// receive_fd_ is from transport->recv_sock. +// rdata_notify_fd_ is from transport->rdata_notify. +// pending_notify_fd_ is from transport->pending_notify. +void zmq::pgm_socket_t::get_sender_fds (int *send_fd_, int *receive_fd_, + int *rdata_notify_fd_, int *pending_notify_fd_) +{ + zmq_assert (send_fd_); + zmq_assert (receive_fd_); + + zmq_assert (rdata_notify_fd_); + zmq_assert (pending_notify_fd_); + + // recv_sock2 should not be used - check it. + zmq_assert (transport->recv_sock2 == -1); + + // Check if transport can send data and can not receive. + zmq_assert (transport->can_send_data); + zmq_assert (!transport->can_recv_data); + + // Take FDs from transport. + *send_fd_ = pgm_transport_get_send_fd (transport); + *receive_fd_ = pgm_transport_get_recv_fd (transport); + + *rdata_notify_fd_ = pgm_transport_get_repair_fd (transport); + *pending_notify_fd_ = pgm_transport_get_pending_fd (transport); +} + +// Send one APDU, transmit window owned memory. +size_t zmq::pgm_socket_t::send (unsigned char *data_, size_t data_len_) +{ + size_t nbytes = 0; + + PGMIOStatus status = pgm_send (transport, data_, data_len_, &nbytes); + + if (nbytes != data_len_) { + zmq_assert (status == PGM_IO_STATUS_RATE_LIMITED); + zmq_assert (nbytes == 0); + } + + // We have to write all data as one packet. + if (nbytes > 0) + zmq_assert ((ssize_t) nbytes == (ssize_t) data_len_); + + return nbytes; +} + +// Return max TSDU size without fragmentation from current PGM transport. +size_t zmq::pgm_socket_t::get_max_tsdu_size () +{ + return (size_t) pgm_transport_max_tsdu (transport, false); +} + +// pgm_transport_recvmsgv is called to fill the pgm_msgv array up to +// pgm_msgv_len. In subsequent calls data from pgm_msgv structure are +// returned. +ssize_t zmq::pgm_socket_t::receive (void **raw_data_, const pgm_tsi_t **tsi_) +{ + size_t raw_data_len = 0; + + // We just sent all data from pgm_transport_recvmsgv up + // and have to return 0 that another engine in this thread is scheduled. + if (nbytes_rec == nbytes_processed && nbytes_rec > 0) { + + // Reset all the counters. + nbytes_rec = 0; + nbytes_processed = 0; + pgm_msgv_processed = 0; + return 0; + } + + // If we have are going first time or if we have processed all pgm_msgv_t + // structure previously read from the pgm socket. + if (nbytes_rec == nbytes_processed) { + + // Check program flow. + zmq_assert (pgm_msgv_processed == 0); + zmq_assert (nbytes_processed == 0); + zmq_assert (nbytes_rec == 0); + + // Receive a vector of Application Protocol Domain Unit's (APDUs) + // from the transport. + GError *pgm_error = NULL; + + const PGMIOStatus status = pgm_recvmsgv (transport, pgm_msgv, + pgm_msgv_len, MSG_DONTWAIT, &nbytes_rec, &pgm_error); + + zmq_assert (status != PGM_IO_STATUS_ERROR); + + // In a case when no ODATA/RDATA fired POLLIN event (SPM...) + // pgm_recvmsg returns ?. + if (status == PGM_IO_STATUS_TIMER_PENDING) { + + zmq_assert (nbytes_rec == 0); + + // In case if no RDATA/ODATA caused POLLIN 0 is + // returned. + nbytes_rec = 0; + return 0; + } + + // Data loss. + if (status == PGM_IO_STATUS_RESET) { + + pgm_peer_t* peer = (pgm_peer_t*) transport->peers_pending->data; + + // Save lost data TSI. + *tsi_ = &peer->tsi; + nbytes_rec = 0; + + // In case of dala loss -1 is returned. + errno = EINVAL; + g_error_free (pgm_error); + return -1; + } + + zmq_assert (status == PGM_IO_STATUS_NORMAL); + } + else + { + zmq_assert (pgm_msgv_processed <= pgm_msgv_len); + } + + zmq_assert (nbytes_rec > 0); + + // Only one APDU per pgm_msgv_t structure is allowed. + zmq_assert (pgm_msgv [pgm_msgv_processed].msgv_len == 1); + + struct pgm_sk_buff_t* skb = + pgm_msgv [pgm_msgv_processed].msgv_skb [0]; + + // Take pointers from pgm_msgv_t structure. + *raw_data_ = skb->data; + raw_data_len = skb->len; + + // Save current TSI. + *tsi_ = &skb->tsi; + + // Move the the next pgm_msgv_t structure. + pgm_msgv_processed++; + zmq_assert (pgm_msgv_processed <= pgm_msgv_len); + nbytes_processed +=raw_data_len; + + return raw_data_len; +} + +void zmq::pgm_socket_t::process_upstream () +{ + pgm_msgv_t dummy_msg; + + size_t dummy_bytes = 0; + GError *pgm_error = NULL; + + PGMIOStatus status = pgm_recvmsgv (transport, &dummy_msg, + 1, MSG_DONTWAIT, &dummy_bytes, &pgm_error); + + zmq_assert (status != PGM_IO_STATUS_ERROR); + + // No data should be returned. + zmq_assert (dummy_bytes == 0 && (status == PGM_IO_STATUS_TIMER_PENDING || + status == PGM_IO_STATUS_RATE_LIMITED)); +} + +#endif + diff --git a/src/pgm_socket.hpp b/src/pgm_socket.hpp new file mode 100644 index 0000000..b9f55d1 --- /dev/null +++ b/src/pgm_socket.hpp @@ -0,0 +1,105 @@ +/* + Copyright (c) 2007-2010 iMatix Corporation + + This file is part of 0MQ. + + 0MQ is free software; you can redistribute it and/or modify it under + the terms of the Lesser GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + 0MQ is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + Lesser GNU General Public License for more details. + + You should have received a copy of the Lesser GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef __PGM_SOCKET_HPP_INCLUDED__ +#define __PGM_SOCKET_HPP_INCLUDED__ + +#include "platform.hpp" + +#if defined ZMQ_HAVE_OPENPGM + +#ifdef ZMQ_HAVE_WINDOWS +#include "windows.hpp" +#endif + +#include <pgm/pgm.h> + +#include "options.hpp" + +namespace zmq +{ + // Encapsulates PGM socket. + class pgm_socket_t + { + + public: + // If receiver_ is true PGM transport is not generating SPM packets. + // interface format: iface;mcast_group:port for raw PGM socket + // udp:iface;mcast_goup:port for UDP encapsulacion + pgm_socket_t (bool receiver_, const options_t &options_); + + // Closes the transport. + ~pgm_socket_t (); + + // Initialize PGM network structures (GSI, GSRs). + int init (bool udp_encapsulation_, const char *network_); + + // Get receiver fds and store them into user allocated memory. + void get_receiver_fds (int *receive_fd_, int *waiting_pipe_fd_); + + // Get sender and receiver fds and store it to user allocated + // memory. Receive fd is used to process NAKs from peers. + void get_sender_fds (int *send_fd_, int *receive_fd_, + int *rdata_notify_fd_, int *pending_notify_fd_); + + // Send data as one APDU, transmit window owned memory. + size_t send (unsigned char *data_, size_t data_len_); + + // Returns max tsdu size without fragmentation. + size_t get_max_tsdu_size (); + + // Receive data from pgm socket. + ssize_t receive (void **data_, const pgm_tsi_t **tsi_); + + // POLLIN on sender side should mean NAK or SPMR receiving. + // process_upstream function is used to handle such a situation. + void process_upstream (); + + private: + + // OpenPGM transport + pgm_transport_t* transport; + + // Associated socket options. + options_t options; + + // true when pgm_socket should create receiving side. + bool receiver; + + // Array of pgm_msgv_t structures to store received data + // from the socket (pgm_transport_recvmsgv). + pgm_msgv_t *pgm_msgv; + + // Size of pgm_msgv array. + size_t pgm_msgv_len; + + // How many bytes were read from pgm socket. + size_t nbytes_rec; + + // How many bytes were processed from last pgm socket read. + size_t nbytes_processed; + + // How many messages from pgm_msgv were already sent up. + size_t pgm_msgv_processed; + }; +} +#endif + +#endif + diff --git a/src/pipe.cpp b/src/pipe.cpp new file mode 100644 index 0000000..1df64e9 --- /dev/null +++ b/src/pipe.cpp @@ -0,0 +1,281 @@ +/* + Copyright (c) 2007-2010 iMatix Corporation + + This file is part of 0MQ. + + 0MQ is free software; you can redistribute it and/or modify it under + the terms of the Lesser GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + 0MQ is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + Lesser GNU General Public License for more details. + + You should have received a copy of the Lesser GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "../include/zmq.h" + +#include "pipe.hpp" + +zmq::reader_t::reader_t (object_t *parent_, + uint64_t hwm_, uint64_t lwm_) : + object_t (parent_), + pipe (NULL), + peer (NULL), + hwm (hwm_), + lwm (lwm_), + msgs_read (0), + endpoint (NULL) +{ + // Adjust lwm and hwm. + if (lwm == 0 || lwm > hwm) + lwm = hwm; +} + +zmq::reader_t::~reader_t () +{ + if (pipe) + unregister_pipe (pipe); +} + +void zmq::reader_t::set_pipe (pipe_t *pipe_) +{ + zmq_assert (!pipe); + pipe = pipe_; + peer = &pipe->writer; + register_pipe (pipe); +} + +bool zmq::reader_t::check_read () +{ + // Check if there's an item in the pipe. + if (pipe->check_read ()) + return true; + + // If not, deactivate the pipe. + endpoint->kill (this); + return false; +} + +bool zmq::reader_t::read (zmq_msg_t *msg_) +{ + if (!pipe->read (msg_)) { + endpoint->kill (this); + return false; + } + + // If delimiter was read, start termination process of the pipe. + unsigned char *offset = 0; + if (msg_->content == (void*) (offset + ZMQ_DELIMITER)) { + if (endpoint) + endpoint->detach_inpipe (this); + term (); + return false; + } + + if (!(msg_->flags & ZMQ_MSG_MORE)) + msgs_read++; + + if (lwm > 0 && msgs_read % lwm == 0) + send_reader_info (peer, msgs_read); + + return true; +} + +void zmq::reader_t::set_endpoint (i_endpoint *endpoint_) +{ + endpoint = endpoint_; +} + +void zmq::reader_t::term () +{ + endpoint = NULL; + send_pipe_term (peer); +} + +void zmq::reader_t::process_revive () +{ + // Beacuse of command throttling mechanism, incoming termination request + // may not have been processed before subsequent send. + // In that case endpoint is NULL. + if (endpoint) + endpoint->revive (this); +} + +void zmq::reader_t::process_pipe_term_ack () +{ + peer = NULL; + delete pipe; +} + +zmq::writer_t::writer_t (object_t *parent_, + uint64_t hwm_, uint64_t lwm_) : + object_t (parent_), + pipe (NULL), + peer (NULL), + hwm (hwm_), + lwm (lwm_), + msgs_read (0), + msgs_written (0), + stalled (false), + endpoint (NULL) +{ + // Adjust lwm and hwm. + if (lwm == 0 || lwm > hwm) + lwm = hwm; +} + +void zmq::writer_t::set_endpoint (i_endpoint *endpoint_) +{ + endpoint = endpoint_; +} + +zmq::writer_t::~writer_t () +{ +} + +void zmq::writer_t::set_pipe (pipe_t *pipe_) +{ + zmq_assert (!pipe); + pipe = pipe_; + peer = &pipe->reader; +} + +bool zmq::writer_t::check_write () +{ + if (pipe_full ()) { + stalled = true; + return false; + } + + return true; +} + +bool zmq::writer_t::write (zmq_msg_t *msg_) +{ + if (pipe_full ()) { + stalled = true; + return false; + } + + pipe->write (*msg_, msg_->flags & ZMQ_MSG_MORE); + if (!(msg_->flags & ZMQ_MSG_MORE)) + msgs_written++; + return true; +} + +void zmq::writer_t::rollback () +{ + zmq_msg_t msg; + + // Remove all incomplete messages from the pipe. + while (pipe->unwrite (&msg)) { + zmq_assert (msg.flags & ZMQ_MSG_MORE); + zmq_msg_close (&msg); + msgs_written--; + } + + if (stalled && endpoint != NULL && !pipe_full()) { + stalled = false; + endpoint->revive (this); + } +} + +void zmq::writer_t::flush () +{ + if (!pipe->flush ()) + send_revive (peer); +} + +void zmq::writer_t::term () +{ + endpoint = NULL; + + // Rollback any unfinished messages. + rollback (); + + // Push delimiter into the pipe. + // Trick the compiler to belive that the tag is a valid pointer. + zmq_msg_t msg; + const unsigned char *offset = 0; + msg.content = (void*) (offset + ZMQ_DELIMITER); + msg.flags = 0; + pipe->write (msg, false); + pipe->flush (); +} + +void zmq::writer_t::process_reader_info (uint64_t msgs_read_) +{ + msgs_read = msgs_read_; + if (stalled && endpoint != NULL) { + stalled = false; + endpoint->revive (this); + } +} + +void zmq::writer_t::process_pipe_term () +{ + if (endpoint) + endpoint->detach_outpipe (this); + + reader_t *p = peer; + peer = NULL; + send_pipe_term_ack (p); +} + +bool zmq::writer_t::pipe_full () +{ + return hwm > 0 && msgs_written - msgs_read == hwm; +} + +zmq::pipe_t::pipe_t (object_t *reader_parent_, object_t *writer_parent_, + uint64_t hwm_) : + reader (reader_parent_, hwm_, compute_lwm (hwm_)), + writer (writer_parent_, hwm_, compute_lwm (hwm_)) +{ + reader.set_pipe (this); + writer.set_pipe (this); +} + +zmq::pipe_t::~pipe_t () +{ + // Deallocate all the unread messages in the pipe. We have to do it by + // hand because zmq_msg_t is a POD, not a class, so there's no associated + // destructor. + zmq_msg_t msg; + while (read (&msg)) + zmq_msg_close (&msg); +} + +uint64_t zmq::pipe_t::compute_lwm (uint64_t hwm_) +{ + // Following point should be taken into consideration when computing + // low watermark: + // + // 1. LWM has to be less than HWM. + // 2. LWM cannot be set to very low value (such as zero) as after filling + // the queue it would start to refill only after all the messages are + // read from it and thus unnecessarily hold the progress back. + // 3. LWM cannot be set to very high value (such as HWM-1) as it would + // result in lock-step filling of the queue - if a single message is read + // from a full queue, writer thread is resumed to write exactly one + // message to the queue and go back to sleep immediately. This would + // result in low performance. + // + // Given the 3. it would be good to keep HWM and LWM as far apart as + // possible to reduce the thread switching overhead to almost zero, + // say HWM-LWM should be 500 (max_wm_delta). + // + // That done, we still we have to account for the cases where HWM<500 thus + // driving LWM to negative numbers. Let's make LWM 1/2 of HWM in such cases. + + if (hwm_ > max_wm_delta * 2) + return hwm_ - max_wm_delta; + else + return hwm_ / 2; +} + diff --git a/src/pipe.hpp b/src/pipe.hpp new file mode 100644 index 0000000..9f57653 --- /dev/null +++ b/src/pipe.hpp @@ -0,0 +1,169 @@ +/* + Copyright (c) 2007-2010 iMatix Corporation + + This file is part of 0MQ. + + 0MQ is free software; you can redistribute it and/or modify it under + the terms of the Lesser GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + 0MQ is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + Lesser GNU General Public License for more details. + + You should have received a copy of the Lesser GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef __ZMQ_PIPE_HPP_INCLUDED__ +#define __ZMQ_PIPE_HPP_INCLUDED__ + +#include "../include/zmq.h" + +#include "stdint.hpp" +#include "i_endpoint.hpp" +#include "yarray_item.hpp" +#include "ypipe.hpp" +#include "config.hpp" +#include "object.hpp" + +namespace zmq +{ + + class reader_t : public object_t, public yarray_item_t + { + public: + + reader_t (class object_t *parent_, + uint64_t hwm_, uint64_t lwm_); + ~reader_t (); + + void set_pipe (class pipe_t *pipe_); + void set_endpoint (i_endpoint *endpoint_); + + // Returns true if there is at least one message to read in the pipe. + bool check_read (); + + // Reads a message to the underlying pipe. + bool read (zmq_msg_t *msg_); + + // Ask pipe to terminate. + void term (); + + private: + + // Command handlers. + void process_revive (); + void process_pipe_term_ack (); + + // The underlying pipe. + class pipe_t *pipe; + + // Pipe writer associated with the other side of the pipe. + class writer_t *peer; + + // High and low watermarks for in-memory storage (in bytes). + uint64_t hwm; + uint64_t lwm; + + // Number of messages read so far. + uint64_t msgs_read; + + // Endpoint (either session or socket) the pipe is attached to. + i_endpoint *endpoint; + + reader_t (const reader_t&); + void operator = (const reader_t&); + }; + + class writer_t : public object_t, public yarray_item_t + { + public: + + writer_t (class object_t *parent_, + uint64_t hwm_, uint64_t lwm_); + ~writer_t (); + + void set_pipe (class pipe_t *pipe_); + void set_endpoint (i_endpoint *endpoint_); + + // Checks whether a message can be written to the pipe. + // If writing the message would cause high watermark to be + // exceeded, the function returns false. + bool check_write (); + + // Writes a message to the underlying pipe. Returns false if the + // message cannot be written because high watermark was reached. + bool write (zmq_msg_t *msg_); + + // Remove unfinished part of a message from the pipe. + void rollback (); + + // Flush the messages downsteam. + void flush (); + + // Ask pipe to terminate. + void term (); + + private: + + void process_reader_info (uint64_t msgs_read_); + + // Command handlers. + void process_pipe_term (); + + // Tests whether the pipe is already full. + bool pipe_full (); + + // The underlying pipe. + class pipe_t *pipe; + + // Pipe reader associated with the other side of the pipe. + class reader_t *peer; + + // High and low watermarks for in-memory storage (in bytes). + uint64_t hwm; + uint64_t lwm; + + // Last confirmed number of messages read from the pipe. + // The actual number can be higher. + uint64_t msgs_read; + + // Number of messages we have written so far. + uint64_t msgs_written; + + // True iff the last attempt to write a message has failed. + bool stalled; + + // Endpoint (either session or socket) the pipe is attached to. + i_endpoint *endpoint; + + writer_t (const writer_t&); + void operator = (const writer_t&); + }; + + // Message pipe. + class pipe_t : public ypipe_t <zmq_msg_t, message_pipe_granularity> + { + public: + + pipe_t (object_t *reader_parent_, object_t *writer_parent_, + uint64_t hwm_); + ~pipe_t (); + + reader_t reader; + writer_t writer; + + private: + + uint64_t compute_lwm (uint64_t hwm_); + + pipe_t (const pipe_t&); + void operator = (const pipe_t&); + }; + +} + +#endif diff --git a/src/platform.hpp.in b/src/platform.hpp.in new file mode 100644 index 0000000..a885c21 --- /dev/null +++ b/src/platform.hpp.in @@ -0,0 +1,228 @@ +/* src/platform.hpp.in. Generated from configure.in by autoheader. */ + +/* Define to 1 if you have the <arpa/inet.h> header file. */ +#undef HAVE_ARPA_INET_H + +/* Define to 1 if you have the <dlfcn.h> header file. */ +#undef HAVE_DLFCN_H + +/* Define to 1 if you have the <errno.h> header file. */ +#undef HAVE_ERRNO_H + +/* Define to 1 if you have the `freeifaddrs' function. */ +#undef HAVE_FREEIFADDRS + +/* Define to 1 if you have the `getifaddrs' function. */ +#undef HAVE_GETIFADDRS + +/* Define to 1 if you have the `gettimeofday' function. */ +#undef HAVE_GETTIMEOFDAY + +/* Define to 1 if you have the <ifaddrs.h> header file. */ +#undef HAVE_IFADDRS_H + +/* Define to 1 if you have the <inttypes.h> header file. */ +#undef HAVE_INTTYPES_H + +/* Define to 1 if you have the `iphlpapi' library (-liphlpapi). */ +#undef HAVE_LIBIPHLPAPI + +/* Define to 1 if you have the `nsl' library (-lnsl). */ +#undef HAVE_LIBNSL + +/* Define to 1 if you have the `pthread' library (-lpthread). */ +#undef HAVE_LIBPTHREAD + +/* Define to 1 if you have the `rpcrt4' library (-lrpcrt4). */ +#undef HAVE_LIBRPCRT4 + +/* Define to 1 if you have the `rt' library (-lrt). */ +#undef HAVE_LIBRT + +/* Define to 1 if you have the `socket' library (-lsocket). */ +#undef HAVE_LIBSOCKET + +/* Define to 1 if you have the `uuid' library (-luuid). */ +#undef HAVE_LIBUUID + +/* Define to 1 if you have the `ws2_32' library (-lws2_32). */ +#undef HAVE_LIBWS2_32 + +/* Define to 1 if you have the <limits.h> header file. */ +#undef HAVE_LIMITS_H + +/* Define to 1 if you have the <memory.h> header file. */ +#undef HAVE_MEMORY_H + +/* Define to 1 if you have the `memset' function. */ +#undef HAVE_MEMSET + +/* Define to 1 if you have the <netinet/in.h> header file. */ +#undef HAVE_NETINET_IN_H + +/* Define to 1 if you have the <netinet/tcp.h> header file. */ +#undef HAVE_NETINET_TCP_H + +/* Define to 1 if you have the `perror' function. */ +#undef HAVE_PERROR + +/* Define to 1 if you have the `socket' function. */ +#undef HAVE_SOCKET + +/* Define to 1 if stdbool.h conforms to C99. */ +#undef HAVE_STDBOOL_H + +/* Define to 1 if you have the <stddef.h> header file. */ +#undef HAVE_STDDEF_H + +/* Define to 1 if you have the <stdint.h> header file. */ +#undef HAVE_STDINT_H + +/* Define to 1 if you have the <stdlib.h> header file. */ +#undef HAVE_STDLIB_H + +/* Define to 1 if you have the <strings.h> header file. */ +#undef HAVE_STRINGS_H + +/* Define to 1 if you have the <string.h> header file. */ +#undef HAVE_STRING_H + +/* Define to 1 if you have the <sys/socket.h> header file. */ +#undef HAVE_SYS_SOCKET_H + +/* Define to 1 if you have the <sys/stat.h> header file. */ +#undef HAVE_SYS_STAT_H + +/* Define to 1 if you have the <sys/time.h> header file. */ +#undef HAVE_SYS_TIME_H + +/* Define to 1 if you have the <sys/types.h> header file. */ +#undef HAVE_SYS_TYPES_H + +/* Define to 1 if you have the <unistd.h> header file. */ +#undef HAVE_UNISTD_H + +/* Define to 1 if you have the <windows.h> header file. */ +#undef HAVE_WINDOWS_H + +/* Define to 1 if the system has the type `_Bool'. */ +#undef HAVE__BOOL + +/* Define to the sub-directory in which libtool stores uninstalled libraries. + */ +#undef LT_OBJDIR + +/* Define to 1 if your C compiler doesn't accept -c and -o together. */ +#undef NO_MINUS_C_MINUS_O + +/* Name of package */ +#undef PACKAGE + +/* Define to the address where bug reports for this package should be sent. */ +#undef PACKAGE_BUGREPORT + +/* Define to the full name of this package. */ +#undef PACKAGE_NAME + +/* Define to the full name and version of this package. */ +#undef PACKAGE_STRING + +/* Define to the one symbol short name of this package. */ +#undef PACKAGE_TARNAME + +/* Define to the version of this package. */ +#undef PACKAGE_VERSION + +/* 0MQ major version */ +#undef PACKAGE_VERSION_MAJOR + +/* 0MQ minor version */ +#undef PACKAGE_VERSION_MINOR + +/* 0MQ patchlevel */ +#undef PACKAGE_VERSION_PATCH + +/* Define as the return type of signal handlers (`int' or `void'). */ +#undef RETSIGTYPE + +/* Define to 1 if you have the ANSI C header files. */ +#undef STDC_HEADERS + +/* Define to 1 if you can safely include both <sys/time.h> and <time.h>. */ +#undef TIME_WITH_SYS_TIME + +/* Version number of package */ +#undef VERSION + +/* Force to use mutexes */ +#undef ZMQ_FORCE_MUTEXES + +/* Have AIX OS */ +#undef ZMQ_HAVE_AIX + +/* Have Cygwin */ +#undef ZMQ_HAVE_CYGWIN + +/* Have FreeBSD OS */ +#undef ZMQ_HAVE_FREEBSD + +/* Have HPUX OS */ +#undef ZMQ_HAVE_HPUX + +/* Have ifaddrs.h header. */ +#undef ZMQ_HAVE_IFADDRS + +/* Have Linux OS */ +#undef ZMQ_HAVE_LINUX + +/* Have MinGW32 */ +#undef ZMQ_HAVE_MINGW32 + +/* Have NetBSD OS */ +#undef ZMQ_HAVE_NETBSD + +/* Have OpenBSD OS */ +#undef ZMQ_HAVE_OPENBSD + +/* Have OpenPGM extension */ +#undef ZMQ_HAVE_OPENPGM + +/* Have DarwinOSX OS */ +#undef ZMQ_HAVE_OSX + +/* Have QNX Neutrino OS */ +#undef ZMQ_HAVE_QNXNTO + +/* Have Solaris OS */ +#undef ZMQ_HAVE_SOLARIS + +/* Have Windows OS */ +#undef ZMQ_HAVE_WINDOWS + +/* Define for Solaris 2.5.1 so the uint32_t typedef from <sys/synch.h>, + <pthread.h>, or <semaphore.h> is not used. If the typedef was allowed, the + #define below would cause a syntax error. */ +#undef _UINT32_T + +/* Define to empty if `const' does not conform to ANSI C. */ +#undef const + +/* Define to `__inline__' or `__inline' if that's what the C compiler + calls it, or to nothing if 'inline' is not supported under any name. */ +#ifndef __cplusplus +#undef inline +#endif + +/* Define to `unsigned int' if <sys/types.h> does not define. */ +#undef size_t + +/* Define to `int' if <sys/types.h> does not define. */ +#undef ssize_t + +/* Define to the type of an unsigned integer type of width exactly 32 bits if + such a type exists and the standard includes do not define it. */ +#undef uint32_t + +/* Define to empty if the keyword `volatile' does not work. Warning: valid + code using `volatile' can become incorrect without. Disable with care. */ +#undef volatile diff --git a/src/poll.cpp b/src/poll.cpp new file mode 100644 index 0000000..4214195 --- /dev/null +++ b/src/poll.cpp @@ -0,0 +1,207 @@ +/* + Copyright (c) 2007-2010 iMatix Corporation + + This file is part of 0MQ. + + 0MQ is free software; you can redistribute it and/or modify it under + the terms of the Lesser GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + 0MQ is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + Lesser GNU General Public License for more details. + + You should have received a copy of the Lesser GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "platform.hpp" + +#if defined ZMQ_HAVE_LINUX || defined ZMQ_HAVE_FREEBSD ||\ + defined ZMQ_HAVE_OPENBSD || defined ZMQ_HAVE_SOLARIS ||\ + defined ZMQ_HAVE_OSX || defined ZMQ_HAVE_QNXNTO ||\ + defined ZMQ_HAVE_HPUX || defined ZMQ_HAVE_AIX ||\ + defined ZMQ_HAVE_NETBSD + +#include <sys/types.h> +#include <sys/time.h> +#include <sys/resource.h> +#include <poll.h> +#include <algorithm> + +#include "poll.hpp" +#include "err.hpp" +#include "config.hpp" +#include "i_poll_events.hpp" + +zmq::poll_t::poll_t () : + retired (false), + stopping (false) +{ + // Get the limit on open file descriptors. Resize fds so that it + // can hold all descriptors. + rlimit rl; + int rc = getrlimit (RLIMIT_NOFILE, &rl); + errno_assert (rc != -1); + fd_table.resize (rl.rlim_cur); + + for (rlim_t i = 0; i < rl.rlim_cur; i ++) + fd_table [i].index = retired_fd; +} + +zmq::poll_t::~poll_t () +{ + worker.stop (); + + // Make sure there are no fds registered on shutdown. + zmq_assert (load.get () == 0); +} + +zmq::poll_t::handle_t zmq::poll_t::add_fd (fd_t fd_, i_poll_events *events_) +{ + pollfd pfd = {fd_, 0, 0}; + pollset.push_back (pfd); + assert (fd_table [fd_].index == retired_fd); + + fd_table [fd_].index = pollset.size() - 1; + fd_table [fd_].events = events_; + + // Increase the load metric of the thread. + load.add (1); + + return fd_; +} + +void zmq::poll_t::rm_fd (handle_t handle_) +{ + fd_t index = fd_table [handle_].index; + assert (index != retired_fd); + + // Mark the fd as unused. + pollset [index].fd = retired_fd; + fd_table [handle_].index = retired_fd; + retired = true; + + // Decrease the load metric of the thread. + load.sub (1); +} + +void zmq::poll_t::set_pollin (handle_t handle_) +{ + int index = fd_table [handle_].index; + pollset [index].events |= POLLIN; +} + +void zmq::poll_t::reset_pollin (handle_t handle_) +{ + int index = fd_table [handle_].index; + pollset [index].events &= ~((short) POLLIN); +} + +void zmq::poll_t::set_pollout (handle_t handle_) +{ + int index = fd_table [handle_].index; + pollset [index].events |= POLLOUT; +} + +void zmq::poll_t::reset_pollout (handle_t handle_) +{ + int index = fd_table [handle_].index; + pollset [index].events &= ~((short) POLLOUT); +} + +void zmq::poll_t::add_timer (i_poll_events *events_) +{ + timers.push_back (events_); +} + +void zmq::poll_t::cancel_timer (i_poll_events *events_) +{ + timers_t::iterator it = std::find (timers.begin (), timers.end (), events_); + if (it != timers.end ()) + timers.erase (it); +} + +int zmq::poll_t::get_load () +{ + return load.get (); +} + +void zmq::poll_t::start () +{ + worker.start (worker_routine, this); +} + +void zmq::poll_t::stop () +{ + stopping = true; +} + +void zmq::poll_t::loop () +{ + while (!stopping) { + + // Wait for events. + int rc = poll (&pollset [0], pollset.size (), + timers.empty () ? -1 : max_timer_period); + if (rc == -1 && errno == EINTR) + continue; + errno_assert (rc != -1); + + // Handle timer. + if (!rc) { + + // Use local list of timers as timer handlers may fill new timers + // into the original array. + timers_t t; + std::swap (timers, t); + + // Trigger all the timers. + for (timers_t::iterator it = t.begin (); it != t.end (); it ++) + (*it)->timer_event (); + + continue; + } + + for (pollset_t::iterator it = pollset.begin (); + it != pollset.end (); it ++) { + + zmq_assert (!(it->revents & POLLNVAL)); + if (it->fd == retired_fd) + continue; + if (it->revents & (POLLERR | POLLHUP)) + fd_table [it->fd].events->in_event (); + if (it->fd == retired_fd) + continue; + if (it->revents & POLLOUT) + fd_table [it->fd].events->out_event (); + if (it->fd == retired_fd) + continue; + if (it->revents & POLLIN) + fd_table [it->fd].events->in_event (); + } + + // Clean up the pollset and update the fd_table accordingly. + if (retired) { + pollset_t::size_type i = 0; + while (i < pollset.size ()) { + if (pollset [i].fd == retired_fd) + pollset.erase (pollset.begin () + i); + else { + fd_table [pollset [i].fd].index = i; + i ++; + } + } + retired = false; + } + } +} + +void zmq::poll_t::worker_routine (void *arg_) +{ + ((poll_t*) arg_)->loop (); +} + +#endif diff --git a/src/poll.hpp b/src/poll.hpp new file mode 100644 index 0000000..f4ae35a --- /dev/null +++ b/src/poll.hpp @@ -0,0 +1,113 @@ +/* + Copyright (c) 2007-2010 iMatix Corporation + + This file is part of 0MQ. + + 0MQ is free software; you can redistribute it and/or modify it under + the terms of the Lesser GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + 0MQ is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + Lesser GNU General Public License for more details. + + You should have received a copy of the Lesser GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef __ZMQ_POLL_HPP_INCLUDED__ +#define __ZMQ_POLL_HPP_INCLUDED__ + +#include "platform.hpp" + +#if defined ZMQ_HAVE_LINUX || defined ZMQ_HAVE_FREEBSD ||\ + defined ZMQ_HAVE_OPENBSD || defined ZMQ_HAVE_SOLARIS ||\ + defined ZMQ_HAVE_OSX || defined ZMQ_HAVE_QNXNTO ||\ + defined ZMQ_HAVE_HPUX || defined ZMQ_HAVE_AIX ||\ + defined ZMQ_HAVE_NETBSD + +#include <poll.h> +#include <stddef.h> +#include <vector> + +#include "fd.hpp" +#include "thread.hpp" +#include "atomic_counter.hpp" + +namespace zmq +{ + + // Implements socket polling mechanism using the POSIX.1-2001 + // poll() system call. + + class poll_t + { + public: + + typedef fd_t handle_t; + + poll_t (); + ~poll_t (); + + // "poller" concept. + handle_t add_fd (fd_t fd_, struct i_poll_events *events_); + void rm_fd (handle_t handle_); + void set_pollin (handle_t handle_); + void reset_pollin (handle_t handle_); + void set_pollout (handle_t handle_); + void reset_pollout (handle_t handle_); + void add_timer (struct i_poll_events *events_); + void cancel_timer (struct i_poll_events *events_); + int get_load (); + void start (); + void stop (); + + private: + + // Main worker thread routine. + static void worker_routine (void *arg_); + + // Main event loop. + void loop (); + + struct fd_entry_t + { + fd_t index; + struct i_poll_events *events; + }; + + // This table stores data for registered descriptors. + std::vector <fd_entry_t> fd_table; + + // Pollset to pass to the poll function. + typedef std::vector <pollfd> pollset_t; + pollset_t pollset; + + // If true, there's at least one retired event source. + bool retired; + + // List of all the engines waiting for the timer event. + typedef std::vector <struct i_poll_events*> timers_t; + timers_t timers; + + // If true, thread is in the process of shutting down. + bool stopping; + + // Handle of the physical thread doing the I/O work. + thread_t worker; + + // Load of the poller. Currently number of file descriptors + // registered with the poller. + atomic_counter_t load; + + poll_t (const poll_t&); + void operator = (const poll_t&); + }; + +} + +#endif + +#endif diff --git a/src/poller.hpp b/src/poller.hpp new file mode 100644 index 0000000..3c29603 --- /dev/null +++ b/src/poller.hpp @@ -0,0 +1,72 @@ +/* + Copyright (c) 2007-2010 iMatix Corporation + + This file is part of 0MQ. + + 0MQ is free software; you can redistribute it and/or modify it under + the terms of the Lesser GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + 0MQ is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + Lesser GNU General Public License for more details. + + You should have received a copy of the Lesser GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef __ZMQ_POLLER_HPP_INCLUDED__ +#define __ZMQ_POLLER_HPP_INCLUDED__ + +#include "epoll.hpp" +#include "poll.hpp" +#include "select.hpp" +#include "devpoll.hpp" +#include "kqueue.hpp" + +namespace zmq +{ + +#if defined ZMQ_FORCE_SELECT + typedef select_t poller_t; +#elif defined ZMQ_FORCE_POLL + typedef poll_t poller_t; +#elif defined ZMQ_FORCE_EPOLL + typedef epoll_t poller_t; +#elif defined ZMQ_FORCE_DEVPOLL + typedef devpoll_t poller_t; +#elif defined ZMQ_FORCE_KQUEUE + typedef kqueue_t poller_t; +#elif defined ZMQ_HAVE_LINUX + typedef epoll_t poller_t; +#elif defined ZMQ_HAVE_WINDOWS + typedef select_t poller_t; +#elif defined ZMQ_HAVE_FREEBSD + typedef kqueue_t poller_t; +#elif defined ZMQ_HAVE_OPENBSD + typedef kqueue_t poller_t; +#elif defined ZMQ_HAVE_NETBSD + typedef kqueue_t poller_t; +#elif defined ZMQ_HAVE_SOLARIS + typedef devpoll_t poller_t; +#elif defined ZMQ_HAVE_OSX + typedef kqueue_t poller_t; +#elif defined ZMQ_HAVE_QNXNTO + typedef poll_t poller_t; +#elif defined ZMQ_HAVE_AIX + typedef poll_t poller_t; +#elif defined ZMQ_HAVE_HPUX + typedef devpoll_t poller_t; +#elif defined ZMQ_HAVE_OPENVMS + typedef select_t poller_t; +#elif defined ZMQ_HAVE_CYGWIN + typedef select_t poller_t; +#else +#error Unsupported platform +#endif + +} + +#endif diff --git a/src/prefix_tree.cpp b/src/prefix_tree.cpp new file mode 100644 index 0000000..51225d6 --- /dev/null +++ b/src/prefix_tree.cpp @@ -0,0 +1,180 @@ +/* + Copyright (c) 2007-2010 iMatix Corporation + + This file is part of 0MQ. + + 0MQ is free software; you can redistribute it and/or modify it under + the terms of the Lesser GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + 0MQ is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + Lesser GNU General Public License for more details. + + You should have received a copy of the Lesser GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <stdlib.h> + +#include <new> +#include <algorithm> + +#include "platform.hpp" +#if defined ZMQ_HAVE_WINDOWS +#include "windows.hpp" +#endif + +#include "err.hpp" +#include "prefix_tree.hpp" + +zmq::prefix_tree_t::prefix_tree_t () : + refcnt (0), + min (0), + count (0) +{ +} + +zmq::prefix_tree_t::~prefix_tree_t () +{ + if (count == 1) + delete next.node; + else if (count > 1) { + for (unsigned char i = 0; i != count; ++i) + if (next.table [i]) + delete next.table [i]; + free (next.table); + } +} + +void zmq::prefix_tree_t::add (unsigned char *prefix_, size_t size_) +{ + // We are at the node corresponding to the prefix. We are done. + if (!size_) { + ++refcnt; + return; + } + + unsigned char c = *prefix_; + if (c < min || c >= min + count) { + + // The character is out of range of currently handled + // charcters. We have to extend the table. + if (!count) { + min = c; + count = 1; + next.node = NULL; + } + else if (count == 1) { + unsigned char oldc = min; + prefix_tree_t *oldp = next.node; + count = (min < c ? c - min : min - c) + 1; + next.table = (prefix_tree_t**) + malloc (sizeof (prefix_tree_t*) * count); + zmq_assert (next.table); + for (unsigned char i = 0; i != count; ++i) + next.table [i] = 0; + min = std::min (min, c); + next.table [oldc - min] = oldp; + } + else if (min < c) { + + // The new character is above the current character range. + unsigned char old_count = count; + count = c - min + 1; + next.table = (prefix_tree_t**) realloc ((void*) next.table, + sizeof (prefix_tree_t*) * count); + zmq_assert (next.table); + for (unsigned char i = old_count; i != count; i++) + next.table [i] = NULL; + } + else { + + // The new character is below the current character range. + unsigned char old_count = count; + count = (min + old_count) - c; + next.table = (prefix_tree_t**) realloc ((void*) next.table, + sizeof (prefix_tree_t*) * count); + zmq_assert (next.table); + memmove (next.table + min - c, next.table, + old_count * sizeof (prefix_tree_t*)); + for (unsigned char i = 0; i != min - c; i++) + next.table [i] = NULL; + min = c; + } + } + + // If next node does not exist, create one. + if (count == 1) { + if (!next.node) { + next.node = new (std::nothrow) prefix_tree_t; + zmq_assert (next.node); + } + next.node->add (prefix_ + 1, size_ - 1); + } + else { + if (!next.table [c - min]) { + next.table [c - min] = new (std::nothrow) prefix_tree_t; + zmq_assert (next.table [c - min]); + } + next.table [c - min]->add (prefix_ + 1, size_ - 1); + } +} + +bool zmq::prefix_tree_t::rm (unsigned char *prefix_, size_t size_) +{ + if (!size_) { + if (!refcnt) + return false; + refcnt--; + return true; + } + + unsigned char c = *prefix_; + if (!count || c < min || c >= min + count) + return false; + + prefix_tree_t *next_node = + count == 1 ? next.node : next.table [c - min]; + + if (!next_node) + return false; + + return next_node->rm (prefix_ + 1, size_ - 1); +} + +bool zmq::prefix_tree_t::check (unsigned char *data_, size_t size_) +{ + // This function is on critical path. It deliberately doesn't use + // recursion to get a bit better performance. + prefix_tree_t *current = this; + while (true) { + + // We've found a corresponding subscription! + if (current->refcnt) + return true; + + // We've checked all the data and haven't found matching subscription. + if (!size_) + return false; + + // If there's no corresponding slot for the first character + // of the prefix, the message does not match. + unsigned char c = *data_; + if (c < current->min || c >= current->min + current->count) + return false; + + // Move to the next character. + if (current->count == 1) + current = current->next.node; + else { + current = current->next.table [c - current->min]; + if (!current) + return false; + } + data_++; + size_--; + } +} diff --git a/src/prefix_tree.hpp b/src/prefix_tree.hpp new file mode 100644 index 0000000..53c7c18 --- /dev/null +++ b/src/prefix_tree.hpp @@ -0,0 +1,55 @@ +/* + Copyright (c) 2007-2010 iMatix Corporation + + This file is part of 0MQ. + + 0MQ is free software; you can redistribute it and/or modify it under + the terms of the Lesser GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + 0MQ is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + Lesser GNU General Public License for more details. + + You should have received a copy of the Lesser GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef __ZMQ_PREFIX_TREE_HPP_INCLUDED__ +#define __ZMQ_PREFIX_TREE_HPP_INCLUDED__ + +#include <stddef.h> + +#include "stdint.hpp" + +namespace zmq +{ + + class prefix_tree_t + { + public: + + prefix_tree_t (); + ~prefix_tree_t (); + + void add (unsigned char *prefix_, size_t size_); + bool rm (unsigned char *prefix_, size_t size_); + bool check (unsigned char *data_, size_t size_); + + private: + + uint32_t refcnt; + unsigned char min; + unsigned char count; + union { + class prefix_tree_t *node; + class prefix_tree_t **table; + } next; + }; + +} + +#endif + diff --git a/src/pub.cpp b/src/pub.cpp new file mode 100644 index 0000000..4e73b19 --- /dev/null +++ b/src/pub.cpp @@ -0,0 +1,177 @@ +/* + Copyright (c) 2007-2010 iMatix Corporation + + This file is part of 0MQ. + + 0MQ is free software; you can redistribute it and/or modify it under + the terms of the Lesser GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + 0MQ is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + Lesser GNU General Public License for more details. + + You should have received a copy of the Lesser GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "../include/zmq.h" + +#include "pub.hpp" +#include "err.hpp" +#include "msg_content.hpp" +#include "pipe.hpp" + +zmq::pub_t::pub_t (class app_thread_t *parent_) : + socket_base_t (parent_), + active (0) +{ + options.requires_in = false; + options.requires_out = true; +} + +zmq::pub_t::~pub_t () +{ + for (pipes_t::size_type i = 0; i != pipes.size (); i++) + pipes [i]->term (); + pipes.clear (); +} + +void zmq::pub_t::xattach_pipes (class reader_t *inpipe_, + class writer_t *outpipe_, const blob_t &peer_identity_) +{ + zmq_assert (!inpipe_); + pipes.push_back (outpipe_); + pipes.swap (active, pipes.size () - 1); + active++; +} + +void zmq::pub_t::xdetach_inpipe (class reader_t *pipe_) +{ + zmq_assert (false); +} + +void zmq::pub_t::xdetach_outpipe (class writer_t *pipe_) +{ + // Remove the pipe from the list; adjust number of active pipes + // accordingly. + if (pipes.index (pipe_) < active) + active--; + pipes.erase (pipe_); +} + +void zmq::pub_t::xkill (class reader_t *pipe_) +{ + zmq_assert (false); +} + +void zmq::pub_t::xrevive (class reader_t *pipe_) +{ + zmq_assert (false); +} + +void zmq::pub_t::xrevive (class writer_t *pipe_) +{ + // Move the pipe to the list of active pipes. + pipes.swap (pipes.index (pipe_), active); + active++; +} + +int zmq::pub_t::xsetsockopt (int option_, const void *optval_, + size_t optvallen_) +{ + errno = EINVAL; + return -1; +} + +int zmq::pub_t::xsend (zmq_msg_t *msg_, int flags_) +{ + // If there are no active pipes available, simply drop the message. + if (active == 0) { + int rc = zmq_msg_close (msg_); + zmq_assert (rc == 0); + rc = zmq_msg_init (msg_); + zmq_assert (rc == 0); + return 0; + } + + msg_content_t *content = (msg_content_t*) msg_->content; + + // For VSMs the copying is straighforward. + if (content == (msg_content_t*) ZMQ_VSM) { + for (pipes_t::size_type i = 0; i != active;) + if (write (pipes [i], msg_)) + i++; + int rc = zmq_msg_init (msg_); + zmq_assert (rc == 0); + return 0; + } + + // Optimisation for the case when there's only a single pipe + // to send the message to - no refcount adjustment i.e. no atomic + // operations are needed. + if (active == 1) { + if (!write (pipes [0], msg_)) { + int rc = zmq_msg_close (msg_); + zmq_assert (rc == 0); + } + int rc = zmq_msg_init (msg_); + zmq_assert (rc == 0); + return 0; + } + + // There are at least 2 destinations for the message. That means we have + // to deal with reference counting. First add N-1 references to + // the content (we are holding one reference anyway, that's why -1). + if (msg_->flags & ZMQ_MSG_SHARED) + content->refcnt.add (active - 1); + else { + content->refcnt.set (active); + msg_->flags |= ZMQ_MSG_SHARED; + } + + // Push the message to all destinations. + for (pipes_t::size_type i = 0; i != active;) { + if (!write (pipes [i], msg_)) + content->refcnt.sub (1); + else + i++; + } + + // Detach the original message from the data buffer. + int rc = zmq_msg_init (msg_); + zmq_assert (rc == 0); + + return 0; +} + +int zmq::pub_t::xrecv (zmq_msg_t *msg_, int flags_) +{ + errno = ENOTSUP; + return -1; +} + +bool zmq::pub_t::xhas_in () +{ + return false; +} + +bool zmq::pub_t::xhas_out () +{ + return true; +} + +bool zmq::pub_t::write (class writer_t *pipe_, zmq_msg_t *msg_) +{ + if (!pipe_->write (msg_)) { + active--; + pipes.swap (pipes.index (pipe_), active); + return false; + } + if (!(msg_->flags & ZMQ_MSG_MORE)) + pipe_->flush (); + return true; +} + diff --git a/src/pub.hpp b/src/pub.hpp new file mode 100644 index 0000000..ac3924a --- /dev/null +++ b/src/pub.hpp @@ -0,0 +1,70 @@ +/* + Copyright (c) 2007-2010 iMatix Corporation + + This file is part of 0MQ. + + 0MQ is free software; you can redistribute it and/or modify it under + the terms of the Lesser GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + 0MQ is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + Lesser GNU General Public License for more details. + + You should have received a copy of the Lesser GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef __ZMQ_PUB_HPP_INCLUDED__ +#define __ZMQ_PUB_HPP_INCLUDED__ + +#include "socket_base.hpp" +#include "yarray.hpp" + +namespace zmq +{ + + class pub_t : public socket_base_t + { + public: + + pub_t (class app_thread_t *parent_); + ~pub_t (); + + // Overloads of functions from socket_base_t. + void xattach_pipes (class reader_t *inpipe_, class writer_t *outpipe_, + const blob_t &peer_identity_); + void xdetach_inpipe (class reader_t *pipe_); + void xdetach_outpipe (class writer_t *pipe_); + void xkill (class reader_t *pipe_); + void xrevive (class reader_t *pipe_); + void xrevive (class writer_t *pipe_); + int xsetsockopt (int option_, const void *optval_, size_t optvallen_); + int xsend (zmq_msg_t *msg_, int flags_); + int xrecv (zmq_msg_t *msg_, int flags_); + bool xhas_in (); + bool xhas_out (); + + private: + + // Write the message to the pipe. Make the pipe inactive if writing + // fails. In such a case false is returned. + bool write (class writer_t *pipe_, zmq_msg_t *msg_); + + // Outbound pipes, i.e. those the socket is sending messages to. + typedef yarray_t <class writer_t> pipes_t; + pipes_t pipes; + + // Number of active pipes. All the active pipes are located at the + // beginning of the pipes array. + pipes_t::size_type active; + + pub_t (const pub_t&); + void operator = (const pub_t&); + }; + +} + +#endif diff --git a/src/queue.cpp b/src/queue.cpp new file mode 100644 index 0000000..470ea67 --- /dev/null +++ b/src/queue.cpp @@ -0,0 +1,101 @@ +/* + Copyright (c) 2007-2010 iMatix Corporation + + This file is part of 0MQ. + + 0MQ is free software; you can redistribute it and/or modify it under + the terms of the Lesser GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + 0MQ is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + Lesser GNU General Public License for more details. + + You should have received a copy of the Lesser GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <stddef.h> + +#include "../include/zmq.h" + +#include "queue.hpp" +#include "socket_base.hpp" +#include "err.hpp" + +int zmq::queue (class socket_base_t *insocket_, + class socket_base_t *outsocket_) +{ + zmq_msg_t msg; + int rc = zmq_msg_init (&msg); + zmq_assert (rc == 0); + + int64_t more; + size_t moresz; + + zmq_pollitem_t items [2]; + items [0].socket = insocket_; + items [0].fd = 0; + items [0].events = ZMQ_POLLIN; + items [0].revents = 0; + items [1].socket = outsocket_; + items [1].fd = 0; + items [1].events = ZMQ_POLLIN; + items [1].revents = 0; + + while (true) { + + // Wait while there are either requests or replies to process. + rc = zmq_poll (&items [0], 2, -1); + errno_assert (rc > 0); + + // The algorithm below asumes ratio of request and replies processed + // under full load to be 1:1. Although processing requests replies + // first is tempting it is suspectible to DoS attacks (overloading + // the system with unsolicited replies). + + // Process a request. + if (items [0].revents & ZMQ_POLLIN) { + while (true) { + + rc = insocket_->recv (&msg, 0); + errno_assert (rc == 0); + + moresz = sizeof (more); + rc = insocket_->getsockopt (ZMQ_RCVMORE, &more, &moresz); + errno_assert (rc == 0); + + rc = outsocket_->send (&msg, more ? ZMQ_SNDMORE : 0); + errno_assert (rc == 0); + + if (!more) + break; + } + } + + // Process a reply. + if (items [1].revents & ZMQ_POLLIN) { + while (true) { + + rc = outsocket_->recv (&msg, 0); + errno_assert (rc == 0); + + moresz = sizeof (more); + rc = outsocket_->getsockopt (ZMQ_RCVMORE, &more, &moresz); + errno_assert (rc == 0); + + rc = insocket_->send (&msg, more ? ZMQ_SNDMORE : 0); + errno_assert (rc == 0); + + if (!more) + break; + } + } + + } + + return 0; +} + diff --git a/src/queue.hpp b/src/queue.hpp new file mode 100644 index 0000000..dc968cb --- /dev/null +++ b/src/queue.hpp @@ -0,0 +1,31 @@ +/* + Copyright (c) 2007-2010 iMatix Corporation + + This file is part of 0MQ. + + 0MQ is free software; you can redistribute it and/or modify it under + the terms of the Lesser GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + 0MQ is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + Lesser GNU General Public License for more details. + + You should have received a copy of the Lesser GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef __ZMQ_QUEUE_HPP_INCLUDED__ +#define __ZMQ_QUEUE_HPP_INCLUDED__ + +namespace zmq +{ + + int queue (class socket_base_t *insocket_, + class socket_base_t *outsocket_); + +} + +#endif diff --git a/src/rep.cpp b/src/rep.cpp new file mode 100644 index 0000000..34b77c4 --- /dev/null +++ b/src/rep.cpp @@ -0,0 +1,280 @@ +/* + Copyright (c) 2007-2010 iMatix Corporation + + This file is part of 0MQ. + + 0MQ is free software; you can redistribute it and/or modify it under + the terms of the Lesser GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + 0MQ is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + Lesser GNU General Public License for more details. + + You should have received a copy of the Lesser GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "../include/zmq.h" + +#include "rep.hpp" +#include "err.hpp" +#include "pipe.hpp" + +zmq::rep_t::rep_t (class app_thread_t *parent_) : + socket_base_t (parent_), + active (0), + current (0), + sending_reply (false), + more (false), + reply_pipe (NULL) +{ + options.requires_in = true; + options.requires_out = true; + + // We don't need immediate connect. We'll be able to send messages + // (replies) only when connection is established and thus requests + // can arrive anyway. + options.immediate_connect = false; +} + +zmq::rep_t::~rep_t () +{ +} + +void zmq::rep_t::xattach_pipes (class reader_t *inpipe_, + class writer_t *outpipe_, const blob_t &peer_identity_) +{ + zmq_assert (inpipe_ && outpipe_); + zmq_assert (in_pipes.size () == out_pipes.size ()); + + in_pipes.push_back (inpipe_); + in_pipes.swap (active, in_pipes.size () - 1); + out_pipes.push_back (outpipe_); + out_pipes.swap (active, out_pipes.size () - 1); + active++; +} + +void zmq::rep_t::xdetach_inpipe (class reader_t *pipe_) +{ + zmq_assert (sending_reply || !more || in_pipes [current] != pipe_); + + zmq_assert (pipe_); + zmq_assert (in_pipes.size () == out_pipes.size ()); + + in_pipes_t::size_type index = in_pipes.index (pipe_); + + if (index < active) { + active--; + if (current == active) + current = 0; + } + + if (out_pipes [index]) + out_pipes [index]->term (); + in_pipes.erase (index); + out_pipes.erase (index); +} + +void zmq::rep_t::xdetach_outpipe (class writer_t *pipe_) +{ + zmq_assert (pipe_); + zmq_assert (in_pipes.size () == out_pipes.size ()); + + out_pipes_t::size_type index = out_pipes.index (pipe_); + + // If the connection we've got the request from disconnects, + // there's nowhere to send the reply. Forget about the reply pipe. + // Once the reply is sent it will be dropped. + if (sending_reply && pipe_ == reply_pipe) + reply_pipe = NULL; + + if (out_pipes.index (pipe_) < active) { + active--; + if (current == active) + current = 0; + } + + if (in_pipes [index]) + in_pipes [index]->term (); + in_pipes.erase (index); + out_pipes.erase (index); +} + +void zmq::rep_t::xkill (class reader_t *pipe_) +{ + // Move the pipe to the list of inactive pipes. + in_pipes_t::size_type index = in_pipes.index (pipe_); + active--; + in_pipes.swap (index, active); + out_pipes.swap (index, active); +} + +void zmq::rep_t::xrevive (class reader_t *pipe_) +{ + // Move the pipe to the list of active pipes. + in_pipes_t::size_type index = in_pipes.index (pipe_); + in_pipes.swap (index, active); + out_pipes.swap (index, active); + active++; +} + +void zmq::rep_t::xrevive (class writer_t *pipe_) +{ +} + +int zmq::rep_t::xsetsockopt (int option_, const void *optval_, + size_t optvallen_) +{ + errno = EINVAL; + return -1; +} + +int zmq::rep_t::xsend (zmq_msg_t *msg_, int flags_) +{ + if (!sending_reply) { + errno = EFSM; + return -1; + } + + if (reply_pipe) { + + // Push message to the reply pipe. + bool written = reply_pipe->write (msg_); + zmq_assert (!more || written); + + // The pipe is full... + // When this happens, we simply return an error. + // This makes REP sockets vulnerable to DoS attack when + // misbehaving requesters stop collecting replies. + // TODO: Tear down the underlying connection (?) + if (!written) { + errno = EAGAIN; + return -1; + } + + more = msg_->flags & ZMQ_MSG_MORE; + } + else { + + // If the requester have disconnected in the meantime, drop the reply. + more = msg_->flags & ZMQ_MSG_MORE; + zmq_msg_close (msg_); + } + + // Flush the reply to the requester. + if (!more) { + if (reply_pipe) + reply_pipe->flush (); + sending_reply = false; + reply_pipe = NULL; + } + + // Detach the message from the data buffer. + int rc = zmq_msg_init (msg_); + zmq_assert (rc == 0); + + return 0; +} + +int zmq::rep_t::xrecv (zmq_msg_t *msg_, int flags_) +{ + // If we are in middle of sending a reply, we cannot receive next request. + if (sending_reply) { + errno = EFSM; + return -1; + } + + // Deallocate old content of the message. + zmq_msg_close (msg_); + + // We haven't started reading a request yet... + if (!more) { + + // Round-robin over the pipes to get next message. + int count; + for (count = active; count != 0; count--) { + if (in_pipes [current]->read (msg_)) + break; + current++; + if (current >= active) + current = 0; + } + + // No message is available. Initialise the output parameter + // to be a 0-byte message. + if (count == 0) { + zmq_msg_init (msg_); + errno = EAGAIN; + return -1; + } + + // We are aware of a new message now. Setup the reply pipe. + reply_pipe = out_pipes [current]; + + // Copy the routing info to the reply pipe. + while (true) { + + // Push message to the reply pipe. + // TODO: What if the pipe is full? + // Tear down the underlying connection? + bool written = reply_pipe->write (msg_); + zmq_assert (written); + + // Message part of zero size delimits the traceback stack. + if (zmq_msg_size (msg_) == 0) + break; + + // Get next part of the message. + bool fetched = in_pipes [current]->read (msg_); + zmq_assert (fetched); + } + } + + // Now the routing info is processed. Get the first part + // of the message payload and exit. + bool fetched = in_pipes [current]->read (msg_); + zmq_assert (fetched); + more = msg_->flags & ZMQ_MSG_MORE; + if (!more) { + current++; + if (current >= active) + current = 0; + sending_reply = true; + } + return 0; +} + +bool zmq::rep_t::xhas_in () +{ + if (sending_reply) + return false; + + if (more) + return true; + + for (int count = active; count != 0; count--) { + if (in_pipes [current]->check_read ()) + return !sending_reply; + current++; + if (current >= active) + current = 0; + } + + return false; +} + +bool zmq::rep_t::xhas_out () +{ + if (!sending_reply) + return false; + + if (more) + return true; + + // TODO: No check for write here... + return sending_reply; +} + diff --git a/src/rep.hpp b/src/rep.hpp new file mode 100644 index 0000000..aef4318 --- /dev/null +++ b/src/rep.hpp @@ -0,0 +1,85 @@ +/* + Copyright (c) 2007-2010 iMatix Corporation + + This file is part of 0MQ. + + 0MQ is free software; you can redistribute it and/or modify it under + the terms of the Lesser GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + 0MQ is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + Lesser GNU General Public License for more details. + + You should have received a copy of the Lesser GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef __ZMQ_REP_HPP_INCLUDED__ +#define __ZMQ_REP_HPP_INCLUDED__ + +#include "socket_base.hpp" +#include "yarray.hpp" + +namespace zmq +{ + + class rep_t : public socket_base_t + { + public: + + rep_t (class app_thread_t *parent_); + ~rep_t (); + + // Overloads of functions from socket_base_t. + void xattach_pipes (class reader_t *inpipe_, class writer_t *outpipe_, + const blob_t &peer_identity_); + void xdetach_inpipe (class reader_t *pipe_); + void xdetach_outpipe (class writer_t *pipe_); + void xkill (class reader_t *pipe_); + void xrevive (class reader_t *pipe_); + void xrevive (class writer_t *pipe_); + int xsetsockopt (int option_, const void *optval_, size_t optvallen_); + int xsend (zmq_msg_t *msg_, int flags_); + int xrecv (zmq_msg_t *msg_, int flags_); + bool xhas_in (); + bool xhas_out (); + + private: + + // List in outbound and inbound pipes. Note that the two lists are + // always in sync. I.e. outpipe with index N communicates with the + // same session as inpipe with index N. + typedef yarray_t <class writer_t> out_pipes_t; + out_pipes_t out_pipes; + typedef yarray_t <class reader_t> in_pipes_t; + in_pipes_t in_pipes; + + // Number of active inpipes. All the active inpipes are located at the + // beginning of the in_pipes array. + in_pipes_t::size_type active; + + // Index of the next inbound pipe to read a request from. + in_pipes_t::size_type current; + + // If true, request was already received and reply wasn't completely + // sent yet. + bool sending_reply; + + // True, if message processed at the moment (either sent or received) + // is processed only partially. + bool more; + + // Pipe we are going to send reply to. + class writer_t *reply_pipe; + + rep_t (const rep_t&); + void operator = (const rep_t&); + + }; + +} + +#endif diff --git a/src/req.cpp b/src/req.cpp new file mode 100644 index 0000000..f3695a2 --- /dev/null +++ b/src/req.cpp @@ -0,0 +1,286 @@ +/* + Copyright (c) 2007-2010 iMatix Corporation + + This file is part of 0MQ. + + 0MQ is free software; you can redistribute it and/or modify it under + the terms of the Lesser GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + 0MQ is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + Lesser GNU General Public License for more details. + + You should have received a copy of the Lesser GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "../include/zmq.h" + +#include "req.hpp" +#include "err.hpp" +#include "pipe.hpp" + +zmq::req_t::req_t (class app_thread_t *parent_) : + socket_base_t (parent_), + active (0), + current (0), + receiving_reply (false), + reply_pipe_active (false), + more (false), + reply_pipe (NULL) +{ + options.requires_in = true; + options.requires_out = true; +} + +zmq::req_t::~req_t () +{ +} + +void zmq::req_t::xattach_pipes (class reader_t *inpipe_, + class writer_t *outpipe_, const blob_t &peer_identity_) +{ + zmq_assert (inpipe_ && outpipe_); + zmq_assert (in_pipes.size () == out_pipes.size ()); + + in_pipes.push_back (inpipe_); + in_pipes.swap (active, in_pipes.size () - 1); + + out_pipes.push_back (outpipe_); + out_pipes.swap (active, out_pipes.size () - 1); + + active++; +} + +void zmq::req_t::xdetach_inpipe (class reader_t *pipe_) +{ + zmq_assert (!receiving_reply || !more || reply_pipe != pipe_); + + zmq_assert (pipe_); + zmq_assert (in_pipes.size () == out_pipes.size ()); + + // TODO: The pipe we are awaiting the reply from is detached. What now? + // Return ECONNRESET from subsequent recv? + if (receiving_reply && pipe_ == reply_pipe) { + zmq_assert (false); + } + + in_pipes_t::size_type index = in_pipes.index (pipe_); + + if (out_pipes [index]) + out_pipes [index]->term (); + in_pipes.erase (index); + out_pipes.erase (index); + if (index < active) { + active--; + if (current == active) + current = 0; + } +} + +void zmq::req_t::xdetach_outpipe (class writer_t *pipe_) +{ + zmq_assert (receiving_reply || !more || out_pipes [current] != pipe_); + + zmq_assert (pipe_); + zmq_assert (in_pipes.size () == out_pipes.size ()); + + out_pipes_t::size_type index = out_pipes.index (pipe_); + + if (in_pipes [index]) + in_pipes [index]->term (); + in_pipes.erase (index); + out_pipes.erase (index); + if (index < active) { + active--; + if (current == active) + current = 0; + } +} + +void zmq::req_t::xkill (class reader_t *pipe_) +{ + zmq_assert (receiving_reply); + zmq_assert (pipe_ == reply_pipe); + + reply_pipe_active = false; +} + +void zmq::req_t::xrevive (class reader_t *pipe_) +{ + // TODO: Actually, misbehaving peer can cause this kind of thing. + // Handle it decently, presumably kill the offending connection. + zmq_assert (pipe_ == reply_pipe); + reply_pipe_active = true; +} + +void zmq::req_t::xrevive (class writer_t *pipe_) +{ + out_pipes_t::size_type index = out_pipes.index (pipe_); + zmq_assert (index >= active); + + if (in_pipes [index] != NULL) { + in_pipes.swap (index, active); + out_pipes.swap (index, active); + active++; + } +} + +int zmq::req_t::xsetsockopt (int option_, const void *optval_, + size_t optvallen_) +{ + errno = EINVAL; + return -1; +} + +int zmq::req_t::xsend (zmq_msg_t *msg_, int flags_) +{ + // If we've sent a request and we still haven't got the reply, + // we can't send another request. + if (receiving_reply) { + errno = EFSM; + return -1; + } + + while (active > 0) { + if (out_pipes [current]->check_write ()) + break; + + zmq_assert (!more); + active--; + if (current < active) { + in_pipes.swap (current, active); + out_pipes.swap (current, active); + } + else + current = 0; + } + + if (active == 0) { + errno = EAGAIN; + return -1; + } + + // If we are starting to send the request, generate a prefix. + if (!more) { + zmq_msg_t prefix; + int rc = zmq_msg_init (&prefix); + zmq_assert (rc == 0); + prefix.flags |= ZMQ_MSG_MORE; + bool written = out_pipes [current]->write (&prefix); + zmq_assert (written); + } + + // Push the message to the selected pipe. + bool written = out_pipes [current]->write (msg_); + zmq_assert (written); + more = msg_->flags & ZMQ_MSG_MORE; + if (!more) { + out_pipes [current]->flush (); + receiving_reply = true; + reply_pipe = in_pipes [current]; + + // We can safely assume that the reply pipe is active as the last time + // we've used it we've read the reply and haven't tried to read from it + // anymore. + reply_pipe_active = true; + + // Move to the next pipe (load-balancing). + current = (current + 1) % active; + } + + // Detach the message from the data buffer. + int rc = zmq_msg_init (msg_); + zmq_assert (rc == 0); + + return 0; +} + +int zmq::req_t::xrecv (zmq_msg_t *msg_, int flags_) +{ + // Deallocate old content of the message. + int rc = zmq_msg_close (msg_); + zmq_assert (rc == 0); + + // If request wasn't send, we can't wait for reply. + if (!receiving_reply) { + zmq_msg_init (msg_); + errno = EFSM; + return -1; + } + + // Get the reply from the reply pipe. + if (!reply_pipe_active || !reply_pipe->read (msg_)) { + zmq_msg_init (msg_); + errno = EAGAIN; + return -1; + } + + // If we are starting to receive new reply, check whether prefix + // is well-formed and drop it. + if (!more) { + zmq_assert (msg_->flags & ZMQ_MSG_MORE); + zmq_assert (zmq_msg_size (msg_) == 0); + rc = zmq_msg_close (msg_); + zmq_assert (rc == 0); + + // Get the actual reply. + bool recvd = reply_pipe->read (msg_); + zmq_assert (recvd); + } + + // If this was last part of the reply, switch to request phase. + more = msg_->flags & ZMQ_MSG_MORE; + if (!more) { + receiving_reply = false; + reply_pipe = NULL; + } + + return 0; +} + +bool zmq::req_t::xhas_in () +{ + if (receiving_reply && more) + return true; + + if (!receiving_reply || !reply_pipe_active) + return false; + + zmq_assert (reply_pipe); + if (!reply_pipe->check_read ()) { + reply_pipe_active = false; + return false; + } + + return true; +} + +bool zmq::req_t::xhas_out () +{ + if (!receiving_reply && more) + return true; + + if (receiving_reply) + return false; + + while (active > 0) { + if (out_pipes [current]->check_write ()) + return true;; + + active--; + if (current < active) { + in_pipes.swap (current, active); + out_pipes.swap (current, active); + } + else + current = 0; + } + + return false; +} + + diff --git a/src/req.hpp b/src/req.hpp new file mode 100644 index 0000000..5ab7bca --- /dev/null +++ b/src/req.hpp @@ -0,0 +1,93 @@ +/* + Copyright (c) 2007-2010 iMatix Corporation + + This file is part of 0MQ. + + 0MQ is free software; you can redistribute it and/or modify it under + the terms of the Lesser GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + 0MQ is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + Lesser GNU General Public License for more details. + + You should have received a copy of the Lesser GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef __ZMQ_REQ_HPP_INCLUDED__ +#define __ZMQ_REQ_HPP_INCLUDED__ + +#include "socket_base.hpp" +#include "yarray.hpp" + +namespace zmq +{ + + class req_t : public socket_base_t + { + public: + + req_t (class app_thread_t *parent_); + ~req_t (); + + // Overloads of functions from socket_base_t. + void xattach_pipes (class reader_t *inpipe_, class writer_t *outpipe_, + const blob_t &peer_identity_); + void xdetach_inpipe (class reader_t *pipe_); + void xdetach_outpipe (class writer_t *pipe_); + void xkill (class reader_t *pipe_); + void xrevive (class reader_t *pipe_); + void xrevive (class writer_t *pipe_); + int xsetsockopt (int option_, const void *optval_, size_t optvallen_); + int xsend (zmq_msg_t *msg_, int flags_); + int xrecv (zmq_msg_t *msg_, int flags_); + bool xhas_in (); + bool xhas_out (); + + private: + + // List in outbound and inbound pipes. Note that the two lists are + // always in sync. I.e. outpipe with index N communicates with the + // same session as inpipe with index N. + // + // TODO: Once we have queue limits in place, list of active outpipes + // is to be held (presumably by stacking active outpipes at + // the beginning of the array). We don't have to do the same thing for + // inpipes, because we know which pipe we want to read the + // reply from. + typedef yarray_t <class writer_t> out_pipes_t; + out_pipes_t out_pipes; + typedef yarray_t <class reader_t> in_pipes_t; + in_pipes_t in_pipes; + + // Number of active pipes. + size_t active; + + // Req_t load-balances the requests - 'current' points to the session + // that's processing the request at the moment. + out_pipes_t::size_type current; + + // If true, request was already sent and reply wasn't received yet or + // was raceived partially. + bool receiving_reply; + + // True, if read can be attempted from the reply pipe. + bool reply_pipe_active; + + // True, if message processed at the moment (either sent or received) + // is processed only partially. + bool more; + + // Pipe we are awaiting the reply from. + class reader_t *reply_pipe; + + req_t (const req_t&); + void operator = (const req_t&); + }; + +} + +#endif diff --git a/src/select.cpp b/src/select.cpp new file mode 100644 index 0000000..be5cd47 --- /dev/null +++ b/src/select.cpp @@ -0,0 +1,234 @@ +/* + Copyright (c) 2007-2010 iMatix Corporation + + This file is part of 0MQ. + + 0MQ is free software; you can redistribute it and/or modify it under + the terms of the Lesser GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + 0MQ is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + Lesser GNU General Public License for more details. + + You should have received a copy of the Lesser GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "platform.hpp" + +#include <string.h> +#include <algorithm> + +#ifdef ZMQ_HAVE_WINDOWS +#include "winsock2.h" +#elif defined ZMQ_HAVE_HPUX +#include <sys/param.h> +#include <sys/types.h> +#include <sys/time.h> +#elif defined ZMQ_HAVE_OPENVMS +#include <sys/types.h> +#include <sys/time.h> +#else +#include <sys/select.h> +#endif + +#include "select.hpp" +#include "err.hpp" +#include "config.hpp" +#include "i_poll_events.hpp" + +zmq::select_t::select_t () : + maxfd (retired_fd), + retired (false), + stopping (false) +{ + // Clear file descriptor sets. + FD_ZERO (&source_set_in); + FD_ZERO (&source_set_out); + FD_ZERO (&source_set_err); +} + +zmq::select_t::~select_t () +{ + worker.stop (); + + // Make sure there are no fds registered on shutdown. + zmq_assert (load.get () == 0); +} + +zmq::select_t::handle_t zmq::select_t::add_fd (fd_t fd_, i_poll_events *events_) +{ + // Store the file descriptor. + fd_entry_t entry = {fd_, events_}; + fds.push_back (entry); + + // Start polling on errors. + FD_SET (fd_, &source_set_err); + + // Adjust maxfd if necessary. + if (fd_ > maxfd) + maxfd = fd_; + + // Increase the load metric of the thread. + load.add (1); + + return fd_; +} + +void zmq::select_t::rm_fd (handle_t handle_) +{ + // Mark the descriptor as retired. + fd_set_t::iterator it; + for (it = fds.begin (); it != fds.end (); it ++) + if (it->fd == handle_) + break; + zmq_assert (it != fds.end ()); + it->fd = retired_fd; + retired = true; + + // Stop polling on the descriptor. + FD_CLR (handle_, &source_set_in); + FD_CLR (handle_, &source_set_out); + FD_CLR (handle_, &source_set_err); + + // Discard all events generated on this file descriptor. + FD_CLR (handle_, &readfds); + FD_CLR (handle_, &writefds); + FD_CLR (handle_, &exceptfds); + + // Adjust the maxfd attribute if we have removed the + // highest-numbered file descriptor. + if (handle_ == maxfd) { + maxfd = retired_fd; + for (fd_set_t::iterator it = fds.begin (); it != fds.end (); it ++) + if (it->fd > maxfd) + maxfd = it->fd; + } + + // Decrease the load metric of the thread. + load.sub (1); +} + +void zmq::select_t::set_pollin (handle_t handle_) +{ + FD_SET (handle_, &source_set_in); +} + +void zmq::select_t::reset_pollin (handle_t handle_) +{ + FD_CLR (handle_, &source_set_in); +} + +void zmq::select_t::set_pollout (handle_t handle_) +{ + FD_SET (handle_, &source_set_out); +} + +void zmq::select_t::reset_pollout (handle_t handle_) +{ + FD_CLR (handle_, &source_set_out); +} + +void zmq::select_t::add_timer (i_poll_events *events_) +{ + timers.push_back (events_); +} + +void zmq::select_t::cancel_timer (i_poll_events *events_) +{ + timers_t::iterator it = std::find (timers.begin (), timers.end (), events_); + if (it != timers.end ()) + timers.erase (it); +} + +int zmq::select_t::get_load () +{ + return load.get (); +} + +void zmq::select_t::start () +{ + worker.start (worker_routine, this); +} + +void zmq::select_t::stop () +{ + stopping = true; +} + +void zmq::select_t::loop () +{ + while (!stopping) { + + // Intialise the pollsets. + memcpy (&readfds, &source_set_in, sizeof source_set_in); + memcpy (&writefds, &source_set_out, sizeof source_set_out); + memcpy (&exceptfds, &source_set_err, sizeof source_set_err); + + // Compute the timout interval. Select is free to overwrite the + // value so we have to compute it each time anew. + timeval timeout = {max_timer_period / 1000, + (max_timer_period % 1000) * 1000}; + + // Wait for events. + int rc = select (maxfd + 1, &readfds, &writefds, &exceptfds, + timers.empty () ? NULL : &timeout); + +#ifdef ZMQ_HAVE_WINDOWS + wsa_assert (rc != SOCKET_ERROR); +#else + if (rc == -1 && errno == EINTR) + continue; + errno_assert (rc != -1); +#endif + + // Handle timer. + if (!rc) { + + // Use local list of timers as timer handlers may fill new timers + // into the original array. + timers_t t; + std::swap (timers, t); + + // Trigger all the timers. + for (timers_t::iterator it = t.begin (); it != t.end (); it ++) + (*it)->timer_event (); + + continue; + } + + for (fd_set_t::size_type i = 0; i < fds.size (); i ++) { + if (fds [i].fd == retired_fd) + continue; + if (FD_ISSET (fds [i].fd, &exceptfds)) + fds [i].events->in_event (); + if (fds [i].fd == retired_fd) + continue; + if (FD_ISSET (fds [i].fd, &writefds)) + fds [i].events->out_event (); + if (fds [i].fd == retired_fd) + continue; + if (FD_ISSET (fds [i].fd, &readfds)) + fds [i].events->in_event (); + } + + // Destroy retired event sources. + if (retired) { + for (fd_set_t::size_type i = 0; i < fds.size (); i ++) { + if (fds [i].fd == retired_fd) { + fds.erase (fds.begin () + i); + i --; + } + } + retired = false; + } + } +} + +void zmq::select_t::worker_routine (void *arg_) +{ + ((select_t*) arg_)->loop (); +} diff --git a/src/select.hpp b/src/select.hpp new file mode 100644 index 0000000..01e9fa8 --- /dev/null +++ b/src/select.hpp @@ -0,0 +1,123 @@ +/* + Copyright (c) 2007-2010 iMatix Corporation + + This file is part of 0MQ. + + 0MQ is free software; you can redistribute it and/or modify it under + the terms of the Lesser GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + 0MQ is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + Lesser GNU General Public License for more details. + + You should have received a copy of the Lesser GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef __ZMQ_SELECT_HPP_INCLUDED__ +#define __ZMQ_SELECT_HPP_INCLUDED__ + +#include "platform.hpp" + +#include <stddef.h> +#include <vector> + +#ifdef ZMQ_HAVE_WINDOWS +#include "winsock2.h" +#elif defined ZMQ_HAVE_OPENVMS +#include <sys/types.h> +#include <sys/time.h> +#else +#include <sys/select.h> +#endif + +#include "fd.hpp" +#include "thread.hpp" +#include "atomic_counter.hpp" + +namespace zmq +{ + + // Implements socket polling mechanism using POSIX.1-2001 select() + // function. + + class select_t + { + public: + + typedef fd_t handle_t; + + select_t (); + ~select_t (); + + // "poller" concept. + handle_t add_fd (fd_t fd_, struct i_poll_events *events_); + void rm_fd (handle_t handle_); + void set_pollin (handle_t handle_); + void reset_pollin (handle_t handle_); + void set_pollout (handle_t handle_); + void reset_pollout (handle_t handle_); + void add_timer (struct i_poll_events *events_); + void cancel_timer (struct i_poll_events *events_); + int get_load (); + void start (); + void stop (); + + private: + + // Main worker thread routine. + static void worker_routine (void *arg_); + + // Main event loop. + void loop (); + + struct fd_entry_t + { + fd_t fd; + struct i_poll_events *events; + }; + + // Set of file descriptors that are used to retreive + // information for fd_set. + typedef std::vector <fd_entry_t> fd_set_t; + fd_set_t fds; + + fd_set source_set_in; + fd_set source_set_out; + fd_set source_set_err; + + fd_set readfds; + fd_set writefds; + fd_set exceptfds; + + // Maximum file descriptor. + fd_t maxfd; + + // If true, at least one file descriptor has retired. + bool retired; + + // List of all the engines waiting for the timer event. + typedef std::vector <struct i_poll_events*> timers_t; + timers_t timers; + + // If true, thread is shutting down. + bool stopping; + + // Handle of the physical thread doing the I/O work. + thread_t worker; + + // Load of the poller. Currently number of file descriptors + // registered with the poller. + atomic_counter_t load; + + select_t (const select_t&); + void operator = (const select_t&); + }; + +} + +#endif + diff --git a/src/session.cpp b/src/session.cpp new file mode 100644 index 0000000..3cd27fb --- /dev/null +++ b/src/session.cpp @@ -0,0 +1,291 @@ +/* + Copyright (c) 2007-2010 iMatix Corporation + + This file is part of 0MQ. + + 0MQ is free software; you can redistribute it and/or modify it under + the terms of the Lesser GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + 0MQ is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + Lesser GNU General Public License for more details. + + You should have received a copy of the Lesser GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <new> + +#include "session.hpp" +#include "i_engine.hpp" +#include "err.hpp" +#include "pipe.hpp" + +zmq::session_t::session_t (object_t *parent_, socket_base_t *owner_, + const options_t &options_) : + owned_t (parent_, owner_), + in_pipe (NULL), + incomplete_in (false), + active (true), + out_pipe (NULL), + engine (NULL), + options (options_) +{ + // It's possible to register the session at this point as it will be + // searched for only on reconnect, i.e. no race condition (session found + // before it is plugged into it's I/O thread) is possible. + ordinal = owner->register_session (this); +} + +zmq::session_t::session_t (object_t *parent_, socket_base_t *owner_, + const options_t &options_, const blob_t &peer_identity_) : + owned_t (parent_, owner_), + in_pipe (NULL), + incomplete_in (false), + active (true), + out_pipe (NULL), + engine (NULL), + ordinal (0), + peer_identity (peer_identity_), + options (options_) +{ + if (!peer_identity.empty () && peer_identity [0] != 0) { + if (!owner->register_session (peer_identity, this)) { + + // TODO: There's already a session with the specified + // identity. We should presumably syslog it and drop the + // session. + zmq_assert (false); + } + } +} + +zmq::session_t::~session_t () +{ + zmq_assert (!in_pipe); + zmq_assert (!out_pipe); +} + +bool zmq::session_t::read (::zmq_msg_t *msg_) +{ + if (!in_pipe || !active) + return false; + + if (!in_pipe->read (msg_)) + return false; + + incomplete_in = msg_->flags & ZMQ_MSG_MORE; + return true; +} + +bool zmq::session_t::write (::zmq_msg_t *msg_) +{ + if (out_pipe && out_pipe->write (msg_)) { + zmq_msg_init (msg_); + return true; + } + + return false; +} + +void zmq::session_t::flush () +{ + if (out_pipe) + out_pipe->flush (); +} + +void zmq::session_t::detach (owned_t *reconnecter_) +{ + // Plug in the reconnecter object if any. + if (reconnecter_) { + send_plug (reconnecter_); + send_own (owner, reconnecter_); + } + + // Engine is terminating itself. No need to deallocate it from here. + engine = NULL; + + // Get rid of half-processed messages in the out pipe. Flush any + // unflushed messages upstream. + if (out_pipe) { + out_pipe->rollback (); + out_pipe->flush (); + } + + // Remove any half-read message from the in pipe. + if (in_pipe) { + while (incomplete_in) { + zmq_msg_t msg; + zmq_msg_init (&msg); + if (!read (&msg)) { + zmq_assert (!incomplete_in); + break; + } + zmq_msg_close (&msg); + } + } + + // Terminate transient session. + if (!ordinal && (peer_identity.empty () || peer_identity [0] == 0)) + term (); +} + +zmq::io_thread_t *zmq::session_t::get_io_thread () +{ + return choose_io_thread (options.affinity); +} + +class zmq::socket_base_t *zmq::session_t::get_owner () +{ + return owner; +} + +uint64_t zmq::session_t::get_ordinal () +{ + zmq_assert (ordinal); + return ordinal; +} + +void zmq::session_t::attach_pipes (class reader_t *inpipe_, + class writer_t *outpipe_, const blob_t &peer_identity_) +{ + if (inpipe_) { + zmq_assert (!in_pipe); + in_pipe = inpipe_; + active = true; + in_pipe->set_endpoint (this); + } + + if (outpipe_) { + zmq_assert (!out_pipe); + out_pipe = outpipe_; + out_pipe->set_endpoint (this); + } +} + +void zmq::session_t::detach_inpipe (reader_t *pipe_) +{ + active = false; + in_pipe = NULL; +} + +void zmq::session_t::detach_outpipe (writer_t *pipe_) +{ + out_pipe = NULL; +} + +void zmq::session_t::kill (reader_t *pipe_) +{ + active = false; +} + +void zmq::session_t::revive (reader_t *pipe_) +{ + zmq_assert (in_pipe == pipe_); + active = true; + if (engine) + engine->revive (); +} + +void zmq::session_t::revive (writer_t *pipe_) +{ + zmq_assert (out_pipe == pipe_); + if (engine) + engine->resume_input (); +} + +void zmq::session_t::process_plug () +{ +} + +void zmq::session_t::process_unplug () +{ + // Unregister the session from the socket. + if (ordinal) + owner->unregister_session (ordinal); + else if (!peer_identity.empty () && peer_identity [0] != 0) + owner->unregister_session (peer_identity); + + // Ask associated pipes to terminate. + if (in_pipe) { + in_pipe->term (); + in_pipe = NULL; + } + if (out_pipe) { + out_pipe->term (); + out_pipe = NULL; + } + + if (engine) { + engine->unplug (); + delete engine; + engine = NULL; + } +} + +void zmq::session_t::process_attach (i_engine *engine_, + const blob_t &peer_identity_) +{ + if (!peer_identity.empty ()) { + + // If both IDs are temporary, no checking is needed. + // TODO: Old ID should be reused in this case... + if (peer_identity.empty () || peer_identity [0] != 0 || + peer_identity_.empty () || peer_identity_ [0] != 0) { + + // If we already know the peer name do nothing, just check whether + // it haven't changed. + zmq_assert (peer_identity == peer_identity_); + } + } + else if (!peer_identity_.empty ()) { + + // Store the peer identity. + peer_identity = peer_identity_; + + // If the session is not registered with the ordinal, let's register + // it using the peer name. + if (!ordinal) { + if (!owner->register_session (peer_identity, this)) { + + // TODO: There's already a session with the specified + // identity. We should presumably syslog it and drop the + // session. + zmq_assert (false); + } + } + } + + // Check whether the required pipes already exist. If not so, we'll + // create them and bind them to the socket object. + reader_t *socket_reader = NULL; + writer_t *socket_writer = NULL; + + if (options.requires_in && !out_pipe) { + pipe_t *pipe = new (std::nothrow) pipe_t (owner, this, options.hwm); + zmq_assert (pipe); + out_pipe = &pipe->writer; + out_pipe->set_endpoint (this); + socket_reader = &pipe->reader; + } + + if (options.requires_out && !in_pipe) { + pipe_t *pipe = new (std::nothrow) pipe_t (this, owner, options.hwm); + zmq_assert (pipe); + in_pipe = &pipe->reader; + in_pipe->set_endpoint (this); + socket_writer = &pipe->writer; + } + + if (socket_reader || socket_writer) + send_bind (owner, socket_reader, socket_writer, peer_identity); + + // Plug in the engine. + zmq_assert (!engine); + zmq_assert (engine_); + engine = engine_; + engine->plug (this); +} diff --git a/src/session.hpp b/src/session.hpp new file mode 100644 index 0000000..9bda1ad --- /dev/null +++ b/src/session.hpp @@ -0,0 +1,104 @@ +/* + Copyright (c) 2007-2010 iMatix Corporation + + This file is part of 0MQ. + + 0MQ is free software; you can redistribute it and/or modify it under + the terms of the Lesser GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + 0MQ is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + Lesser GNU General Public License for more details. + + You should have received a copy of the Lesser GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef __ZMQ_SESSION_HPP_INCLUDED__ +#define __ZMQ_SESSION_HPP_INCLUDED__ + +#include "i_inout.hpp" +#include "i_endpoint.hpp" +#include "owned.hpp" +#include "options.hpp" +#include "blob.hpp" + +namespace zmq +{ + + class session_t : public owned_t, public i_inout, public i_endpoint + { + public: + + // Creates unnamed session. + session_t (object_t *parent_, socket_base_t *owner_, + const options_t &options_); + + // Creates named session. + session_t (object_t *parent_, socket_base_t *owner_, + const options_t &options_, const blob_t &peer_identity_); + + // i_inout interface implementation. + bool read (::zmq_msg_t *msg_); + bool write (::zmq_msg_t *msg_); + void flush (); + void detach (owned_t *reconnecter_); + class io_thread_t *get_io_thread (); + class socket_base_t *get_owner (); + uint64_t get_ordinal (); + + // i_endpoint interface implementation. + void attach_pipes (class reader_t *inpipe_, class writer_t *outpipe_, + const blob_t &peer_identity_); + void detach_inpipe (class reader_t *pipe_); + void detach_outpipe (class writer_t *pipe_); + void kill (class reader_t *pipe_); + void revive (class reader_t *pipe_); + void revive (class writer_t *pipe_); + + private: + + ~session_t (); + + // Handlers for incoming commands. + void process_plug (); + void process_unplug (); + void process_attach (struct i_engine *engine_, + const blob_t &peer_identity_); + + // Inbound pipe, i.e. one the session is getting messages from. + class reader_t *in_pipe; + + // This flag is true if the remainder of the message being processed + // is still in the in pipe. + bool incomplete_in; + + // If true, in_pipe is active. Otherwise there are no messages to get. + bool active; + + // Outbound pipe, i.e. one the socket is sending messages to. + class writer_t *out_pipe; + + struct i_engine *engine; + + // Session is identified by ordinal in the case when it was created + // before connection to the peer was established and thus we are + // unaware of peer's identity. + uint64_t ordinal; + + // Identity of the peer. + blob_t peer_identity; + + // Inherited socket options. + options_t options; + + session_t (const session_t&); + void operator = (const session_t&); + }; + +} + +#endif diff --git a/src/signaler.cpp b/src/signaler.cpp new file mode 100644 index 0000000..592688b --- /dev/null +++ b/src/signaler.cpp @@ -0,0 +1,339 @@ +/* + Copyright (c) 2007-2010 iMatix Corporation + + This file is part of 0MQ. + + 0MQ is free software; you can redistribute it and/or modify it under + the terms of the Lesser GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + 0MQ is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + Lesser GNU General Public License for more details. + + You should have received a copy of the Lesser GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "signaler.hpp" +#include "platform.hpp" +#include "err.hpp" +#include "fd.hpp" +#include "ip.hpp" + +#if defined ZMQ_HAVE_OPENVMS +#include <netinet/tcp.h> +#include <unistd.h> +#elif defined ZMQ_HAVE_WINDOWS +#include "windows.hpp" +#else +#include <unistd.h> +#include <fcntl.h> +#include <limits.h> +#endif + +zmq::fd_t zmq::signaler_t::get_fd () +{ + return r; +} + +#if defined ZMQ_HAVE_WINDOWS + +zmq::signaler_t::signaler_t () +{ + // Windows have no 'socketpair' function. CreatePipe is no good as pipe + // handles cannot be polled on. Here we create the socketpair by hand. + + struct sockaddr_in addr; + SOCKET listener; + int addrlen = sizeof (addr); + + w = INVALID_SOCKET; + r = INVALID_SOCKET; + + fd_t rcs = (listener = socket (AF_INET, SOCK_STREAM, 0)); + wsa_assert (rcs != INVALID_SOCKET); + + memset (&addr, 0, sizeof (addr)); + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = htonl (INADDR_LOOPBACK); + addr.sin_port = 0; + + int rc = bind (listener, (const struct sockaddr*) &addr, sizeof (addr)); + wsa_assert (rc != SOCKET_ERROR); + + rc = getsockname (listener, (struct sockaddr*) &addr, &addrlen); + wsa_assert (rc != SOCKET_ERROR); + + // Listen for incomming connections. + rc = listen (listener, 1); + wsa_assert (rc != SOCKET_ERROR); + + // Create the socket. + w = WSASocket (AF_INET, SOCK_STREAM, 0, NULL, 0, 0); + wsa_assert (w != INVALID_SOCKET); + + // Connect to the remote peer. + rc = connect (w, (sockaddr *) &addr, sizeof (addr)); + wsa_assert (rc != SOCKET_ERROR); + + // Accept connection from w. + r = accept (listener, NULL, NULL); + wsa_assert (r != INVALID_SOCKET); + + // Set the read site of the pair to non-blocking mode. + unsigned long argp = 1; + rc = ioctlsocket (r, FIONBIO, &argp); + wsa_assert (rc != SOCKET_ERROR); + + // We don't need the listening socket anymore. Close it. + rc = closesocket (listener); + wsa_assert (rc != SOCKET_ERROR); +} + +zmq::signaler_t::~signaler_t () +{ + int rc = closesocket (w); + wsa_assert (rc != SOCKET_ERROR); + + rc = closesocket (r); + wsa_assert (rc != SOCKET_ERROR); +} + +void zmq::signaler_t::send (const command_t &cmd_) +{ + // TODO: Note that send is a blocking operation. + // How should we behave if the signal cannot be written to the signaler? + // Even worse: What if half of a command is written? + int rc = ::send (w, (char*) &cmd_, sizeof (command_t), 0); + win_assert (rc != SOCKET_ERROR); + zmq_assert (rc == sizeof (command_t)); +} + +bool zmq::signaler_t::recv (command_t *cmd_, bool block_) +{ + if (block_) { + + // Switch to blocking mode. + unsigned long argp = 0; + int rc = ioctlsocket (r, FIONBIO, &argp); + wsa_assert (rc != SOCKET_ERROR); + } + + bool result; + int nbytes = ::recv (r, (char*) cmd_, sizeof (command_t), 0); + if (nbytes == -1 && WSAGetLastError () == WSAEWOULDBLOCK) { + result = false; + } + else { + wsa_assert (nbytes != -1); + + // Check whether we haven't got half of a signal. + zmq_assert (nbytes % sizeof (uint32_t) == 0); + + result = true; + } + + if (block_) { + + // Switch back to non-blocking mode. + unsigned long argp = 1; + int rc = ioctlsocket (r, FIONBIO, &argp); + wsa_assert (rc != SOCKET_ERROR); + } + + return result; +} + +#elif defined ZMQ_HAVE_HPUX || defined ZMQ_HAVE_AIX + +#include <sys/types.h> +#include <sys/socket.h> + +zmq::signaler_t::signaler_t () +{ + int sv [2]; + int rc = socketpair (AF_UNIX, SOCK_STREAM, 0, sv); + errno_assert (rc == 0); + w = sv [0]; + r = sv [1]; + + // Set the reader to non-blocking mode. + int flags = fcntl (r, F_GETFL, 0); + if (flags == -1) + flags = 0; + rc = fcntl (r, F_SETFL, flags | O_NONBLOCK); + errno_assert (rc != -1); +} + +zmq::signaler_t::~signaler_t () +{ + close (w); + close (r); +} + +void zmq::signaler_t::send (const command_t &cmd_) +{ + ssize_t nbytes = send (w, &cmd_, sizeof (command_t), 0); + errno_assert (nbytes != -1); + zmq_assert (nbytes == sizeof (command_t)); +} + +bool zmq::signaler_t::recv (command_t &cmd_, bool block_) +{ + if (block_) { + + // Set the reader to blocking mode. + int flags = fcntl (r, F_GETFL, 0); + if (flags == -1) + flags = 0; + int rc = fcntl (r, F_SETFL, flags & ~O_NONBLOCK); + errno_assert (rc != -1); + } + + bool result; + ssize_t nbytes = recv (r, buffer, sizeof (command_t), 0); + if (nbytes == -1 && errno == EAGAIN) { + result = false; + } + else { + zmq_assert (nbytes != -1); + + // Check whether we haven't got half of command. + zmq_assert (nbytes == sizeof (command_t)); + + result = true; + } + + if (block_) + + // Set the reader to non-blocking mode. + int flags = fcntl (r, F_GETFL, 0); + if (flags == -1) + flags = 0; + int rc = fcntl (r, F_SETFL, flags | O_NONBLOCK); + errno_assert (rc != -1); + } + + return result; +} + +#else + +#include <sys/types.h> +#include <sys/socket.h> + +zmq::signaler_t::signaler_t () +{ + // Make sure that command can be written to the socket in atomic fashion. + // If this wasn't guaranteed, commands from different threads would be + // interleaved. + zmq_assert (sizeof (command_t) <= PIPE_BUF); + + int sv [2]; + int rc = socketpair (AF_UNIX, SOCK_STREAM, 0, sv); + errno_assert (rc == 0); + w = sv [0]; + r = sv [1]; +} + +zmq::signaler_t::~signaler_t () +{ + close (w); + close (r); +} + +void zmq::signaler_t::send (const command_t &cmd_) +{ + // TODO: Note that send is a blocking operation. + // How should we behave if the command cannot be written to the signaler? + ssize_t nbytes = ::send (w, &cmd_, sizeof (command_t), 0); + errno_assert (nbytes != -1); + + // This should never happen as we've already checked that command size is + // less than PIPE_BUF. + zmq_assert (nbytes == sizeof (command_t)); +} + +bool zmq::signaler_t::recv (command_t *cmd_, bool block_) +{ + ssize_t nbytes = ::recv (r, cmd_, sizeof (command_t), + block_ ? 0 : MSG_DONTWAIT); + + // If there's no signal available return false. + if (nbytes == -1 && errno == EAGAIN) + return false; + + errno_assert (nbytes != -1); + + // Check whether we haven't got half of command. + zmq_assert (nbytes == sizeof (command_t)); + + return true; +} + +#endif + +#if defined ZMQ_HAVE_OPENVMS + +int zmq::signaler_t::socketpair (int domain_, int type_, int protocol_, + int sv_ [2]) +{ + int listener; + sockaddr_in lcladdr; + socklen_t lcladdr_len; + int rc; + int on = 1; + + zmq_assert (type_ == SOCK_STREAM); + + // Fill in the localhost address (127.0.0.1). + memset (&lcladdr, 0, sizeof (lcladdr)); + lcladdr.sin_family = AF_INET; + lcladdr.sin_addr.s_addr = htonl (INADDR_LOOPBACK); + lcladdr.sin_port = 0; + + listener = socket (AF_INET, SOCK_STREAM, 0); + errno_assert (listener != -1); + + rc = setsockopt (listener, IPPROTO_TCP, TCP_NODELAY, &on, sizeof (on)); + errno_assert (rc != -1); + + rc = setsockopt (listener, IPPROTO_TCP, TCP_NODELACK, &on, sizeof (on)); + errno_assert (rc != -1); + + rc = bind(listener, (struct sockaddr*) &lcladdr, sizeof (lcladdr)); + errno_assert (rc != -1); + + lcladdr_len = sizeof (lcladdr); + + rc = getsockname (listener, (struct sockaddr*) &lcladdr, &lcladdr_len); + errno_assert (rc != -1); + + rc = listen (listener, 1); + errno_assert (rc != -1); + + sv_ [0] = socket (AF_INET, SOCK_STREAM, 0); + errno_assert (rc != -1); + + rc = setsockopt (sv_ [0], IPPROTO_TCP, TCP_NODELAY, &on, sizeof (on)); + errno_assert (rc != -1); + + rc = setsockopt (sv_ [0], IPPROTO_TCP, TCP_NODELACK, &on, sizeof (on)); + errno_assert (rc != -1); + + rc = connect (sv_ [0], (struct sockaddr*) &lcladdr, sizeof (lcladdr)); + errno_assert (rc != -1); + + sv_ [1] = accept (listener, NULL, NULL); + errno_assert (sv_ [1] != -1); + + close (listener); + + return 0; +} + +#endif + diff --git a/src/signaler.hpp b/src/signaler.hpp new file mode 100644 index 0000000..64a1899 --- /dev/null +++ b/src/signaler.hpp @@ -0,0 +1,71 @@ +/* + Copyright (c) 2007-2010 iMatix Corporation + + This file is part of 0MQ. + + 0MQ is free software; you can redistribute it and/or modify it under + the terms of the Lesser GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + 0MQ is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + Lesser GNU General Public License for more details. + + You should have received a copy of the Lesser GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef __ZMQ_SIGNALER_HPP_INCLUDED__ +#define __ZMQ_SIGNALER_HPP_INCLUDED__ + +#include <stddef.h> + +#include "platform.hpp" +#include "fd.hpp" +#include "stdint.hpp" +#include "config.hpp" +#include "command.hpp" + +namespace zmq +{ + + class signaler_t + { + public: + + signaler_t (); + ~signaler_t (); + + fd_t get_fd (); + void send (const command_t &cmd_); + bool recv (command_t *cmd_, bool block_); + + private: + +#if defined ZMQ_HAVE_OPENVMS + + // Whilst OpenVMS supports socketpair - it maps to AF_INET only. + // Further, it does not set the socket options TCP_NODELAY and + // TCP_NODELACK which can lead to performance problems. We'll + // overload the socketpair function for this class. + // + // The bug will be fixed in V5.6 ECO4 and beyond. In the + // meantime, we'll create the socket pair manually. + static int socketpair (int domain_, int type_, int protocol_, + int sv_ [2]); +#endif + + // Write & read end of the socketpair. + fd_t w; + fd_t r; + + // Disable copying of fd_signeler object. + signaler_t (const signaler_t&); + void operator = (const signaler_t&); + }; + +} + +#endif diff --git a/src/socket_base.cpp b/src/socket_base.cpp new file mode 100644 index 0000000..eddb297 --- /dev/null +++ b/src/socket_base.cpp @@ -0,0 +1,681 @@ +/* + Copyright (c) 2007-2010 iMatix Corporation + + This file is part of 0MQ. + + 0MQ is free software; you can redistribute it and/or modify it under + the terms of the Lesser GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + 0MQ is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + Lesser GNU General Public License for more details. + + You should have received a copy of the Lesser GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <new> +#include <string> +#include <algorithm> + +#include "../include/zmq.h" + +#include "socket_base.hpp" +#include "app_thread.hpp" + +#include "zmq_listener.hpp" +#include "zmq_connecter.hpp" +#include "io_thread.hpp" +#include "session.hpp" +#include "config.hpp" +#include "owned.hpp" +#include "pipe.hpp" +#include "err.hpp" +#include "ctx.hpp" +#include "platform.hpp" +#include "pgm_sender.hpp" +#include "pgm_receiver.hpp" +#include "likely.hpp" + +zmq::socket_base_t::socket_base_t (app_thread_t *parent_) : + object_t (parent_), + pending_term_acks (0), + ticks (0), + rcvmore (false), + app_thread (parent_), + shutting_down (false), + sent_seqnum (0), + processed_seqnum (0), + next_ordinal (1) +{ +} + +zmq::socket_base_t::~socket_base_t () +{ +} + +int zmq::socket_base_t::setsockopt (int option_, const void *optval_, + size_t optvallen_) +{ + if (unlikely (app_thread->is_terminated ())) { + errno = ETERM; + return -1; + } + + // First, check whether specific socket type overloads the option. + int rc = xsetsockopt (option_, optval_, optvallen_); + if (rc == 0 || errno != EINVAL) + return rc; + + // If the socket type doesn't support the option, pass it to + // the generic option parser. + return options.setsockopt (option_, optval_, optvallen_); +} + +int zmq::socket_base_t::getsockopt (int option_, void *optval_, + size_t *optvallen_) +{ + if (unlikely (app_thread->is_terminated ())) { + errno = ETERM; + return -1; + } + + if (option_ == ZMQ_RCVMORE) { + if (*optvallen_ < sizeof (int64_t)) { + errno = EINVAL; + return -1; + } + *((int64_t*) optval_) = rcvmore ? 1 : 0; + *optvallen_ = sizeof (int64_t); + return 0; + } + + return options.getsockopt (option_, optval_, optvallen_); +} + +int zmq::socket_base_t::bind (const char *addr_) +{ + if (unlikely (app_thread->is_terminated ())) { + errno = ETERM; + return -1; + } + + // Parse addr_ string. + std::string addr_type; + std::string addr_args; + + std::string addr (addr_); + std::string::size_type pos = addr.find ("://"); + + if (pos == std::string::npos) { + errno = EINVAL; + return -1; + } + + addr_type = addr.substr (0, pos); + addr_args = addr.substr (pos + 3); + + if (addr_type == "inproc") + return register_endpoint (addr_args.c_str (), this); + + if (addr_type == "tcp" || addr_type == "ipc") { + +#if defined ZMQ_HAVE_WINDOWS || defined ZMQ_HAVE_OPENVMS + if (addr_type == "ipc") { + errno = EPROTONOSUPPORT; + return -1; + } +#endif + + zmq_listener_t *listener = new (std::nothrow) zmq_listener_t ( + choose_io_thread (options.affinity), this, options); + zmq_assert (listener); + int rc = listener->set_address (addr_type.c_str(), addr_args.c_str ()); + if (rc != 0) { + delete listener; + return -1; + } + + send_plug (listener); + send_own (this, listener); + return 0; + } + +#if defined ZMQ_HAVE_OPENPGM + if (addr_type == "pgm" || addr_type == "epgm") { + // In the case of PGM bind behaves the same like connect. + return connect (addr_); + } +#endif + + // Unknown protocol. + errno = EPROTONOSUPPORT; + return -1; +} + +int zmq::socket_base_t::connect (const char *addr_) +{ + if (unlikely (app_thread->is_terminated ())) { + errno = ETERM; + return -1; + } + + // Parse addr_ string. + std::string addr_type; + std::string addr_args; + + std::string addr (addr_); + std::string::size_type pos = addr.find ("://"); + + if (pos == std::string::npos) { + errno = EINVAL; + return -1; + } + + addr_type = addr.substr (0, pos); + addr_args = addr.substr (pos + 3); + + if (addr_type == "inproc") { + + // TODO: inproc connect is specific with respect to creating pipes + // as there's no 'reconnect' functionality implemented. Once that + // is in place we should follow generic pipe creation algorithm. + + // Find the peer socket. + socket_base_t *peer = find_endpoint (addr_args.c_str ()); + if (!peer) + return -1; + + pipe_t *in_pipe = NULL; + pipe_t *out_pipe = NULL; + + // Create inbound pipe, if required. + if (options.requires_in) { + in_pipe = new (std::nothrow) pipe_t (this, peer, options.hwm); + zmq_assert (in_pipe); + } + + // Create outbound pipe, if required. + if (options.requires_out) { + out_pipe = new (std::nothrow) pipe_t (peer, this, options.hwm); + zmq_assert (out_pipe); + } + + // Attach the pipes to this socket object. + attach_pipes (in_pipe ? &in_pipe->reader : NULL, + out_pipe ? &out_pipe->writer : NULL, blob_t ()); + + // Attach the pipes to the peer socket. Note that peer's seqnum + // was incremented in find_endpoint function. The callee is notified + // about the fact via the last parameter. + send_bind (peer, out_pipe ? &out_pipe->reader : NULL, + in_pipe ? &in_pipe->writer : NULL, options.identity, false); + + return 0; + } + + // Create unnamed session. + io_thread_t *io_thread = choose_io_thread (options.affinity); + session_t *session = new (std::nothrow) session_t (io_thread, + this, options); + zmq_assert (session); + + // If 'immediate connect' feature is required, we'll created the pipes + // to the session straight away. Otherwise, they'll be created by the + // session once the connection is established. + if (options.immediate_connect) { + + pipe_t *in_pipe = NULL; + pipe_t *out_pipe = NULL; + + // Create inbound pipe, if required. + if (options.requires_in) { + in_pipe = new (std::nothrow) pipe_t (this, session, options.hwm); + zmq_assert (in_pipe); + + } + + // Create outbound pipe, if required. + if (options.requires_out) { + out_pipe = new (std::nothrow) pipe_t (session, this, options.hwm); + zmq_assert (out_pipe); + } + + // Attach the pipes to the socket object. + attach_pipes (in_pipe ? &in_pipe->reader : NULL, + out_pipe ? &out_pipe->writer : NULL, blob_t ()); + + // Attach the pipes to the session object. + session->attach_pipes (out_pipe ? &out_pipe->reader : NULL, + in_pipe ? &in_pipe->writer : NULL, blob_t ()); + } + + // Activate the session. + send_plug (session); + send_own (this, session); + + if (addr_type == "tcp" || addr_type == "ipc") { + +#if defined ZMQ_HAVE_WINDOWS || defined ZMQ_HAVE_OPENVMS + // Windows named pipes are not compatible with Winsock API. + // There's no UNIX domain socket implementation on OpenVMS. + if (addr_type == "ipc") { + errno = EPROTONOSUPPORT; + return -1; + } +#endif + + // Create the connecter object. Supply it with the session name + // so that it can bind the new connection to the session once + // it is established. + zmq_connecter_t *connecter = new (std::nothrow) zmq_connecter_t ( + choose_io_thread (options.affinity), this, options, + session->get_ordinal (), false); + zmq_assert (connecter); + int rc = connecter->set_address (addr_type.c_str(), addr_args.c_str ()); + if (rc != 0) { + delete connecter; + return -1; + } + send_plug (connecter); + send_own (this, connecter); + + return 0; + } + +#if defined ZMQ_HAVE_OPENPGM + if (addr_type == "pgm" || addr_type == "epgm") { + + // If the socket type requires bi-directional communication + // multicast is not an option (it is uni-directional). + if (options.requires_in && options.requires_out) { + errno = ENOCOMPATPROTO; + return -1; + } + + // For epgm, pgm transport with UDP encapsulation is used. + bool udp_encapsulation = (addr_type == "epgm"); + + // At this point we'll create message pipes to the session straight + // away. There's no point in delaying it as no concept of 'connect' + // exists with PGM anyway. + if (options.requires_out) { + + // PGM sender. + pgm_sender_t *pgm_sender = new (std::nothrow) pgm_sender_t ( + choose_io_thread (options.affinity), options); + zmq_assert (pgm_sender); + + int rc = pgm_sender->init (udp_encapsulation, addr_args.c_str ()); + if (rc != 0) { + delete pgm_sender; + return -1; + } + + send_attach (session, pgm_sender, blob_t ()); + } + else if (options.requires_in) { + + // PGM receiver. + pgm_receiver_t *pgm_receiver = new (std::nothrow) pgm_receiver_t ( + choose_io_thread (options.affinity), options); + zmq_assert (pgm_receiver); + + int rc = pgm_receiver->init (udp_encapsulation, addr_args.c_str ()); + if (rc != 0) { + delete pgm_receiver; + return -1; + } + + send_attach (session, pgm_receiver, blob_t ()); + } + else + zmq_assert (false); + + return 0; + } +#endif + + // Unknown protoco. + errno = EPROTONOSUPPORT; + return -1; +} + +int zmq::socket_base_t::send (::zmq_msg_t *msg_, int flags_) +{ + // Process pending commands, if any. + if (unlikely (!app_thread->process_commands (false, true))) { + errno = ETERM; + return -1; + } + + // At this point we impose the MORE flag on the message. + if (flags_ & ZMQ_SNDMORE) + msg_->flags |= ZMQ_MSG_MORE; + + // Try to send the message. + int rc = xsend (msg_, flags_); + if (rc == 0) + return 0; + + // In case of non-blocking send we'll simply propagate + // the error - including EAGAIN - upwards. + if (flags_ & ZMQ_NOBLOCK) + return -1; + + // Oops, we couldn't send the message. Wait for the next + // command, process it and try to send the message again. + while (rc != 0) { + if (errno != EAGAIN) + return -1; + if (unlikely (!app_thread->process_commands (true, false))) { + errno = ETERM; + return -1; + } + rc = xsend (msg_, flags_); + } + return 0; +} + +int zmq::socket_base_t::recv (::zmq_msg_t *msg_, int flags_) +{ + // Get the message. + int rc = xrecv (msg_, flags_); + int err = errno; + + // Once every inbound_poll_rate messages check for signals and process + // incoming commands. This happens only if we are not polling altogether + // because there are messages available all the time. If poll occurs, + // ticks is set to zero and thus we avoid this code. + // + // Note that 'recv' uses different command throttling algorithm (the one + // described above) from the one used by 'send'. This is because counting + // ticks is more efficient than doing rdtsc all the time. + if (++ticks == inbound_poll_rate) { + if (unlikely (!app_thread->process_commands (false, false))) { + errno = ETERM; + return -1; + } + ticks = 0; + } + + // If we have the message, return immediately. + if (rc == 0) { + rcvmore = msg_->flags & ZMQ_MSG_MORE; + if (rcvmore) + msg_->flags &= ~ZMQ_MSG_MORE; + return 0; + } + + // If we don't have the message, restore the original cause of the problem. + errno = err; + + // If the message cannot be fetched immediately, there are two scenarios. + // For non-blocking recv, commands are processed in case there's a revive + // command already waiting int a command pipe. If it's not, return EAGAIN. + if (flags_ & ZMQ_NOBLOCK) { + if (errno != EAGAIN) + return -1; + if (unlikely (!app_thread->process_commands (false, false))) { + errno = ETERM; + return -1; + } + ticks = 0; + return xrecv (msg_, flags_); + } + + // In blocking scenario, commands are processed over and over again until + // we are able to fetch a message. + while (rc != 0) { + if (errno != EAGAIN) + return -1; + if (unlikely (!app_thread->process_commands (true, false))) { + errno = ETERM; + return -1; + } + rc = xrecv (msg_, flags_); + ticks = 0; + } + + rcvmore = msg_->flags & ZMQ_MSG_MORE; + if (rcvmore) + msg_->flags &= ~ZMQ_MSG_MORE; + return 0; +} + +int zmq::socket_base_t::close () +{ + shutting_down = true; + + // Let the thread know that the socket is no longer available. + app_thread->remove_socket (this); + + // Pointer to the context must be retrieved before the socket is + // deallocated. Afterwards it is not available. + ctx_t *ctx = get_ctx (); + + // Unregister all inproc endpoints associated with this socket. + // From this point we are sure that inc_seqnum won't be called again + // on this object. + ctx->unregister_endpoints (this); + + // Wait till all undelivered commands are delivered. This should happen + // very quickly. There's no way to wait here for extensive period of time. + while (processed_seqnum != sent_seqnum.get ()) + app_thread->process_commands (true, false); + + while (true) { + + // On third pass of the loop there should be no more I/O objects + // because all connecters and listerners were destroyed during + // the first pass and all engines delivered by delayed 'own' commands + // are destroyed during the second pass. + if (io_objects.empty () && !pending_term_acks) + break; + + // Send termination request to all associated I/O objects. + for (io_objects_t::iterator it = io_objects.begin (); + it != io_objects.end (); it++) + send_term (*it); + + // Move the objects to the list of pending term acks. + pending_term_acks += io_objects.size (); + io_objects.clear (); + + // Process commands till we get all the termination acknowledgements. + while (pending_term_acks) + app_thread->process_commands (true, false); + } + + // Check whether there are no session leaks. + sessions_sync.lock (); + zmq_assert (named_sessions.empty ()); + zmq_assert (unnamed_sessions.empty ()); + sessions_sync.unlock (); + + delete this; + + // This function must be called after the socket is completely deallocated + // as it may cause termination of the whole 0MQ infrastructure. + ctx->destroy_socket (); + + return 0; +} + +void zmq::socket_base_t::inc_seqnum () +{ + // NB: This function may be called from a different thread! + sent_seqnum.add (1); +} + +zmq::app_thread_t *zmq::socket_base_t::get_thread () +{ + return app_thread; +} + +bool zmq::socket_base_t::has_in () +{ + return xhas_in (); +} + +bool zmq::socket_base_t::has_out () +{ + return xhas_out (); +} + +bool zmq::socket_base_t::register_session (const blob_t &peer_identity_, + session_t *session_) +{ + sessions_sync.lock (); + bool registered = named_sessions.insert ( + std::make_pair (peer_identity_, session_)).second; + sessions_sync.unlock (); + return registered; +} + +void zmq::socket_base_t::unregister_session (const blob_t &peer_identity_) +{ + sessions_sync.lock (); + named_sessions_t::iterator it = named_sessions.find (peer_identity_); + zmq_assert (it != named_sessions.end ()); + named_sessions.erase (it); + sessions_sync.unlock (); +} + +zmq::session_t *zmq::socket_base_t::find_session (const blob_t &peer_identity_) +{ + sessions_sync.lock (); + named_sessions_t::iterator it = named_sessions.find (peer_identity_); + if (it == named_sessions.end ()) { + sessions_sync.unlock (); + return NULL; + } + session_t *session = it->second; + + // Prepare the session for subsequent attach command. + session->inc_seqnum (); + + sessions_sync.unlock (); + return session; +} + +uint64_t zmq::socket_base_t::register_session (session_t *session_) +{ + sessions_sync.lock (); + uint64_t ordinal = next_ordinal; + next_ordinal++; + unnamed_sessions.insert (std::make_pair (ordinal, session_)); + sessions_sync.unlock (); + return ordinal; +} + +void zmq::socket_base_t::unregister_session (uint64_t ordinal_) +{ + sessions_sync.lock (); + unnamed_sessions_t::iterator it = unnamed_sessions.find (ordinal_); + zmq_assert (it != unnamed_sessions.end ()); + unnamed_sessions.erase (it); + sessions_sync.unlock (); +} + +zmq::session_t *zmq::socket_base_t::find_session (uint64_t ordinal_) +{ + sessions_sync.lock (); + + unnamed_sessions_t::iterator it = unnamed_sessions.find (ordinal_); + if (it == unnamed_sessions.end ()) { + sessions_sync.unlock (); + return NULL; + } + session_t *session = it->second; + + // Prepare the session for subsequent attach command. + session->inc_seqnum (); + + sessions_sync.unlock (); + return session; +} + +void zmq::socket_base_t::kill (reader_t *pipe_) +{ + xkill (pipe_); +} + +void zmq::socket_base_t::revive (reader_t *pipe_) +{ + xrevive (pipe_); +} + +void zmq::socket_base_t::revive (writer_t *pipe_) +{ + xrevive (pipe_); +} + +void zmq::socket_base_t::attach_pipes (class reader_t *inpipe_, + class writer_t *outpipe_, const blob_t &peer_identity_) +{ + if (inpipe_) + inpipe_->set_endpoint (this); + if (outpipe_) + outpipe_->set_endpoint (this); + xattach_pipes (inpipe_, outpipe_, peer_identity_); +} + +void zmq::socket_base_t::detach_inpipe (class reader_t *pipe_) +{ + xdetach_inpipe (pipe_); + pipe_->set_endpoint (NULL); // ? +} + +void zmq::socket_base_t::detach_outpipe (class writer_t *pipe_) +{ + xdetach_outpipe (pipe_); + pipe_->set_endpoint (NULL); // ? +} + +void zmq::socket_base_t::process_own (owned_t *object_) +{ + io_objects.insert (object_); +} + +void zmq::socket_base_t::process_bind (reader_t *in_pipe_, writer_t *out_pipe_, + const blob_t &peer_identity_) +{ + attach_pipes (in_pipe_, out_pipe_, peer_identity_); +} + +void zmq::socket_base_t::process_term_req (owned_t *object_) +{ + // When shutting down we can ignore termination requests from owned + // objects. They are going to be terminated anyway. + if (shutting_down) + return; + + // If I/O object is well and alive ask it to terminate. + io_objects_t::iterator it = std::find (io_objects.begin (), + io_objects.end (), object_); + + // If not found, we assume that termination request was already sent to + // the object so we can sagely ignore the request. + if (it == io_objects.end ()) + return; + + pending_term_acks++; + io_objects.erase (it); + send_term (object_); +} + +void zmq::socket_base_t::process_term_ack () +{ + zmq_assert (pending_term_acks); + pending_term_acks--; +} + +void zmq::socket_base_t::process_seqnum () +{ + processed_seqnum++; +} + diff --git a/src/socket_base.hpp b/src/socket_base.hpp new file mode 100644 index 0000000..3d95cec --- /dev/null +++ b/src/socket_base.hpp @@ -0,0 +1,177 @@ +/* + Copyright (c) 2007-2010 iMatix Corporation + + This file is part of 0MQ. + + 0MQ is free software; you can redistribute it and/or modify it under + the terms of the Lesser GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + 0MQ is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + Lesser GNU General Public License for more details. + + You should have received a copy of the Lesser GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef __ZMQ_SOCKET_BASE_HPP_INCLUDED__ +#define __ZMQ_SOCKET_BASE_HPP_INCLUDED__ + +#include <set> +#include <map> +#include <vector> + +#include "../include/zmq.h" + +#include "i_endpoint.hpp" +#include "object.hpp" +#include "yarray_item.hpp" +#include "mutex.hpp" +#include "options.hpp" +#include "stdint.hpp" +#include "atomic_counter.hpp" +#include "stdint.hpp" +#include "blob.hpp" + +namespace zmq +{ + + class socket_base_t : + public object_t, public i_endpoint, public yarray_item_t + { + public: + + socket_base_t (class app_thread_t *parent_); + + // Interface for communication with the API layer. + int setsockopt (int option_, const void *optval_, size_t optvallen_); + int getsockopt (int option_, void *optval_, size_t *optvallen_); + int bind (const char *addr_); + int connect (const char *addr_); + int send (zmq_msg_t *msg_, int flags_); + int recv (zmq_msg_t *msg_, int flags_); + int close (); + + // When another owned object wants to send command to this object + // it calls this function to let it know it should not shut down + // before the command is delivered. + void inc_seqnum (); + + // This function is used by the polling mechanism to determine + // whether the socket belongs to the application thread the poll + // is called from. + class app_thread_t *get_thread (); + + // These functions are used by the polling mechanism to determine + // which events are to be reported from this socket. + bool has_in (); + bool has_out (); + + // The list of sessions cannot be accessed via inter-thread + // commands as it is unacceptable to wait for the completion of the + // action till user application yields control of the application + // thread to 0MQ. Locking is used instead. + // There are two distinct types of sessions: those identified by name + // and those identified by ordinal number. Thus two sets of session + // management functions. + bool register_session (const blob_t &peer_identity_, + class session_t *session_); + void unregister_session (const blob_t &peer_identity_); + class session_t *find_session (const blob_t &peer_identity_); + uint64_t register_session (class session_t *session_); + void unregister_session (uint64_t ordinal_); + class session_t *find_session (uint64_t ordinal_); + + // i_endpoint interface implementation. + void attach_pipes (class reader_t *inpipe_, class writer_t *outpipe_, + const blob_t &peer_identity_); + void detach_inpipe (class reader_t *pipe_); + void detach_outpipe (class writer_t *pipe_); + void kill (class reader_t *pipe_); + void revive (class reader_t *pipe_); + void revive (class writer_t *pipe_); + + protected: + + // Destructor is protected. Socket is closed using 'close' function. + virtual ~socket_base_t (); + + // Pipe management is done by individual socket types. + virtual void xattach_pipes (class reader_t *inpipe_, + class writer_t *outpipe_, const blob_t &peer_identity_) = 0; + virtual void xdetach_inpipe (class reader_t *pipe_) = 0; + virtual void xdetach_outpipe (class writer_t *pipe_) = 0; + virtual void xkill (class reader_t *pipe_) = 0; + virtual void xrevive (class reader_t *pipe_) = 0; + virtual void xrevive (class writer_t *pipe_) = 0; + + // Actual algorithms are to be defined by individual socket types. + virtual int xsetsockopt (int option_, const void *optval_, + size_t optvallen_) = 0; + virtual int xsend (zmq_msg_t *msg_, int options_) = 0; + virtual int xrecv (zmq_msg_t *msg_, int options_) = 0; + virtual bool xhas_in () = 0; + virtual bool xhas_out () = 0; + + // Socket options. + options_t options; + + private: + + // Handlers for incoming commands. + void process_own (class owned_t *object_); + void process_bind (class reader_t *in_pipe_, class writer_t *out_pipe_, + const blob_t &peer_identity_); + void process_term_req (class owned_t *object_); + void process_term_ack (); + void process_seqnum (); + + // List of all I/O objects owned by this socket. The socket is + // responsible for deallocating them before it quits. + typedef std::set <class owned_t*> io_objects_t; + io_objects_t io_objects; + + // Number of I/O objects that were already asked to terminate + // but haven't acknowledged it yet. + int pending_term_acks; + + // Number of messages received since last command processing. + int ticks; + + // If true there's a half-read message in the socket. + bool rcvmore; + + // Application thread the socket lives in. + class app_thread_t *app_thread; + + // If true, socket is already shutting down. No new work should be + // started. + bool shutting_down; + + // Sequence number of the last command sent to this object. + atomic_counter_t sent_seqnum; + + // Sequence number of the last command processed by this object. + uint64_t processed_seqnum; + + // Lists of existing sessions. This lists are never referenced from + // within the socket, instead they are used by I/O objects owned by + // the socket. As those objects can live in different threads, + // the access is synchronised by mutex. + typedef std::map <blob_t, session_t*> named_sessions_t; + named_sessions_t named_sessions; + typedef std::map <uint64_t, session_t*> unnamed_sessions_t; + unnamed_sessions_t unnamed_sessions; + uint64_t next_ordinal; + mutex_t sessions_sync; + + socket_base_t (const socket_base_t&); + void operator = (const socket_base_t&); + }; + +} + +#endif diff --git a/src/stdint.hpp b/src/stdint.hpp new file mode 100644 index 0000000..fe1bff6 --- /dev/null +++ b/src/stdint.hpp @@ -0,0 +1,70 @@ +/* + Copyright (c) 2007-2010 iMatix Corporation + + This file is part of 0MQ. + + 0MQ is free software; you can redistribute it and/or modify it under + the terms of the Lesser GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + 0MQ is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + Lesser GNU General Public License for more details. + + You should have received a copy of the Lesser GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef __ZMQ_STDINT_HPP_INCLUDED__ +#define __ZMQ_STDINT_HPP_INCLUDED__ + +#include "platform.hpp" + +#ifdef ZMQ_HAVE_SOLARIS + +#include <inttypes.h> + +#elif defined _MSC_VER + +#ifndef int8_t +typedef __int8 int8_t; +#endif +#ifndef int16_t +typedef __int16 int16_t; +#endif +#ifndef int32_t +typedef __int32 int32_t; +#endif +#ifndef int64_t +typedef __int64 int64_t; +#endif +#ifndef uint8_t +typedef unsigned __int8 uint8_t; +#endif +#ifndef uint16_t +typedef unsigned __int16 uint16_t; +#endif +#ifndef uint32_t +typedef unsigned __int32 uint32_t; +#endif +#ifndef uint64_t +typedef unsigned __int64 uint64_t; +#endif + +#elif defined ZMQ_HAVE_OPENVMS + +#include <types.h> +typedef unsigned __int8 uint8_t; +typedef unsigned __int16 uint16_t; +typedef unsigned __int32 uint32_t; +typedef unsigned __int64 uint64_t; + +#else + +#include <stdint.h> + +#endif + +#endif diff --git a/src/streamer.cpp b/src/streamer.cpp new file mode 100644 index 0000000..796771b --- /dev/null +++ b/src/streamer.cpp @@ -0,0 +1,38 @@ +/* + Copyright (c) 2007-2010 iMatix Corporation + + This file is part of 0MQ. + + 0MQ is free software; you can redistribute it and/or modify it under + the terms of the Lesser GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + 0MQ is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + Lesser GNU General Public License for more details. + + You should have received a copy of the Lesser GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "../include/zmq.h" + +#include "streamer.hpp" +#include "socket_base.hpp" +#include "err.hpp" + +int zmq::streamer (socket_base_t *insocket_, socket_base_t *outsocket_) +{ + zmq_msg_t msg; + int rc = zmq_msg_init (&msg); + errno_assert (rc == 0); + + while (true) { + insocket_->recv (&msg, 0); + outsocket_->send (&msg, 0); + } + + return 0; +} diff --git a/src/streamer.hpp b/src/streamer.hpp new file mode 100644 index 0000000..8827cff --- /dev/null +++ b/src/streamer.hpp @@ -0,0 +1,31 @@ +/* + Copyright (c) 2007-2010 iMatix Corporation + + This file is part of 0MQ. + + 0MQ is free software; you can redistribute it and/or modify it under + the terms of the Lesser GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + 0MQ is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + Lesser GNU General Public License for more details. + + You should have received a copy of the Lesser GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef __ZMQ_STREAMER_HPP_INCLUDED__ +#define __ZMQ_STREAMER_HPP_INCLUDED__ + +namespace zmq +{ + + int streamer (class socket_base_t *insocket_, + class socket_base_t *outsocket_); + +} + +#endif diff --git a/src/sub.cpp b/src/sub.cpp new file mode 100644 index 0000000..eeb50cd --- /dev/null +++ b/src/sub.cpp @@ -0,0 +1,191 @@ +/* + Copyright (c) 2007-2010 iMatix Corporation + + This file is part of 0MQ. + + 0MQ is free software; you can redistribute it and/or modify it under + the terms of the Lesser GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + 0MQ is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + Lesser GNU General Public License for more details. + + You should have received a copy of the Lesser GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <string.h> + +#include "../include/zmq.h" + +#include "sub.hpp" +#include "err.hpp" + +zmq::sub_t::sub_t (class app_thread_t *parent_) : + socket_base_t (parent_), + has_message (false), + more (false) +{ + options.requires_in = true; + options.requires_out = false; + zmq_msg_init (&message); +} + +zmq::sub_t::~sub_t () +{ + zmq_msg_close (&message); +} + +void zmq::sub_t::xattach_pipes (class reader_t *inpipe_, + class writer_t *outpipe_, const blob_t &peer_identity_) +{ + zmq_assert (inpipe_ && !outpipe_); + fq.attach (inpipe_); +} + +void zmq::sub_t::xdetach_inpipe (class reader_t *pipe_) +{ + zmq_assert (pipe_); + fq.detach (pipe_); +} + +void zmq::sub_t::xdetach_outpipe (class writer_t *pipe_) +{ + // SUB socket is read-only thus there should be no outpipes. + zmq_assert (false); +} + +void zmq::sub_t::xkill (class reader_t *pipe_) +{ + fq.kill (pipe_); +} + +void zmq::sub_t::xrevive (class reader_t *pipe_) +{ + fq.revive (pipe_); +} + +void zmq::sub_t::xrevive (class writer_t *pipe_) +{ + zmq_assert (false); +} + +int zmq::sub_t::xsetsockopt (int option_, const void *optval_, + size_t optvallen_) +{ + if (option_ == ZMQ_SUBSCRIBE) { + subscriptions.add ((unsigned char*) optval_, optvallen_); + return 0; + } + + if (option_ == ZMQ_UNSUBSCRIBE) { + if (!subscriptions.rm ((unsigned char*) optval_, optvallen_)) { + errno = EINVAL; + return -1; + } + return 0; + } + + errno = EINVAL; + return -1; +} + +int zmq::sub_t::xsend (zmq_msg_t *msg_, int flags_) +{ + errno = ENOTSUP; + return -1; +} + +int zmq::sub_t::xrecv (zmq_msg_t *msg_, int flags_) +{ + // If there's already a message prepared by a previous call to zmq_poll, + // return it straight ahead. + if (has_message) { + zmq_msg_move (msg_, &message); + has_message = false; + more = msg_->flags & ZMQ_MSG_MORE; + return 0; + } + + // TODO: This can result in infinite loop in the case of continuous + // stream of non-matching messages which breaks the non-blocking recv + // semantics. + while (true) { + + // Get a message using fair queueing algorithm. + int rc = fq.recv (msg_, flags_); + + // If there's no message available, return immediately. + // The same when error occurs. + if (rc != 0) + return -1; + + // Check whether the message matches at least one subscription. + // Non-initial parts of the message are passed + if (more || match (msg_)) { + more = msg_->flags & ZMQ_MSG_MORE; + return 0; + } + + // Message doesn't match. Pop any remaining parts of the message + // from the pipe. + while (msg_->flags & ZMQ_MSG_MORE) { + rc = fq.recv (msg_, ZMQ_NOBLOCK); + zmq_assert (rc == 0); + } + } +} + +bool zmq::sub_t::xhas_in () +{ + // There are subsequent parts of the partly-read message available. + if (more) + return true; + + // If there's already a message prepared by a previous call to zmq_poll, + // return straight ahead. + if (has_message) + return true; + + // TODO: This can result in infinite loop in the case of continuous + // stream of non-matching messages. + while (true) { + + // Get a message using fair queueing algorithm. + int rc = fq.recv (&message, ZMQ_NOBLOCK); + + // If there's no message available, return immediately. + // The same when error occurs. + if (rc != 0) { + zmq_assert (errno == EAGAIN); + return false; + } + + // Check whether the message matches at least one subscription. + if (match (&message)) { + has_message = true; + return true; + } + + // Message doesn't match. Pop any remaining parts of the message + // from the pipe. + while (message.flags & ZMQ_MSG_MORE) { + rc = fq.recv (&message, ZMQ_NOBLOCK); + zmq_assert (rc == 0); + } + } +} + +bool zmq::sub_t::xhas_out () +{ + return false; +} + +bool zmq::sub_t::match (zmq_msg_t *msg_) +{ + return subscriptions.check ((unsigned char*) zmq_msg_data (msg_), + zmq_msg_size (msg_)); +} diff --git a/src/sub.hpp b/src/sub.hpp new file mode 100644 index 0000000..7b997c9 --- /dev/null +++ b/src/sub.hpp @@ -0,0 +1,81 @@ +/* + Copyright (c) 2007-2010 iMatix Corporation + + This file is part of 0MQ. + + 0MQ is free software; you can redistribute it and/or modify it under + the terms of the Lesser GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + 0MQ is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + Lesser GNU General Public License for more details. + + You should have received a copy of the Lesser GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef __ZMQ_SUB_HPP_INCLUDED__ +#define __ZMQ_SUB_HPP_INCLUDED__ + +#include "../include/zmq.h" + +#include "prefix_tree.hpp" +#include "socket_base.hpp" +#include "fq.hpp" + +namespace zmq +{ + + class sub_t : public socket_base_t + { + public: + + sub_t (class app_thread_t *parent_); + ~sub_t (); + + protected: + + // Overloads of functions from socket_base_t. + void xattach_pipes (class reader_t *inpipe_, class writer_t *outpipe_, + const blob_t &peer_identity_); + void xdetach_inpipe (class reader_t *pipe_); + void xdetach_outpipe (class writer_t *pipe_); + void xkill (class reader_t *pipe_); + void xrevive (class reader_t *pipe_); + void xrevive (class writer_t *pipe_); + int xsetsockopt (int option_, const void *optval_, size_t optvallen_); + int xsend (zmq_msg_t *msg_, int flags_); + int xrecv (zmq_msg_t *msg_, int flags_); + bool xhas_in (); + bool xhas_out (); + + private: + + // Check whether the message matches at least one subscription. + bool match (zmq_msg_t *msg_); + + // Fair queueing object for inbound pipes. + fq_t fq; + + // The repository of subscriptions. + prefix_tree_t subscriptions; + + // If true, 'message' contains a matching message to return on the + // next recv call. + bool has_message; + zmq_msg_t message; + + // If true, part of a multipart message was already received, but + // there are following parts still waiting. + bool more; + + sub_t (const sub_t&); + void operator = (const sub_t&); + }; + +} + +#endif diff --git a/src/tcp_connecter.cpp b/src/tcp_connecter.cpp new file mode 100644 index 0000000..17c0257 --- /dev/null +++ b/src/tcp_connecter.cpp @@ -0,0 +1,307 @@ +/* + Copyright (c) 2007-2010 iMatix Corporation + + This file is part of 0MQ. + + 0MQ is free software; you can redistribute it and/or modify it under + the terms of the Lesser GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + 0MQ is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + Lesser GNU General Public License for more details. + + You should have received a copy of the Lesser GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <string.h> + +#include <string> + +#include "../include/zmq.h" + +#include "tcp_connecter.hpp" +#include "platform.hpp" +#include "ip.hpp" +#include "err.hpp" + +#ifdef ZMQ_HAVE_WINDOWS + +zmq::tcp_connecter_t::tcp_connecter_t () : + s (retired_fd) +{ + memset (&addr, 0, sizeof (addr)); + addr_len = 0; +} + +zmq::tcp_connecter_t::~tcp_connecter_t () +{ + if (s != retired_fd) + close (); +} + +int zmq::tcp_connecter_t::set_address (const char *protocol_, const char *addr_) +{ + if (strcmp (protocol_, "tcp") == 0) + return resolve_ip_hostname (&addr, &addr_len, addr_); + + errno = EPROTONOSUPPORT; + return -1; +} + +int zmq::tcp_connecter_t::open () +{ + zmq_assert (s == retired_fd); + + // Create the socket. + s = socket (addr.ss_family, SOCK_STREAM, IPPROTO_TCP); + if (s == INVALID_SOCKET) { + wsa_error_to_errno (); + return -1; + } + + // Set to non-blocking mode. + unsigned long argp = 1; + int rc = ioctlsocket (s, FIONBIO, &argp); + wsa_assert (rc != SOCKET_ERROR); + + // Disable Nagle's algorithm. + int flag = 1; + rc = setsockopt (s, IPPROTO_TCP, TCP_NODELAY, (char*) &flag, + sizeof (int)); + wsa_assert (rc != SOCKET_ERROR); + + // Connect to the remote peer. + rc = ::connect (s, (sockaddr*) &addr, addr_len); + + // Connect was successfull immediately. + if (rc == 0) + return 0; + + // Asynchronous connect was launched. + if (rc == SOCKET_ERROR && (WSAGetLastError () == WSAEINPROGRESS || + WSAGetLastError () == WSAEWOULDBLOCK)) { + errno = EAGAIN; + return -1; + } + + wsa_error_to_errno (); + return -1; +} + +int zmq::tcp_connecter_t::close () +{ + zmq_assert (s != retired_fd); + int rc = closesocket (s); + wsa_assert (rc != SOCKET_ERROR); + s = retired_fd; + return 0; +} + +zmq::fd_t zmq::tcp_connecter_t::get_fd () +{ + return s; +} + +zmq::fd_t zmq::tcp_connecter_t::connect () +{ + // Nonblocking connect have finished. Check whether an error occured. + int err = 0; + socklen_t len = sizeof err; + int rc = getsockopt (s, SOL_SOCKET, SO_ERROR, (char*) &err, &len); + zmq_assert (rc == 0); + if (err != 0) { + + // Assert that the error was caused by the networking problems + // rather than 0MQ bug. + zmq_assert (err == WSAECONNREFUSED || err == WSAETIMEDOUT || + err == WSAECONNABORTED); + + errno = err; + return retired_fd; + } + + // Return the newly connected socket. + fd_t result = s; + s = retired_fd; + return result; +} + +#else + +#include <unistd.h> +#include <sys/socket.h> +#include <arpa/inet.h> +#include <netinet/tcp.h> +#include <netinet/in.h> +#include <netdb.h> +#include <fcntl.h> + +#ifdef ZMQ_HAVE_OPENVMS +#include <ioctl.h> +#endif + +zmq::tcp_connecter_t::tcp_connecter_t () : + s (retired_fd) +{ + memset (&addr, 0, sizeof (addr)); +} + +zmq::tcp_connecter_t::~tcp_connecter_t () +{ + if (s != retired_fd) + close (); +} + +int zmq::tcp_connecter_t::set_address (const char *protocol_, const char *addr_) +{ + if (strcmp (protocol_, "tcp") == 0) + return resolve_ip_hostname (&addr, &addr_len, addr_); + else if (strcmp (protocol_, "ipc") == 0) + return resolve_local_path (&addr, &addr_len, addr_); + + errno = EPROTONOSUPPORT; + return -1; +} + +int zmq::tcp_connecter_t::open () +{ + zmq_assert (s == retired_fd); + struct sockaddr *sa = (struct sockaddr*) &addr; + + if (AF_UNIX != sa->sa_family) { + + // Create the socket. + s = socket (sa->sa_family, SOCK_STREAM, IPPROTO_TCP); + if (s == -1) + return -1; + + // Set to non-blocking mode. +#ifdef ZMQ_HAVE_OPENVMS + int flags = 1; + int rc = ioctl (s, FIONBIO, &flags); + errno_assert (rc != -1); +#else + int flags = fcntl (s, F_GETFL, 0); + if (flags == -1) + flags = 0; + int rc = fcntl (s, F_SETFL, flags | O_NONBLOCK); + errno_assert (rc != -1); +#endif + + // Disable Nagle's algorithm. + int flag = 1; + rc = setsockopt (s, IPPROTO_TCP, TCP_NODELAY, (char*) &flag, + sizeof (int)); + errno_assert (rc == 0); + +#ifdef ZMQ_HAVE_OPENVMS + // Disable delayed acknowledgements. + flag = 1; + rc = setsockopt (s, IPPROTO_TCP, TCP_NODELACK, (char*) &flag, + sizeof (int)); + errno_assert (rc != SOCKET_ERROR); +#endif + + // Connect to the remote peer. + rc = ::connect (s, (struct sockaddr*) &addr, addr_len); + + // Connect was successfull immediately. + if (rc == 0) + return 0; + + // Asynchronous connect was launched. + if (rc == -1 && errno == EINPROGRESS) { + errno = EAGAIN; + return -1; + } + + // Error occured. + int err = errno; + close (); + errno = err; + return -1; + } + +#ifndef ZMQ_HAVE_OPENVMS + else { + + // Create the socket. + zmq_assert (AF_UNIX == sa->sa_family); + s = socket (AF_UNIX, SOCK_STREAM, 0); + if (s == -1) + return -1; + + // Set the non-blocking flag. + int flag = fcntl (s, F_GETFL, 0); + if (flag == -1) + flag = 0; + int rc = fcntl (s, F_SETFL, flag | O_NONBLOCK); + errno_assert (rc != -1); + + // Connect to the remote peer. + rc = ::connect (s, (struct sockaddr*) &addr, sizeof (sockaddr_un)); + + // Connect was successfull immediately. + if (rc == 0) + return 0; + + // Error occured. + int err = errno; + close (); + errno = err; + return -1; + } +#endif + + zmq_assert (false); + return -1; +} + +int zmq::tcp_connecter_t::close () +{ + zmq_assert (s != retired_fd); + int rc = ::close (s); + if (rc != 0) + return -1; + s = retired_fd; + return 0; +} + +zmq::fd_t zmq::tcp_connecter_t::get_fd () +{ + return s; +} + +zmq::fd_t zmq::tcp_connecter_t::connect () +{ + // Following code should handle both Berkeley-derived socket + // implementations and Solaris. + int err = 0; +#if defined ZMQ_HAVE_HPUX + int len = sizeof (err); +#else + socklen_t len = sizeof (err); +#endif + int rc = getsockopt (s, SOL_SOCKET, SO_ERROR, (char*) &err, &len); + if (rc == -1) + err = errno; + if (err != 0) { + + // Assert that the error was caused by the networking problems + // rather than 0MQ bug. + zmq_assert (err == ECONNREFUSED || err == ETIMEDOUT); + + errno = err; + return retired_fd; + } + + fd_t result = s; + s = retired_fd; + return result; +} + +#endif diff --git a/src/tcp_connecter.hpp b/src/tcp_connecter.hpp new file mode 100644 index 0000000..f1a124f --- /dev/null +++ b/src/tcp_connecter.hpp @@ -0,0 +1,80 @@ +/* + Copyright (c) 2007-2010 iMatix Corporation + + This file is part of 0MQ. + + 0MQ is free software; you can redistribute it and/or modify it under + the terms of the Lesser GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + 0MQ is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + Lesser GNU General Public License for more details. + + You should have received a copy of the Lesser GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef __ZMQ_TCP_CONNECTER_HPP_INCLUDED__ +#define __ZMQ_TCP_CONNECTER_HPP_INCLUDED__ + +#include "platform.hpp" +#include "fd.hpp" + +#ifdef ZMQ_HAVE_WINDOWS +#include "windows.hpp" +#else +#include <sys/types.h> +#include <sys/socket.h> +#endif + +namespace zmq +{ + + // The class encapsulating simple TCP listening socket. + + class tcp_connecter_t + { + public: + + tcp_connecter_t (); + ~tcp_connecter_t (); + + // Set address to connect to. + int set_address (const char *protocol, const char *addr_); + + // Open TCP connecting socket. Address is in + // <hostname>:<port-number> format. Returns -1 in case of error, + // 0 if connect was successfull immediately and 1 if async connect + // was launched. + int open (); + + // Close the connecting socket. + int close (); + + // Get the file descriptor to poll on to get notified about + // connection success. + fd_t get_fd (); + + // Get the file descriptor of newly created connection. Returns + // retired_fd if the connection was unsuccessfull. + fd_t connect (); + + private: + + // Address to connect to. + sockaddr_storage addr; + socklen_t addr_len; + + // Underlying socket. + fd_t s; + + tcp_connecter_t (const tcp_connecter_t&); + void operator = (const tcp_connecter_t&); + }; + +} + +#endif diff --git a/src/tcp_listener.cpp b/src/tcp_listener.cpp new file mode 100644 index 0000000..0cb9a6e --- /dev/null +++ b/src/tcp_listener.cpp @@ -0,0 +1,352 @@ +/* + Copyright (c) 2007-2010 iMatix Corporation + + This file is part of 0MQ. + + 0MQ is free software; you can redistribute it and/or modify it under + the terms of the Lesser GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + 0MQ is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + Lesser GNU General Public License for more details. + + You should have received a copy of the Lesser GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <string.h> + +#include "../include/zmq.h" + +#include "tcp_listener.hpp" +#include "platform.hpp" +#include "ip.hpp" +#include "config.hpp" +#include "err.hpp" + +#ifdef ZMQ_HAVE_WINDOWS + +zmq::tcp_listener_t::tcp_listener_t () : + s (retired_fd) +{ + memset (&addr, 0, sizeof (addr)); + addr_len = 0; +} + +zmq::tcp_listener_t::~tcp_listener_t () +{ + if (s != retired_fd) + close (); +} + +int zmq::tcp_listener_t::set_address (const char *protocol_, const char *addr_) +{ + // IPC protocol is not supported on Windows platform. + if (strcmp (protocol_, "tcp") != 0 ) { + errno = EPROTONOSUPPORT; + return -1; + } + + // Convert the interface into sockaddr_in structure. + int rc = resolve_ip_interface (&addr, &addr_len, addr_); + if (rc != 0) + return rc; + + // Create a listening socket. + s = socket (addr.ss_family, SOCK_STREAM, IPPROTO_TCP); + if (s == INVALID_SOCKET) { + wsa_error_to_errno (); + return -1; + } + + // Allow reusing of the address. + int flag = 1; + rc = setsockopt (s, SOL_SOCKET, SO_REUSEADDR, + (const char*) &flag, sizeof (int)); + wsa_assert (rc != SOCKET_ERROR); + + // Set the non-blocking flag. + u_long uflag = 1; + rc = ioctlsocket (s, FIONBIO, &uflag); + wsa_assert (rc != SOCKET_ERROR); + + // Bind the socket to the network interface and port. + rc = bind (s, (struct sockaddr*) &addr, addr_len); + if (rc == SOCKET_ERROR) { + wsa_error_to_errno (); + return -1; + } + + // Listen for incomming connections. + rc = listen (s, 1); + if (rc == SOCKET_ERROR) { + wsa_error_to_errno (); + return -1; + } + + return 0; +} + +int zmq::tcp_listener_t::close () +{ + zmq_assert (s != retired_fd); + int rc = closesocket (s); + wsa_assert (rc != SOCKET_ERROR); + s = retired_fd; + return 0; +} + +zmq::fd_t zmq::tcp_listener_t::get_fd () +{ + return s; +} + +zmq::fd_t zmq::tcp_listener_t::accept () +{ + zmq_assert (s != retired_fd); + + // Accept one incoming connection. + fd_t sock = ::accept (s, NULL, NULL); + if (sock == INVALID_SOCKET && + (WSAGetLastError () == WSAEWOULDBLOCK || + WSAGetLastError () == WSAECONNRESET)) + return retired_fd; + + zmq_assert (sock != INVALID_SOCKET); + + // Set to non-blocking mode. + unsigned long argp = 1; + int rc = ioctlsocket (sock, FIONBIO, &argp); + wsa_assert (rc != SOCKET_ERROR); + + // Disable Nagle's algorithm. + int flag = 1; + rc = setsockopt (sock, IPPROTO_TCP, TCP_NODELAY, (char*) &flag, + sizeof (int)); + wsa_assert (rc != SOCKET_ERROR); + + return sock; +} + +#else + +#include <unistd.h> +#include <sys/socket.h> +#include <arpa/inet.h> +#include <netinet/tcp.h> +#include <netinet/in.h> +#include <netdb.h> +#include <fcntl.h> + +#ifndef ZMQ_HAVE_OPENVMS +#include <sys/un.h> +#endif + +#ifdef ZMQ_HAVE_OPENVMS +#include <ioctl.h> +#endif + +zmq::tcp_listener_t::tcp_listener_t () : + s (retired_fd) +{ + memset (&addr, 0, sizeof (addr)); +} + +zmq::tcp_listener_t::~tcp_listener_t () +{ + if (s != retired_fd) + close (); +} + +int zmq::tcp_listener_t::set_address (const char *protocol_, const char *addr_) +{ + if (strcmp (protocol_, "tcp") == 0 ) { + + // Resolve the sockaddr to bind to. + int rc = resolve_ip_interface (&addr, &addr_len, addr_); + if (rc != 0) + return -1; + + // Create a listening socket. + s = socket (addr.ss_family, SOCK_STREAM, IPPROTO_TCP); + if (s == -1) + return -1; + + // Allow reusing of the address. + int flag = 1; + rc = setsockopt (s, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof (int)); + errno_assert (rc == 0); + + // Set the non-blocking flag. +#ifdef ZMQ_HAVE_OPENVMS + flag = 1; + rc = ioctl (s, FIONBIO, &flag); + errno_assert (rc != -1); +#else + flag = fcntl (s, F_GETFL, 0); + if (flag == -1) + flag = 0; + rc = fcntl (s, F_SETFL, flag | O_NONBLOCK); + errno_assert (rc != -1); +#endif + + // Bind the socket to the network interface and port. + rc = bind (s, (struct sockaddr*) &addr, addr_len); + if (rc != 0) { + close (); + return -1; + } + + // Listen for incomming connections. + rc = listen (s, tcp_connection_backlog); + if (rc != 0) { + close (); + return -1; + } + + return 0; + } +#ifndef ZMQ_HAVE_OPENVMS + else if (strcmp (protocol_, "ipc") == 0) { + + // Get rid of the file associated with the UNIX domain socket that + // may have been left behind by the previous run of the application. + ::unlink (addr_); + + // Convert the address into sockaddr_un structure. + int rc = resolve_local_path (&addr, &addr_len, addr_); + if (rc != 0) + return -1; + + // Create a listening socket. + s = socket (AF_UNIX, SOCK_STREAM, 0); + if (s == -1) + return -1; + + // Set the non-blocking flag. + int flag = fcntl (s, F_GETFL, 0); + if (flag == -1) + flag = 0; + rc = fcntl (s, F_SETFL, flag | O_NONBLOCK); + errno_assert (rc != -1); + + // Bind the socket to the file path. + rc = bind (s, (struct sockaddr*) &addr, sizeof (sockaddr_un)); + if (rc != 0) { + close (); + return -1; + } + + // Listen for incomming connections. + rc = listen (s, tcp_connection_backlog); + if (rc != 0) { + close (); + return -1; + } + + return 0; + } +#endif + else { + errno = EPROTONOSUPPORT; + return -1; + } +} + +int zmq::tcp_listener_t::close () +{ + zmq_assert (s != retired_fd); + int rc = ::close (s); + if (rc != 0) + return -1; + s = retired_fd; + +#ifndef ZMQ_HAVE_OPENVMS + // If there's an underlying UNIX domain socket, get rid of the file it + // is associated with. + struct sockaddr_un *su = (struct sockaddr_un*) &addr; + if (AF_UNIX == su->sun_family) { + rc = ::unlink(su->sun_path); + if (rc != 0) + return -1; + } +#endif + + return 0; +} + +zmq::fd_t zmq::tcp_listener_t::get_fd () +{ + return s; +} + +zmq::fd_t zmq::tcp_listener_t::accept () +{ + zmq_assert (s != retired_fd); + + // Accept one incoming connection. + fd_t sock = ::accept (s, NULL, NULL); + +#if (defined ZMQ_HAVE_LINUX || defined ZMQ_HAVE_FREEBSD || \ + defined ZMQ_HAVE_OPENBSD || defined ZMQ_HAVE_OSX || \ + defined ZMQ_HAVE_OPENVMS || defined ZMQ_HAVE_NETBSD) + if (sock == -1 && + (errno == EAGAIN || errno == EWOULDBLOCK || + errno == EINTR || errno == ECONNABORTED)) + return retired_fd; +#elif (defined ZMQ_HAVE_SOLARIS || defined ZMQ_HAVE_AIX) + if (sock == -1 && + (errno == EWOULDBLOCK || errno == EINTR || + errno == ECONNABORTED || errno == EPROTO)) + return retired_fd; +#elif defined ZMQ_HAVE_HPUX + if (sock == -1 && + (errno == EAGAIN || errno == EWOULDBLOCK || + errno == EINTR || errno == ECONNABORTED || errno == ENOBUFS)) + return retired_fd; +#elif defined ZMQ_HAVE_QNXNTO + if (sock == -1 && + (errno == EWOULDBLOCK || errno == EINTR || errno == ECONNABORTED)) + return retired_fd; +#endif + + errno_assert (sock != -1); + + // Set to non-blocking mode. +#ifdef ZMQ_HAVE_OPENVMS + int flags = 1; + int rc = ioctl (sock, FIONBIO, &flags); + errno_assert (rc != -1); +#else + int flags = fcntl (s, F_GETFL, 0); + if (flags == -1) + flags = 0; + int rc = fcntl (sock, F_SETFL, flags | O_NONBLOCK); + errno_assert (rc != -1); +#endif + + struct sockaddr *sa = (struct sockaddr*) &addr; + if (AF_UNIX != sa->sa_family) { + + // Disable Nagle's algorithm. + int flag = 1; + rc = setsockopt (sock, IPPROTO_TCP, TCP_NODELAY, (char*) &flag, + sizeof (int)); + errno_assert (rc == 0); + +#ifdef ZMQ_HAVE_OPENVMS + // Disable delayed acknowledgements. + flag = 1; + rc = setsockopt (sock, IPPROTO_TCP, TCP_NODELACK, (char*) &flag, + sizeof (int)); + errno_assert (rc != SOCKET_ERROR); +#endif + } + + return sock; +} + +#endif diff --git a/src/tcp_listener.hpp b/src/tcp_listener.hpp new file mode 100644 index 0000000..3b60719 --- /dev/null +++ b/src/tcp_listener.hpp @@ -0,0 +1,68 @@ +/* + Copyright (c) 2007-2010 iMatix Corporation + + This file is part of 0MQ. + + 0MQ is free software; you can redistribute it and/or modify it under + the terms of the Lesser GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + 0MQ is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + Lesser GNU General Public License for more details. + + You should have received a copy of the Lesser GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef __ZMQ_TCP_LISTENER_HPP_INCLUDED__ +#define __ZMQ_TCP_LISTENER_HPP_INCLUDED__ + +#include "fd.hpp" +#include "ip.hpp" + +namespace zmq +{ + + // The class encapsulating simple TCP listening socket. + + class tcp_listener_t + { + public: + + tcp_listener_t (); + ~tcp_listener_t (); + + // Start listening on the interface. + int set_address (const char *protocol_, const char *addr_); + + // Close the listening socket. + int close (); + + // Get the file descriptor to poll on to get notified about + // newly created connections. + fd_t get_fd (); + + // Accept the new connection. Returns the file descriptor of the + // newly created connection. The function may return retired_fd + // if the connection was dropped while waiting in the listen backlog. + fd_t accept (); + + private: + + // Address to listen on. + sockaddr_storage addr; + socklen_t addr_len; + + // Underlying socket. + fd_t s; + + tcp_listener_t (const tcp_listener_t&); + void operator = (const tcp_listener_t&); + }; + +} + +#endif diff --git a/src/tcp_socket.cpp b/src/tcp_socket.cpp new file mode 100644 index 0000000..cc426d7 --- /dev/null +++ b/src/tcp_socket.cpp @@ -0,0 +1,226 @@ +/* + Copyright (c) 2007-2010 iMatix Corporation + + This file is part of 0MQ. + + 0MQ is free software; you can redistribute it and/or modify it under + the terms of the Lesser GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + 0MQ is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + Lesser GNU General Public License for more details. + + You should have received a copy of the Lesser GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "tcp_socket.hpp" +#include "platform.hpp" +#include "err.hpp" + +#ifdef ZMQ_HAVE_WINDOWS + +zmq::tcp_socket_t::tcp_socket_t () : + s (retired_fd) +{ +} + +zmq::tcp_socket_t::~tcp_socket_t () +{ + if (s != retired_fd) + close (); +} + +int zmq::tcp_socket_t::open (fd_t fd_, uint64_t sndbuf_, uint64_t rcvbuf_) +{ + zmq_assert (s == retired_fd); + s = fd_; + + if (sndbuf_) { + int sz = (int) sndbuf_; + int rc = setsockopt (s, SOL_SOCKET, SO_SNDBUF, + (char*) &sz, sizeof (int)); + errno_assert (rc == 0); + } + + if (rcvbuf_) { + int sz = (int) rcvbuf_; + int rc = setsockopt (s, SOL_SOCKET, SO_RCVBUF, + (char*) &sz, sizeof (int)); + errno_assert (rc == 0); + } + + return 0; +} + +int zmq::tcp_socket_t::close () +{ + zmq_assert (s != retired_fd); + int rc = closesocket (s); + wsa_assert (rc != SOCKET_ERROR); + s = retired_fd; + return 0; +} + +zmq::fd_t zmq::tcp_socket_t::get_fd () +{ + return s; +} + +int zmq::tcp_socket_t::write (const void *data, int size) +{ + int nbytes = send (s, (char*) data, size, 0); + + // If not a single byte can be written to the socket in non-blocking mode + // we'll get an error (this may happen during the speculative write). + if (nbytes == SOCKET_ERROR && WSAGetLastError () == WSAEWOULDBLOCK) + return 0; + + // Signalise peer failure. + if (nbytes == -1 && ( + WSAGetLastError () == WSAENETDOWN || + WSAGetLastError () == WSAENETRESET || + WSAGetLastError () == WSAEHOSTUNREACH || + WSAGetLastError () == WSAECONNABORTED || + WSAGetLastError () == WSAETIMEDOUT || + WSAGetLastError () == WSAECONNRESET)) + return -1; + + wsa_assert (nbytes != SOCKET_ERROR); + + return (size_t) nbytes; +} + +int zmq::tcp_socket_t::read (void *data, int size) +{ + int nbytes = recv (s, (char*) data, size, 0); + + // If not a single byte can be read from the socket in non-blocking mode + // we'll get an error (this may happen during the speculative read). + if (nbytes == SOCKET_ERROR && WSAGetLastError () == WSAEWOULDBLOCK) + return 0; + + // Connection failure. + if (nbytes == -1 && ( + WSAGetLastError () == WSAENETDOWN || + WSAGetLastError () == WSAENETRESET || + WSAGetLastError () == WSAECONNABORTED || + WSAGetLastError () == WSAETIMEDOUT || + WSAGetLastError () == WSAECONNRESET || + WSAGetLastError () == WSAECONNREFUSED || + WSAGetLastError () == WSAENOTCONN)) + return -1; + + wsa_assert (nbytes != SOCKET_ERROR); + + // Orderly shutdown by the other peer. + if (nbytes == 0) + return -1; + + return (size_t) nbytes; +} + +#else + +#include <unistd.h> +#include <sys/socket.h> +#include <arpa/inet.h> +#include <netinet/tcp.h> +#include <netinet/in.h> +#include <netdb.h> +#include <fcntl.h> + +zmq::tcp_socket_t::tcp_socket_t () : + s (retired_fd) +{ +} + +zmq::tcp_socket_t::~tcp_socket_t () +{ + if (s != retired_fd) + close (); +} + +int zmq::tcp_socket_t::open (fd_t fd_, uint64_t sndbuf_, uint64_t rcvbuf_) +{ + assert (s == retired_fd); + s = fd_; + + if (sndbuf_) { + int sz = (int) sndbuf_; + int rc = setsockopt (s, SOL_SOCKET, SO_SNDBUF, &sz, sizeof (int)); + errno_assert (rc == 0); + } + + if (rcvbuf_) { + int sz = (int) rcvbuf_; + int rc = setsockopt (s, SOL_SOCKET, SO_RCVBUF, &sz, sizeof (int)); + errno_assert (rc == 0); + } + + return 0; +} + +int zmq::tcp_socket_t::close () +{ + zmq_assert (s != retired_fd); + int rc = ::close (s); + if (rc != 0) + return -1; + s = retired_fd; + return 0; +} + +zmq::fd_t zmq::tcp_socket_t::get_fd () +{ + return s; +} + +int zmq::tcp_socket_t::write (const void *data, int size) +{ + ssize_t nbytes = send (s, data, size, 0); + + // Several errors are OK. When speculative write is being done we may not + // be able to write a single byte to the socket. Also, SIGSTOP issued + // by a debugging tool can result in EINTR error. + if (nbytes == -1 && (errno == EAGAIN || errno == EWOULDBLOCK || + errno == EINTR)) + return 0; + + // Signalise peer failure. + if (nbytes == -1 && (errno == ECONNRESET || errno == EPIPE)) + return -1; + + errno_assert (nbytes != -1); + return (size_t) nbytes; +} + +int zmq::tcp_socket_t::read (void *data, int size) +{ + ssize_t nbytes = recv (s, data, size, 0); + + // Several errors are OK. When speculative read is being done we may not + // be able to read a single byte to the socket. Also, SIGSTOP issued + // by a debugging tool can result in EINTR error. + if (nbytes == -1 && (errno == EAGAIN || errno == EWOULDBLOCK || + errno == EINTR)) + return 0; + + // Signalise peer failure. + if (nbytes == -1 && (errno == ECONNRESET || errno == ECONNREFUSED)) + return -1; + + errno_assert (nbytes != -1); + + // Orderly shutdown by the other peer. + if (nbytes == 0) + return -1; + + return (size_t) nbytes; +} + +#endif + diff --git a/src/tcp_socket.hpp b/src/tcp_socket.hpp new file mode 100644 index 0000000..3aae060 --- /dev/null +++ b/src/tcp_socket.hpp @@ -0,0 +1,71 @@ +/* + Copyright (c) 2007-2010 iMatix Corporation + + This file is part of 0MQ. + + 0MQ is free software; you can redistribute it and/or modify it under + the terms of the Lesser GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + 0MQ is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + Lesser GNU General Public License for more details. + + You should have received a copy of the Lesser GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef __ZMQ_TCP_SOCKET_HPP_INCLUDED__ +#define __ZMQ_TCP_SOCKET_HPP_INCLUDED__ + +#include "fd.hpp" +#include "stdint.hpp" + +namespace zmq +{ + + // The class encapsulating simple TCP read/write socket. + + class tcp_socket_t + { + public: + + tcp_socket_t (); + ~tcp_socket_t (); + + // Associates a socket with a native socket descriptor. + int open (fd_t fd_, uint64_t sndbuf_, uint64_t rcvbuf_); + + // Closes the underlying socket. + int close (); + + // Returns the underlying socket. Returns retired_fd when the socket + // is in the closed state. + fd_t get_fd (); + + // Writes data to the socket. Returns the number of bytes actually + // written (even zero is to be considered to be a success). In case + // of error or orderly shutdown by the other peer -1 is returned. + int write (const void *data, int size); + + // Reads data from the socket (up to 'size' bytes). Returns the number + // of bytes actually read (even zero is to be considered to be + // a success). In case of error or orderly shutdown by the other + // peer -1 is returned. + int read (void *data, int size); + + private: + + // Underlying socket. + fd_t s; + + // Disable copy construction of tcp_socket. + tcp_socket_t (const tcp_socket_t&); + void operator = (const tcp_socket_t&); + }; + +} + +#endif diff --git a/src/thread.cpp b/src/thread.cpp new file mode 100644 index 0000000..602ca8b --- /dev/null +++ b/src/thread.cpp @@ -0,0 +1,108 @@ +/* + Copyright (c) 2007-2010 iMatix Corporation + + This file is part of 0MQ. + + 0MQ is free software; you can redistribute it and/or modify it under + the terms of the Lesser GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + 0MQ is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + Lesser GNU General Public License for more details. + + You should have received a copy of the Lesser GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "thread.hpp" +#include "err.hpp" +#include "platform.hpp" + +#ifdef ZMQ_HAVE_WINDOWS + +void zmq::thread_t::start (thread_fn *tfn_, void *arg_) +{ + tfn = tfn_; + arg =arg_; + descriptor = (HANDLE) _beginthreadex (NULL, 0, + &zmq::thread_t::thread_routine, this, 0 , NULL); + win_assert (descriptor != NULL); +} + +void zmq::thread_t::stop () +{ + DWORD rc = WaitForSingleObject (descriptor, INFINITE); + win_assert (rc != WAIT_FAILED); +} + +zmq::thread_t::id_t zmq::thread_t::id () +{ + return GetCurrentThreadId (); +} + +bool zmq::thread_t::equal (id_t id1_, id_t id2_) +{ + return id1_ == id2_; +} + +unsigned int __stdcall zmq::thread_t::thread_routine (void *arg_) +{ + thread_t *self = (thread_t*) arg_; + self->tfn (self->arg); + return 0; +} + +#else + +#include <signal.h> + +void zmq::thread_t::start (thread_fn *tfn_, void *arg_) +{ + tfn = tfn_; + arg =arg_; + int rc = pthread_create (&descriptor, NULL, thread_routine, this); + errno_assert (rc == 0); +} + +void zmq::thread_t::stop () +{ + int rc = pthread_join (descriptor, NULL); + errno_assert (rc == 0); +} + +zmq::thread_t::id_t zmq::thread_t::id () +{ + return pthread_self (); +} + +bool zmq::thread_t::equal (id_t id1_, id_t id2_) +{ + return pthread_equal (id1_, id2_) != 0; +} + +void *zmq::thread_t::thread_routine (void *arg_) +{ +#if !defined ZMQ_HAVE_OPENVMS + // Following code will guarantee more predictable latecnies as it'll + // disallow any signal handling in the I/O thread. + sigset_t signal_set; + int rc = sigfillset (&signal_set); + errno_assert (rc == 0); + rc = pthread_sigmask (SIG_BLOCK, &signal_set, NULL); + errno_assert (rc == 0); +#endif + + thread_t *self = (thread_t*) arg_; + self->tfn (self->arg); + return NULL; +} + +#endif + + + + + diff --git a/src/thread.hpp b/src/thread.hpp new file mode 100644 index 0000000..432770c --- /dev/null +++ b/src/thread.hpp @@ -0,0 +1,86 @@ +/* + Copyright (c) 2007-2010 iMatix Corporation + + This file is part of 0MQ. + + 0MQ is free software; you can redistribute it and/or modify it under + the terms of the Lesser GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + 0MQ is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + Lesser GNU General Public License for more details. + + You should have received a copy of the Lesser GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef __ZMQ_THREAD_HPP_INCLUDED__ +#define __ZMQ_THREAD_HPP_INCLUDED__ + +#include "platform.hpp" + +#ifdef ZMQ_HAVE_WINDOWS +#include "windows.hpp" +#else +#include <pthread.h> +#endif + +namespace zmq +{ + + typedef void (thread_fn) (void*); + + // Class encapsulating OS thread. Thread initiation/termination is done + // using special functions rather than in constructor/destructor so that + // thread isn't created during object construction by accident, causing + // newly created thread to access half-initialised object. Same applies + // to the destruction process: Thread should be terminated before object + // destruction begins, otherwise it can access half-destructed object. + + class thread_t + { + public: + + inline thread_t () + { + } + + // Creates OS thread. 'tfn' is main thread function. It'll be passed + // 'arg' as an argument. + void start (thread_fn *tfn_, void *arg_); + + // Waits for thread termination. + void stop (); + +#ifdef ZMQ_HAVE_WINDOWS + typedef DWORD id_t; +#else + typedef pthread_t id_t; +#endif + + static id_t id (); + static bool equal (id_t id1_, id_t id2_); + + private: + +#ifdef ZMQ_HAVE_WINDOWS + static unsigned int __stdcall thread_routine (void *arg_); + HANDLE descriptor; +#else + static void *thread_routine (void *arg_); + pthread_t descriptor; +#endif + + thread_fn *tfn; + void *arg; + + thread_t (const thread_t&); + void operator = (const thread_t&); + }; + +} + +#endif diff --git a/src/upstream.cpp b/src/upstream.cpp new file mode 100644 index 0000000..1498c31 --- /dev/null +++ b/src/upstream.cpp @@ -0,0 +1,98 @@ +/* + Copyright (c) 2007-2010 iMatix Corporation + + This file is part of 0MQ. + + 0MQ is free software; you can redistribute it and/or modify it under + the terms of the Lesser GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + 0MQ is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + Lesser GNU General Public License for more details. + + You should have received a copy of the Lesser GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "../include/zmq.h" + +#include "upstream.hpp" +#include "err.hpp" + +zmq::upstream_t::upstream_t (class app_thread_t *parent_) : + socket_base_t (parent_) +{ + options.requires_in = true; + options.requires_out = false; +} + +zmq::upstream_t::~upstream_t () +{ +} + +void zmq::upstream_t::xattach_pipes (class reader_t *inpipe_, + class writer_t *outpipe_, const blob_t &peer_identity_) +{ + zmq_assert (inpipe_ && !outpipe_); + fq.attach (inpipe_); +} + +void zmq::upstream_t::xdetach_inpipe (class reader_t *pipe_) +{ + zmq_assert (pipe_); + fq.detach (pipe_); +} + +void zmq::upstream_t::xdetach_outpipe (class writer_t *pipe_) +{ + // There are no outpipes, so this function shouldn't be called at all. + zmq_assert (false); +} + +void zmq::upstream_t::xkill (class reader_t *pipe_) +{ + fq.kill (pipe_); +} + +void zmq::upstream_t::xrevive (class reader_t *pipe_) +{ + fq.revive (pipe_); +} + +void zmq::upstream_t::xrevive (class writer_t *pipe_) +{ + zmq_assert (false); +} + +int zmq::upstream_t::xsetsockopt (int option_, const void *optval_, + size_t optvallen_) +{ + // No special options for this socket type. + errno = EINVAL; + return -1; +} + +int zmq::upstream_t::xsend (zmq_msg_t *msg_, int flags_) +{ + errno = ENOTSUP; + return -1; +} + +int zmq::upstream_t::xrecv (zmq_msg_t *msg_, int flags_) +{ + return fq.recv (msg_, flags_); +} + +bool zmq::upstream_t::xhas_in () +{ + return fq.has_in (); +} + +bool zmq::upstream_t::xhas_out () +{ + return false; +} + diff --git a/src/upstream.hpp b/src/upstream.hpp new file mode 100644 index 0000000..5fe42ae --- /dev/null +++ b/src/upstream.hpp @@ -0,0 +1,62 @@ +/* + Copyright (c) 2007-2010 iMatix Corporation + + This file is part of 0MQ. + + 0MQ is free software; you can redistribute it and/or modify it under + the terms of the Lesser GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + 0MQ is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + Lesser GNU General Public License for more details. + + You should have received a copy of the Lesser GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef __ZMQ_UPSTREAM_HPP_INCLUDED__ +#define __ZMQ_UPSTREAM_HPP_INCLUDED__ + +#include "socket_base.hpp" +#include "fq.hpp" + +namespace zmq +{ + + class upstream_t : public socket_base_t + { + public: + + upstream_t (class app_thread_t *parent_); + ~upstream_t (); + + // Overloads of functions from socket_base_t. + void xattach_pipes (class reader_t *inpipe_, class writer_t *outpipe_, + const blob_t &peer_identity_); + void xdetach_inpipe (class reader_t *pipe_); + void xdetach_outpipe (class writer_t *pipe_); + void xkill (class reader_t *pipe_); + void xrevive (class reader_t *pipe_); + void xrevive (class writer_t *pipe_); + int xsetsockopt (int option_, const void *optval_, size_t optvallen_); + int xsend (zmq_msg_t *msg_, int flags_); + int xrecv (zmq_msg_t *msg_, int flags_); + bool xhas_in (); + bool xhas_out (); + + private: + + // Fair queueing object for inbound pipes. + fq_t fq; + + upstream_t (const upstream_t&); + void operator = (const upstream_t&); + + }; + +} + +#endif diff --git a/src/uuid.cpp b/src/uuid.cpp new file mode 100644 index 0000000..406bbb4 --- /dev/null +++ b/src/uuid.cpp @@ -0,0 +1,235 @@ +/* + Copyright (c) 2007-2010 iMatix Corporation + + This file is part of 0MQ. + + 0MQ is free software; you can redistribute it and/or modify it under + the terms of the Lesser GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + 0MQ is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + Lesser GNU General Public License for more details. + + You should have received a copy of the Lesser GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "uuid.hpp" +#include "err.hpp" + +#if defined ZMQ_HAVE_WINDOWS + +zmq::uuid_t::uuid_t () +{ + RPC_STATUS ret = UuidCreate (&uuid); + zmq_assert (ret == RPC_S_OK); + ret = UuidToString (&uuid, &string_buf); + zmq_assert (ret == RPC_S_OK); + + /* + HRESULT hr = CoCreateGUID (&uuid); + zmq_assert (hr == S_OK); + int rc = StringFromGUID2 (uuid, string_buf, 40); + zmq_assert (rc != 0); + */ + + create_blob (); +} + +zmq::uuid_t::~uuid_t () +{ +} + +const char *zmq::uuid_t::to_string () +{ + return (char*) string_buf; +} + +#elif defined ZMQ_HAVE_FREEBSD || defined ZMQ_HAVE_NETBSD + +#include <stdlib.h> +#include <uuid.h> + +zmq::uuid_t::uuid_t () +{ + uint32_t status; + uuid_create (&uuid, &status); + zmq_assert (status == uuid_s_ok); + uuid_to_string (&uuid, &string_buf, &status); + zmq_assert (status == uuid_s_ok); + + create_blob (); +} + +zmq::uuid_t::~uuid_t () +{ + free (string_buf); +} + +const char *zmq::uuid_t::to_string () +{ + return string_buf; +} + +#elif defined ZMQ_HAVE_LINUX || defined ZMQ_HAVE_SOLARIS ||\ + defined ZMQ_HAVE_OSX || defined ZMQ_HAVE_CYGWIN + +#include <uuid/uuid.h> + +zmq::uuid_t::uuid_t () +{ + uuid_generate (uuid); + uuid_unparse (uuid, string_buf); + + create_blob (); +} + +zmq::uuid_t::~uuid_t () +{ +} + +const char *zmq::uuid_t::to_string () +{ + return string_buf; +} + +#elif defined ZMQ_HAVE_OPENVMS + +#include <starlet.h> + +#define uuid_generate(x) sys$create_uid(&(x)) + +#define uuid_unparse(x, y) \ + sprintf (y, "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x", \ + x.data0, x.data1, x.data2, \ + x.data3 [0], x.data3 [1], \ + x.data3 [2], x.data3 [3], \ + x.data3 [4], x.data3 [5], \ + x.data3 [6], x.data3 [7]); + +zmq::uuid_t::uuid_t () +{ + uuid_generate (uuid); + uuid_unparse (uuid, string_buf); +} + +zmq::uuid_t::~uuid_t () +{ +} + +const char *zmq::uuid_t::to_string () +{ + return string_buf; +} + +#else + +#include <stdio.h> +#include <string.h> +#include <openssl/rand.h> + +zmq::uuid_t::uuid_t () +{ + unsigned char rand_buf [16]; + int ret = RAND_bytes (rand_buf, sizeof rand_buf); + zmq_assert (ret == 1); + + // Read in UUID fields. + memcpy (&time_low, rand_buf, sizeof time_low); + memcpy (&time_mid, rand_buf + 4, sizeof time_mid); + memcpy (&time_hi_and_version, rand_buf + 6, sizeof time_hi_and_version); + memcpy (&clock_seq_hi_and_reserved, rand_buf + 8, + sizeof clock_seq_hi_and_reserved); + memcpy (&clock_seq_low, rand_buf + 9, sizeof clock_seq_low); + memcpy (&node [0], rand_buf + 10, sizeof node); + + // Store UUID version number. + time_hi_and_version = (time_hi_and_version & 0x0fff) | 4 << 12; + + // Store UUID type. + clock_seq_hi_and_reserved = (clock_seq_hi_and_reserved & 0x3f) | 0x80; + + snprintf (string_buf, sizeof string_buf, + "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x", + time_low, + time_mid, + time_hi_and_version, + clock_seq_hi_and_reserved, + clock_seq_low, + node [0], node [1], node [2], node [3], node [4], node [5]); + + create_blob (); +} + +zmq::uuid_t::~uuid_t () +{ +} + +const char *zmq::uuid_t::to_string () +{ + return string_buf; +} + +#endif + +const unsigned char *zmq::uuid_t::to_blob () +{ + return blob_buf; +} + +unsigned char zmq::uuid_t::convert_byte (const char *hexa_) +{ + unsigned char byte; + + if (*hexa_ >= '0' && *hexa_ <= '9') + byte = *hexa_ - '0'; + else if (*hexa_ >= 'A' && *hexa_ <= 'F') + byte = *hexa_ - 'A' + 10; + else if (*hexa_ >= 'a' && *hexa_ <= 'f') + byte = *hexa_ - 'a' + 10; + else + zmq_assert (false); + + byte *= 16; + + hexa_++; + if (*hexa_ >= '0' && *hexa_ <= '9') + byte += *hexa_ - '0'; + else if (*hexa_ >= 'A' && *hexa_ <= 'F') + byte += *hexa_ - 'A' + 10; + else if (*hexa_ >= 'a' && *hexa_ <= 'f') + byte += *hexa_ - 'a' + 10; + else + zmq_assert (false); + + return byte; +} + +void zmq::uuid_t::create_blob () +{ + const char *buf = (const char*) string_buf; + + blob_buf [0] = convert_byte (buf + 0); + blob_buf [1] = convert_byte (buf + 2); + blob_buf [2] = convert_byte (buf + 4); + blob_buf [3] = convert_byte (buf + 6); + + blob_buf [4] = convert_byte (buf + 9); + blob_buf [5] = convert_byte (buf + 11); + + blob_buf [6] = convert_byte (buf + 14); + blob_buf [7] = convert_byte (buf + 16); + + blob_buf [8] = convert_byte (buf + 19); + blob_buf [9] = convert_byte (buf + 21); + + blob_buf [10] = convert_byte (buf + 24); + blob_buf [11] = convert_byte (buf + 26); + blob_buf [12] = convert_byte (buf + 28); + blob_buf [13] = convert_byte (buf + 30); + blob_buf [14] = convert_byte (buf + 32); + blob_buf [15] = convert_byte (buf + 34); +} diff --git a/src/uuid.hpp b/src/uuid.hpp new file mode 100644 index 0000000..871f827 --- /dev/null +++ b/src/uuid.hpp @@ -0,0 +1,110 @@ +/* + Copyright (c) 2007-2010 iMatix Corporation + + This file is part of 0MQ. + + 0MQ is free software; you can redistribute it and/or modify it under + the terms of the Lesser GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + 0MQ is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + Lesser GNU General Public License for more details. + + You should have received a copy of the Lesser GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef __ZMQ_UUID_HPP_INCLUDED__ +#define __ZMQ_UUID_HPP_INCLUDED__ + +#include "platform.hpp" +#include "stdint.hpp" + +#if defined ZMQ_HAVE_FREEBSD || defined ZMQ_HAVE_NETBSD +#include <uuid.h> +#elif defined ZMQ_HAVE_LINUX || defined ZMQ_HAVE_SOLARIS ||\ + defined ZMQ_HAVE_OSX || defined ZMQ_HAVE_CYGWIN +#include <uuid/uuid.h> +#elif defined ZMQ_HAVE_WINDOWS +#include "windows.hpp" +#include <rpc.h> +#elif defined ZMQ_HAVE_OPENVMS +typedef struct +{ + unsigned long data0; + unsigned short data1; + unsigned short data2; + unsigned char data3 [8]; +} uuid_t; +#endif + +namespace zmq +{ + + // This class provides RFC 4122 (a Universally Unique IDentifier) + // implementation. + + class uuid_t + { + public: + + uuid_t (); + ~uuid_t (); + + // The length of textual representation of UUID. + enum { uuid_string_len = 36 }; + + // Returns a pointer to buffer containing the textual + // representation of the UUID. The caller is reponsible to + // free the allocated memory. + const char *to_string (); + + // The length of binary representation of UUID. + enum { uuid_blob_len = 16 }; + + const unsigned char *to_blob (); + + private: + + // Converts one byte from hexa representation to binary. + unsigned char convert_byte (const char *hexa_); + + // Converts string representation of UUID into standardised BLOB. + // The function is endianness agnostic. + void create_blob (); + +#if defined ZMQ_HAVE_WINDOWS +#ifdef ZMQ_HAVE_MINGW32 + typedef unsigned char* RPC_CSTR; +#endif + ::UUID uuid; + RPC_CSTR string_buf; +#elif defined ZMQ_HAVE_FREEBSD || defined ZMQ_HAVE_NETBSD + ::uuid_t uuid; + char *string_buf; +#elif defined ZMQ_HAVE_LINUX || defined ZMQ_HAVE_SOLARIS ||\ + defined ZMQ_HAVE_OSX || defined ZMQ_HAVE_CYGWIN ||\ + defined ZMQ_HAVE_OPENVMS + ::uuid_t uuid; + char string_buf [uuid_string_len + 1]; +#else + // RFC 4122 UUID's fields + uint32_t time_low; + uint16_t time_mid; + uint16_t time_hi_and_version; + uint8_t clock_seq_hi_and_reserved; + uint8_t clock_seq_low; + uint8_t node [6]; + + char string_buf [uuid_string_len + 1]; +#endif + + unsigned char blob_buf [uuid_blob_len]; + }; + +} + +#endif diff --git a/src/windows.hpp b/src/windows.hpp new file mode 100644 index 0000000..8fc98b0 --- /dev/null +++ b/src/windows.hpp @@ -0,0 +1,78 @@ +/* + Copyright (c) 2007-2010 iMatix Corporation + + This file is part of 0MQ. + + 0MQ is free software; you can redistribute it and/or modify it under + the terms of the Lesser GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + 0MQ is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + Lesser GNU General Public License for more details. + + You should have received a copy of the Lesser GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef __ZMQ_WINDOWS_HPP_INCLUDED__ +#define __ZMQ_WINDOWS_HPP_INCLUDED__ + +// The purpose of this header file is to turn on only the items actually needed +// on the windows platform. + +#define _WINSOCKAPI_ +#ifndef NOMINMAX +#define NOMINMAX // No min and max functions, these clash with C++. +#endif +#define _CRT_SECURE_NO_WARNINGS + +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif +#ifndef NOUSER // No USER defines and routines. +#define NOUSER +#endif +#ifndef NOMCX // No Modem Configuration Extensions. +#define NOMCX +#endif +#ifndef NOIME // No Input Method Editor. +#define NOIME +#endif +#ifndef NOSOUND // No Sound driver routines. +#define NOSOUND +#endif + +#ifdef ZMQ_HAVE_MINGW32 +#ifdef WINVER +#undef WINVER +#endif +#define WINVER 0x0501 +#endif + +#include <windows.h> + +// MSVC++ 2005 on Win2000 does not define _WIN32_WINNT. +#ifndef _WIN32_WINNT +#define _WIN32_WINNT WINVER +#endif + +// Enable winsock (not included when WIN32_LEAN_AND_MEAN is defined). +#if(_WIN32_WINNT >= 0x0400) +#include <winsock2.h> +#include <mswsock.h> +#else +#include <winsock.h> +#endif + +#include <ws2tcpip.h> +#include <ipexport.h> +#include <process.h> + +// On mingw environment AI_NUMERICSERV is not defined, needed in ip.cpp. +#ifndef AI_NUMERICSERV +#define AI_NUMERICSERV 0x0400 +#endif +#endif diff --git a/src/wire.hpp b/src/wire.hpp new file mode 100644 index 0000000..9534cf6 --- /dev/null +++ b/src/wire.hpp @@ -0,0 +1,98 @@ +/* + Copyright (c) 2007-2010 iMatix Corporation + + This file is part of 0MQ. + + 0MQ is free software; you can redistribute it and/or modify it under + the terms of the Lesser GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + 0MQ is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + Lesser GNU General Public License for more details. + + You should have received a copy of the Lesser GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef __ZMQ_WIRE_HPP_INCLUDED__ +#define __ZMQ_WIRE_HPP_INCLUDED__ + +#include "stdint.hpp" + +namespace zmq +{ + + // Helper functions to convert different integer types to/from network + // byte order. + + inline void put_uint8 (unsigned char *buffer_, uint8_t value) + { + *buffer_ = value; + } + + inline uint8_t get_uint8 (unsigned char *buffer_) + { + return *buffer_; + } + + inline void put_uint16 (unsigned char *buffer_, uint16_t value) + { + buffer_ [0] = (unsigned char) (((value) >> 8) & 0xff); + buffer_ [1] = (unsigned char) (value & 0xff); + } + + inline uint16_t get_uint16 (unsigned char *buffer_) + { + return + (((uint16_t) buffer_ [0]) << 8) | + ((uint16_t) buffer_ [1]); + } + + inline void put_uint32 (unsigned char *buffer_, uint32_t value) + { + buffer_ [0] = (unsigned char) (((value) >> 24) & 0xff); + buffer_ [1] = (unsigned char) (((value) >> 16) & 0xff); + buffer_ [2] = (unsigned char) (((value) >> 8) & 0xff); + buffer_ [3] = (unsigned char) (value & 0xff); + } + + inline uint32_t get_uint32 (unsigned char *buffer_) + { + return + (((uint32_t) buffer_ [0]) << 24) | + (((uint32_t) buffer_ [1]) << 16) | + (((uint32_t) buffer_ [2]) << 8) | + ((uint32_t) buffer_ [3]); + } + + inline void put_uint64 (unsigned char *buffer_, uint64_t value) + { + buffer_ [0] = (unsigned char) (((value) >> 56) & 0xff); + buffer_ [1] = (unsigned char) (((value) >> 48) & 0xff); + buffer_ [2] = (unsigned char) (((value) >> 40) & 0xff); + buffer_ [3] = (unsigned char) (((value) >> 32) & 0xff); + buffer_ [4] = (unsigned char) (((value) >> 24) & 0xff); + buffer_ [5] = (unsigned char) (((value) >> 16) & 0xff); + buffer_ [6] = (unsigned char) (((value) >> 8) & 0xff); + buffer_ [7] = (unsigned char) (value & 0xff); + } + + inline uint64_t get_uint64 (unsigned char *buffer_) + { + return + (((uint64_t) buffer_ [0]) << 56) | + (((uint64_t) buffer_ [1]) << 48) | + (((uint64_t) buffer_ [2]) << 40) | + (((uint64_t) buffer_ [3]) << 32) | + (((uint64_t) buffer_ [4]) << 24) | + (((uint64_t) buffer_ [5]) << 16) | + (((uint64_t) buffer_ [6]) << 8) | + ((uint64_t) buffer_ [7]); + } + +} + +#endif diff --git a/src/xrep.cpp b/src/xrep.cpp new file mode 100644 index 0000000..4e8d18a --- /dev/null +++ b/src/xrep.cpp @@ -0,0 +1,271 @@ +/* + Copyright (c) 2007-2010 iMatix Corporation + + This file is part of 0MQ. + + 0MQ is free software; you can redistribute it and/or modify it under + the terms of the Lesser GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + 0MQ is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + Lesser GNU General Public License for more details. + + You should have received a copy of the Lesser GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "../include/zmq.h" + +#include "xrep.hpp" +#include "err.hpp" +#include "pipe.hpp" + +zmq::xrep_t::xrep_t (class app_thread_t *parent_) : + socket_base_t (parent_), + current_in (0), + more_in (false), + current_out (NULL), + more_out (false) +{ + options.requires_in = true; + options.requires_out = true; + + // On connect, pipes are created only after initial handshaking. + // That way we are aware of the peer's identity when binding to the pipes. + options.immediate_connect = false; +} + +zmq::xrep_t::~xrep_t () +{ + for (inpipes_t::iterator it = inpipes.begin (); it != inpipes.end (); it++) + it->reader->term (); + for (outpipes_t::iterator it = outpipes.begin (); it != outpipes.end (); + it++) + it->second.writer->term (); +} + +void zmq::xrep_t::xattach_pipes (class reader_t *inpipe_, + class writer_t *outpipe_, const blob_t &peer_identity_) +{ + zmq_assert (inpipe_ && outpipe_); + + // TODO: What if new connection has same peer identity as the old one? + outpipe_t outpipe = {outpipe_, true}; + bool ok = outpipes.insert (std::make_pair ( + peer_identity_, outpipe)).second; + zmq_assert (ok); + + inpipe_t inpipe = {inpipe_, peer_identity_, true}; + inpipes.push_back (inpipe); +} + +void zmq::xrep_t::xdetach_inpipe (class reader_t *pipe_) +{ +// TODO:! + for (inpipes_t::iterator it = inpipes.begin (); it != inpipes.end (); + it++) { + if (it->reader == pipe_) { + inpipes.erase (it); + return; + } + } + zmq_assert (false); +} + +void zmq::xrep_t::xdetach_outpipe (class writer_t *pipe_) +{ + for (outpipes_t::iterator it = outpipes.begin (); + it != outpipes.end (); ++it) { + if (it->second.writer == pipe_) { + outpipes.erase (it); + if (pipe_ == current_out) + current_out = NULL; + return; + } + } + zmq_assert (false); +} + +void zmq::xrep_t::xkill (class reader_t *pipe_) +{ + for (inpipes_t::iterator it = inpipes.begin (); it != inpipes.end (); + it++) { + if (it->reader == pipe_) { + zmq_assert (it->active); + it->active = false; + return; + } + } + zmq_assert (false); +} + +void zmq::xrep_t::xrevive (class reader_t *pipe_) +{ + for (inpipes_t::iterator it = inpipes.begin (); it != inpipes.end (); + it++) { + if (it->reader == pipe_) { + zmq_assert (!it->active); + it->active = true; + return; + } + } + zmq_assert (false); +} + +void zmq::xrep_t::xrevive (class writer_t *pipe_) +{ + for (outpipes_t::iterator it = outpipes.begin (); + it != outpipes.end (); ++it) { + if (it->second.writer == pipe_) { + zmq_assert (!it->second.active); + it->second.active = true; + return; + } + } + zmq_assert (false); +} + +int zmq::xrep_t::xsetsockopt (int option_, const void *optval_, + size_t optvallen_) +{ + errno = EINVAL; + return -1; +} + +int zmq::xrep_t::xsend (zmq_msg_t *msg_, int flags_) +{ + // If this is the first part of the message it's the identity of the + // peer to send the message to. + if (!more_out) { + zmq_assert (!current_out); + + // There's no such thing as prefix with no subsequent message. + zmq_assert (msg_->flags & ZMQ_MSG_MORE); + more_out = true; + + // Find the pipe associated with the identity stored in the prefix. + // If there's no such pipe just silently drop the message. + blob_t identity ((unsigned char*) zmq_msg_data (msg_), + zmq_msg_size (msg_)); + outpipes_t::iterator it = outpipes.find (identity); + if (it == outpipes.end ()) + return 0; + + // Remember the outgoing pipe. + current_out = it->second.writer; + + return 0; + } + + // Check whether this is the last part of the message. + more_out = msg_->flags & ZMQ_MSG_MORE; + + // Push the message into the pipe. If there's no out pipe, just drop it. + if (current_out) { + bool ok = current_out->write (msg_); + zmq_assert (ok); + if (!more_out) { + current_out->flush (); + current_out = NULL; + } + } + else { + int rc = zmq_msg_close (msg_); + zmq_assert (rc == 0); + } + + // Detach the message from the data buffer. + int rc = zmq_msg_init (msg_); + zmq_assert (rc == 0); + + return 0; +} + +int zmq::xrep_t::xrecv (zmq_msg_t *msg_, int flags_) +{ + // Deallocate old content of the message. + zmq_msg_close (msg_); + + // If we are in the middle of reading a message, just grab next part of it. + if (more_in) { + zmq_assert (inpipes [current_in].active); + bool fetched = inpipes [current_in].reader->read (msg_); + zmq_assert (fetched); + more_in = msg_->flags & ZMQ_MSG_MORE; + if (!more_in) { + current_in++; + if (current_in >= inpipes.size ()) + current_in = 0; + } + return 0; + } + + // Round-robin over the pipes to get the next message. + for (int count = inpipes.size (); count != 0; count--) { + + // Try to fetch new message. + bool fetched; + if (!inpipes [current_in].active) + fetched = false; + else + fetched = inpipes [current_in].reader->check_read (); + + // If we have a message, create a prefix and return it to the caller. + if (fetched) { + int rc = zmq_msg_init_size (msg_, + inpipes [current_in].identity.size ()); + zmq_assert (rc == 0); + memcpy (zmq_msg_data (msg_), inpipes [current_in].identity.data (), + zmq_msg_size (msg_)); + msg_->flags = ZMQ_MSG_MORE; + more_in = true; + return 0; + } + + // If me don't have a message, move to next pipe. + current_in++; + if (current_in >= inpipes.size ()) + current_in = 0; + } + + // No message is available. Initialise the output parameter + // to be a 0-byte message. + zmq_msg_init (msg_); + errno = EAGAIN; + return -1; +} + +bool zmq::xrep_t::xhas_in () +{ + // There are subsequent parts of the partly-read message available. + if (more_in) + return true; + + // Note that messing with current doesn't break the fairness of fair + // queueing algorithm. If there are no messages available current will + // get back to its original value. Otherwise it'll point to the first + // pipe holding messages, skipping only pipes with no messages available. + for (int count = inpipes.size (); count != 0; count--) { + if (inpipes [current_in].active && + inpipes [current_in].reader->check_read ()) + return true; + current_in++; + if (current_in >= inpipes.size ()) + current_in = 0; + } + + return false; +} + +bool zmq::xrep_t::xhas_out () +{ + // In theory, XREP socket is always ready for writing. Whether actual + // attempt to write succeeds depends on whitch pipe the message is going + // to be routed to. + return true; +} + + diff --git a/src/xrep.hpp b/src/xrep.hpp new file mode 100644 index 0000000..940d288 --- /dev/null +++ b/src/xrep.hpp @@ -0,0 +1,95 @@ +/* + Copyright (c) 2007-2010 iMatix Corporation + + This file is part of 0MQ. + + 0MQ is free software; you can redistribute it and/or modify it under + the terms of the Lesser GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + 0MQ is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + Lesser GNU General Public License for more details. + + You should have received a copy of the Lesser GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef __ZMQ_XREP_HPP_INCLUDED__ +#define __ZMQ_XREP_HPP_INCLUDED__ + +#include <map> +#include <vector> + +#include "socket_base.hpp" +#include "blob.hpp" + +namespace zmq +{ + + // TODO: This class uses O(n) scheduling. Rewrite it to use O(1) algorithm. + class xrep_t : public socket_base_t + { + public: + + xrep_t (class app_thread_t *parent_); + ~xrep_t (); + + // Overloads of functions from socket_base_t. + void xattach_pipes (class reader_t *inpipe_, class writer_t *outpipe_, + const blob_t &peer_identity_); + void xdetach_inpipe (class reader_t *pipe_); + void xdetach_outpipe (class writer_t *pipe_); + void xkill (class reader_t *pipe_); + void xrevive (class reader_t *pipe_); + void xrevive (class writer_t *pipe_); + int xsetsockopt (int option_, const void *optval_, size_t optvallen_); + int xsend (zmq_msg_t *msg_, int flags_); + int xrecv (zmq_msg_t *msg_, int flags_); + bool xhas_in (); + bool xhas_out (); + + private: + + struct inpipe_t + { + class reader_t *reader; + blob_t identity; + bool active; + }; + + // Inbound pipes with the names of corresponging peers. + typedef std::vector <inpipe_t> inpipes_t; + inpipes_t inpipes; + + // The pipe we are currently reading from. + inpipes_t::size_type current_in; + + // If true, more incoming message parts are expected. + bool more_in; + + struct outpipe_t + { + class writer_t *writer; + bool active; + }; + + // Outbound pipes indexed by the peer names. + typedef std::map <blob_t, outpipe_t> outpipes_t; + outpipes_t outpipes; + + // The pipe we are currently writing to. + class writer_t *current_out; + + // If true, more outgoing message parts are expected. + bool more_out; + + xrep_t (const xrep_t&); + void operator = (const xrep_t&); + }; + +} + +#endif diff --git a/src/xreq.cpp b/src/xreq.cpp new file mode 100644 index 0000000..ab90f68 --- /dev/null +++ b/src/xreq.cpp @@ -0,0 +1,118 @@ +/* + Copyright (c) 2007-2010 iMatix Corporation + + This file is part of 0MQ. + + 0MQ is free software; you can redistribute it and/or modify it under + the terms of the Lesser GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + 0MQ is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + Lesser GNU General Public License for more details. + + You should have received a copy of the Lesser GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "../include/zmq.h" + +#include "xreq.hpp" +#include "err.hpp" + +zmq::xreq_t::xreq_t (class app_thread_t *parent_) : + socket_base_t (parent_), + dropping (false) +{ + options.requires_in = true; + options.requires_out = true; +} + +zmq::xreq_t::~xreq_t () +{ +} + +void zmq::xreq_t::xattach_pipes (class reader_t *inpipe_, + class writer_t *outpipe_, const blob_t &peer_identity_) +{ + zmq_assert (inpipe_ && outpipe_); + fq.attach (inpipe_); + lb.attach (outpipe_); +} + +void zmq::xreq_t::xdetach_inpipe (class reader_t *pipe_) +{ + zmq_assert (pipe_); + fq.detach (pipe_); +} + +void zmq::xreq_t::xdetach_outpipe (class writer_t *pipe_) +{ + zmq_assert (pipe_); + lb.detach (pipe_); +} + +void zmq::xreq_t::xkill (class reader_t *pipe_) +{ + fq.kill (pipe_); +} + +void zmq::xreq_t::xrevive (class reader_t *pipe_) +{ + fq.revive (pipe_); +} + +void zmq::xreq_t::xrevive (class writer_t *pipe_) +{ + lb.revive (pipe_); +} + +int zmq::xreq_t::xsetsockopt (int option_, const void *optval_, + size_t optvallen_) +{ + errno = EINVAL; + return -1; +} + +int zmq::xreq_t::xsend (zmq_msg_t *msg_, int flags_) +{ + while (true) { + + // If we are ignoring the current message, just drop it and return. + if (dropping) { + if (!(msg_->flags & ZMQ_MSG_MORE)) + dropping = false; + int rc = zmq_msg_close (msg_); + zmq_assert (rc == 0); + rc = zmq_msg_init (msg_); + zmq_assert (rc == 0); + return 0; + } + + int rc = lb.send (msg_, flags_); + if (rc != 0 && errno == EAGAIN) + dropping = true; + else + return rc; + } +} + +int zmq::xreq_t::xrecv (zmq_msg_t *msg_, int flags_) +{ + return fq.recv (msg_, flags_); +} + +bool zmq::xreq_t::xhas_in () +{ + return fq.has_in (); +} + +bool zmq::xreq_t::xhas_out () +{ + // Socket is always ready for writing. When the queue is full, message + // will be silently dropped. + return true; +} + diff --git a/src/xreq.hpp b/src/xreq.hpp new file mode 100644 index 0000000..25a97f1 --- /dev/null +++ b/src/xreq.hpp @@ -0,0 +1,67 @@ +/* + Copyright (c) 2007-2010 iMatix Corporation + + This file is part of 0MQ. + + 0MQ is free software; you can redistribute it and/or modify it under + the terms of the Lesser GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + 0MQ is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + Lesser GNU General Public License for more details. + + You should have received a copy of the Lesser GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef __ZMQ_XREQ_HPP_INCLUDED__ +#define __ZMQ_XREQ_HPP_INCLUDED__ + +#include "socket_base.hpp" +#include "fq.hpp" +#include "lb.hpp" + +namespace zmq +{ + + class xreq_t : public socket_base_t + { + public: + + xreq_t (class app_thread_t *parent_); + ~xreq_t (); + + // Overloads of functions from socket_base_t. + void xattach_pipes (class reader_t *inpipe_, class writer_t *outpipe_, + const blob_t &peer_identity_); + void xdetach_inpipe (class reader_t *pipe_); + void xdetach_outpipe (class writer_t *pipe_); + void xkill (class reader_t *pipe_); + void xrevive (class reader_t *pipe_); + void xrevive (class writer_t *pipe_); + int xsetsockopt (int option_, const void *optval_, size_t optvallen_); + int xsend (zmq_msg_t *msg_, int flags_); + int xrecv (zmq_msg_t *msg_, int flags_); + bool xhas_in (); + bool xhas_out (); + + private: + + // Messages are fair-queued from inbound pipes. And load-balanced to + // the outbound pipes. + fq_t fq; + lb_t lb; + + // If true, curently sent message is being dropped. + bool dropping; + + xreq_t (const xreq_t&); + void operator = (const xreq_t&); + }; + +} + +#endif diff --git a/src/yarray.hpp b/src/yarray.hpp new file mode 100644 index 0000000..8c79b99 --- /dev/null +++ b/src/yarray.hpp @@ -0,0 +1,110 @@ +/* + Copyright (c) 2007-2010 iMatix Corporation + + This file is part of 0MQ. + + 0MQ is free software; you can redistribute it and/or modify it under + the terms of the Lesser GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + 0MQ is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + Lesser GNU General Public License for more details. + + You should have received a copy of the Lesser GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef __ZMQ_YARRAY_INCLUDED__ +#define __ZMQ_YARRAY_INCLUDED__ + +#include <vector> +#include <algorithm> + +namespace zmq +{ + + // Fast array implementation with O(1) access to item, insertion and + // removal. Yarray stores pointers rather than objects. The objects have + // to be derived from yarray_item_t class. + + template <typename T> class yarray_t + { + public: + + typedef typename std::vector <T*>::size_type size_type; + + inline yarray_t () + { + } + + inline ~yarray_t () + { + } + + inline size_type size () + { + return items.size (); + } + + inline bool empty () + { + return items.empty (); + } + + inline T *&operator [] (size_type index_) + { + return items [index_]; + } + + inline void push_back (T *item_) + { + if (item_) + item_->set_yarray_index (items.size ()); + items.push_back (item_); + } + + inline void erase (T *item_) { + erase (item_->get_yarray_index ()); + } + + inline void erase (size_type index_) { + if (items.back ()) + items.back ()->set_yarray_index (index_); + items [index_] = items.back (); + items.pop_back (); + } + + inline void swap (size_type index1_, size_type index2_) + { + if (items [index1_]) + items [index1_]->set_yarray_index (index2_); + if (items [index2_]) + items [index2_]->set_yarray_index (index1_); + std::swap (items [index1_], items [index2_]); + } + + inline void clear () + { + items.clear (); + } + + inline size_type index (T *item_) + { + return (size_type) item_->get_yarray_index (); + } + + private: + + typedef std::vector <T*> items_t; + items_t items; + + yarray_t (const yarray_t&); + void operator = (const yarray_t&); + }; + +} + +#endif diff --git a/src/yarray_item.hpp b/src/yarray_item.hpp new file mode 100644 index 0000000..b6d89cc --- /dev/null +++ b/src/yarray_item.hpp @@ -0,0 +1,62 @@ +/* + Copyright (c) 2007-2010 iMatix Corporation + + This file is part of 0MQ. + + 0MQ is free software; you can redistribute it and/or modify it under + the terms of the Lesser GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + 0MQ is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + Lesser GNU General Public License for more details. + + You should have received a copy of the Lesser GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef __ZMQ_YARRAY_ITEM_INCLUDED__ +#define __ZMQ_YARRAY_ITEM_INCLUDED__ + +namespace zmq +{ + + // Base class for objects stored in yarray. Note that each object can + // be stored in at most one yarray. + + class yarray_item_t + { + public: + + inline yarray_item_t () : + yarray_index (-1) + { + } + + inline ~yarray_item_t () + { + } + + inline void set_yarray_index (int index_) + { + yarray_index = index_; + } + + inline int get_yarray_index () + { + return yarray_index; + } + + private: + + int yarray_index; + + yarray_item_t (const yarray_item_t&); + void operator = (const yarray_item_t&); + }; + +} + +#endif diff --git a/src/ypipe.hpp b/src/ypipe.hpp new file mode 100644 index 0000000..df5b3d0 --- /dev/null +++ b/src/ypipe.hpp @@ -0,0 +1,191 @@ +/* + Copyright (c) 2007-2010 iMatix Corporation + + This file is part of 0MQ. + + 0MQ is free software; you can redistribute it and/or modify it under + the terms of the Lesser GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + 0MQ is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + Lesser GNU General Public License for more details. + + You should have received a copy of the Lesser GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef __ZMQ_YPIPE_HPP_INCLUDED__ +#define __ZMQ_YPIPE_HPP_INCLUDED__ + +#include "atomic_ptr.hpp" +#include "yqueue.hpp" +#include "platform.hpp" + +namespace zmq +{ + + // Lock-free queue implementation. + // Only a single thread can read from the pipe at any specific moment. + // Only a single thread can write to the pipe at any specific moment. + // T is the type of the object in the queue. + // N is granularity of the pipe, i.e. how many items are needed to + // perform next memory allocation. + + template <typename T, int N> class ypipe_t + { + public: + + // Initialises the pipe. + inline ypipe_t () + { + // Insert terminator element into the queue. + queue.push (); + + // Let all the pointers to point to the terminator. + // (unless pipe is dead, in which case c is set to NULL). + r = w = f = &queue.back (); + c.set (&queue.back ()); + } + + // Following function (write) deliberately copies uninitialised data + // when used with zmq_msg. Initialising the VSM body for + // non-VSM messages won't be good for performance. + +#ifdef ZMQ_HAVE_OPENVMS +#pragma message save +#pragma message disable(UNINIT) +#endif + + // Write an item to the pipe. Don't flush it yet. If incomplete is + // set to true the item is assumed to be continued by items + // subsequently written to the pipe. Incomplete items are never + // flushed down the stream. + inline void write (const T &value_, bool incomplete_) + { + // Place the value to the queue, add new terminator element. + queue.back () = value_; + queue.push (); + + // Move the "flush up to here" poiter. + if (!incomplete_) + f = &queue.back (); + } + +#ifdef ZMQ_HAVE_OPENVMS +#pragma message restore +#endif + + // Pop an incomplete item from the pipe. Returns true is such + // item exists, false otherwise. + inline bool unwrite (T *value_) + { + if (f == &queue.back ()) + return false; + queue.unpush (); + *value_ = queue.back (); + return true; + } + + // Flush all the completed items into the pipe. Returns false if + // the reader thread is sleeping. In that case, caller is obliged to + // wake the reader up before using the pipe again. + inline bool flush () + { + // If there are no un-flushed items, do nothing. + if (w == f) + return true; + + // Try to set 'c' to 'f'. + if (c.cas (w, f) != w) { + + // Compare-and-swap was unseccessful because 'c' is NULL. + // This means that the reader is asleep. Therefore we don't + // care about thread-safeness and update c in non-atomic + // manner. We'll return false to let the caller know + // that reader is sleeping. + c.set (f); + w = f; + return false; + } + + // Reader is alive. Nothing special to do now. Just move + // the 'first un-flushed item' pointer to 'f'. + w = f; + return true; + } + + // Check whether item is available for reading. + inline bool check_read () + { + // Was the value prefetched already? If so, return. + if (&queue.front () != r) + return true; + + // There's no prefetched value, so let us prefetch more values. + // Prefetching is to simply retrieve the + // pointer from c in atomic fashion. If there are no + // items to prefetch, set c to NULL (using compare-and-swap). + r = c.cas (&queue.front (), NULL); + + // If there are no elements prefetched, exit. + // During pipe's lifetime r should never be NULL, however, + // it can happen during pipe shutdown when items + // are being deallocated. + if (&queue.front () == r || !r) + return false; + + // There was at least one value prefetched. + return true; + } + + // Reads an item from the pipe. Returns false if there is no value. + // available. + inline bool read (T *value_) + { + // Try to prefetch a value. + if (!check_read ()) + return false; + + // There was at least one value prefetched. + // Return it to the caller. + *value_ = queue.front (); + queue.pop (); + return true; + } + + protected: + + // Allocation-efficient queue to store pipe items. + // Front of the queue points to the first prefetched item, back of + // the pipe points to last un-flushed item. Front is used only by + // reader thread, while back is used only by writer thread. + yqueue_t <T, N> queue; + + // Points to the first un-flushed item. This variable is used + // exclusively by writer thread. + T *w; + + // Points to the first un-prefetched item. This variable is used + // exclusively by reader thread. + T *r; + + // Points to the first item to be flushed in the future. + T *f; + + // The single point of contention between writer and reader thread. + // Points past the last flushed item. If it is NULL, + // reader is asleep. This pointer should be always accessed using + // atomic operations. + atomic_ptr_t <T> c; + + // Disable copying of ypipe object. + ypipe_t (const ypipe_t&); + void operator = (const ypipe_t&); + }; + +} + +#endif diff --git a/src/yqueue.hpp b/src/yqueue.hpp new file mode 100644 index 0000000..9eaceb5 --- /dev/null +++ b/src/yqueue.hpp @@ -0,0 +1,197 @@ +/* + Copyright (c) 2007-2010 iMatix Corporation + + This file is part of 0MQ. + + 0MQ is free software; you can redistribute it and/or modify it under + the terms of the Lesser GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + 0MQ is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + Lesser GNU General Public License for more details. + + You should have received a copy of the Lesser GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef __ZMQ_YQUEUE_HPP_INCLUDED__ +#define __ZMQ_YQUEUE_HPP_INCLUDED__ + +#include <stdlib.h> +#include <stddef.h> + +#include "err.hpp" +#include "atomic_ptr.hpp" + +namespace zmq +{ + + // yqueue is an efficient queue implementation. The main goal is + // to minimise number of allocations/deallocations needed. Thus yqueue + // allocates/deallocates elements in batches of N. + // + // yqueue allows one thread to use push/back function and another one + // to use pop/front functions. However, user must ensure that there's no + // pop on the empty queue and that both threads don't access the same + // element in unsynchronised manner. + // + // T is the type of the object in the queue. + // N is granularity of the queue (how many pushes have to be done till + // actual memory allocation is required). + + template <typename T, int N> class yqueue_t + { + public: + + // Create the queue. + inline yqueue_t () + { + begin_chunk = (chunk_t*) malloc (sizeof (chunk_t)); + zmq_assert (begin_chunk); + begin_pos = 0; + back_chunk = NULL; + back_pos = 0; + end_chunk = begin_chunk; + end_pos = 0; + } + + // Destroy the queue. + inline ~yqueue_t () + { + while (true) { + if (begin_chunk == end_chunk) { + free (begin_chunk); + break; + } + chunk_t *o = begin_chunk; + begin_chunk = begin_chunk->next; + free (o); + } + + chunk_t *sc = spare_chunk.xchg (NULL); + if (sc) + free (sc); + } + + // Returns reference to the front element of the queue. + // If the queue is empty, behaviour is undefined. + inline T &front () + { + return begin_chunk->values [begin_pos]; + } + + // Returns reference to the back element of the queue. + // If the queue is empty, behaviour is undefined. + inline T &back () + { + return back_chunk->values [back_pos]; + } + + // Adds an element to the back end of the queue. + inline void push () + { + back_chunk = end_chunk; + back_pos = end_pos; + + if (++end_pos != N) + return; + + chunk_t *sc = spare_chunk.xchg (NULL); + if (sc) { + end_chunk->next = sc; + sc->prev = end_chunk; + } else { + end_chunk->next = (chunk_t*) malloc (sizeof (chunk_t)); + zmq_assert (end_chunk->next); + end_chunk->next->prev = end_chunk; + } + end_chunk = end_chunk->next; + end_pos = 0; + } + + // Removes element from the back end of the queue. In other words + // it rollbacks last push to the queue. Take care: Caller is + // responsible for destroying the object being unpushed. + // The caller must also guarantee that the queue isn't empty when + // unpush is called. It cannot be done automatically as the read + // side of the queue can be managed by different, completely + // unsynchronised thread. + inline void unpush () + { + // First, move 'back' one position backwards. + if (back_pos) + --back_pos; + else { + back_pos = N - 1; + back_chunk = back_chunk->prev; + } + + // Now, move 'end' position backwards. Note that obsolete end chunk + // is not used as a spare chunk. The analysis shows that doing so + // would require free and atomic operation per chunk deallocated + // instead of a simple free. + if (end_pos) + --end_pos; + else { + end_pos = N - 1; + end_chunk = end_chunk->prev; + free (end_chunk->next); + end_chunk->next = NULL; + } + } + + // Removes an element from the front end of the queue. + inline void pop () + { + if (++ begin_pos == N) { + chunk_t *o = begin_chunk; + begin_chunk = begin_chunk->next; + begin_chunk->prev = NULL; + begin_pos = 0; + + // 'o' has been more recently used than spare_chunk, + // so for cache reasons we'll get rid of the spare and + // use 'o' as the spare. + chunk_t *cs = spare_chunk.xchg (o); + if (cs) + free (cs); + } + } + + private: + + // Individual memory chunk to hold N elements. + struct chunk_t + { + T values [N]; + chunk_t *prev; + chunk_t *next; + }; + + // Back position may point to invalid memory if the queue is empty, + // while begin & end positions are always valid. Begin position is + // accessed exclusively be queue reader (front/pop), while back and + // end positions are accessed exclusively by queue writer (back/push). + chunk_t *begin_chunk; + int begin_pos; + chunk_t *back_chunk; + int back_pos; + chunk_t *end_chunk; + int end_pos; + + // People are likely to produce and consume at similar rates. In + // this scenario holding onto the most recently freed chunk saves + // us from having to call malloc/free. + atomic_ptr_t<chunk_t> spare_chunk; + + // Disable copying of yqueue. + yqueue_t (const yqueue_t&); + void operator = (const yqueue_t&); + }; + +} + +#endif diff --git a/src/zmq.cpp b/src/zmq.cpp new file mode 100644 index 0000000..c8f419a --- /dev/null +++ b/src/zmq.cpp @@ -0,0 +1,663 @@ +/* + Copyright (c) 2007-2010 iMatix Corporation + + This file is part of 0MQ. + + 0MQ is free software; you can redistribute it and/or modify it under + the terms of the Lesser GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + 0MQ is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + Lesser GNU General Public License for more details. + + You should have received a copy of the Lesser GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "../include/zmq.h" + +#include <string.h> +#include <errno.h> +#include <stdlib.h> +#include <new> + +#include "forwarder.hpp" +#include "queue.hpp" +#include "streamer.hpp" +#include "socket_base.hpp" +#include "app_thread.hpp" +#include "msg_content.hpp" +#include "platform.hpp" +#include "stdint.hpp" +#include "config.hpp" +#include "ctx.hpp" +#include "err.hpp" +#include "fd.hpp" + +#if defined ZMQ_HAVE_LINUX || defined ZMQ_HAVE_FREEBSD ||\ + defined ZMQ_HAVE_OPENBSD || defined ZMQ_HAVE_SOLARIS ||\ + defined ZMQ_HAVE_OSX || defined ZMQ_HAVE_QNXNTO ||\ + defined ZMQ_HAVE_HPUX || defined ZMQ_HAVE_AIX ||\ + defined ZMQ_HAVE_NETBSD +#include <poll.h> +#endif + +#if !defined ZMQ_HAVE_WINDOWS +#include <unistd.h> +#include <sys/time.h> +#endif + +#if defined ZMQ_HAVE_OPENPGM +#include <pgm/pgm.h> +#endif + +void zmq_version (int *major_, int *minor_, int *patch_) +{ + *major_ = PACKAGE_VERSION_MAJOR; + *minor_ = PACKAGE_VERSION_MINOR; + *patch_ = PACKAGE_VERSION_PATCH; +} + +const char *zmq_strerror (int errnum_) +{ + switch (errnum_) { +#if defined ZMQ_HAVE_WINDOWS + case ENOTSUP: + return "Not supported"; + case EPROTONOSUPPORT: + return "Protocol not supported"; + case ENOBUFS: + return "No buffer space available"; + case ENETDOWN: + return "Network is down"; + case EADDRINUSE: + return "Address in use"; + case EADDRNOTAVAIL: + return "Address not available"; + case ECONNREFUSED: + return "Connection refused"; + case EINPROGRESS: + return "Operation in progress"; +#endif + case EMTHREAD: + return "Number of preallocated application threads exceeded"; + case EFSM: + return "Operation cannot be accomplished in current state"; + case ENOCOMPATPROTO: + return "The protocol is not compatible with the socket type"; + case ETERM: + return "Context was terminated"; + default: +#if defined _MSC_VER +#pragma warning (push) +#pragma warning (disable:4996) +#endif + return strerror (errnum_); +#if defined _MSC_VER +#pragma warning (pop) +#endif + } +} + +int zmq_msg_init (zmq_msg_t *msg_) +{ + msg_->content = (zmq::msg_content_t*) ZMQ_VSM; + msg_->flags = 0; + msg_->vsm_size = 0; + return 0; +} + +int zmq_msg_init_size (zmq_msg_t *msg_, size_t size_) +{ + if (size_ <= ZMQ_MAX_VSM_SIZE) { + msg_->content = (zmq::msg_content_t*) ZMQ_VSM; + msg_->flags = 0; + msg_->vsm_size = (uint8_t) size_; + } + else { + msg_->content = + (zmq::msg_content_t*) malloc (sizeof (zmq::msg_content_t) + size_); + if (!msg_->content) { + errno = ENOMEM; + return -1; + } + msg_->flags = 0; + + zmq::msg_content_t *content = (zmq::msg_content_t*) msg_->content; + content->data = (void*) (content + 1); + content->size = size_; + content->ffn = NULL; + content->hint = NULL; + new (&content->refcnt) zmq::atomic_counter_t (); + } + return 0; +} + +int zmq_msg_init_data (zmq_msg_t *msg_, void *data_, size_t size_, + zmq_free_fn *ffn_, void *hint_) +{ + msg_->content = (zmq::msg_content_t*) malloc (sizeof (zmq::msg_content_t)); + zmq_assert (msg_->content); + msg_->flags = 0; + zmq::msg_content_t *content = (zmq::msg_content_t*) msg_->content; + content->data = data_; + content->size = size_; + content->ffn = ffn_; + content->hint = hint_; + new (&content->refcnt) zmq::atomic_counter_t (); + return 0; +} + +int zmq_msg_close (zmq_msg_t *msg_) +{ + // For VSMs and delimiters there are no resources to free. + if (msg_->content == (zmq::msg_content_t*) ZMQ_DELIMITER || + msg_->content == (zmq::msg_content_t*) ZMQ_VSM) + return 0; + + // If the content is not shared, or if it is shared and the reference. + // count has dropped to zero, deallocate it. + zmq::msg_content_t *content = (zmq::msg_content_t*) msg_->content; + if (!(msg_->flags & ZMQ_MSG_SHARED) || !content->refcnt.sub (1)) { + + // We used "placement new" operator to initialize the reference. + // counter so we call its destructor now. + content->refcnt.~atomic_counter_t (); + + if (content->ffn) + content->ffn (content->data, content->hint); + free (content); + } + + return 0; +} + +int zmq_msg_move (zmq_msg_t *dest_, zmq_msg_t *src_) +{ + zmq_msg_close (dest_); + *dest_ = *src_; + zmq_msg_init (src_); + return 0; +} + +int zmq_msg_copy (zmq_msg_t *dest_, zmq_msg_t *src_) +{ + zmq_msg_close (dest_); + + // VSMs and delimiters require no special handling. + if (src_->content != (zmq::msg_content_t*) ZMQ_DELIMITER && + src_->content != (zmq::msg_content_t*) ZMQ_VSM) { + + // One reference is added to shared messages. Non-shared messages + // are turned into shared messages and reference count is set to 2. + zmq::msg_content_t *content = (zmq::msg_content_t*) src_->content; + if (src_->flags & ZMQ_MSG_SHARED) + content->refcnt.add (1); + else { + src_->flags |= ZMQ_MSG_SHARED; + content->refcnt.set (2); + } + } + + *dest_ = *src_; + return 0; +} + +void *zmq_msg_data (zmq_msg_t *msg_) +{ + if (msg_->content == (zmq::msg_content_t*) ZMQ_VSM) + return msg_->vsm_data; + if (msg_->content == (zmq::msg_content_t*) ZMQ_DELIMITER) + return NULL; + + return ((zmq::msg_content_t*) msg_->content)->data; +} + +size_t zmq_msg_size (zmq_msg_t *msg_) +{ + if (msg_->content == (zmq::msg_content_t*) ZMQ_VSM) + return msg_->vsm_size; + if (msg_->content == (zmq::msg_content_t*) ZMQ_DELIMITER) + return 0; + + return ((zmq::msg_content_t*) msg_->content)->size; +} + +void *zmq_init (int io_threads_) +{ + if (io_threads_ < 0) { + errno = EINVAL; + return NULL; + } + +#if defined ZMQ_HAVE_OPENPGM + // Unfortunately, OpenPGM doesn't support refcounted init/shutdown, thus, + // let's fail if it was initialised beforehand. + zmq_assert (!pgm_supported ()); + + // Init PGM transport. Ensure threading and timer are enabled. Find PGM + // protocol ID. Note that if you want to use gettimeofday and sleep for + // openPGM timing, set environment variables PGM_TIMER to "GTOD" and + // PGM_SLEEP to "USLEEP". + GError *pgm_error = NULL; + int rc = pgm_init (&pgm_error); + if (rc != TRUE) { + if (pgm_error->domain == PGM_IF_ERROR && ( + pgm_error->code == PGM_IF_ERROR_INVAL || + pgm_error->code == PGM_IF_ERROR_XDEV || + pgm_error->code == PGM_IF_ERROR_NODEV || + pgm_error->code == PGM_IF_ERROR_NOTUNIQ || + pgm_error->code == PGM_IF_ERROR_ADDRFAMILY || + pgm_error->code == PGM_IF_ERROR_FAMILY || + pgm_error->code == PGM_IF_ERROR_NODATA || + pgm_error->code == PGM_IF_ERROR_NONAME || + pgm_error->code == PGM_IF_ERROR_SERVICE)) { + g_error_free (pgm_error); + errno = EINVAL; + return NULL; + } + zmq_assert (false); + } +#endif + + // Create 0MQ context. + zmq::ctx_t *ctx = new (std::nothrow) zmq::ctx_t ((uint32_t) io_threads_); + zmq_assert (ctx); + return (void*) ctx; +} + +int zmq_term (void *ctx_) +{ + int rc = ((zmq::ctx_t*) ctx_)->term (); + int en = errno; + +#if defined ZMQ_HAVE_OPENPGM + // Shut down the OpenPGM library. + if (pgm_shutdown () != TRUE) + zmq_assert (false); +#endif + + errno = en; + return rc; +} + +void *zmq_socket (void *ctx_, int type_) +{ + return (void*) (((zmq::ctx_t*) ctx_)->create_socket (type_)); +} + +int zmq_close (void *s_) +{ + ((zmq::socket_base_t*) s_)->close (); + return 0; +} + +int zmq_setsockopt (void *s_, int option_, const void *optval_, + size_t optvallen_) +{ + return (((zmq::socket_base_t*) s_)->setsockopt (option_, optval_, + optvallen_)); +} + +int zmq_getsockopt (void *s_, int option_, void *optval_, size_t *optvallen_) +{ + return (((zmq::socket_base_t*) s_)->getsockopt (option_, optval_, + optvallen_)); +} + +int zmq_bind (void *s_, const char *addr_) +{ + return (((zmq::socket_base_t*) s_)->bind (addr_)); +} + +int zmq_connect (void *s_, const char *addr_) +{ + return (((zmq::socket_base_t*) s_)->connect (addr_)); +} + +int zmq_send (void *s_, zmq_msg_t *msg_, int flags_) +{ + return (((zmq::socket_base_t*) s_)->send (msg_, flags_)); +} + +int zmq_recv (void *s_, zmq_msg_t *msg_, int flags_) +{ + return (((zmq::socket_base_t*) s_)->recv (msg_, flags_)); +} + +int zmq_poll (zmq_pollitem_t *items_, int nitems_, long timeout_) +{ +#if defined ZMQ_HAVE_LINUX || defined ZMQ_HAVE_FREEBSD ||\ + defined ZMQ_HAVE_OPENBSD || defined ZMQ_HAVE_SOLARIS ||\ + defined ZMQ_HAVE_OSX || defined ZMQ_HAVE_QNXNTO ||\ + defined ZMQ_HAVE_HPUX || defined ZMQ_HAVE_AIX ||\ + defined ZMQ_HAVE_NETBSD + + pollfd *pollfds = (pollfd*) malloc (nitems_ * sizeof (pollfd)); + zmq_assert (pollfds); + int npollfds = 0; + int nsockets = 0; + + zmq::app_thread_t *app_thread = NULL; + + for (int i = 0; i != nitems_; i++) { + + // 0MQ sockets. + if (items_ [i].socket) { + + // Get the app_thread the socket is living in. If there are two + // sockets in the same pollset with different app threads, fail. + zmq::socket_base_t *s = (zmq::socket_base_t*) items_ [i].socket; + if (app_thread) { + if (app_thread != s->get_thread ()) { + free (pollfds); + errno = EFAULT; + return -1; + } + } + else + app_thread = s->get_thread (); + + nsockets++; + continue; + } + + // Raw file descriptors. + pollfds [npollfds].fd = items_ [i].fd; + pollfds [npollfds].events = + (items_ [i].events & ZMQ_POLLIN ? POLLIN : 0) | + (items_ [i].events & ZMQ_POLLOUT ? POLLOUT : 0); + npollfds++; + } + + // If there's at least one 0MQ socket in the pollset we have to poll + // for 0MQ commands. If ZMQ_POLL was not set, fail. + if (nsockets) { + pollfds [npollfds].fd = app_thread->get_signaler ()->get_fd (); + if (pollfds [npollfds].fd == zmq::retired_fd) { + free (pollfds); + errno = ENOTSUP; + return -1; + } + pollfds [npollfds].events = POLLIN; + npollfds++; + } + + // First iteration just check for events, don't block. Waiting would + // prevent exiting on any events that may already been signaled on + // 0MQ sockets. + int rc = poll (pollfds, npollfds, 0); + if (rc == -1 && errno == EINTR && timeout_ >= 0) { + free (pollfds); + return 0; + } + errno_assert (rc >= 0 || (rc == -1 && errno == EINTR)); + + int timeout = timeout_ > 0 ? timeout_ / 1000 : -1; + int nevents = 0; + + while (true) { + + // Process 0MQ commands if needed. + if (nsockets && pollfds [npollfds -1].revents & POLLIN) + if (!app_thread->process_commands (false, false)) { + free (pollfds); + errno = ETERM; + return -1; + } + + // Check for the events. + int pollfd_pos = 0; + for (int i = 0; i != nitems_; i++) { + + // If the poll item is a raw file descriptor, simply convert + // the events to zmq_pollitem_t-style format. + if (!items_ [i].socket) { + items_ [i].revents = 0; + if (pollfds [pollfd_pos].revents & POLLIN) + items_ [i].revents |= ZMQ_POLLIN; + if (pollfds [pollfd_pos].revents & POLLOUT) + items_ [i].revents |= ZMQ_POLLOUT; + if (pollfds [pollfd_pos].revents & ~(POLLIN | POLLOUT)) + items_ [i].revents |= ZMQ_POLLERR; + + if (items_ [i].revents) + nevents++; + pollfd_pos++; + continue; + } + + // The poll item is a 0MQ socket. + zmq::socket_base_t *s = (zmq::socket_base_t*) items_ [i].socket; + items_ [i].revents = 0; + if ((items_ [i].events & ZMQ_POLLOUT) && s->has_out ()) + items_ [i].revents |= ZMQ_POLLOUT; + if ((items_ [i].events & ZMQ_POLLIN) && s->has_in ()) + items_ [i].revents |= ZMQ_POLLIN; + if (items_ [i].revents) + nevents++; + } + + // If there's at least one event, or if we are asked not to block, + // return immediately. + if (nevents || !timeout_) + break; + + // Wait for events. Ignore interrupts if there's infinite timeout. + while (true) { + rc = poll (pollfds, npollfds, timeout); + if (rc == -1 && errno == EINTR) { + if (timeout_ < 0) + continue; + else { + rc = 0; + break; + } + } + errno_assert (rc >= 0); + break; + } + + // If timeout was hit with no events signaled, return zero. + if (rc == 0) + break; + + // If timeout was already applied, we don't want to poll anymore. + // Setting timeout to zero will cause termination of the function + // once the events we've got are processed. + if (timeout > 0) + timeout = 0; + } + + free (pollfds); + return nevents; + +#elif defined ZMQ_HAVE_WINDOWS || defined ZMQ_HAVE_OPENVMS + + fd_set pollset_in; + FD_ZERO (&pollset_in); + fd_set pollset_out; + FD_ZERO (&pollset_out); + fd_set pollset_err; + FD_ZERO (&pollset_err); + + zmq::app_thread_t *app_thread = NULL; + int nsockets = 0; + zmq::fd_t maxfd = zmq::retired_fd; + zmq::fd_t notify_fd = zmq::retired_fd; + + for (int i = 0; i != nitems_; i++) { + + // 0MQ sockets. + if (items_ [i].socket) { + + // Get the app_thread the socket is living in. If there are two + // sockets in the same pollset with different app threads, fail. + zmq::socket_base_t *s = (zmq::socket_base_t*) items_ [i].socket; + if (app_thread) { + if (app_thread != s->get_thread ()) { + errno = EFAULT; + return -1; + } + } + else + app_thread = s->get_thread (); + + nsockets++; + continue; + } + + // Raw file descriptors. + if (items_ [i].events & ZMQ_POLLIN) + FD_SET (items_ [i].fd, &pollset_in); + if (items_ [i].events & ZMQ_POLLOUT) + FD_SET (items_ [i].fd, &pollset_out); + if (items_ [i].events & ZMQ_POLLERR) + FD_SET (items_ [i].fd, &pollset_err); + if (maxfd == zmq::retired_fd || maxfd < items_ [i].fd) + maxfd = items_ [i].fd; + } + + // If there's at least one 0MQ socket in the pollset we have to poll + // for 0MQ commands. If ZMQ_POLL was not set, fail. + if (nsockets) { + notify_fd = app_thread->get_signaler ()->get_fd (); + if (notify_fd == zmq::retired_fd) { + errno = ENOTSUP; + return -1; + } + FD_SET (notify_fd, &pollset_in); + if (maxfd == zmq::retired_fd || maxfd < notify_fd) + maxfd = notify_fd; + } + + bool block = (timeout_ < 0); + timeval timeout = {timeout_ / 1000000, timeout_ % 1000000}; + timeval zero_timeout = {0, 0}; + int nevents = 0; + + // First iteration just check for events, don't block. Waiting would + // prevent exiting on any events that may already been signaled on + // 0MQ sockets. + fd_set inset, outset, errset; + memcpy (&inset, &pollset_in, sizeof (fd_set)); + memcpy (&outset, &pollset_out, sizeof (fd_set)); + memcpy (&errset, &pollset_err, sizeof (fd_set)); + int rc = select (maxfd, &inset, &outset, &errset, &zero_timeout); +#if defined ZMQ_HAVE_WINDOWS + wsa_assert (rc != SOCKET_ERROR); +#else + if (rc == -1 && errno == EINTR && timeout_ >= 0) + return 0; + errno_assert (rc >= 0 || (rc == -1 && errno == EINTR)); +#endif + + while (true) { + + // Process 0MQ commands if needed. + if (nsockets && FD_ISSET (notify_fd, &inset)) + if (!app_thread->process_commands (false, false)) { + errno = ETERM; + return -1; + } + + // Check for the events. + for (int i = 0; i != nitems_; i++) { + + // If the poll item is a raw file descriptor, simply convert + // the events to zmq_pollitem_t-style format. + if (!items_ [i].socket) { + items_ [i].revents = 0; + if (FD_ISSET (items_ [i].fd, &inset)) + items_ [i].revents |= ZMQ_POLLIN; + if (FD_ISSET (items_ [i].fd, &outset)) + items_ [i].revents |= ZMQ_POLLOUT; + if (FD_ISSET (items_ [i].fd, &errset)) + items_ [i].revents |= ZMQ_POLLERR; + if (items_ [i].revents) + nevents++; + continue; + } + + // The poll item is a 0MQ socket. + zmq::socket_base_t *s = (zmq::socket_base_t*) items_ [i].socket; + items_ [i].revents = 0; + if ((items_ [i].events & ZMQ_POLLOUT) && s->has_out ()) + items_ [i].revents |= ZMQ_POLLOUT; + if ((items_ [i].events & ZMQ_POLLIN) && s->has_in ()) + items_ [i].revents |= ZMQ_POLLIN; + if (items_ [i].revents) + nevents++; + } + + // If there's at least one event, or if we are asked not to block, + // return immediately. + if (nevents || (timeout.tv_sec == 0 && timeout.tv_usec == 0)) + break; + + // Wait for events. Ignore interrupts if there's infinite timeout. + while (true) { + memcpy (&inset, &pollset_in, sizeof (fd_set)); + memcpy (&outset, &pollset_out, sizeof (fd_set)); + memcpy (&errset, &pollset_err, sizeof (fd_set)); + rc = select (maxfd, &inset, &outset, &errset, + block ? NULL : &timeout); +#if defined ZMQ_HAVE_WINDOWS + wsa_assert (rc != SOCKET_ERROR); +#else + if (rc == -1 && errno == EINTR) { + if (timeout_ < 0) + continue; + else { + rc = 0; + break; + } + } + errno_assert (rc >= 0); +#endif + break; + } + + // If timeout was hit with no events signaled, return zero. + if (rc == 0) + break; + + // If timeout was already applied, we don't want to poll anymore. + // Setting timeout to zero will cause termination of the function + // once the events we've got are processed. + if (!block) + timeout = zero_timeout; + } + + return nevents; + +#else + errno = ENOTSUP; + return -1; +#endif +} + +int zmq_errno () +{ + return errno; +} + +int zmq_device (int device_, void *insocket_, void *outsocket_) +{ + switch (device_) { + case ZMQ_FORWARDER: + return zmq::forwarder ((zmq::socket_base_t*) insocket_, + (zmq::socket_base_t*) outsocket_); + case ZMQ_QUEUE: + return zmq::queue ((zmq::socket_base_t*) insocket_, + (zmq::socket_base_t*) outsocket_); + case ZMQ_STREAMER: + return zmq::streamer ((zmq::socket_base_t*) insocket_, + (zmq::socket_base_t*) outsocket_); + default: + return EINVAL; + } +} diff --git a/src/zmq_connecter.cpp b/src/zmq_connecter.cpp new file mode 100644 index 0000000..ebd7572 --- /dev/null +++ b/src/zmq_connecter.cpp @@ -0,0 +1,136 @@ +/* + Copyright (c) 2007-2010 iMatix Corporation + + This file is part of 0MQ. + + 0MQ is free software; you can redistribute it and/or modify it under + the terms of the Lesser GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + 0MQ is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + Lesser GNU General Public License for more details. + + You should have received a copy of the Lesser GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <new> + +#include "zmq_connecter.hpp" +#include "zmq_engine.hpp" +#include "zmq_init.hpp" +#include "io_thread.hpp" +#include "err.hpp" + +zmq::zmq_connecter_t::zmq_connecter_t (io_thread_t *parent_, + socket_base_t *owner_, const options_t &options_, + uint64_t session_ordinal_, bool wait_) : + owned_t (parent_, owner_), + io_object_t (parent_), + handle_valid (false), + wait (wait_), + session_ordinal (session_ordinal_), + options (options_) +{ +} + +zmq::zmq_connecter_t::~zmq_connecter_t () +{ +} + +int zmq::zmq_connecter_t::set_address (const char *protocol_, + const char *address_) +{ + int rc = tcp_connecter.set_address (protocol_, address_); + if (rc != 0) + return rc; + protocol = protocol_; + address = address_; + return 0; +} + +void zmq::zmq_connecter_t::process_plug () +{ + if (wait) + add_timer (); + else + start_connecting (); +} + +void zmq::zmq_connecter_t::process_unplug () +{ + if (wait) + cancel_timer (); + if (handle_valid) + rm_fd (handle); +} + +void zmq::zmq_connecter_t::in_event () +{ + // We are not polling for incomming data, so we are actually called + // because of error here. However, we can get error on out event as well + // on some platforms, so we'll simply handle both events in the same way. + out_event (); +} + +void zmq::zmq_connecter_t::out_event () +{ + fd_t fd = tcp_connecter.connect (); + rm_fd (handle); + handle_valid = false; + + // Handle the error condition by attempt to reconnect. + if (fd == retired_fd) { + tcp_connecter.close (); + wait = true; + add_timer (); + return; + } + + // Create an init object. + zmq_init_t *init = new (std::nothrow) zmq_init_t ( + choose_io_thread (options.affinity), owner, + fd, options, true, protocol.c_str (), address.c_str (), + session_ordinal); + zmq_assert (init); + send_plug (init); + send_own (owner, init); + + // Ask owner socket to shut the connecter down. + term (); +} + +void zmq::zmq_connecter_t::timer_event () +{ + wait = false; + start_connecting (); +} + +void zmq::zmq_connecter_t::start_connecting () +{ + // Open the connecting socket. + int rc = tcp_connecter.open (); + + // Connect may succeed in synchronous manner. + if (rc == 0) { + handle = add_fd (tcp_connecter.get_fd ()); + handle_valid = true; + out_event (); + return; + } + + // Connection establishment may be dealyed. Poll for its completion. + else if (rc == -1 && errno == EAGAIN) { + handle = add_fd (tcp_connecter.get_fd ()); + handle_valid = true; + set_pollout (handle); + return; + } + + // Handle any other error condition by eventual reconnect. + wait = true; + add_timer (); +} diff --git a/src/zmq_connecter.hpp b/src/zmq_connecter.hpp new file mode 100644 index 0000000..328dd6a --- /dev/null +++ b/src/zmq_connecter.hpp @@ -0,0 +1,88 @@ +/* + Copyright (c) 2007-2010 iMatix Corporation + + This file is part of 0MQ. + + 0MQ is free software; you can redistribute it and/or modify it under + the terms of the Lesser GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + 0MQ is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + Lesser GNU General Public License for more details. + + You should have received a copy of the Lesser GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef __ZMQ_ZMQ_CONNECTER_HPP_INCLUDED__ +#define __ZMQ_ZMQ_CONNECTER_HPP_INCLUDED__ + +#include <string> + +#include "owned.hpp" +#include "io_object.hpp" +#include "tcp_connecter.hpp" +#include "options.hpp" +#include "stdint.hpp" + +namespace zmq +{ + + class zmq_connecter_t : public owned_t, public io_object_t + { + public: + + zmq_connecter_t (class io_thread_t *parent_, socket_base_t *owner_, + const options_t &options_, uint64_t session_ordinal_, bool wait_); + ~zmq_connecter_t (); + + // Set address to connect to. + int set_address (const char *protocol_, const char *address_); + + private: + + // Handlers for incoming commands. + void process_plug (); + void process_unplug (); + + // Handlers for I/O events. + void in_event (); + void out_event (); + void timer_event (); + + // Internal function to start the actual connection establishment. + void start_connecting (); + + // Actual connecting socket. + tcp_connecter_t tcp_connecter; + + // Handle corresponding to the listening socket. + handle_t handle; + + // If true file descriptor is registered with the poller and 'handle' + // contains valid value. + bool handle_valid; + + // If true, connecter is waiting a while before trying to connect. + bool wait; + + // Ordinal of the session to attach to. + uint64_t session_ordinal; + + // Associated socket options. + options_t options; + + // Protocol and address to connect to. + std::string protocol; + std::string address; + + zmq_connecter_t (const zmq_connecter_t&); + void operator = (const zmq_connecter_t&); + }; + +} + +#endif diff --git a/src/zmq_decoder.cpp b/src/zmq_decoder.cpp new file mode 100644 index 0000000..8e335c9 --- /dev/null +++ b/src/zmq_decoder.cpp @@ -0,0 +1,107 @@ +/* + Copyright (c) 2007-2010 iMatix Corporation + + This file is part of 0MQ. + + 0MQ is free software; you can redistribute it and/or modify it under + the terms of the Lesser GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + 0MQ is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + Lesser GNU General Public License for more details. + + You should have received a copy of the Lesser GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <stdlib.h> +#include <string.h> + +#include "zmq_decoder.hpp" +#include "i_inout.hpp" +#include "wire.hpp" +#include "err.hpp" + +zmq::zmq_decoder_t::zmq_decoder_t (size_t bufsize_) : + decoder_t <zmq_decoder_t> (bufsize_), + destination (NULL) +{ + zmq_msg_init (&in_progress); + + // At the beginning, read one byte and go to one_byte_size_ready state. + next_step (tmpbuf, 1, &zmq_decoder_t::one_byte_size_ready); +} + +zmq::zmq_decoder_t::~zmq_decoder_t () +{ + zmq_msg_close (&in_progress); +} + +void zmq::zmq_decoder_t::set_inout (i_inout *destination_) +{ + destination = destination_; +} + +bool zmq::zmq_decoder_t::one_byte_size_ready () +{ + // First byte of size is read. If it is 0xff read 8-byte size. + // Otherwise allocate the buffer for message data and read the + // message data into it. + if (*tmpbuf == 0xff) + next_step (tmpbuf, 8, &zmq_decoder_t::eight_byte_size_ready); + else { + + // TODO: Handle over-sized message decently. + + // in_progress is initialised at this point so in theory we should + // close it before calling zmq_msg_init_size, however, it's a 0-byte + // message and thus we can treat it as uninitialised... + int rc = zmq_msg_init_size (&in_progress, *tmpbuf - 1); + errno_assert (rc == 0); + next_step (tmpbuf, 1, &zmq_decoder_t::flags_ready); + } + return true; +} + +bool zmq::zmq_decoder_t::eight_byte_size_ready () +{ + // 8-byte size is read. Allocate the buffer for message body and + // read the message data into it. + size_t size = (size_t) get_uint64 (tmpbuf); + + // TODO: Handle over-sized message decently. + + // in_progress is initialised at this point so in theory we should + // close it before calling zmq_msg_init_size, however, it's a 0-byte + // message and thus we can treat it as uninitialised... + int rc = zmq_msg_init_size (&in_progress, size - 1); + errno_assert (rc == 0); + next_step (tmpbuf, 1, &zmq_decoder_t::flags_ready); + + return true; +} + +bool zmq::zmq_decoder_t::flags_ready () +{ + // Store the flags from the wire into the message structure. + in_progress.flags = tmpbuf [0]; + + next_step (zmq_msg_data (&in_progress), zmq_msg_size (&in_progress), + &zmq_decoder_t::message_ready); + + return true; +} + +bool zmq::zmq_decoder_t::message_ready () +{ + // Message is completely read. Push it further and start reading + // new message. (in_progress is a 0-byte message after this point.) + if (!destination || !destination->write (&in_progress)) + return false; + + next_step (tmpbuf, 1, &zmq_decoder_t::one_byte_size_ready); + return true; +} diff --git a/src/zmq_decoder.hpp b/src/zmq_decoder.hpp new file mode 100644 index 0000000..c1e3e3e --- /dev/null +++ b/src/zmq_decoder.hpp @@ -0,0 +1,59 @@ +/* + Copyright (c) 2007-2010 iMatix Corporation + + This file is part of 0MQ. + + 0MQ is free software; you can redistribute it and/or modify it under + the terms of the Lesser GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + 0MQ is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + Lesser GNU General Public License for more details. + + You should have received a copy of the Lesser GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef __ZMQ_ZMQ_DECODER_HPP_INCLUDED__ +#define __ZMQ_ZMQ_DECODER_HPP_INCLUDED__ + +#include "../include/zmq.h" + +#include "decoder.hpp" +#include "blob.hpp" + +namespace zmq +{ + // Decoder for 0MQ backend protocol. Converts data batches into messages. + + class zmq_decoder_t : public decoder_t <zmq_decoder_t> + { + public: + + zmq_decoder_t (size_t bufsize_); + ~zmq_decoder_t (); + + void set_inout (struct i_inout *destination_); + + private: + + bool one_byte_size_ready (); + bool eight_byte_size_ready (); + bool flags_ready (); + bool message_ready (); + + struct i_inout *destination; + unsigned char tmpbuf [8]; + ::zmq_msg_t in_progress; + + zmq_decoder_t (const zmq_decoder_t&); + void operator = (const zmq_decoder_t&); + }; + +} + +#endif + diff --git a/src/zmq_encoder.cpp b/src/zmq_encoder.cpp new file mode 100644 index 0000000..077286f --- /dev/null +++ b/src/zmq_encoder.cpp @@ -0,0 +1,89 @@ +/* + Copyright (c) 2007-2010 iMatix Corporation + + This file is part of 0MQ. + + 0MQ is free software; you can redistribute it and/or modify it under + the terms of the Lesser GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + 0MQ is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + Lesser GNU General Public License for more details. + + You should have received a copy of the Lesser GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "zmq_encoder.hpp" +#include "i_inout.hpp" +#include "wire.hpp" + +zmq::zmq_encoder_t::zmq_encoder_t (size_t bufsize_) : + encoder_t <zmq_encoder_t> (bufsize_), + source (NULL) +{ + zmq_msg_init (&in_progress); + + // Write 0 bytes to the batch and go to message_ready state. + next_step (NULL, 0, &zmq_encoder_t::message_ready, true); +} + +zmq::zmq_encoder_t::~zmq_encoder_t () +{ + zmq_msg_close (&in_progress); +} + +void zmq::zmq_encoder_t::set_inout (i_inout *source_) +{ + source = source_; +} + +bool zmq::zmq_encoder_t::size_ready () +{ + // Write message body into the buffer. + next_step (zmq_msg_data (&in_progress), zmq_msg_size (&in_progress), + &zmq_encoder_t::message_ready, false); + return true; +} + +bool zmq::zmq_encoder_t::message_ready () +{ + // Destroy content of the old message. + zmq_msg_close(&in_progress); + + // Read new message. If there is none, return false. + // Note that new state is set only if write is successful. That way + // unsuccessful write will cause retry on the next state machine + // invocation. + if (!source || !source->read (&in_progress)) { + zmq_msg_init (&in_progress); + return false; + } + + // Get the message size. + size_t size = zmq_msg_size (&in_progress); + + // Account for the 'flags' byte. + size++; + + // For messages less than 255 bytes long, write one byte of message size. + // For longer messages write 0xff escape character followed by 8-byte + // message size. In both cases 'flags' field follows. + if (size < 255) { + tmpbuf [0] = (unsigned char) size; + tmpbuf [1] = (in_progress.flags & ~ZMQ_MSG_SHARED); + next_step (tmpbuf, 2, &zmq_encoder_t::size_ready, + !(in_progress.flags & ZMQ_MSG_MORE)); + } + else { + tmpbuf [0] = 0xff; + put_uint64 (tmpbuf + 1, size); + tmpbuf [9] = (in_progress.flags & ~ZMQ_MSG_SHARED); + next_step (tmpbuf, 10, &zmq_encoder_t::size_ready, + !(in_progress.flags & ZMQ_MSG_MORE)); + } + return true; +} diff --git a/src/zmq_encoder.hpp b/src/zmq_encoder.hpp new file mode 100644 index 0000000..61899f4 --- /dev/null +++ b/src/zmq_encoder.hpp @@ -0,0 +1,55 @@ +/* + Copyright (c) 2007-2010 iMatix Corporation + + This file is part of 0MQ. + + 0MQ is free software; you can redistribute it and/or modify it under + the terms of the Lesser GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + 0MQ is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + Lesser GNU General Public License for more details. + + You should have received a copy of the Lesser GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef __ZMQ_ZMQ_ENCODER_HPP_INCLUDED__ +#define __ZMQ_ZMQ_ENCODER_HPP_INCLUDED__ + +#include "../include/zmq.h" + +#include "encoder.hpp" + +namespace zmq +{ + // Encoder for 0MQ backend protocol. Converts messages into data batches. + + class zmq_encoder_t : public encoder_t <zmq_encoder_t> + { + public: + + zmq_encoder_t (size_t bufsize_); + ~zmq_encoder_t (); + + void set_inout (struct i_inout *source_); + + private: + + bool size_ready (); + bool message_ready (); + + struct i_inout *source; + ::zmq_msg_t in_progress; + unsigned char tmpbuf [10]; + + zmq_encoder_t (const zmq_encoder_t&); + void operator = (const zmq_encoder_t&); + }; +} + +#endif + diff --git a/src/zmq_engine.cpp b/src/zmq_engine.cpp new file mode 100644 index 0000000..41b10c8 --- /dev/null +++ b/src/zmq_engine.cpp @@ -0,0 +1,195 @@ +/* + Copyright (c) 2007-2010 iMatix Corporation + + This file is part of 0MQ. + + 0MQ is free software; you can redistribute it and/or modify it under + the terms of the Lesser GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + 0MQ is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + Lesser GNU General Public License for more details. + + You should have received a copy of the Lesser GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "platform.hpp" +#if defined ZMQ_HAVE_WINDOWS +#include "windows.hpp" +#endif + +#include <string.h> +#include <new> + +#include "zmq_engine.hpp" +#include "zmq_connecter.hpp" +#include "io_thread.hpp" +#include "i_inout.hpp" +#include "config.hpp" +#include "err.hpp" + +zmq::zmq_engine_t::zmq_engine_t (io_thread_t *parent_, fd_t fd_, + const options_t &options_, bool reconnect_, + const char *protocol_, const char *address_) : + io_object_t (parent_), + inpos (NULL), + insize (0), + decoder (in_batch_size), + outpos (NULL), + outsize (0), + encoder (out_batch_size), + inout (NULL), + options (options_), + reconnect (reconnect_) +{ + if (reconnect) { + protocol = protocol_; + address = address_; + } + + // Initialise the underlying socket. + int rc = tcp_socket.open (fd_, options.sndbuf, options.rcvbuf); + zmq_assert (rc == 0); +} + +zmq::zmq_engine_t::~zmq_engine_t () +{ +} + +void zmq::zmq_engine_t::plug (i_inout *inout_) +{ + zmq_assert (!inout); + + encoder.set_inout (inout_); + decoder.set_inout (inout_); + + handle = add_fd (tcp_socket.get_fd ()); + set_pollin (handle); + set_pollout (handle); + + inout = inout_; + + // Flush all the data that may have been already received downstream. + in_event (); +} + +void zmq::zmq_engine_t::unplug () +{ + rm_fd (handle); + encoder.set_inout (NULL); + decoder.set_inout (NULL); + inout = NULL; +} + +void zmq::zmq_engine_t::in_event () +{ + bool disconnection = false; + + // If there's no data to process in the buffer... + if (!insize) { + + // Retrieve the buffer and read as much data as possible. + decoder.get_buffer (&inpos, &insize); + insize = tcp_socket.read (inpos, insize); + + // Check whether the peer has closed the connection. + if (insize == (size_t) -1) { + insize = 0; + disconnection = true; + } + } + + // Push the data to the decoder. + size_t processed = decoder.process_buffer (inpos, insize); + + // Stop polling for input if we got stuck. + if (processed < insize) { + + // This may happen if queue limits are in effect or when + // init object reads all required information from the socket + // and rejects to read more data. + reset_pollin (handle); + } + + // Adjust the buffer. + inpos += processed; + insize -= processed; + + // Flush all messages the decoder may have produced. + inout->flush (); + + if (disconnection) + error (); +} + +void zmq::zmq_engine_t::out_event () +{ + // If write buffer is empty, try to read new data from the encoder. + if (!outsize) { + + outpos = NULL; + encoder.get_data (&outpos, &outsize); + + // If there is no data to send, stop polling for output. + if (outsize == 0) { + reset_pollout (handle); + return; + } + } + + // If there are any data to write in write buffer, write as much as + // possible to the socket. + int nbytes = tcp_socket.write (outpos, outsize); + + // Handle problems with the connection. + if (nbytes == -1) { + error (); + return; + } + + outpos += nbytes; + outsize -= nbytes; +} + +void zmq::zmq_engine_t::revive () +{ + set_pollout (handle); + + // Speculative write: The assumption is that at the moment new message + // was sent by the user the socket is probably available for writing. + // Thus we try to write the data to socket avoiding polling for POLLOUT. + // Consequently, the latency should be better in request/reply scenarios. + out_event (); +} + +void zmq::zmq_engine_t::resume_input () +{ + set_pollin (handle); + + in_event (); +} + +void zmq::zmq_engine_t::error () +{ + zmq_assert (inout); + + zmq_connecter_t *reconnecter = NULL; + if (reconnect) { + + // Create a connecter object to attempt reconnect. + // Ask it to wait for a while before reconnecting. + reconnecter = new (std::nothrow) zmq_connecter_t ( + inout->get_io_thread (), inout->get_owner (), + options, inout->get_ordinal (), true); + zmq_assert (reconnecter); + reconnecter->set_address (protocol.c_str(), address.c_str ()); + } + + inout->detach (reconnecter); + unplug (); + delete this; +} diff --git a/src/zmq_engine.hpp b/src/zmq_engine.hpp new file mode 100644 index 0000000..d89dccc --- /dev/null +++ b/src/zmq_engine.hpp @@ -0,0 +1,86 @@ +/* + Copyright (c) 2007-2010 iMatix Corporation + + This file is part of 0MQ. + + 0MQ is free software; you can redistribute it and/or modify it under + the terms of the Lesser GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + 0MQ is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + Lesser GNU General Public License for more details. + + You should have received a copy of the Lesser GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef __ZMQ_ZMQ_ENGINE_HPP_INCLUDED__ +#define __ZMQ_ZMQ_ENGINE_HPP_INCLUDED__ + +#include <stddef.h> + +#include <string> + +#include "i_engine.hpp" +#include "io_object.hpp" +#include "tcp_socket.hpp" +#include "zmq_encoder.hpp" +#include "zmq_decoder.hpp" +#include "options.hpp" + +namespace zmq +{ + + class zmq_engine_t : public io_object_t, public i_engine + { + public: + + zmq_engine_t (class io_thread_t *parent_, fd_t fd_, + const options_t &options_, bool reconnect_, + const char *protocol_, const char *address_); + ~zmq_engine_t (); + + // i_engine interface implementation. + void plug (struct i_inout *inout_); + void unplug (); + void revive (); + void resume_input (); + + // i_poll_events interface implementation. + void in_event (); + void out_event (); + + private: + + // Function to handle network disconnections. + void error (); + + tcp_socket_t tcp_socket; + handle_t handle; + + unsigned char *inpos; + size_t insize; + zmq_decoder_t decoder; + + unsigned char *outpos; + size_t outsize; + zmq_encoder_t encoder; + + i_inout *inout; + + options_t options; + + bool reconnect; + std::string protocol; + std::string address; + + zmq_engine_t (const zmq_engine_t&); + void operator = (const zmq_engine_t&); + }; + +} + +#endif diff --git a/src/zmq_init.cpp b/src/zmq_init.cpp new file mode 100644 index 0000000..5824f5c --- /dev/null +++ b/src/zmq_init.cpp @@ -0,0 +1,195 @@ +/* + Copyright (c) 2007-2010 iMatix Corporation + + This file is part of 0MQ. + + 0MQ is free software; you can redistribute it and/or modify it under + the terms of the Lesser GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + 0MQ is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + Lesser GNU General Public License for more details. + + You should have received a copy of the Lesser GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <string.h> + +#include "zmq_init.hpp" +#include "zmq_engine.hpp" +#include "io_thread.hpp" +#include "session.hpp" +#include "uuid.hpp" +#include "err.hpp" + +zmq::zmq_init_t::zmq_init_t (io_thread_t *parent_, socket_base_t *owner_, + fd_t fd_, const options_t &options_, bool reconnect_, + const char *protocol_, const char *address_, uint64_t session_ordinal_) : + owned_t (parent_, owner_), + sent (false), + received (false), + session_ordinal (session_ordinal_), + options (options_) +{ + // Create the engine object for this connection. + engine = new (std::nothrow) zmq_engine_t (parent_, fd_, options, + reconnect_, protocol_, address_); + zmq_assert (engine); +} + +zmq::zmq_init_t::~zmq_init_t () +{ + if (engine) + delete engine; +} + +bool zmq::zmq_init_t::read (::zmq_msg_t *msg_) +{ + // If the identity was already sent, do nothing. + if (sent) + return false; + + // Send the identity. + int rc = zmq_msg_init_size (msg_, options.identity.size ()); + zmq_assert (rc == 0); + memcpy (zmq_msg_data (msg_), options.identity.c_str (), + options.identity.size ()); + sent = true; + + // If initialisation is done, pass the engine to the session and + // destroy the init object. + finalise (); + + return true; +} + +bool zmq::zmq_init_t::write (::zmq_msg_t *msg_) +{ + // If identity was already received, we are not interested + // in subsequent messages. + if (received) + return false; + + // Retreieve the remote identity. If it's empty, generate a unique name. + if (!zmq_msg_size (msg_)) { + unsigned char identity [uuid_t::uuid_blob_len + 1]; + identity [0] = 0; + memcpy (identity + 1, uuid_t ().to_blob (), uuid_t::uuid_blob_len); + peer_identity.assign (identity, uuid_t::uuid_blob_len + 1); + } + else { + peer_identity.assign ((const unsigned char*) zmq_msg_data (msg_), + zmq_msg_size (msg_)); + } + + received = true; + + return true; +} + +void zmq::zmq_init_t::flush () +{ + // Check if there's anything to flush. + if (!received) + return; + + // If initialisation is done, pass the engine to the session and + // destroy the init object. + finalise (); +} + +void zmq::zmq_init_t::detach (owned_t *reconnecter_) +{ + // This function is called by engine when disconnection occurs. + + // If required, launch the reconnecter. + if (reconnecter_) { + send_plug (reconnecter_); + send_own (owner, reconnecter_); + } + + // The engine will destroy itself, so let's just drop the pointer here and + // start termination of the init object. + engine = NULL; + term (); +} + +zmq::io_thread_t *zmq::zmq_init_t::get_io_thread () +{ + return choose_io_thread (options.affinity); +} + +class zmq::socket_base_t *zmq::zmq_init_t::get_owner () +{ + return owner; +} + +uint64_t zmq::zmq_init_t::get_ordinal () +{ + return session_ordinal; +} + +void zmq::zmq_init_t::process_plug () +{ + zmq_assert (engine); + engine->plug (this); +} + +void zmq::zmq_init_t::process_unplug () +{ + if (engine) + engine->unplug (); +} + +void zmq::zmq_init_t::finalise () +{ + if (sent && received) { + + // Disconnect the engine from the init object. + engine->unplug (); + + session_t *session = NULL; + + // If we have the session ordinal, let's use it to find the session. + // If it is not found, it means socket is already being shut down + // and the session have been deallocated. + // TODO: We should check whether the name of the peer haven't changed + // upon reconnection. + if (session_ordinal) { + session = owner->find_session (session_ordinal); + if (!session) { + term (); + return; + } + } + else { + + // If the peer has a unique name, find the associated session. + // If it does not exist, create it. + zmq_assert (!peer_identity.empty ()); + session = owner->find_session (peer_identity); + if (!session) { + session = new (std::nothrow) session_t ( + choose_io_thread (options.affinity), owner, options, + peer_identity); + zmq_assert (session); + send_plug (session); + send_own (owner, session); + + // Reserve a sequence number for following 'attach' command. + session->inc_seqnum (); + } + } + + // No need to increment seqnum as it was already incremented above. + send_attach (session, engine, peer_identity, false); + + // Destroy the init object. + engine = NULL; + term (); + } +} diff --git a/src/zmq_init.hpp b/src/zmq_init.hpp new file mode 100644 index 0000000..6f935c2 --- /dev/null +++ b/src/zmq_init.hpp @@ -0,0 +1,89 @@ +/* + Copyright (c) 2007-2010 iMatix Corporation + + This file is part of 0MQ. + + 0MQ is free software; you can redistribute it and/or modify it under + the terms of the Lesser GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + 0MQ is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + Lesser GNU General Public License for more details. + + You should have received a copy of the Lesser GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef __ZMQ_ZMQ_INIT_HPP_INCLUDED__ +#define __ZMQ_ZMQ_INIT_HPP_INCLUDED__ + +#include "i_inout.hpp" +#include "i_engine.hpp" +#include "owned.hpp" +#include "fd.hpp" +#include "stdint.hpp" +#include "options.hpp" +#include "stdint.hpp" +#include "blob.hpp" + +namespace zmq +{ + + // The class handles initialisation phase of 0MQ wire-level protocol. + + class zmq_init_t : public owned_t, public i_inout + { + public: + + zmq_init_t (class io_thread_t *parent_, socket_base_t *owner_, + fd_t fd_, const options_t &options_, bool reconnect_, + const char *protocol_, const char *address_, + uint64_t session_ordinal_); + ~zmq_init_t (); + + private: + + void finalise (); + + // i_inout interface implementation. + bool read (::zmq_msg_t *msg_); + bool write (::zmq_msg_t *msg_); + void flush (); + void detach (owned_t *reconnecter_); + class io_thread_t *get_io_thread (); + class socket_base_t *get_owner (); + uint64_t get_ordinal (); + + // Handlers for incoming commands. + void process_plug (); + void process_unplug (); + + // Associated wite-protocol engine. + i_engine *engine; + + // True if our own identity was already sent to the peer. + bool sent; + + // True if peer's identity was already received. + bool received; + + // Identity of the peer socket. + blob_t peer_identity; + + // TCP connecter creates session before the name of the peer is known. + // Thus we know only its ordinal number. + uint64_t session_ordinal; + + // Associated socket options. + options_t options; + + zmq_init_t (const zmq_init_t&); + void operator = (const zmq_init_t&); + }; + +} + +#endif diff --git a/src/zmq_listener.cpp b/src/zmq_listener.cpp new file mode 100644 index 0000000..d7cf292 --- /dev/null +++ b/src/zmq_listener.cpp @@ -0,0 +1,75 @@ +/* + Copyright (c) 2007-2010 iMatix Corporation + + This file is part of 0MQ. + + 0MQ is free software; you can redistribute it and/or modify it under + the terms of the Lesser GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + 0MQ is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + Lesser GNU General Public License for more details. + + You should have received a copy of the Lesser GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <new> + +#include "zmq_listener.hpp" +#include "zmq_init.hpp" +#include "io_thread.hpp" +#include "err.hpp" + +zmq::zmq_listener_t::zmq_listener_t (io_thread_t *parent_, + socket_base_t *owner_, const options_t &options_) : + owned_t (parent_, owner_), + io_object_t (parent_), + options (options_) +{ +} + +zmq::zmq_listener_t::~zmq_listener_t () +{ +} + +int zmq::zmq_listener_t::set_address (const char *protocol_, const char *addr_) +{ + return tcp_listener.set_address (protocol_, addr_); +} + +void zmq::zmq_listener_t::process_plug () +{ + // Start polling for incoming connections. + handle = add_fd (tcp_listener.get_fd ()); + set_pollin (handle); +} + +void zmq::zmq_listener_t::process_unplug () +{ + rm_fd (handle); +} + +void zmq::zmq_listener_t::in_event () +{ + fd_t fd = tcp_listener.accept (); + + // If connection was reset by the peer in the meantime, just ignore it. + // TODO: Handle specific errors like ENFILE/EMFILE etc. + if (fd == retired_fd) + return; + + // Create an init object. + io_thread_t *io_thread = choose_io_thread (options.affinity); + zmq_init_t *init = new (std::nothrow) zmq_init_t ( + io_thread, owner, fd, options, false, NULL, NULL, 0); + zmq_assert (init); + send_plug (init); + send_own (owner, init); +} + + + diff --git a/src/zmq_listener.hpp b/src/zmq_listener.hpp new file mode 100644 index 0000000..c990b02 --- /dev/null +++ b/src/zmq_listener.hpp @@ -0,0 +1,67 @@ +/* + Copyright (c) 2007-2010 iMatix Corporation + + This file is part of 0MQ. + + 0MQ is free software; you can redistribute it and/or modify it under + the terms of the Lesser GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + 0MQ is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + Lesser GNU General Public License for more details. + + You should have received a copy of the Lesser GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef __ZMQ_ZMQ_LISTENER_HPP_INCLUDED__ +#define __ZMQ_ZMQ_LISTENER_HPP_INCLUDED__ + +#include "owned.hpp" +#include "io_object.hpp" +#include "tcp_listener.hpp" +#include "options.hpp" +#include "stdint.hpp" + +namespace zmq +{ + + class zmq_listener_t : public owned_t, public io_object_t + { + public: + + zmq_listener_t (class io_thread_t *parent_, socket_base_t *owner_, + const options_t &options_); + ~zmq_listener_t (); + + // Set address to listen on. + int set_address (const char* protocol_, const char *addr_); + + private: + + // Handlers for incoming commands. + void process_plug (); + void process_unplug (); + + // Handlers for I/O events. + void in_event (); + + // Actual listening socket. + tcp_listener_t tcp_listener; + + // Handle corresponding to the listening socket. + handle_t handle; + + // Associated socket options. + options_t options; + + zmq_listener_t (const zmq_listener_t&); + void operator = (const zmq_listener_t&); + }; + +} + +#endif |